From 843f99649ede6cc029e865682cfcf5d34a745c8b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 31 Mar 2025 18:32:22 +0200 Subject: [PATCH 001/183] Scenarios: Prepare DataModel (#213) * Update core.py to work with another dimension * Add scenarios to TimeSeries * Update TimeSeriesCollection * Update get_numeric_stats() to return values per scenario * Update repr and str * Improve stats * Add utility methods to analyze data * Move test insto class * Improve DataConverter * Improve DataConverter * Improve conversion and copying * Improve conversion and copying * Update tests * Update test * Bugfix stats * Bugfix stored_data.setter * Improve __str__ of TimeSeries * Bugfixes * Add tests * Temp * Simplify the TImeSeriesCollection * Simplify the TImeSeriesCollection * Add test script * Improve TImeSeriesAllocator * Update TimeSeries * Update TimeSeries * Update selection * Renaming * Update TimeSeriesAllocator * Update TimeSeriesAllocator * Update TimeSeriesAllocator * Update TimeSeriesAllocator * Update selection * Improve selection * Improve validation of Timesteps * Improve TimeSeries * Improve TimeSeriesAllocator * Update calculation and FlowSystem * rename active_data to selected_data * Add property * Improve type hints * Improve type hints * Add options to get data without extra timestep * Rename * Update tests * Bugfix for TImeSeriesData to work * Update calculation.py * Bugfix * Improve as_dataset to improve aggregation * Bugfix * Update test * Remove test script * ruff check * Revert some renaming * Bugfix in test --- flixopt/calculation.py | 24 +- flixopt/components.py | 24 +- flixopt/core.py | 1354 ++++++++++++++++++++++++----------- flixopt/effects.py | 6 +- flixopt/elements.py | 8 +- flixopt/features.py | 4 +- flixopt/flow_system.py | 24 +- flixopt/io.py | 2 +- flixopt/structure.py | 2 +- tests/test_dataconverter.py | 871 ++++++++++++++++++++-- tests/test_timeseries.py | 716 ++++++++++-------- 11 files changed, 2244 insertions(+), 791 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index c7367cad2..03cf8b9a6 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -183,8 +183,8 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma def _activate_time_series(self): self.flow_system.transform_data() - self.flow_system.time_series_collection.activate_timesteps( - active_timesteps=self.active_timesteps, + self.flow_system.time_series_collection.set_selection( + timesteps=self.active_timesteps ) @@ -217,6 +217,8 @@ def __init__( list with indices, which should be used for calculation. If None, then all timesteps are used. folder: folder where results should be saved. If None, then the current working directory is used. """ + if flow_system.time_series_collection.scenarios is not None: + raise ValueError('Aggregation is not supported for scenarios yet. Please use FullCalculation instead.') super().__init__(name, flow_system, active_timesteps, folder=folder) self.aggregation_parameters = aggregation_parameters self.components_to_clusterize = components_to_clusterize @@ -272,9 +274,9 @@ def _perform_aggregation(self): # Aggregation - creation of aggregated timeseries: self.aggregation = Aggregation( - original_data=self.flow_system.time_series_collection.to_dataframe( - include_extra_timestep=False - ), # Exclude last row (NaN) + original_data=self.flow_system.time_series_collection.as_dataset( + with_extra_timestep=False, with_constants=False + ).to_dataframe(), hours_per_time_step=float(dt_min), hours_per_period=self.aggregation_parameters.hours_per_period, nr_of_periods=self.aggregation_parameters.nr_of_periods, @@ -286,9 +288,11 @@ def _perform_aggregation(self): self.aggregation.cluster() self.aggregation.plot(show=True, save=self.folder / 'aggregation.html') if self.aggregation_parameters.aggregate_data_and_fix_non_binary_vars: - self.flow_system.time_series_collection.insert_new_data( - self.aggregation.aggregated_data, include_extra_timestep=False - ) + for col in self.aggregation.aggregated_data.columns: + data = self.aggregation.aggregated_data[col].values + if col in self.flow_system.time_series_collection._has_extra_timestep: + data = np.append(data, data[-1]) + self.flow_system.time_series_collection.update_time_series(col, data) self.durations['aggregation'] = round(timeit.default_timer() - t_start_agg, 2) @@ -327,8 +331,8 @@ def __init__( self.nr_of_previous_values = nr_of_previous_values self.sub_calculations: List[FullCalculation] = [] - self.all_timesteps = self.flow_system.time_series_collection.all_timesteps - self.all_timesteps_extra = self.flow_system.time_series_collection.all_timesteps_extra + self.all_timesteps = self.flow_system.time_series_collection._full_timesteps + self.all_timesteps_extra = self.flow_system.time_series_collection._full_timesteps_extra self.segment_names = [ f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment)) diff --git a/flixopt/components.py b/flixopt/components.py index d5d1df12d..2a69c6165 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -194,12 +194,12 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: self.relative_minimum_charge_state = flow_system.create_time_series( f'{self.label_full}|relative_minimum_charge_state', self.relative_minimum_charge_state, - needs_extra_timestep=True, + has_extra_timestep=True, ) self.relative_maximum_charge_state = flow_system.create_time_series( f'{self.label_full}|relative_maximum_charge_state', self.relative_maximum_charge_state, - needs_extra_timestep=True, + has_extra_timestep=True, ) self.eta_charge = flow_system.create_time_series(f'{self.label_full}|eta_charge', self.eta_charge) self.eta_discharge = flow_system.create_time_series(f'{self.label_full}|eta_discharge', self.eta_discharge) @@ -342,7 +342,7 @@ def __init__(self, model: SystemModel, element: Transmission): def do_modeling(self): """Initiates all FlowModels""" # Force On Variable if absolute losses are present - if (self.element.absolute_losses is not None) and np.any(self.element.absolute_losses.active_data != 0): + if (self.element.absolute_losses is not None) and np.any(self.element.absolute_losses.selected_data != 0): for flow in self.element.inputs + self.element.outputs: if flow.on_off_parameters is None: flow.on_off_parameters = OnOffParameters() @@ -379,14 +379,14 @@ def create_transmission_equation(self, name: str, in_flow: Flow, out_flow: Flow) # eq: out(t) + on(t)*loss_abs(t) = in(t)*(1 - loss_rel(t)) con_transmission = self.add( self._model.add_constraints( - out_flow.model.flow_rate == -in_flow.model.flow_rate * (self.element.relative_losses.active_data - 1), + out_flow.model.flow_rate == -in_flow.model.flow_rate * (self.element.relative_losses.selected_data - 1), name=f'{self.label_full}|{name}', ), name, ) if self.element.absolute_losses is not None: - con_transmission.lhs += in_flow.model.on_off.on * self.element.absolute_losses.active_data + con_transmission.lhs += in_flow.model.on_off.on * self.element.absolute_losses.selected_data return con_transmission @@ -413,8 +413,8 @@ def do_modeling(self): self.add( self._model.add_constraints( - sum([flow.model.flow_rate * conv_factors[flow.label].active_data for flow in used_inputs]) - == sum([flow.model.flow_rate * conv_factors[flow.label].active_data for flow in used_outputs]), + sum([flow.model.flow_rate * conv_factors[flow.label].selected_data for flow in used_inputs]) + == sum([flow.model.flow_rate * conv_factors[flow.label].selected_data for flow in used_outputs]), name=f'{self.label_full}|conversion_{i}', ) ) @@ -474,12 +474,12 @@ def do_modeling(self): ) charge_state = self.charge_state - rel_loss = self.element.relative_loss_per_hour.active_data + rel_loss = self.element.relative_loss_per_hour.selected_data hours_per_step = self._model.hours_per_step charge_rate = self.element.charging.model.flow_rate discharge_rate = self.element.discharging.model.flow_rate - eff_charge = self.element.eta_charge.active_data - eff_discharge = self.element.eta_discharge.active_data + eff_charge = self.element.eta_charge.selected_data + eff_discharge = self.element.eta_discharge.selected_data self.add( self._model.add_constraints( @@ -565,8 +565,8 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: @property def relative_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: return ( - self.element.relative_minimum_charge_state.active_data, - self.element.relative_maximum_charge_state.active_data, + self.element.relative_minimum_charge_state.selected_data, + self.element.relative_maximum_charge_state.selected_data, ) diff --git a/flixopt/core.py b/flixopt/core.py index 379828554..d2a8edd59 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -7,6 +7,7 @@ import json import logging import pathlib +import textwrap from collections import Counter from typing import Any, Dict, Iterator, List, Literal, Optional, Tuple, Union @@ -40,56 +41,563 @@ class ConversionError(Exception): class DataConverter: """ - Converts various data types into xarray.DataArray with a timesteps index. - - Supports: scalars, arrays, Series, DataFrames, and DataArrays. + Converts various data types into xarray.DataArray with timesteps and optional scenarios dimensions. + + Supports: + - Scalar values (broadcast to all timesteps/scenarios) + - 1D arrays (mapped to timesteps, broadcast to scenarios if provided) + - 2D arrays (mapped to scenarios × timesteps if dimensions match) + - Series with time index (broadcast to scenarios if provided) + - DataFrames with time index and a single column (broadcast to scenarios if provided) + - Series/DataFrames with MultiIndex (scenario, time) + - Existing DataArrays """ + #TODO: Allow DataFrame with scenarios as columns + @staticmethod - def as_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray: - """Convert data to xarray.DataArray with specified timesteps index.""" - if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: - raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') - if not timesteps.name == 'time': - raise ConversionError(f'DatetimeIndex is not named correctly. Must be named "time", got {timesteps.name=}') + def as_dataarray( + data: NumericData, timesteps: pd.DatetimeIndex, scenarios: Optional[pd.Index] = None + ) -> xr.DataArray: + """ + Convert data to xarray.DataArray with specified timesteps and optional scenarios dimensions. + + Args: + data: The data to convert (scalar, array, Series, DataFrame, or DataArray) + timesteps: DatetimeIndex representing the time dimension (must be named 'time') + scenarios: Optional Index representing scenarios (must be named 'scenario') + + Returns: + DataArray with the converted data + + Raises: + ValueError: If timesteps or scenarios are invalid + ConversionError: If the data cannot be converted to the expected dimensions + """ + # Validate inputs + DataConverter._validate_timesteps(timesteps) + if scenarios is not None: + DataConverter._validate_scenarios(scenarios) - coords = [timesteps] - dims = ['time'] - expected_shape = (len(timesteps),) + # Determine dimensions and coordinates + coords, dims, expected_shape = DataConverter._get_dimensions(timesteps, scenarios) try: + # Convert different data types using specialized methods if isinstance(data, (int, float, np.integer, np.floating)): - return xr.DataArray(data, coords=coords, dims=dims) + return DataConverter._convert_scalar(data, coords, dims) + elif isinstance(data, pd.DataFrame): - if not data.index.equals(timesteps): - raise ConversionError("DataFrame index doesn't match timesteps index") - if not len(data.columns) == 1: - raise ConversionError('DataFrame must have exactly one column') - return xr.DataArray(data.values.flatten(), coords=coords, dims=dims) + return DataConverter._convert_dataframe(data, timesteps, scenarios, coords, dims) + elif isinstance(data, pd.Series): - if not data.index.equals(timesteps): - raise ConversionError("Series index doesn't match timesteps index") - return xr.DataArray(data.values, coords=coords, dims=dims) + return DataConverter._convert_series(data, timesteps, scenarios, coords, dims) + elif isinstance(data, np.ndarray): - if data.ndim != 1: - raise ConversionError(f'Array must be 1-dimensional, got {data.ndim}') - elif data.shape[0] != expected_shape[0]: - raise ConversionError(f"Array shape {data.shape} doesn't match expected {expected_shape}") - return xr.DataArray(data, coords=coords, dims=dims) + return DataConverter._convert_ndarray(data, timesteps, scenarios, coords, dims, expected_shape) + elif isinstance(data, xr.DataArray): - if data.dims != tuple(dims): - raise ConversionError(f"DataArray dimensions {data.dims} don't match expected {dims}") - if data.sizes[dims[0]] != len(coords[0]): - raise ConversionError( - f"DataArray length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" - ) - return data.copy(deep=True) + return DataConverter._convert_dataarray(data, timesteps, scenarios, coords, dims) + else: raise ConversionError(f'Unsupported type: {type(data).__name__}') + except Exception as e: if isinstance(e, ConversionError): raise - raise ConversionError(f'Converting data {type(data)} to xarray.Dataset raised an error: {str(e)}') from e + raise ConversionError(f'Converting {type(data)} to DataArray raised an error: {str(e)}') from e + + @staticmethod + def _validate_timesteps(timesteps: pd.DatetimeIndex) -> None: + """ + Validate that timesteps is a properly named non-empty DatetimeIndex. + + Args: + timesteps: The DatetimeIndex to validate + + Raises: + ValueError: If timesteps is not a non-empty DatetimeIndex + ConversionError: If timesteps is not named 'time' + """ + if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: + raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') + if timesteps.name != 'time': + raise ConversionError(f'DatetimeIndex must be named "time", got {timesteps.name=}') + + @staticmethod + def _validate_scenarios(scenarios: pd.Index) -> None: + """ + Validate that scenarios is a properly named non-empty Index. + + Args: + scenarios: The Index to validate + + Raises: + ValueError: If scenarios is not a non-empty Index + ConversionError: If scenarios is not named 'scenario' + """ + if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: + raise ValueError(f'Scenarios must be a non-empty Index, got {type(scenarios).__name__}') + if scenarios.name != 'scenario': + raise ConversionError(f'Scenarios Index must be named "scenario", got {scenarios.name=}') + + @staticmethod + def _get_dimensions( + timesteps: pd.DatetimeIndex, scenarios: Optional[pd.Index] = None + ) -> Tuple[Dict[str, pd.Index], Tuple[str, ...], Tuple[int, ...]]: + """ + Create the coordinates, dimensions, and expected shape for the output DataArray. + + Args: + timesteps: The time index + scenarios: Optional scenario index + + Returns: + Tuple containing: + - Dict mapping dimension names to coordinate indexes + - Tuple of dimension names + - Tuple of expected shape + """ + if scenarios is not None: + coords = {'scenario': scenarios, 'time': timesteps} + dims = ('scenario', 'time') + expected_shape = (len(scenarios), len(timesteps)) + else: + coords = {'time': timesteps} + dims = ('time',) + expected_shape = (len(timesteps),) + + return coords, dims, expected_shape + + @staticmethod + def _convert_scalar( + data: Union[int, float, np.integer, np.floating], coords: Dict[str, pd.Index], dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Convert a scalar value to a DataArray. + + Args: + data: The scalar value to convert + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray with the scalar value broadcast to all coordinates + """ + return xr.DataArray(data, coords=coords, dims=dims) + + @staticmethod + def _convert_dataframe( + df: pd.DataFrame, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a DataFrame to a DataArray. + + Args: + df: The DataFrame to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the DataFrame + + Raises: + ConversionError: If the DataFrame cannot be converted to the expected dimensions + """ + # Case 1: DataFrame with MultiIndex (scenario, time) + if ( + isinstance(df.index, pd.MultiIndex) + and len(df.index.names) == 2 + and 'scenario' in df.index.names + and 'time' in df.index.names + and scenarios is not None + ): + return DataConverter._convert_multi_index_dataframe(df, timesteps, scenarios, coords, dims) + + # Case 2: Standard DataFrame with time index + elif not isinstance(df.index, pd.MultiIndex): + return DataConverter._convert_standard_dataframe(df, timesteps, scenarios, coords, dims) + + else: + raise ConversionError(f'Unsupported DataFrame index structure: {df}') + + @staticmethod + def _convert_multi_index_dataframe( + df: pd.DataFrame, + timesteps: pd.DatetimeIndex, + scenarios: pd.Index, + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a DataFrame with MultiIndex (scenario, time) to a DataArray. + + Args: + df: The DataFrame with MultiIndex to convert + timesteps: The time index + scenarios: The scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the MultiIndex DataFrame + + Raises: + ConversionError: If the DataFrame's index doesn't match expected or has multiple columns + """ + # Validate that the index contains the expected values + if not set(df.index.get_level_values('time')).issubset(set(timesteps)): + raise ConversionError("DataFrame time index doesn't match or isn't a subset of timesteps") + if not set(df.index.get_level_values('scenario')).issubset(set(scenarios)): + raise ConversionError("DataFrame scenario index doesn't match or isn't a subset of scenarios") + + # Ensure single column + if len(df.columns) != 1: + raise ConversionError('DataFrame must have exactly one column') + + # Reindex to ensure complete coverage and correct order + multi_idx = pd.MultiIndex.from_product([scenarios, timesteps], names=['scenario', 'time']) + reindexed = df.reindex(multi_idx).iloc[:, 0] + + # Reshape to 2D array + reshaped = reindexed.values.reshape(len(scenarios), len(timesteps)) + return xr.DataArray(reshaped, coords=coords, dims=dims) + + @staticmethod + def _convert_standard_dataframe( + df: pd.DataFrame, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a standard DataFrame with time index to a DataArray. + + Args: + df: The DataFrame to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the DataFrame + + Raises: + ConversionError: If the DataFrame's index doesn't match timesteps or has multiple columns + """ + if not df.index.equals(timesteps): + raise ConversionError("DataFrame index doesn't match timesteps index") + if len(df.columns) != 1: + raise ConversionError('DataFrame must have exactly one column') + + # Get values + values = df.values.flatten() + + if scenarios is not None: + # Broadcast to scenarios dimension + values = np.tile(values, (len(scenarios), 1)) + + return xr.DataArray(values, coords=coords, dims=dims) + + @staticmethod + def _convert_series( + series: pd.Series, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a Series to a DataArray. + + Args: + series: The Series to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the Series + + Raises: + ConversionError: If the Series cannot be converted to the expected dimensions + """ + # Case 1: Series with MultiIndex (scenario, time) + if ( + isinstance(series.index, pd.MultiIndex) + and len(series.index.names) == 2 + and 'scenario' in series.index.names + and 'time' in series.index.names + and scenarios is not None + ): + return DataConverter._convert_multi_index_series(series, timesteps, scenarios, coords, dims) + + # Case 2: Standard Series with time index + elif not isinstance(series.index, pd.MultiIndex): + return DataConverter._convert_standard_series(series, timesteps, scenarios, coords, dims) + + else: + raise ConversionError('Unsupported Series index structure') + + @staticmethod + def _convert_multi_index_series( + series: pd.Series, + timesteps: pd.DatetimeIndex, + scenarios: pd.Index, + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a Series with MultiIndex (scenario, time) to a DataArray. + + Args: + series: The Series with MultiIndex to convert + timesteps: The time index + scenarios: The scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the MultiIndex Series + + Raises: + ConversionError: If the Series' index doesn't match expected + """ + # Validate that the index contains the expected values + if not set(series.index.get_level_values('time')).issubset(set(timesteps)): + raise ConversionError("Series time index doesn't match or isn't a subset of timesteps") + if not set(series.index.get_level_values('scenario')).issubset(set(scenarios)): + raise ConversionError("Series scenario index doesn't match or isn't a subset of scenarios") + + # Reindex to ensure complete coverage and correct order + multi_idx = pd.MultiIndex.from_product([scenarios, timesteps], names=['scenario', 'time']) + reindexed = series.reindex(multi_idx) + + # Reshape to 2D array + reshaped = reindexed.values.reshape(len(scenarios), len(timesteps)) + return xr.DataArray(reshaped, coords=coords, dims=dims) + + @staticmethod + def _convert_standard_series( + series: pd.Series, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert a standard Series with time index to a DataArray. + + Args: + series: The Series to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the Series + + Raises: + ConversionError: If the Series' index doesn't match timesteps + """ + if not series.index.equals(timesteps): + raise ConversionError("Series index doesn't match timesteps index") + + # Get values + values = series.values + + if scenarios is not None: + # Broadcast to scenarios dimension + values = np.tile(values, (len(scenarios), 1)) + + return xr.DataArray(values, coords=coords, dims=dims) + + @staticmethod + def _convert_ndarray( + arr: np.ndarray, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + expected_shape: Tuple[int, ...], + ) -> xr.DataArray: + """ + Convert a numpy array to a DataArray. + + Args: + arr: The numpy array to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + expected_shape: Expected shape of the resulting array + + Returns: + DataArray created from the numpy array + + Raises: + ConversionError: If the array cannot be converted to the expected dimensions + """ + # Case 1: With scenarios - array can be 1D or 2D + if scenarios is not None: + return DataConverter._convert_ndarray_with_scenarios( + arr, timesteps, scenarios, coords, dims, expected_shape + ) + + # Case 2: Without scenarios - array must be 1D + else: + return DataConverter._convert_ndarray_without_scenarios(arr, timesteps, coords, dims) + + @staticmethod + def _convert_ndarray_with_scenarios( + arr: np.ndarray, + timesteps: pd.DatetimeIndex, + scenarios: pd.Index, + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + expected_shape: Tuple[int, ...], + ) -> xr.DataArray: + """ + Convert a numpy array to a DataArray with scenarios dimension. + + Args: + arr: The numpy array to convert + timesteps: The time index + scenarios: The scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + expected_shape: Expected shape (scenarios, timesteps) + + Returns: + DataArray created from the numpy array + + Raises: + ConversionError: If the array dimensions don't match expected + """ + if arr.ndim == 1: + # 1D array should match timesteps and be broadcast to scenarios + if arr.shape[0] != len(timesteps): + raise ConversionError(f"1D array length {arr.shape[0]} doesn't match timesteps length {len(timesteps)}") + # Broadcast to scenarios + values = np.tile(arr, (len(scenarios), 1)) + return xr.DataArray(values, coords=coords, dims=dims) + + elif arr.ndim == 2: + # 2D array should match (scenarios, timesteps) + if arr.shape != expected_shape: + raise ConversionError(f"2D array shape {arr.shape} doesn't match expected shape {expected_shape}") + return xr.DataArray(arr, coords=coords, dims=dims) + + else: + raise ConversionError(f'Array must be 1D or 2D, got {arr.ndim}D') + + @staticmethod + def _convert_ndarray_without_scenarios( + arr: np.ndarray, timesteps: pd.DatetimeIndex, coords: Dict[str, pd.Index], dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Convert a numpy array to a DataArray without scenarios dimension. + + Args: + arr: The numpy array to convert + timesteps: The time index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray created from the numpy array + + Raises: + ConversionError: If the array isn't 1D or doesn't match timesteps length + """ + if arr.ndim != 1: + raise ConversionError(f'Without scenarios, array must be 1D, got {arr.ndim}D') + if arr.shape[0] != len(timesteps): + raise ConversionError(f"Array shape {arr.shape} doesn't match expected length {len(timesteps)}") + return xr.DataArray(arr, coords=coords, dims=dims) + + @staticmethod + def _convert_dataarray( + da: xr.DataArray, + timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index], + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Convert an existing DataArray to a new DataArray with the desired dimensions. + + Args: + da: The DataArray to convert + timesteps: The time index + scenarios: Optional scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + New DataArray with the specified coordinates and dimensions + + Raises: + ConversionError: If the DataArray dimensions don't match expected + """ + # Case 1: DataArray with only time dimension when scenarios are provided + if scenarios is not None and set(da.dims) == {'time'}: + return DataConverter._broadcast_time_only_dataarray(da, timesteps, scenarios, coords, dims) + + # Case 2: DataArray dimensions should match expected + elif set(da.dims) != set(dims): + raise ConversionError(f"DataArray dimensions {da.dims} don't match expected {dims}") + + # Validate dimensions sizes + for dim in dims: + if not np.array_equal(da.coords[dim].values, coords[dim].values): + raise ConversionError(f"DataArray dimension '{dim}' doesn't match expected {coords[dim]}") + + # Create a new DataArray with our coordinates to ensure consistency + result = xr.DataArray(da.values.copy(), coords=coords, dims=dims) + return result + + @staticmethod + def _broadcast_time_only_dataarray( + da: xr.DataArray, + timesteps: pd.DatetimeIndex, + scenarios: pd.Index, + coords: Dict[str, pd.Index], + dims: Tuple[str, ...], + ) -> xr.DataArray: + """ + Broadcast a time-only DataArray to include the scenarios dimension. + + Args: + da: The DataArray with only time dimension + timesteps: The time index + scenarios: The scenario index + coords: Dictionary mapping dimension names to coordinate indexes + dims: Tuple of dimension names + + Returns: + DataArray with the data broadcast to include scenarios dimension + + Raises: + ConversionError: If the DataArray time coordinates aren't compatible with timesteps + """ + # Ensure the time dimension is compatible + if not np.array_equal(da.coords['time'].values, timesteps.values): + raise ConversionError("DataArray time coordinates aren't compatible with timesteps") + + # Broadcast to scenarios + values = np.tile(da.values.copy(), (len(scenarios), 1)) + return xr.DataArray(values, coords=coords, dims=dims) class TimeSeriesData: @@ -146,18 +654,19 @@ class TimeSeries: name (str): The name of the time series aggregation_weight (Optional[float]): Weight used for aggregation aggregation_group (Optional[str]): Group name for shared aggregation weighting - needs_extra_timestep (bool): Whether this series needs an extra timestep + has_extra_timestep (bool): Whether this series needs an extra timestep """ @classmethod def from_datasource( cls, - data: NumericData, + data: NumericDataTS, name: str, timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index] = None, aggregation_weight: Optional[float] = None, aggregation_group: Optional[str] = None, - needs_extra_timestep: bool = False, + has_extra_timestep: bool = False, ) -> 'TimeSeries': """ Initialize the TimeSeries from multiple data sources. @@ -166,19 +675,20 @@ def from_datasource( data: The time series data name: The name of the TimeSeries timesteps: The timesteps of the TimeSeries + scenarios: The scenarios of the TimeSeries aggregation_weight: The weight in aggregation calculations aggregation_group: Group this TimeSeries belongs to for aggregation weight sharing - needs_extra_timestep: Whether this series requires an extra timestep + has_extra_timestep: Whether this series requires an extra timestep Returns: A new TimeSeries instance """ return cls( - DataConverter.as_dataarray(data, timesteps), + DataConverter.as_dataarray(data, timesteps, scenarios), name, aggregation_weight, aggregation_group, - needs_extra_timestep, + has_extra_timestep, ) @classmethod @@ -212,7 +722,7 @@ def from_json(cls, data: Optional[Dict[str, Any]] = None, path: Optional[str] = name=data['name'], aggregation_weight=data['aggregation_weight'], aggregation_group=data['aggregation_group'], - needs_extra_timestep=data['needs_extra_timestep'], + has_extra_timestep=data['has_extra_timestep'], ) def __init__( @@ -221,7 +731,7 @@ def __init__( name: str, aggregation_weight: Optional[float] = None, aggregation_group: Optional[str] = None, - needs_extra_timestep: bool = False, + has_extra_timestep: bool = False, ): """ Initialize a TimeSeries with a DataArray. @@ -231,35 +741,42 @@ def __init__( name: The name of the TimeSeries aggregation_weight: The weight in aggregation calculations aggregation_group: Group this TimeSeries belongs to for weight sharing - needs_extra_timestep: Whether this series requires an extra timestep + has_extra_timestep: Whether this series requires an extra timestep Raises: - ValueError: If data doesn't have a 'time' index or has more than 1 dimension + ValueError: If data doesn't have a 'time' index or has unsupported dimensions """ if 'time' not in data.indexes: raise ValueError(f'DataArray must have a "time" index. Got {data.indexes}') - if data.ndim > 1: - raise ValueError(f'Number of dimensions of DataArray must be 1. Got {data.ndim}') + + allowed_dims = {'time', 'scenario'} + if not set(data.dims).issubset(allowed_dims): + raise ValueError(f'DataArray dimensions must be subset of {allowed_dims}. Got {data.dims}') self.name = name self.aggregation_weight = aggregation_weight self.aggregation_group = aggregation_group - self.needs_extra_timestep = needs_extra_timestep + self.has_extra_timestep = has_extra_timestep # Data management self._stored_data = data.copy(deep=True) self._backup = self._stored_data.copy(deep=True) - self._active_timesteps = self._stored_data.indexes['time'] - self._active_data = None - self._update_active_data() - def reset(self): + # Selection state + self._selected_timesteps: Optional[pd.DatetimeIndex] = None + self._selected_scenarios: Optional[pd.Index] = None + + # Flag for whether this series has scenarios + self._has_scenarios = 'scenario' in data.dims + + def reset(self) -> None: """ - Reset active timesteps to the full set of stored timesteps. + Reset selections to include all timesteps and scenarios. + This is equivalent to clearing all selections. """ - self.active_timesteps = None + self.clear_selection() - def restore_data(self): + def restore_data(self) -> None: """ Restore stored_data from the backup and reset active timesteps. """ @@ -280,8 +797,8 @@ def to_json(self, path: Optional[pathlib.Path] = None) -> Dict[str, Any]: 'name': self.name, 'aggregation_weight': self.aggregation_weight, 'aggregation_group': self.aggregation_group, - 'needs_extra_timestep': self.needs_extra_timestep, - 'data': self.active_data.to_dict(), + 'has_extra_timestep': self.has_extra_timestep, + 'data': self.selected_data.to_dict(), } # Convert datetime objects to ISO strings @@ -303,84 +820,100 @@ def stats(self) -> str: Returns: String representation of data statistics """ - return get_numeric_stats(self.active_data, padd=0) - - def _update_active_data(self): - """ - Update the active data based on active_timesteps. - """ - self._active_data = self._stored_data.sel(time=self.active_timesteps) + return get_numeric_stats(self.selected_data, padd=0, by_scenario=True) @property def all_equal(self) -> bool: """Check if all values in the series are equal.""" - return np.unique(self.active_data.values).size == 1 + return np.unique(self.selected_data.values).size == 1 @property - def active_timesteps(self) -> pd.DatetimeIndex: - """Get the current active timesteps.""" - return self._active_timesteps - - @active_timesteps.setter - def active_timesteps(self, timesteps: Optional[pd.DatetimeIndex]): + def selected_data(self) -> xr.DataArray: """ - Set active_timesteps and refresh active_data. - - Args: - timesteps: New timesteps to activate, or None to use all stored timesteps - - Raises: - TypeError: If timesteps is not a pandas DatetimeIndex or None + Get a view of stored_data based on current selections. + This computes the view dynamically based on the current selection state. """ - if timesteps is None: - self._active_timesteps = self.stored_data.indexes['time'] - elif isinstance(timesteps, pd.DatetimeIndex): - self._active_timesteps = timesteps - else: - raise TypeError('active_timesteps must be a pandas DatetimeIndex or None') + return self._stored_data.sel(**self._valid_selector) - self._update_active_data() + @property + def active_timesteps(self) -> pd.DatetimeIndex: + """Get the current active timesteps.""" + if self._selected_timesteps is None: + return self._stored_data.indexes['time'] + return self._selected_timesteps @property - def active_data(self) -> xr.DataArray: - """Get a view of stored_data based on active_timesteps.""" - return self._active_data + def active_scenarios(self) -> Optional[pd.Index]: + """Get the current active scenarios.""" + if not self._has_scenarios: + return None + if self._selected_scenarios is None: + return self._stored_data.indexes['scenario'] + return self._selected_scenarios @property def stored_data(self) -> xr.DataArray: """Get a copy of the full stored data.""" return self._stored_data.copy() - @stored_data.setter - def stored_data(self, value: NumericData): + def update_stored_data(self, value: xr.DataArray) -> None: """ - Update stored_data and refresh active_data. + Update stored_data and refresh selected_data. Args: value: New data to store """ - new_data = DataConverter.as_dataarray(value, timesteps=self.active_timesteps) + new_data = DataConverter.as_dataarray( + value, + timesteps=self.active_timesteps, + scenarios=self.active_scenarios if self._has_scenarios else None + ) # Skip if data is unchanged to avoid overwriting backup if new_data.equals(self._stored_data): return self._stored_data = new_data - self.active_timesteps = None # Reset to full timeline + self.clear_selection() # Reset selections to full dataset + + def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> None: + if timesteps: + self._selected_timesteps = None + if scenarios: + self._selected_scenarios = None + + def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: + if timesteps is None: + self.clear_selection(timesteps=True, scenarios=False) + else: + self._selected_timesteps = timesteps + + if scenarios is None: + self.clear_selection(timesteps=False, scenarios=True) + else: + self._selected_scenarios = scenarios @property def sel(self): - return self.active_data.sel + """Direct access to the selected_data's sel method for convenience.""" + return self.selected_data.sel @property def isel(self): - return self.active_data.isel + """Direct access to the selected_data's isel method for convenience.""" + return self.selected_data.isel + + @property + def _valid_selector(self) -> Dict[str, pd.Index]: + """Get the current selection as a dictionary.""" + full_selection = {'time': self._selected_timesteps, 'scenario': self._selected_scenarios} + return {dim: sel for dim, sel in full_selection.items() if dim in self._stored_data.dims and sel is not None} def _apply_operation(self, other, op): """Apply an operation between this TimeSeries and another object.""" if isinstance(other, TimeSeries): - other = other.active_data - return op(self.active_data, other) + other = other.selected_data + return op(self.selected_data, other) def __add__(self, other): return self._apply_operation(other, lambda x, y: x + y) @@ -395,25 +928,25 @@ def __truediv__(self, other): return self._apply_operation(other, lambda x, y: x / y) def __radd__(self, other): - return other + self.active_data + return other + self.selected_data def __rsub__(self, other): - return other - self.active_data + return other - self.selected_data def __rmul__(self, other): - return other * self.active_data + return other * self.selected_data def __rtruediv__(self, other): - return other / self.active_data + return other / self.selected_data def __neg__(self) -> xr.DataArray: - return -self.active_data + return -self.selected_data def __pos__(self) -> xr.DataArray: - return +self.active_data + return +self.selected_data def __abs__(self) -> xr.DataArray: - return abs(self.active_data) + return abs(self.selected_data) def __gt__(self, other): """ @@ -426,7 +959,7 @@ def __gt__(self, other): True if all values in this TimeSeries are greater than other """ if isinstance(other, TimeSeries): - return (self.active_data > other.active_data).all().item() + return (self.selected_data > other.selected_data).all().item() return NotImplemented def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): @@ -435,8 +968,8 @@ def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): This allows NumPy functions to work with TimeSeries objects. """ - # Convert any TimeSeries inputs to their active_data - inputs = [x.active_data if isinstance(x, TimeSeries) else x for x in inputs] + # Convert any TimeSeries inputs to their selected_data + inputs = [x.selected_data if isinstance(x, TimeSeries) else x for x in inputs] return getattr(ufunc, method)(*inputs, **kwargs) def __repr__(self): @@ -450,10 +983,10 @@ def __repr__(self): 'name': self.name, 'aggregation_weight': self.aggregation_weight, 'aggregation_group': self.aggregation_group, - 'needs_extra_timestep': self.needs_extra_timestep, - 'shape': self.active_data.shape, - 'time_range': f'{self.active_timesteps[0]} to {self.active_timesteps[-1]}', + 'has_extra_timestep': self.has_extra_timestep, + 'shape': self.selected_data.shape, } + attr_str = ', '.join(f'{k}={repr(v)}' for k, v in attrs.items()) return f'TimeSeries({attr_str})' @@ -464,281 +997,329 @@ def __str__(self): Returns: Descriptive string with statistics """ - return f"TimeSeries '{self.name}': {self.stats}" + return f'TimeSeries "{self.name}":\n{textwrap.indent(self.stats, " ")}' class TimeSeriesCollection: """ - Collection of TimeSeries objects with shared timestep management. + Simplified central manager for time series data with reference tracking. - TimeSeriesCollection handles multiple TimeSeries objects with synchronized - timesteps, provides operations on collections, and manages extra timesteps. + Provides a way to store time series data and work with subsets of dimensions + that automatically update all references when changed. """ - def __init__( self, timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index] = None, hours_of_last_timestep: Optional[float] = None, hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] = None, ): - """ - Args: - timesteps: The timesteps of the Collection. - hours_of_last_timestep: The duration of the last time step. Uses the last time interval if not specified - hours_of_previous_timesteps: The duration of previous timesteps. - If None, the first time increment of time_series is used. - This is needed to calculate previous durations (for example consecutive_on_hours). - If you use an array, take care that its long enough to cover all previous values! - """ - # Prepare and validate timesteps + """Initialize a TimeSeriesCollection.""" self._validate_timesteps(timesteps) self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps - ) + ) #TODO: Make dynamic - # Set up timesteps and hours - self.all_timesteps = timesteps - self.all_timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - self.all_hours_per_timestep = self.calculate_hours_per_timestep(self.all_timesteps_extra) + self._full_timesteps = timesteps + self._full_timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) + self._full_hours_per_timestep = self.calculate_hours_per_timestep(self._full_timesteps_extra) - # Active timestep tracking - self._active_timesteps = None - self._active_timesteps_extra = None - self._active_hours_per_timestep = None + self._full_scenarios = scenarios - # Dictionary of time series by name - self.time_series_data: Dict[str, TimeSeries] = {} + # Series that need extra timestep + self._has_extra_timestep: set = set() - # Aggregation - self.group_weights: Dict[str, float] = {} - self.weights: Dict[str, float] = {} + # Storage for TimeSeries objects + self._time_series: Dict[str, TimeSeries] = {} - @classmethod - def with_uniform_timesteps( - cls, start_time: pd.Timestamp, periods: int, freq: str, hours_per_step: Optional[float] = None - ) -> 'TimeSeriesCollection': - """Create a collection with uniform timesteps.""" - timesteps = pd.date_range(start_time, periods=periods, freq=freq, name='time') - return cls(timesteps, hours_of_previous_timesteps=hours_per_step) - - def create_time_series( - self, data: Union[NumericData, TimeSeriesData], name: str, needs_extra_timestep: bool = False + # Active subset selectors + self._selected_timesteps: Optional[pd.DatetimeIndex] = None + self._selected_scenarios: Optional[pd.Index] = None + self._selected_timesteps_extra: Optional[pd.DatetimeIndex] = None + self._selected_hours_per_timestep: Optional[xr.DataArray] = None + + def add_time_series( + self, + name: str, + data: Union[NumericDataTS, TimeSeries], + aggregation_weight: Optional[float] = None, + aggregation_group: Optional[str] = None, + has_extra_timestep: bool = False, ) -> TimeSeries: """ - Creates a TimeSeries from the given data and adds it to the collection. + Add a new TimeSeries to the allocator. Args: - data: The data to create the TimeSeries from. - name: The name of the TimeSeries. - needs_extra_timestep: Whether to create an additional timestep at the end of the timesteps. - The data to create the TimeSeries from. + name: Name of the time series + data: Data for the time series (can be raw data or an existing TimeSeries) + aggregation_weight: Weight used for aggregation + aggregation_group: Group name for shared aggregation weighting + has_extra_timestep: Whether this series needs an extra timestep Returns: - The created TimeSeries. - + The created TimeSeries object """ - # Check for duplicate name - if name in self.time_series_data: - raise ValueError(f"TimeSeries '{name}' already exists in this collection") - - # Determine which timesteps to use - timesteps_to_use = self.timesteps_extra if needs_extra_timestep else self.timesteps - - # Create the time series - if isinstance(data, TimeSeriesData): - time_series = TimeSeries.from_datasource( + if name in self._time_series: + raise KeyError(f"TimeSeries '{name}' already exists in allocator") + + # Choose which timesteps to use + target_timesteps = self.timesteps_extra if has_extra_timestep else self.timesteps + + # Create or adapt the TimeSeries object + if isinstance(data, TimeSeries): + # Use the existing TimeSeries but update its parameters + time_series = data + # Update the stored data to use our timesteps and scenarios + data_array = DataConverter.as_dataarray( + time_series.stored_data, timesteps=target_timesteps, scenarios=self.scenarios + ) + time_series = TimeSeries( + data=data_array, name=name, - data=data.data, - timesteps=timesteps_to_use, - aggregation_weight=data.agg_weight, - aggregation_group=data.agg_group, - needs_extra_timestep=needs_extra_timestep, + aggregation_weight=aggregation_weight or time_series.aggregation_weight, + aggregation_group=aggregation_group or time_series.aggregation_group, + has_extra_timestep=has_extra_timestep or time_series.has_extra_timestep, ) - # Connect the user time series to the created TimeSeries - data.label = name else: + # Create a new TimeSeries from raw data time_series = TimeSeries.from_datasource( - name=name, data=data, timesteps=timesteps_to_use, needs_extra_timestep=needs_extra_timestep + data=data, + name=name, + timesteps=target_timesteps, + scenarios=self.scenarios, + aggregation_weight=aggregation_weight, + aggregation_group=aggregation_group, + has_extra_timestep=has_extra_timestep, ) - # Add to the collection - self.add_time_series(time_series) + # Add to storage + self._time_series[name] = time_series - return time_series + # Track if it needs extra timestep + if has_extra_timestep: + self._has_extra_timestep.add(name) - def calculate_aggregation_weights(self) -> Dict[str, float]: - """Calculate and return aggregation weights for all time series.""" - self.group_weights = self._calculate_group_weights() - self.weights = self._calculate_weights() - - if np.all(np.isclose(list(self.weights.values()), 1, atol=1e-6)): - logger.info('All Aggregation weights were set to 1') - - return self.weights + # Return the TimeSeries object + return time_series - def activate_timesteps(self, active_timesteps: Optional[pd.DatetimeIndex] = None): + def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> None: """ - Update active timesteps for the collection and all time series. - If no arguments are provided, the active timesteps are reset. + Clear selection for timesteps and/or scenarios. Args: - active_timesteps: The active timesteps of the model. - If None, the all timesteps of the TimeSeriesCollection are taken. + timesteps: Whether to clear timesteps selection + scenarios: Whether to clear scenarios selection """ - if active_timesteps is None: - return self.reset() + if timesteps: + self._update_selected_timesteps(timesteps=None) + if scenarios: + self._selected_scenarios = None - if not np.all(np.isin(active_timesteps, self.all_timesteps)): - raise ValueError('active_timesteps must be a subset of the timesteps of the TimeSeriesCollection') + # Apply the selection to all TimeSeries objects + self._propagate_selection_to_time_series() - # Calculate derived timesteps - self._active_timesteps = active_timesteps - first_ts_index = np.where(self.all_timesteps == active_timesteps[0])[0][0] - last_ts_idx = np.where(self.all_timesteps == active_timesteps[-1])[0][0] - self._active_timesteps_extra = self.all_timesteps_extra[first_ts_index : last_ts_idx + 2] - self._active_hours_per_timestep = self.all_hours_per_timestep.isel(time=slice(first_ts_index, last_ts_idx + 1)) + def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: + """ + Set active subset for timesteps and scenarios. - # Update all time series - self._update_time_series_timesteps() + Args: + timesteps: Timesteps to activate, or None to clear + scenarios: Scenarios to activate, or None to clear + """ + if timesteps is None: + self.clear_selection(timesteps=True, scenarios=False) + else: + self._update_selected_timesteps(timesteps) - def reset(self): - """Reset active timesteps to defaults for all time series.""" - self._active_timesteps = None - self._active_timesteps_extra = None - self._active_hours_per_timestep = None + if scenarios is None: + self.clear_selection(timesteps=False, scenarios=True) + else: + self._selected_scenarios = scenarios - for time_series in self.time_series_data.values(): - time_series.reset() + # Apply the selection to all TimeSeries objects + self._propagate_selection_to_time_series() - def restore_data(self): - """Restore original data for all time series.""" - for time_series in self.time_series_data.values(): - time_series.restore_data() + def _update_selected_timesteps(self, timesteps: Optional[pd.DatetimeIndex]) -> None: + """ + Updates the timestep and related metrics (timesteps_extra, hours_per_timestep) based on the current selection. + """ + if timesteps is None: + self._selected_timesteps = None + self._selected_timesteps_extra = None + self._selected_hours_per_timestep = None + return - def add_time_series(self, time_series: TimeSeries): - """Add an existing TimeSeries to the collection.""" - if time_series.name in self.time_series_data: - raise ValueError(f"TimeSeries '{time_series.name}' already exists in this collection") + self._validate_timesteps(timesteps, self._full_timesteps) - self.time_series_data[time_series.name] = time_series + self._selected_timesteps = timesteps + self._selected_hours_per_timestep = self._full_hours_per_timestep.sel(time=timesteps) + self._selected_timesteps_extra = self._create_timesteps_with_extra( + timesteps, self._selected_hours_per_timestep.isel(time=-1).max().item() + ) - def insert_new_data(self, data: pd.DataFrame, include_extra_timestep: bool = False): + def as_dataset(self, with_extra_timestep: bool = True, with_constants: bool = True) -> xr.Dataset: """ - Update time series with new data from a DataFrame. + Convert the TimeSeriesCollection to a xarray Dataset, containing the data of each TimeSeries. Args: - data: DataFrame containing new data with timestamps as index - include_extra_timestep: Whether the provided data already includes the extra timestep, by default False + with_extra_timestep: Whether to exclude the extra timesteps. + Effectively, this removes the last timestep for certain TImeSeries, but mitigates the presence of NANs in others. + with_constants: Whether to exclude TimeSeries with a constant value from the dataset. """ - if not isinstance(data, pd.DataFrame): - raise TypeError(f'data must be a pandas DataFrame, got {type(data).__name__}') + if self.scenarios is None: + ds = xr.Dataset(coords={'time': self.timesteps_extra}) + else: + ds = xr.Dataset(coords={'scenario': self.scenarios, 'time': self.timesteps_extra}) + + for ts in self._time_series.values(): + if not with_constants and ts.all_equal: + continue + ds[ts.name] = ts.selected_data - # Check if the DataFrame index matches the expected timesteps - expected_timesteps = self.timesteps_extra if include_extra_timestep else self.timesteps - if not data.index.equals(expected_timesteps): - raise ValueError( - f'DataFrame index must match {"collection timesteps with extra timestep" if include_extra_timestep else "collection timesteps"}' + if not with_extra_timestep: + return ds.sel(time=self.timesteps) + + return ds + + @property + def timesteps(self) -> pd.DatetimeIndex: + """Get the current active timesteps.""" + if self._selected_timesteps is None: + return self._full_timesteps + return self._selected_timesteps + + @property + def timesteps_extra(self) -> pd.DatetimeIndex: + """Get the current active timesteps with extra timestep.""" + if self._selected_timesteps_extra is None: + return self._full_timesteps_extra + return self._selected_timesteps_extra + + @property + def hours_per_timestep(self) -> xr.DataArray: + """Get the current active hours per timestep.""" + if self._selected_hours_per_timestep is None: + return self._full_hours_per_timestep + return self._selected_hours_per_timestep + + @property + def scenarios(self) -> Optional[pd.Index]: + """Get the current active scenarios.""" + if self._selected_scenarios is None: + return self._full_scenarios + return self._selected_scenarios + + def _propagate_selection_to_time_series(self) -> None: + """Apply the current selection to all TimeSeries objects.""" + for ts_name, ts in self._time_series.items(): + timesteps = self._selected_timesteps_extra if ts_name in self._has_extra_timestep else self._selected_timesteps + ts.set_selection( + timesteps=timesteps, + scenarios=self._selected_scenarios ) - for name, ts in self.time_series_data.items(): - if name in data.columns: - if not ts.needs_extra_timestep: - # For time series without extra timestep - if include_extra_timestep: - # If data includes extra timestep but series doesn't need it, exclude the last point - ts.stored_data = data[name].iloc[:-1] - else: - # Use data as is - ts.stored_data = data[name] - else: - # For time series with extra timestep - if include_extra_timestep: - # Data already includes extra timestep - ts.stored_data = data[name] - else: - # Need to add extra timestep - extrapolate from the last value - extra_step_value = data[name].iloc[-1] - extra_step_index = pd.DatetimeIndex([self.timesteps_extra[-1]], name='time') - extra_step_series = pd.Series([extra_step_value], index=extra_step_index) - - # Combine the regular data with the extra timestep - ts.stored_data = pd.concat([data[name], extra_step_series]) - - logger.debug(f'Updated data for {name}') - - def to_dataframe( - self, filtered: Literal['all', 'constant', 'non_constant'] = 'non_constant', include_extra_timestep: bool = True - ) -> pd.DataFrame: - """ - Convert collection to DataFrame with optional filtering and timestep control. + def __getitem__(self, name: str) -> TimeSeries: + """ + Get a reference to a time series or data array. Args: - filtered: Filter time series by variability, by default 'non_constant' - include_extra_timestep: Whether to include the extra timestep in the result, by default True + name: Name of the data array or time series Returns: - DataFrame representation of the collection + TimeSeries object if it exists, otherwise DataArray with current selection applied """ - include_constants = filtered != 'non_constant' - ds = self.to_dataset(include_constants=include_constants) - - if not include_extra_timestep: - ds = ds.isel(time=slice(None, -1)) + # First check if this is a TimeSeries + if name in self._time_series: + # Return the TimeSeries object (it will handle selection internally) + return self._time_series[name] + raise ValueError(f'No TimeSeries named "{name}" found') + + def __contains__(self, value) -> bool: + if isinstance(value, str): + return value in self._time_series + elif isinstance(value, TimeSeries): + return value.name in self._time_series + raise TypeError(f'Invalid type for __contains__ of {self.__class__.__name__}: {type(value)}') - df = ds.to_dataframe() - - # Apply filtering - if filtered == 'all': - return df - elif filtered == 'constant': - return df.loc[:, df.nunique() == 1] - elif filtered == 'non_constant': - return df.loc[:, df.nunique() > 1] - else: - raise ValueError("filtered must be one of: 'all', 'constant', 'non_constant'") + def __iter__(self) -> Iterator[TimeSeries]: + """Iterate over TimeSeries objects.""" + return iter(self._time_series.values()) - def to_dataset(self, include_constants: bool = True) -> xr.Dataset: + def update_time_series(self, name: str, data: NumericData) -> TimeSeries: """ - Combine all time series into a single Dataset with all timesteps. + Update an existing TimeSeries with new data. Args: - include_constants: Whether to include time series with constant values, by default True + name: Name of the TimeSeries to update + data: New data to assign Returns: - Dataset containing all selected time series with all timesteps - """ - # Determine which series to include - if include_constants: - series_to_include = self.time_series_data.values() - else: - series_to_include = self.non_constants + The updated TimeSeries - # Create individual datasets and merge them - ds = xr.merge([ts.active_data.to_dataset(name=ts.name) for ts in series_to_include]) + Raises: + KeyError: If no TimeSeries with the given name exists + """ + if name not in self._time_series: + raise KeyError(f"No TimeSeries named '{name}' found") - # Ensure the correct time coordinates - ds = ds.reindex(time=self.timesteps_extra) + # Get the TimeSeries + ts = self._time_series[name] - ds.attrs.update( - { - 'timesteps_extra': f'{self.timesteps_extra[0]} ... {self.timesteps_extra[-1]} | len={len(self.timesteps_extra)}', - 'hours_per_timestep': self._format_stats(self.hours_per_timestep), - } + # Convert data to proper format + data_array = DataConverter.as_dataarray( + data, + self.timesteps_extra if name in self._has_extra_timestep else self.timesteps, + self.scenarios ) - return ds + # Update the TimeSeries + ts.update_stored_data(data_array) + + return ts + + def calculate_aggregation_weights(self) -> Dict[str, float]: + """Calculate and return aggregation weights for all time series.""" + group_weights = self._calculate_group_weights() - def _update_time_series_timesteps(self): - """Update active timesteps for all time series.""" - for ts in self.time_series_data.values(): - if ts.needs_extra_timestep: - ts.active_timesteps = self.timesteps_extra + weights = {} + for name, ts in self._time_series.items(): + if ts.aggregation_group is not None: + # Use group weight + weights[name] = group_weights.get(ts.aggregation_group, 1) else: - ts.active_timesteps = self.timesteps + # Use individual weight or default to 1 + weights[name] = ts.aggregation_weight or 1 + + if np.all(np.isclose(list(weights.values()), 1, atol=1e-6)): + logger.info('All Aggregation weights were set to 1') + + return weights + + def _calculate_group_weights(self) -> Dict[str, float]: + """Calculate weights for aggregation groups.""" + # Count series in each group + groups = [ts.aggregation_group for ts in self._time_series.values() if ts.aggregation_group is not None] + group_counts = Counter(groups) + + # Calculate weight for each group (1/count) + return {group: 1 / count for group, count in group_counts.items()} @staticmethod - def _validate_timesteps(timesteps: pd.DatetimeIndex): - """Validate timesteps format and rename if needed.""" + def _validate_timesteps(timesteps: pd.DatetimeIndex, present_timesteps: Optional[pd.DatetimeIndex] = None): + """ + Validate timesteps format and rename if needed. + Args: + timesteps: The timesteps to validate + present_timesteps: The timesteps that are present in the dataset + + Raises: + ValueError: If timesteps is not a pandas DatetimeIndex + ValueError: If timesteps is not at least 2 timestamps + ValueError: If timesteps has a different name than 'time' + ValueError: If timesteps is not sorted + ValueError: If timesteps contains duplicates + ValueError: If timesteps is not a subset of present_timesteps + """ if not isinstance(timesteps, pd.DatetimeIndex): raise TypeError('timesteps must be a pandas DatetimeIndex') @@ -750,6 +1331,18 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex): logger.warning('Renamed timesteps to "time" (was "%s")', timesteps.name) timesteps.name = 'time' + # Ensure timesteps is sorted + if not timesteps.is_monotonic_increasing: + raise ValueError('timesteps must be sorted') + + # Ensure timesteps has no duplicates + if len(timesteps) != len(timesteps.drop_duplicates()): + raise ValueError('timesteps must not contain duplicates') + + # Ensure timesteps is a subset of present_timesteps + if present_timesteps is not None and not set(timesteps).issubset(set(present_timesteps)): + raise ValueError('timesteps must be a subset of present_timesteps') + @staticmethod def _create_timesteps_with_extra( timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] @@ -787,128 +1380,49 @@ def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataAr data=hours_per_step, coords={'time': timesteps_extra[:-1]}, dims=('time',), name='hours_per_step' ) - def _calculate_group_weights(self) -> Dict[str, float]: - """Calculate weights for aggregation groups.""" - # Count series in each group - groups = [ts.aggregation_group for ts in self.time_series_data.values() if ts.aggregation_group is not None] - group_counts = Counter(groups) - # Calculate weight for each group (1/count) - return {group: 1 / count for group, count in group_counts.items()} - - def _calculate_weights(self) -> Dict[str, float]: - """Calculate weights for all time series.""" - # Calculate weight for each time series - weights = {} - for name, ts in self.time_series_data.items(): - if ts.aggregation_group is not None: - # Use group weight - weights[name] = self.group_weights.get(ts.aggregation_group, 1) - else: - # Use individual weight or default to 1 - weights[name] = ts.aggregation_weight or 1 - - return weights - - def _format_stats(self, data) -> str: - """Format statistics for a data array.""" - if hasattr(data, 'values'): - values = data.values - else: - values = np.asarray(data) - - mean_val = np.mean(values) - min_val = np.min(values) - max_val = np.max(values) - - return f'mean: {mean_val:.2f}, min: {min_val:.2f}, max: {max_val:.2f}' - - def __getitem__(self, name: str) -> TimeSeries: - """Get a TimeSeries by name.""" - try: - return self.time_series_data[name] - except KeyError as e: - raise KeyError(f'TimeSeries "{name}" not found in the TimeSeriesCollection') from e - - def __iter__(self) -> Iterator[TimeSeries]: - """Iterate through all TimeSeries in the collection.""" - return iter(self.time_series_data.values()) - - def __len__(self) -> int: - """Get the number of TimeSeries in the collection.""" - return len(self.time_series_data) - - def __contains__(self, item: Union[str, TimeSeries]) -> bool: - """Check if a TimeSeries exists in the collection.""" - if isinstance(item, str): - return item in self.time_series_data - elif isinstance(item, TimeSeries): - return item in self.time_series_data.values() - return False - - @property - def non_constants(self) -> List[TimeSeries]: - """Get time series with varying values.""" - return [ts for ts in self.time_series_data.values() if not ts.all_equal] - - @property - def constants(self) -> List[TimeSeries]: - """Get time series with constant values.""" - return [ts for ts in self.time_series_data.values() if ts.all_equal] - - @property - def timesteps(self) -> pd.DatetimeIndex: - """Get the active timesteps.""" - return self.all_timesteps if self._active_timesteps is None else self._active_timesteps - - @property - def timesteps_extra(self) -> pd.DatetimeIndex: - """Get the active timesteps with extra step.""" - return self.all_timesteps_extra if self._active_timesteps_extra is None else self._active_timesteps_extra - - @property - def hours_per_timestep(self) -> xr.DataArray: - """Get the duration of each active timestep.""" - return ( - self.all_hours_per_timestep if self._active_hours_per_timestep is None else self._active_hours_per_timestep - ) - - @property - def hours_of_last_timestep(self) -> float: - """Get the duration of the last timestep.""" - return float(self.hours_per_timestep[-1].item()) - - def __repr__(self): - return f'TimeSeriesCollection:\n{self.to_dataset()}' - - def __str__(self): - longest_name = max([time_series.name for time_series in self.time_series_data], key=len) - - stats_summary = '\n'.join( - [ - f' - {time_series.name:<{len(longest_name)}}: {get_numeric_stats(time_series.active_data)}' - for time_series in self.time_series_data - ] - ) - - return ( - f'TimeSeriesCollection with {len(self.time_series_data)} series\n' - f' Time Range: {self.timesteps[0]} → {self.timesteps[-1]}\n' - f' No. of timesteps: {len(self.timesteps)} + 1 extra\n' - f' Hours per timestep: {get_numeric_stats(self.hours_per_timestep)}\n' - f' Time Series Data:\n' - f'{stats_summary}' - ) +def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10, by_scenario: bool = False) -> str: + """ + Calculates the mean, median, min, max, and standard deviation of a numeric DataArray. + Args: + data: The DataArray to analyze + decimals: Number of decimal places to show + padd: Padding for alignment + by_scenario: Whether to break down stats by scenario -def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10) -> str: - """Calculates the mean, median, min, max, and standard deviation of a numeric DataArray.""" + Returns: + String representation of data statistics + """ format_spec = f'>{padd}.{decimals}f' if padd else f'.{decimals}f' + + # If by_scenario is True and there's a scenario dimension with multiple values + if by_scenario and 'scenario' in data.dims and data.sizes['scenario'] > 1: + results = [] + for scenario in data.coords['scenario'].values: + scenario_data = data.sel(scenario=scenario) + if np.unique(scenario_data).size == 1: + results.append(f' {scenario}: {scenario_data.max().item():{format_spec}} (constant)') + else: + mean = scenario_data.mean().item() + median = scenario_data.median().item() + min_val = scenario_data.min().item() + max_val = scenario_data.max().item() + std = scenario_data.std().item() + results.append( + f' {scenario}: {mean:{format_spec}} (mean), {median:{format_spec}} (median), ' + f'{min_val:{format_spec}} (min), {max_val:{format_spec}} (max), {std:{format_spec}} (std)' + ) + return '\n'.join(['By scenario:'] + results) + + # Standard logic for non-scenario data or aggregated stats if np.unique(data).size == 1: return f'{data.max().item():{format_spec}} (constant)' + mean = data.mean().item() median = data.median().item() min_val = data.min().item() max_val = data.max().item() std = data.std().item() + return f'{mean:{format_spec}} (mean), {median:{format_spec}} (median), {min_val:{format_spec}} (min), {max_val:{format_spec}} (max), {std:{format_spec}} (std)' diff --git a/flixopt/effects.py b/flixopt/effects.py index 82aa63a43..9b5ea41d6 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -137,10 +137,10 @@ def __init__(self, model: SystemModel, element: Effect): label_full=f'{self.label_full}(operation)', total_max=self.element.maximum_operation, total_min=self.element.minimum_operation, - min_per_hour=self.element.minimum_operation_per_hour.active_data + min_per_hour=self.element.minimum_operation_per_hour.selected_data if self.element.minimum_operation_per_hour is not None else None, - max_per_hour=self.element.maximum_operation_per_hour.active_data + max_per_hour=self.element.maximum_operation_per_hour.selected_data if self.element.maximum_operation_per_hour is not None else None, ) @@ -376,7 +376,7 @@ def _add_share_between_effects(self): for target_effect, time_series in origin_effect.specific_share_to_other_effects_operation.items(): self.effects[target_effect].model.operation.add_share( origin_effect.model.operation.label_full, - origin_effect.model.operation.total_per_timestep * time_series.active_data, + origin_effect.model.operation.total_per_timestep * time_series.selected_data, ) # 2. invest: -> hier ist es Scalar (share) for target_effect, factor in origin_effect.specific_share_to_other_effects_invest.items(): diff --git a/flixopt/elements.py b/flixopt/elements.py index 05898d4e5..95536b910 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -374,7 +374,7 @@ def _create_shares(self): self._model.effects.add_share_to_effects( name=self.label_full, # Use the full label of the element expressions={ - effect: self.flow_rate * self._model.hours_per_step * factor.active_data + effect: self.flow_rate * self._model.hours_per_step * factor.selected_data for effect, factor in self.element.effects_per_flow_hour.items() }, target='operation', @@ -429,8 +429,8 @@ def relative_flow_rate_bounds(self) -> Tuple[NumericData, NumericData]: """Returns relative flow rate bounds.""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: - return self.element.relative_minimum.active_data, self.element.relative_maximum.active_data - return fixed_profile.active_data, fixed_profile.active_data + return self.element.relative_minimum.selected_data, self.element.relative_maximum.selected_data + return fixed_profile.selected_data, fixed_profile.selected_data class BusModel(ElementModel): @@ -451,7 +451,7 @@ def do_modeling(self) -> None: # Fehlerplus/-minus: if self.element.with_excess: excess_penalty = np.multiply( - self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.active_data + self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.selected_data ) self.excess_input = self.add( self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_input'), diff --git a/flixopt/features.py b/flixopt/features.py index 92caf9dc2..32c382486 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -441,7 +441,7 @@ def _get_duration_in_hours( if previous_duration + self._model.hours_per_step[0] > first_step_max: logger.warning( - f'The maximum duration of "{variable_name}" is set to {maximum_duration.active_data}h, ' + f'The maximum duration of "{variable_name}" is set to {maximum_duration.selected_data}h, ' f'but the consecutive_duration previous to this model is {previous_duration}h. ' f'This forces "{binary_variable.name} = 0" in the first time step ' f'(dt={self._model.hours_per_step[0]}h)!' @@ -450,7 +450,7 @@ def _get_duration_in_hours( duration_in_hours = self.add( self._model.add_variables( lower=0, - upper=maximum_duration.active_data if maximum_duration is not None else mega, + upper=maximum_duration.selected_data if maximum_duration is not None else mega, coords=self._model.coords, name=f'{self.label_full}|{variable_name}', ), diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 93720de60..e39d71e94 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -35,12 +35,14 @@ class FlowSystem: def __init__( self, timesteps: pd.DatetimeIndex, + scenarios: Optional[pd.Index] = None, hours_of_last_timestep: Optional[float] = None, hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, ): """ Args: timesteps: The timesteps of the model. + scenarios: The scenarios of the model. hours_of_last_timestep: The duration of the last time step. Uses the last time interval if not specified hours_of_previous_timesteps: The duration of previous timesteps. If None, the first time increment of time_series is used. @@ -49,6 +51,7 @@ def __init__( """ self.time_series_collection = TimeSeriesCollection( timesteps=timesteps, + scenarios=scenarios, hours_of_last_timestep=hours_of_last_timestep, hours_of_previous_timesteps=hours_of_previous_timesteps, ) @@ -184,7 +187,7 @@ def as_dataset(self, constants_in_dataset: bool = False) -> xr.Dataset: Args: constants_in_dataset: If True, constants are included as Dataset variables. """ - ds = self.time_series_collection.to_dataset(include_constants=constants_in_dataset) + ds = self.time_series_collection.as_dataset() ds.attrs = self.as_dict(data_mode='name') return ds @@ -275,7 +278,7 @@ def create_time_series( self, name: str, data: Optional[Union[NumericData, TimeSeriesData, TimeSeries]], - needs_extra_timestep: bool = False, + has_extra_timestep: bool = False, ) -> Optional[TimeSeries]: """ Tries to create a TimeSeries from NumericData Data and adds it to the time_series_collection @@ -290,11 +293,20 @@ def create_time_series( data.restore_data() if data in self.time_series_collection: return data - return self.time_series_collection.create_time_series( - data=data.active_data, name=name, needs_extra_timestep=needs_extra_timestep + return self.time_series_collection.add_time_series( + data=data.selected_data, name=name, has_extra_timestep=has_extra_timestep ) - return self.time_series_collection.create_time_series( - data=data, name=name, needs_extra_timestep=needs_extra_timestep + elif isinstance(data, TimeSeriesData): + data.label = name + return self.time_series_collection.add_time_series( + data=data.data, + name=name, + has_extra_timestep=has_extra_timestep, + aggregation_weight=data.agg_weight, + aggregation_group=data.agg_group + ) + return self.time_series_collection.add_time_series( + data=data, name=name, has_extra_timestep=has_extra_timestep ) def create_effect_time_series( diff --git a/flixopt/io.py b/flixopt/io.py index 35d927136..5cc353836 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -23,7 +23,7 @@ def replace_timeseries(obj, mode: Literal['name', 'stats', 'data'] = 'name'): return [replace_timeseries(v, mode) for v in obj] elif isinstance(obj, TimeSeries): # Adjust this based on the actual class if obj.all_equal: - return obj.active_data.values[0].item() + return obj.selected_data.values[0].item() elif mode == 'name': return f'::::{obj.name}' elif mode == 'stats': diff --git a/flixopt/structure.py b/flixopt/structure.py index e7f1c62a4..2e136c652 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -534,7 +534,7 @@ def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_la return copy_and_convert_datatypes(data.tolist(), use_numpy, use_element_label) elif isinstance(data, TimeSeries): - return copy_and_convert_datatypes(data.active_data, use_numpy, use_element_label) + return copy_and_convert_datatypes(data.selected_data, use_numpy, use_element_label) elif isinstance(data, TimeSeriesData): return copy_and_convert_datatypes(data.data, use_numpy, use_element_label) diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 49f1438e7..579de9c00 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -3,69 +3,848 @@ import pytest import xarray as xr -from flixopt.core import ConversionError, DataConverter # Adjust this import to match your project structure +from flixopt.core import ( # Adjust this import to match your project structure + ConversionError, + DataConverter, + TimeSeries, +) @pytest.fixture -def sample_time_index(request): - index = pd.date_range('2024-01-01', periods=5, freq='D', name='time') - return index +def sample_time_index(): + return pd.date_range('2024-01-01', periods=5, freq='D', name='time') -def test_scalar_conversion(sample_time_index): - # Test scalar conversion - result = DataConverter.as_dataarray(42, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (len(sample_time_index),) - assert result.dims == ('time',) - assert np.all(result.values == 42) +@pytest.fixture +def sample_scenario_index(): + return pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') + + +@pytest.fixture +def multi_index(sample_time_index, sample_scenario_index): + """Create a sample MultiIndex combining scenarios and times.""" + return pd.MultiIndex.from_product([sample_scenario_index, sample_time_index], names=['scenario', 'time']) + + +class TestSingleDimensionConversion: + """Tests for converting data without scenarios (1D: time only).""" + + def test_scalar_conversion(self, sample_time_index): + """Test converting a scalar value.""" + # Test with integer + result = DataConverter.as_dataarray(42, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (len(sample_time_index),) + assert result.dims == ('time',) + assert np.all(result.values == 42) + + # Test with float + result = DataConverter.as_dataarray(42.5, sample_time_index) + assert np.all(result.values == 42.5) + + # Test with numpy scalar types + result = DataConverter.as_dataarray(np.int64(42), sample_time_index) + assert np.all(result.values == 42) + result = DataConverter.as_dataarray(np.float32(42.5), sample_time_index) + assert np.all(result.values == 42.5) + + def test_series_conversion(self, sample_time_index): + """Test converting a pandas Series.""" + # Test with integer values + series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) + result = DataConverter.as_dataarray(series, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, series.values) + + # Test with float values + series = pd.Series([1.1, 2.2, 3.3, 4.4, 5.5], index=sample_time_index) + result = DataConverter.as_dataarray(series, sample_time_index) + assert np.array_equal(result.values, series.values) + + # Test with mixed NA values + series = pd.Series([1, np.nan, 3, None, 5], index=sample_time_index) + result = DataConverter.as_dataarray(series, sample_time_index) + assert np.array_equal(np.isnan(result.values), np.isnan(series.values)) + assert np.array_equal(result.values[~np.isnan(result.values)], series.values[~np.isnan(series.values)]) + + def test_dataframe_conversion(self, sample_time_index): + """Test converting a pandas DataFrame.""" + # Test with a single-column DataFrame + df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) + result = DataConverter.as_dataarray(df, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values.flatten(), df['A'].values) + + # Test with float values + df = pd.DataFrame({'A': [1.1, 2.2, 3.3, 4.4, 5.5]}, index=sample_time_index) + result = DataConverter.as_dataarray(df, sample_time_index) + assert np.array_equal(result.values.flatten(), df['A'].values) + + # Test with NA values + df = pd.DataFrame({'A': [1, np.nan, 3, None, 5]}, index=sample_time_index) + result = DataConverter.as_dataarray(df, sample_time_index) + assert np.array_equal(np.isnan(result.values), np.isnan(df['A'].values)) + assert np.array_equal(result.values[~np.isnan(result.values)], df['A'].values[~np.isnan(df['A'].values)]) + + def test_ndarray_conversion(self, sample_time_index): + """Test converting a numpy ndarray.""" + # Test with integer 1D array + arr_1d = np.array([1, 2, 3, 4, 5]) + result = DataConverter.as_dataarray(arr_1d, sample_time_index) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, arr_1d) + + # Test with float 1D array + arr_1d = np.array([1.1, 2.2, 3.3, 4.4, 5.5]) + result = DataConverter.as_dataarray(arr_1d, sample_time_index) + assert np.array_equal(result.values, arr_1d) + + # Test with array containing NaN + arr_1d = np.array([1, np.nan, 3, np.nan, 5]) + result = DataConverter.as_dataarray(arr_1d, sample_time_index) + assert np.array_equal(np.isnan(result.values), np.isnan(arr_1d)) + assert np.array_equal(result.values[~np.isnan(result.values)], arr_1d[~np.isnan(arr_1d)]) + + def test_dataarray_conversion(self, sample_time_index): + """Test converting an existing xarray DataArray.""" + # Create original DataArray + original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) + + # Convert and check + result = DataConverter.as_dataarray(original, sample_time_index) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, original.values) + + # Ensure it's a copy + result[0] = 999 + assert original[0].item() == 1 # Original should be unchanged + + # Test with different time coordinates but same length + different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': different_times}, dims=['time']) + + # Should raise an error for mismatched time coordinates + with pytest.raises(ConversionError): + DataConverter.as_dataarray(original, sample_time_index) + + +class TestMultiDimensionConversion: + """Tests for converting data with scenarios (2D: scenario × time).""" + + def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting scalar values with scenario dimension.""" + # Test with integer + result = DataConverter.as_dataarray(42, sample_time_index, sample_scenario_index) + + assert isinstance(result, xr.DataArray) + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + assert np.all(result.values == 42) + assert set(result.coords['scenario'].values) == set(sample_scenario_index.values) + assert set(result.coords['time'].values) == set(sample_time_index.values) + + # Test with float + result = DataConverter.as_dataarray(42.5, sample_time_index, sample_scenario_index) + assert np.all(result.values == 42.5) + + def test_series_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting Series with scenario dimension.""" + # Create time series data + series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) + + # Convert with scenario dimension + result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Values should be broadcast to all scenarios + for scenario in sample_scenario_index: + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(scenario_slice.values, series.values) + + # Test with series containing NaN + series = pd.Series([1, np.nan, 3, np.nan, 5], index=sample_time_index) + result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + + # Each scenario should have the same pattern of NaNs + for scenario in sample_scenario_index: + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(np.isnan(scenario_slice.values), np.isnan(series.values)) + assert np.array_equal( + scenario_slice.values[~np.isnan(scenario_slice.values)], series.values[~np.isnan(series.values)] + ) + + def test_multi_index_series(self, sample_time_index, sample_scenario_index, multi_index): + """Test converting a Series with MultiIndex (scenario, time).""" + # Create a MultiIndex Series with scenario-specific values + values = [ + # baseline scenario + 10, + 20, + 30, + 40, + 50, + # high_demand scenario + 15, + 25, + 35, + 45, + 55, + # low_price scenario + 5, + 15, + 25, + 35, + 45, + ] + series_multi = pd.Series(values, index=multi_index) + + # Convert the MultiIndex Series + result = DataConverter.as_dataarray(series_multi, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Check values for each scenario + baseline_values = result.sel(scenario='baseline').values + assert np.array_equal(baseline_values, [10, 20, 30, 40, 50]) + + high_demand_values = result.sel(scenario='high_demand').values + assert np.array_equal(high_demand_values, [15, 25, 35, 45, 55]) + + low_price_values = result.sel(scenario='low_price').values + assert np.array_equal(low_price_values, [5, 15, 25, 35, 45]) + + # Test with some missing values in the MultiIndex + incomplete_index = multi_index[:-2] # Remove last two entries + incomplete_values = values[:-2] # Remove corresponding values + incomplete_series = pd.Series(incomplete_values, index=incomplete_index) + + result = DataConverter.as_dataarray(incomplete_series, sample_time_index, sample_scenario_index) + + # The last value of low_price scenario should be NaN + assert np.isnan(result.sel(scenario='low_price').values[-1]) + + def test_dataframe_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting DataFrame with scenario dimension.""" + # Create a single-column DataFrame + df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) + + # Convert with scenario dimension + result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Values should be broadcast to all scenarios + for scenario in sample_scenario_index: + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(scenario_slice.values, df['A'].values) + + def test_multi_index_dataframe(self, sample_time_index, sample_scenario_index, multi_index): + """Test converting a DataFrame with MultiIndex (scenario, time).""" + # Create a MultiIndex DataFrame with scenario-specific values + values = [ + # baseline scenario + 10, + 20, + 30, + 40, + 50, + # high_demand scenario + 15, + 25, + 35, + 45, + 55, + # low_price scenario + 5, + 15, + 25, + 35, + 45, + ] + df_multi = pd.DataFrame({'A': values}, index=multi_index) + + # Convert the MultiIndex DataFrame + result = DataConverter.as_dataarray(df_multi, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Check values for each scenario + baseline_values = result.sel(scenario='baseline').values + assert np.array_equal(baseline_values, [10, 20, 30, 40, 50]) + + high_demand_values = result.sel(scenario='high_demand').values + assert np.array_equal(high_demand_values, [15, 25, 35, 45, 55]) + + low_price_values = result.sel(scenario='low_price').values + assert np.array_equal(low_price_values, [5, 15, 25, 35, 45]) + + # Test with missing values + incomplete_index = multi_index[:-2] # Remove last two entries + incomplete_values = values[:-2] # Remove corresponding values + incomplete_df = pd.DataFrame({'A': incomplete_values}, index=incomplete_index) + + result = DataConverter.as_dataarray(incomplete_df, sample_time_index, sample_scenario_index) + + # The last value of low_price scenario should be NaN + assert np.isnan(result.sel(scenario='low_price').values[-1]) + + # Test with multiple columns (should raise error) + df_multi_col = pd.DataFrame({'A': values, 'B': [v * 2 for v in values]}, index=multi_index) + + with pytest.raises(ConversionError): + DataConverter.as_dataarray(df_multi_col, sample_time_index, sample_scenario_index) + + def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting 1D array with scenario dimension (broadcasting).""" + # Create 1D array matching timesteps length + arr_1d = np.array([1, 2, 3, 4, 5]) + + # Convert with scenarios + result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Each scenario should have the same values (broadcasting) + for scenario in sample_scenario_index: + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(scenario_slice.values, arr_1d) + + def test_2d_array_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting 2D array with scenario dimension.""" + # Create 2D array with different values per scenario + arr_2d = np.array( + [ + [1, 2, 3, 4, 5], # baseline scenario + [6, 7, 8, 9, 10], # high_demand scenario + [11, 12, 13, 14, 15], # low_price scenario + ] + ) + + # Convert to DataArray + result = DataConverter.as_dataarray(arr_2d, sample_time_index, sample_scenario_index) + + assert result.shape == (3, 5) + assert result.dims == ('scenario', 'time') + + # Check that each scenario has correct values + assert np.array_equal(result.sel(scenario='baseline').values, arr_2d[0]) + assert np.array_equal(result.sel(scenario='high_demand').values, arr_2d[1]) + assert np.array_equal(result.sel(scenario='low_price').values, arr_2d[2]) + + def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting an existing DataArray with scenarios.""" + # Create a multi-scenario DataArray + original = xr.DataArray( + data=np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]), + coords={'scenario': sample_scenario_index, 'time': sample_time_index}, + dims=['scenario', 'time'], + ) + + # Test conversion + result = DataConverter.as_dataarray(original, sample_time_index, sample_scenario_index) + + assert result.shape == (3, 5) + assert result.dims == ('scenario', 'time') + assert np.array_equal(result.values, original.values) + + # Ensure it's a copy + result.loc['baseline'] = 999 + assert original.sel(scenario='baseline')[0].item() == 1 # Original should be unchanged + + def test_time_only_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test broadcasting a time-only DataArray to scenarios.""" + # Create a DataArray with only time dimension + time_only = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) + + # Convert with scenarios - should broadcast to all scenarios + result = DataConverter.as_dataarray(time_only, sample_time_index, sample_scenario_index) + + assert result.shape == (3, 5) + assert result.dims == ('scenario', 'time') + + # Each scenario should have same values + for scenario in sample_scenario_index: + assert np.array_equal(result.sel(scenario=scenario).values, time_only.values) + + +class TestInvalidInputs: + """Tests for invalid inputs and error handling.""" + + def test_time_index_validation(self): + """Test validation of time index.""" + # Test with unnamed index + unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') + with pytest.raises(ConversionError): + DataConverter.as_dataarray(42, unnamed_index) + + # Test with empty index + empty_index = pd.DatetimeIndex([], name='time') + with pytest.raises(ValueError): + DataConverter.as_dataarray(42, empty_index) + + # Test with non-DatetimeIndex + wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') + with pytest.raises(ValueError): + DataConverter.as_dataarray(42, wrong_type_index) + + def test_scenario_index_validation(self, sample_time_index): + """Test validation of scenario index.""" + # Test with unnamed scenario index + unnamed_index = pd.Index(['baseline', 'high_demand']) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(42, sample_time_index, unnamed_index) + # Test with empty scenario index + empty_index = pd.Index([], name='scenario') + with pytest.raises(ValueError): + DataConverter.as_dataarray(42, sample_time_index, empty_index) -def test_series_conversion(sample_time_index): - series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) + # Test with non-Index scenario + with pytest.raises(ValueError): + DataConverter.as_dataarray(42, sample_time_index, ['baseline', 'high_demand']) - # Test Series conversion - result = DataConverter.as_dataarray(series, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, series.values) + def test_invalid_data_types(self, sample_time_index, sample_scenario_index): + """Test handling of invalid data types.""" + # Test invalid input type (string) + with pytest.raises(ConversionError): + DataConverter.as_dataarray('invalid_string', sample_time_index) + # Test invalid input type with scenarios + with pytest.raises(ConversionError): + DataConverter.as_dataarray('invalid_string', sample_time_index, sample_scenario_index) -def test_dataframe_conversion(sample_time_index): - # Create a single-column DataFrame - df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) + # Test unsupported complex object + with pytest.raises(ConversionError): + DataConverter.as_dataarray(object(), sample_time_index) - # Test DataFrame conversion - result = DataConverter.as_dataarray(df, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values.flatten(), df['A'].values) + # Test None value + with pytest.raises(ConversionError): + DataConverter.as_dataarray(None, sample_time_index) + def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_index): + """Test handling of mismatched input dimensions.""" + # Test mismatched Series index + mismatched_series = pd.Series( + [1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D', name='time') + ) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(mismatched_series, sample_time_index) -def test_ndarray_conversion(sample_time_index): - # Test 1D array conversion - arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, arr_1d) + # Test DataFrame with multiple columns + df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(df_multi_col, sample_time_index) + # Test mismatched array shape for time-only + with pytest.raises(ConversionError): + DataConverter.as_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length -def test_dataarray_conversion(sample_time_index): - # Create a DataArray - original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) + # Test mismatched array shape for scenario × time + # Array shape should be (n_scenarios, n_timesteps) + wrong_shape_array = np.array( + [ + [1, 2, 3, 4], # Missing a timestep + [5, 6, 7, 8], + [9, 10, 11, 12], + ] + ) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(wrong_shape_array, sample_time_index, sample_scenario_index) - # Test DataArray conversion - result = DataConverter.as_dataarray(original, sample_time_index) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, original.values) + # Test array with too many dimensions + with pytest.raises(ConversionError): + # 3D array not allowed + DataConverter.as_dataarray(np.ones((3, 5, 2)), sample_time_index, sample_scenario_index) - # Ensure it's a copy - result[0] = 999 - assert original[0].item() == 1 # Original should be unchanged + def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_index): + """Test handling of mismatched DataArray dimensions.""" + # Create DataArray with wrong dimensions + wrong_dims = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'wrong_dim': range(5)}, dims=['wrong_dim']) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(wrong_dims, sample_time_index) + + # Create DataArray with scenario but no time + wrong_dims_2 = xr.DataArray(data=np.array([1, 2, 3]), coords={'scenario': ['a', 'b', 'c']}, dims=['scenario']) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(wrong_dims_2, sample_time_index, sample_scenario_index) + + # Create DataArray with right dims but wrong length + wrong_length = xr.DataArray( + data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), + coords={ + 'scenario': sample_scenario_index, + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), + }, + dims=['scenario', 'time'], + ) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(wrong_length, sample_time_index, sample_scenario_index) + + +class TestEdgeCases: + """Tests for edge cases and special scenarios.""" + + def test_single_timestep(self, sample_scenario_index): + """Test with a single timestep.""" + # Test with only one timestep + single_timestep = pd.DatetimeIndex(['2024-01-01'], name='time') + + # Scalar conversion + result = DataConverter.as_dataarray(42, single_timestep) + assert result.shape == (1,) + assert result.dims == ('time',) + + # With scenarios + result_with_scenarios = DataConverter.as_dataarray(42, single_timestep, sample_scenario_index) + assert result_with_scenarios.shape == (len(sample_scenario_index), 1) + assert result_with_scenarios.dims == ('scenario', 'time') + + def test_single_scenario(self, sample_time_index): + """Test with a single scenario.""" + # Test with only one scenario + single_scenario = pd.Index(['baseline'], name='scenario') + + # Scalar conversion with single scenario + result = DataConverter.as_dataarray(42, sample_time_index, single_scenario) + assert result.shape == (1, len(sample_time_index)) + assert result.dims == ('scenario', 'time') + + # Array conversion with single scenario + arr = np.array([1, 2, 3, 4, 5]) + result_arr = DataConverter.as_dataarray(arr, sample_time_index, single_scenario) + assert result_arr.shape == (1, 5) + assert np.array_equal(result_arr.sel(scenario='baseline').values, arr) + + # 2D array with single scenario + arr_2d = np.array([[1, 2, 3, 4, 5]]) # Note the extra dimension + result_arr_2d = DataConverter.as_dataarray(arr_2d, sample_time_index, single_scenario) + assert result_arr_2d.shape == (1, 5) + assert np.array_equal(result_arr_2d.sel(scenario='baseline').values, arr_2d[0]) + + def test_different_scenario_order(self, sample_time_index): + """Test that scenario order is preserved.""" + # Test with different scenario orders + scenarios1 = pd.Index(['a', 'b', 'c'], name='scenario') + scenarios2 = pd.Index(['c', 'b', 'a'], name='scenario') + + # Create DataArray with first order + data = np.array( + [ + [1, 2, 3, 4, 5], # a + [6, 7, 8, 9, 10], # b + [11, 12, 13, 14, 15], # c + ] + ) + + result1 = DataConverter.as_dataarray(data, sample_time_index, scenarios1) + assert np.array_equal(result1.sel(scenario='a').values, [1, 2, 3, 4, 5]) + assert np.array_equal(result1.sel(scenario='c').values, [11, 12, 13, 14, 15]) + + # Create DataArray with second order + result2 = DataConverter.as_dataarray(data, sample_time_index, scenarios2) + # First row should match 'c' now + assert np.array_equal(result2.sel(scenario='c').values, [1, 2, 3, 4, 5]) + # Last row should match 'a' now + assert np.array_equal(result2.sel(scenario='a').values, [11, 12, 13, 14, 15]) + + def test_all_nan_data(self, sample_time_index, sample_scenario_index): + """Test handling of all-NaN data.""" + # Create array of all NaNs + all_nan_array = np.full(5, np.nan) + result = DataConverter.as_dataarray(all_nan_array, sample_time_index) + assert np.all(np.isnan(result.values)) + + # With scenarios + result = DataConverter.as_dataarray(all_nan_array, sample_time_index, sample_scenario_index) + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert np.all(np.isnan(result.values)) + + # Series of all NaNs + all_nan_series = pd.Series([np.nan, np.nan, np.nan, np.nan, np.nan], index=sample_time_index) + result = DataConverter.as_dataarray(all_nan_series, sample_time_index, sample_scenario_index) + assert np.all(np.isnan(result.values)) + + def test_subset_index_multiindex(self, sample_time_index, sample_scenario_index): + """Test handling of MultiIndex Series/DataFrames with subset of expected indices.""" + # Create a subset of the expected indexes + subset_time = sample_time_index[1:4] # Middle subset + subset_scenarios = sample_scenario_index[0:2] # First two scenarios + + # Create MultiIndex with subset + subset_multi_index = pd.MultiIndex.from_product([subset_scenarios, subset_time], names=['scenario', 'time']) + + # Create Series with subset of data + values = [ + # baseline (3 values) + 20, + 30, + 40, + # high_demand (3 values) + 25, + 35, + 45, + ] + subset_series = pd.Series(values, index=subset_multi_index) + + # Convert and test + result = DataConverter.as_dataarray(subset_series, sample_time_index, sample_scenario_index) + + # Shape should be full size + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + + # Check values - present values should match + assert result.sel(scenario='baseline', time=subset_time[0]).item() == 20 + assert result.sel(scenario='high_demand', time=subset_time[1]).item() == 35 + + # Missing values should be NaN + assert np.isnan(result.sel(scenario='baseline', time=sample_time_index[0]).item()) + assert np.isnan(result.sel(scenario='low_price', time=sample_time_index[2]).item()) + + def test_mixed_data_types(self, sample_time_index, sample_scenario_index): + """Test conversion of mixed integer and float data.""" + # Create array with mixed types + mixed_array = np.array([1, 2.5, 3, 4.5, 5]) + result = DataConverter.as_dataarray(mixed_array, sample_time_index) + + # Result should be float dtype + assert np.issubdtype(result.dtype, np.floating) + assert np.array_equal(result.values, mixed_array) + + # With scenarios + result = DataConverter.as_dataarray(mixed_array, sample_time_index, sample_scenario_index) + assert np.issubdtype(result.dtype, np.floating) + for scenario in sample_scenario_index: + assert np.array_equal(result.sel(scenario=scenario).values, mixed_array) + + +class TestFunctionalUseCase: + """Tests for realistic use cases combining multiple features.""" + + def test_multiindex_with_nans_and_partial_data(self, sample_time_index, sample_scenario_index): + """Test MultiIndex Series with partial data and NaN values.""" + # Create a MultiIndex Series with missing values and partial coverage + time_subset = sample_time_index[1:4] # Middle 3 timestamps only + + # Build index with holes + idx_tuples = [] + for scenario in sample_scenario_index: + for time in time_subset: + # Skip some combinations to create holes + if scenario == 'baseline' and time == time_subset[0]: + continue + if scenario == 'high_demand' and time == time_subset[2]: + continue + idx_tuples.append((scenario, time)) + + partial_idx = pd.MultiIndex.from_tuples(idx_tuples, names=['scenario', 'time']) + + # Create values with some NaNs + values = [ + # baseline (2 values, skipping first) + 30, + 40, + # high_demand (2 values, skipping last) + 25, + 35, + # low_price (3 values) + 15, + np.nan, + 35, + ] + + # Create Series + partial_series = pd.Series(values, index=partial_idx) + + # Convert and test + result = DataConverter.as_dataarray(partial_series, sample_time_index, sample_scenario_index) + + # Shape should be full size + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + + # Check specific values + assert result.sel(scenario='baseline', time=time_subset[1]).item() == 30 + assert result.sel(scenario='high_demand', time=time_subset[0]).item() == 25 + assert np.isnan(result.sel(scenario='low_price', time=time_subset[1]).item()) + + # All skipped combinations should be NaN + assert np.isnan(result.sel(scenario='baseline', time=time_subset[0]).item()) + assert np.isnan(result.sel(scenario='high_demand', time=time_subset[2]).item()) + + # First and last timestamps should all be NaN (not in original subset) + assert np.all(np.isnan(result.sel(time=sample_time_index[0]).values)) + assert np.all(np.isnan(result.sel(time=sample_time_index[-1]).values)) + + def test_scenario_broadcast_with_nan_values(self, sample_time_index, sample_scenario_index): + """Test broadcasting a Series with NaN values to scenarios.""" + # Create Series with some NaN values + series = pd.Series([1, np.nan, 3, np.nan, 5], index=sample_time_index) + + # Convert with scenario broadcasting + result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + + # All scenarios should have the same pattern of NaN values + for scenario in sample_scenario_index: + scenario_data = result.sel(scenario=scenario) + assert np.isnan(scenario_data[1].item()) + assert np.isnan(scenario_data[3].item()) + assert scenario_data[0].item() == 1 + assert scenario_data[2].item() == 3 + assert scenario_data[4].item() == 5 + + def test_large_dataset(self, sample_scenario_index): + """Test with a larger dataset to ensure performance.""" + # Create a larger timestep array (e.g., hourly for a year) + large_timesteps = pd.date_range( + '2024-01-01', + periods=8760, # Hours in a year + freq='H', + name='time', + ) + + # Create large 2D array (3 scenarios × 8760 hours) + large_data = np.random.rand(len(sample_scenario_index), len(large_timesteps)) + + # Convert and check + result = DataConverter.as_dataarray(large_data, large_timesteps, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(large_timesteps)) + assert result.dims == ('scenario', 'time') + assert np.array_equal(result.values, large_data) + + +class TestMultiScenarioArrayConversion: + """Tests specifically focused on array conversion with scenarios.""" + + def test_1d_array_broadcasting(self, sample_time_index, sample_scenario_index): + """Test that 1D arrays are properly broadcast to all scenarios.""" + arr_1d = np.array([1, 2, 3, 4, 5]) + result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + + # Each scenario should have identical values + for i, scenario in enumerate(sample_scenario_index): + assert np.array_equal(result.sel(scenario=scenario).values, arr_1d) + + # Modify one scenario's values + result.loc[dict(scenario=scenario)] = np.ones(len(sample_time_index)) * i + + # Ensure modifications are isolated to each scenario + for i, scenario in enumerate(sample_scenario_index): + assert np.all(result.sel(scenario=scenario).values == i) + + def test_2d_array_different_shapes(self, sample_time_index): + """Test different scenario shapes with 2D arrays.""" + # Test with 1 scenario + single_scenario = pd.Index(['baseline'], name='scenario') + arr_1_scenario = np.array([[1, 2, 3, 4, 5]]) + + result = DataConverter.as_dataarray(arr_1_scenario, sample_time_index, single_scenario) + assert result.shape == (1, len(sample_time_index)) + + # Test with 2 scenarios + two_scenarios = pd.Index(['baseline', 'high_demand'], name='scenario') + arr_2_scenarios = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) + + result = DataConverter.as_dataarray(arr_2_scenarios, sample_time_index, two_scenarios) + assert result.shape == (2, len(sample_time_index)) + assert np.array_equal(result.sel(scenario='baseline').values, arr_2_scenarios[0]) + assert np.array_equal(result.sel(scenario='high_demand').values, arr_2_scenarios[1]) + + # Test mismatched scenarios count + three_scenarios = pd.Index(['a', 'b', 'c'], name='scenario') + with pytest.raises(ConversionError): + DataConverter.as_dataarray(arr_2_scenarios, sample_time_index, three_scenarios) + + def test_array_handling_edge_cases(self, sample_time_index, sample_scenario_index): + """Test array edge cases.""" + # Test with boolean array + bool_array = np.array([True, False, True, False, True]) + result = DataConverter.as_dataarray(bool_array, sample_time_index, sample_scenario_index) + assert result.dtype == bool + assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + + # Test with array containing infinite values + inf_array = np.array([1, np.inf, 3, -np.inf, 5]) + result = DataConverter.as_dataarray(inf_array, sample_time_index, sample_scenario_index) + for scenario in sample_scenario_index: + scenario_data = result.sel(scenario=scenario) + assert np.isinf(scenario_data[1].item()) + assert np.isinf(scenario_data[3].item()) + assert scenario_data[3].item() < 0 # Negative infinity + + +class TestScenarioReindexing: + """Tests for reindexing and coordinate preservation in DataConverter.""" + + def test_preserving_scenario_order(self, sample_time_index): + """Test that scenario order is preserved in converted DataArrays.""" + # Define scenarios in a specific order + scenarios = pd.Index(['scenario3', 'scenario1', 'scenario2'], name='scenario') + + # Create 2D array + data = np.array( + [ + [1, 2, 3, 4, 5], # scenario3 + [6, 7, 8, 9, 10], # scenario1 + [11, 12, 13, 14, 15], # scenario2 + ] + ) + + # Convert to DataArray + result = DataConverter.as_dataarray(data, sample_time_index, scenarios) + + # Verify order of scenarios is preserved + assert list(result.coords['scenario'].values) == list(scenarios) + + # Verify data for each scenario + assert np.array_equal(result.sel(scenario='scenario3').values, data[0]) + assert np.array_equal(result.sel(scenario='scenario1').values, data[1]) + assert np.array_equal(result.sel(scenario='scenario2').values, data[2]) + + def test_multiindex_reindexing(self, sample_time_index): + """Test reindexing of MultiIndex Series.""" + # Create scenarios with intentional different order + scenarios = pd.Index(['z_scenario', 'a_scenario', 'm_scenario'], name='scenario') + + # Create MultiIndex with different order than the target + source_scenarios = pd.Index(['a_scenario', 'm_scenario', 'z_scenario'], name='scenario') + multi_idx = pd.MultiIndex.from_product([source_scenarios, sample_time_index], names=['scenario', 'time']) + + # Create values - order should match the source index + values = [] + for i, _ in enumerate(source_scenarios): + values.extend([i * 10 + j for j in range(1, len(sample_time_index) + 1)]) + + # Create Series + series = pd.Series(values, index=multi_idx) + + # Convert using the target scenario order + result = DataConverter.as_dataarray(series, sample_time_index, scenarios) + + # Verify scenario order matches the target + assert list(result.coords['scenario'].values) == list(scenarios) + + # Verify values are correctly indexed + assert np.array_equal(result.sel(scenario='a_scenario').values, [1, 2, 3, 4, 5]) + assert np.array_equal(result.sel(scenario='m_scenario').values, [11, 12, 13, 14, 15]) + assert np.array_equal(result.sel(scenario='z_scenario').values, [21, 22, 23, 24, 25]) + + +if __name__ == '__main__': + pytest.main() def test_invalid_inputs(sample_time_index): diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index 48c7ab7b2..50136536b 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -8,7 +8,7 @@ import pytest import xarray as xr -from flixopt.core import ConversionError, DataConverter, TimeSeries, TimeSeriesCollection, TimeSeriesData +from flixopt.core import ConversionError, DataConverter, TimeSeries, TimeSeriesCollection @pytest.fixture @@ -44,13 +44,13 @@ def test_initialization(self, simple_dataarray): # Check data initialization assert isinstance(ts.stored_data, xr.DataArray) assert ts.stored_data.equals(simple_dataarray) - assert ts.active_data.equals(simple_dataarray) + assert ts.selected_data.equals(simple_dataarray) # Check backup was created assert ts._backup.equals(simple_dataarray) # Check active timesteps - assert ts.active_timesteps.equals(simple_dataarray.indexes['time']) + assert ts._valid_selector == {} # No selections initially def test_initialization_with_aggregation_params(self, simple_dataarray): """Test initialization with aggregation parameters.""" @@ -73,53 +73,51 @@ def test_initialization_validation(self, sample_timesteps): multi_dim_data = xr.DataArray( [[1, 2, 3], [4, 5, 6]], coords={'dim1': [0, 1], 'time': sample_timesteps[:3]}, dims=['dim1', 'time'] ) - with pytest.raises(ValueError, match='dimensions of DataArray must be 1'): + with pytest.raises(ValueError, match='DataArray dimensions must be subset of'): TimeSeries(multi_dim_data, name='Multi-dim Series') - def test_active_timesteps_getter_setter(self, sample_timeseries, sample_timesteps): - """Test active_timesteps getter and setter.""" - # Initial state should use all timesteps - assert sample_timeseries.active_timesteps.equals(sample_timesteps) + def test_selection_methods(self, sample_timeseries, sample_timesteps): + """Test selection methods.""" + # Initial state should have no selections + assert sample_timeseries._selected_timesteps is None + assert sample_timeseries._selected_scenarios is None # Set to a subset subset_index = sample_timesteps[1:3] - sample_timeseries.active_timesteps = subset_index - assert sample_timeseries.active_timesteps.equals(subset_index) + sample_timeseries.set_selection(timesteps=subset_index) + assert sample_timeseries._selected_timesteps.equals(subset_index) # Active data should reflect the subset - assert sample_timeseries.active_data.equals(sample_timeseries.stored_data.sel(time=subset_index)) + assert sample_timeseries.selected_data.equals(sample_timeseries.stored_data.sel(time=subset_index)) - # Reset to full index - sample_timeseries.active_timesteps = None - assert sample_timeseries.active_timesteps.equals(sample_timesteps) - - # Test invalid type - with pytest.raises(TypeError, match='must be a pandas DatetimeIndex'): - sample_timeseries.active_timesteps = 'invalid' + # Clear selection + sample_timeseries.clear_selection() + assert sample_timeseries._selected_timesteps is None + assert sample_timeseries.selected_data.equals(sample_timeseries.stored_data) def test_reset(self, sample_timeseries, sample_timesteps): """Test reset method.""" # Set to subset first subset_index = sample_timesteps[1:3] - sample_timeseries.active_timesteps = subset_index + sample_timeseries.set_selection(timesteps=subset_index) # Reset sample_timeseries.reset() - # Should be back to full index - assert sample_timeseries.active_timesteps.equals(sample_timesteps) - assert sample_timeseries.active_data.equals(sample_timeseries.stored_data) + # Should be back to full index (all selections cleared) + assert sample_timeseries._selected_timesteps is None + assert sample_timeseries.selected_data.equals(sample_timeseries.stored_data) def test_restore_data(self, sample_timeseries, simple_dataarray): """Test restore_data method.""" # Modify the stored data - new_data = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) + new_data = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) # Store original data for comparison original_data = sample_timeseries.stored_data - # Set new data - sample_timeseries.stored_data = new_data + # Update data + sample_timeseries.update_stored_data(new_data) assert sample_timeseries.stored_data.equals(new_data) # Restore from backup @@ -127,42 +125,42 @@ def test_restore_data(self, sample_timeseries, simple_dataarray): # Should be back to original data assert sample_timeseries.stored_data.equals(original_data) - assert sample_timeseries.active_data.equals(original_data) + assert sample_timeseries.selected_data.equals(original_data) - def test_stored_data_setter(self, sample_timeseries, sample_timesteps): - """Test stored_data setter with different data types.""" + def test_update_stored_data(self, sample_timeseries, sample_timesteps): + """Test update_stored_data method with different data types.""" # Test with a Series series_data = pd.Series([5, 6, 7, 8, 9], index=sample_timesteps) - sample_timeseries.stored_data = series_data + sample_timeseries.update_stored_data(series_data) assert np.array_equal(sample_timeseries.stored_data.values, series_data.values) # Test with a single-column DataFrame df_data = pd.DataFrame({'col1': [15, 16, 17, 18, 19]}, index=sample_timesteps) - sample_timeseries.stored_data = df_data + sample_timeseries.update_stored_data(df_data) assert np.array_equal(sample_timeseries.stored_data.values, df_data['col1'].values) # Test with a NumPy array array_data = np.array([25, 26, 27, 28, 29]) - sample_timeseries.stored_data = array_data + sample_timeseries.update_stored_data(array_data) assert np.array_equal(sample_timeseries.stored_data.values, array_data) # Test with a scalar - sample_timeseries.stored_data = 42 + sample_timeseries.update_stored_data(42) assert np.all(sample_timeseries.stored_data.values == 42) # Test with another DataArray another_dataarray = xr.DataArray([30, 31, 32, 33, 34], coords={'time': sample_timesteps}, dims=['time']) - sample_timeseries.stored_data = another_dataarray + sample_timeseries.update_stored_data(another_dataarray) assert sample_timeseries.stored_data.equals(another_dataarray) def test_stored_data_setter_no_change(self, sample_timeseries): - """Test stored_data setter when data doesn't change.""" + """Test update_stored_data method when data doesn't change.""" # Get current data current_data = sample_timeseries.stored_data current_backup = sample_timeseries._backup # Set the same data - sample_timeseries.stored_data = current_data + sample_timeseries.update_stored_data(current_data) # Backup shouldn't change assert sample_timeseries._backup is current_backup # Should be the same object @@ -229,35 +227,35 @@ def test_all_equal(self, sample_timesteps): def test_arithmetic_operations(self, sample_timeseries): """Test arithmetic operations.""" # Create a second TimeSeries for testing - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) + data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) ts2 = TimeSeries(data2, 'Second Series') # Test operations between two TimeSeries objects assert np.array_equal( - (sample_timeseries + ts2).values, sample_timeseries.active_data.values + ts2.active_data.values + (sample_timeseries + ts2).values, sample_timeseries.selected_data.values + ts2.selected_data.values ) assert np.array_equal( - (sample_timeseries - ts2).values, sample_timeseries.active_data.values - ts2.active_data.values + (sample_timeseries - ts2).values, sample_timeseries.selected_data.values - ts2.selected_data.values ) assert np.array_equal( - (sample_timeseries * ts2).values, sample_timeseries.active_data.values * ts2.active_data.values + (sample_timeseries * ts2).values, sample_timeseries.selected_data.values * ts2.selected_data.values ) assert np.array_equal( - (sample_timeseries / ts2).values, sample_timeseries.active_data.values / ts2.active_data.values + (sample_timeseries / ts2).values, sample_timeseries.selected_data.values / ts2.selected_data.values ) # Test operations with DataArrays - assert np.array_equal((sample_timeseries + data2).values, sample_timeseries.active_data.values + data2.values) - assert np.array_equal((data2 + sample_timeseries).values, data2.values + sample_timeseries.active_data.values) + assert np.array_equal((sample_timeseries + data2).values, sample_timeseries.selected_data.values + data2.values) + assert np.array_equal((data2 + sample_timeseries).values, data2.values + sample_timeseries.selected_data.values) # Test operations with scalars - assert np.array_equal((sample_timeseries + 5).values, sample_timeseries.active_data.values + 5) - assert np.array_equal((5 + sample_timeseries).values, 5 + sample_timeseries.active_data.values) + assert np.array_equal((sample_timeseries + 5).values, sample_timeseries.selected_data.values + 5) + assert np.array_equal((5 + sample_timeseries).values, 5 + sample_timeseries.selected_data.values) # Test unary operations - assert np.array_equal((-sample_timeseries).values, -sample_timeseries.active_data.values) - assert np.array_equal((+sample_timeseries).values, +sample_timeseries.active_data.values) - assert np.array_equal((abs(sample_timeseries)).values, abs(sample_timeseries.active_data.values)) + assert np.array_equal((-sample_timeseries).values, -sample_timeseries.selected_data.values) + assert np.array_equal((+sample_timeseries).values, +sample_timeseries.selected_data.values) + assert np.array_equal((abs(sample_timeseries)).values, abs(sample_timeseries.selected_data.values)) def test_comparison_operations(self, sample_timesteps): """Test comparison operations.""" @@ -279,327 +277,473 @@ def test_comparison_operations(self, sample_timesteps): def test_numpy_ufunc(self, sample_timeseries): """Test numpy ufunc compatibility.""" # Test basic numpy functions - assert np.array_equal(np.add(sample_timeseries, 5).values, np.add(sample_timeseries.active_data, 5).values) + assert np.array_equal(np.add(sample_timeseries, 5).values, np.add(sample_timeseries.selected_data, 5).values) assert np.array_equal( - np.multiply(sample_timeseries, 2).values, np.multiply(sample_timeseries.active_data, 2).values + np.multiply(sample_timeseries, 2).values, np.multiply(sample_timeseries.selected_data, 2).values ) # Test with two TimeSeries objects - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) + data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) ts2 = TimeSeries(data2, 'Second Series') assert np.array_equal( - np.add(sample_timeseries, ts2).values, np.add(sample_timeseries.active_data, ts2.active_data).values + np.add(sample_timeseries, ts2).values, np.add(sample_timeseries.selected_data, ts2.selected_data).values ) def test_sel_and_isel_properties(self, sample_timeseries): """Test sel and isel properties.""" # Test that sel property works - selected = sample_timeseries.sel(time=sample_timeseries.active_timesteps[0]) - assert selected.item() == sample_timeseries.active_data.values[0] + selected = sample_timeseries.sel(time=sample_timeseries.stored_data.coords['time'][0]) + assert selected.item() == sample_timeseries.selected_data.values[0] # Test that isel property works indexed = sample_timeseries.isel(time=0) - assert indexed.item() == sample_timeseries.active_data.values[0] + assert indexed.item() == sample_timeseries.selected_data.values[0] + + +@pytest.fixture +def sample_scenario_index(): + """Create a sample scenario index with the required 'scenario' name.""" + return pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') + + +@pytest.fixture +def simple_scenario_dataarray(sample_timesteps, sample_scenario_index): + """Create a DataArray with both scenario and time dimensions.""" + data = np.array([ + [10, 20, 30, 40, 50], # baseline + [15, 25, 35, 45, 55], # high_demand + [5, 15, 25, 35, 45] # low_price + ]) + return xr.DataArray( + data=data, + coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, + dims=['scenario', 'time'] + ) @pytest.fixture -def sample_collection(sample_timesteps): +def sample_scenario_timeseries(simple_scenario_dataarray): + """Create a sample TimeSeries object with scenario dimension.""" + return TimeSeries(simple_scenario_dataarray, name='Test Scenario Series') + + +@pytest.fixture +def sample_allocator(sample_timesteps): """Create a sample TimeSeriesCollection.""" return TimeSeriesCollection(sample_timesteps) @pytest.fixture -def populated_collection(sample_collection): - """Create a TimeSeriesCollection with test data.""" - # Add a constant time series - sample_collection.create_time_series(42, 'constant_series') - - # Add a varying time series - varying_data = np.array([10, 20, 30, 40, 50]) - sample_collection.create_time_series(varying_data, 'varying_series') - - # Add a time series with extra timestep - sample_collection.create_time_series( - np.array([1, 2, 3, 4, 5, 6]), 'extra_timestep_series', needs_extra_timestep=True - ) +def sample_scenario_allocator(sample_timesteps, sample_scenario_index): + """Create a sample TimeSeriesCollection with scenarios.""" + return TimeSeriesCollection(sample_timesteps, scenarios=sample_scenario_index) - # Add series with aggregation settings - sample_collection.create_time_series( - TimeSeriesData(np.array([5, 5, 5, 5, 5]), agg_group='group1'), 'group1_series1' - ) - sample_collection.create_time_series( - TimeSeriesData(np.array([6, 6, 6, 6, 6]), agg_group='group1'), 'group1_series2' - ) - sample_collection.create_time_series( - TimeSeriesData(np.array([10, 10, 10, 10, 10]), agg_weight=0.5), 'weighted_series' - ) - return sample_collection +class TestTimeSeriesWithScenarios: + """Test suite for TimeSeries class with scenarios.""" + + def test_initialization_with_scenarios(self, simple_scenario_dataarray): + """Test initialization of TimeSeries with scenario dimension.""" + ts = TimeSeries(simple_scenario_dataarray, name='Scenario Series') + # Check basic properties + assert ts.name == 'Scenario Series' + assert ts._has_scenarios is True + assert ts._selected_scenarios is None # No selection initially + + # Check data initialization + assert isinstance(ts.stored_data, xr.DataArray) + assert ts.stored_data.equals(simple_scenario_dataarray) + assert ts.selected_data.equals(simple_scenario_dataarray) + + # Check backup was created + assert ts._backup.equals(simple_scenario_dataarray) + + def test_reset_with_scenarios(self, sample_scenario_timeseries, simple_scenario_dataarray): + """Test reset method with scenarios.""" + # Get original full indexes + full_timesteps = simple_scenario_dataarray.coords['time'] + full_scenarios = simple_scenario_dataarray.coords['scenario'] + + # Set to subset timesteps and scenarios + subset_timesteps = full_timesteps[1:3] + subset_scenarios = full_scenarios[:2] -class TestTimeSeriesCollection: - """Test suite for TimeSeriesCollection.""" + sample_scenario_timeseries.set_selection(timesteps=subset_timesteps, scenarios=subset_scenarios) + + # Verify subsets were set + assert sample_scenario_timeseries._selected_timesteps.equals(subset_timesteps) + assert sample_scenario_timeseries._selected_scenarios.equals(subset_scenarios) + assert sample_scenario_timeseries.selected_data.shape == (len(subset_scenarios), len(subset_timesteps)) + + # Reset + sample_scenario_timeseries.reset() + + # Should be back to full indexes + assert sample_scenario_timeseries._selected_timesteps is None + assert sample_scenario_timeseries._selected_scenarios is None + assert sample_scenario_timeseries.selected_data.shape == (len(full_scenarios), len(full_timesteps)) + + def test_scenario_selection(self, sample_scenario_timeseries, sample_scenario_index): + """Test scenario selection.""" + # Initial state should use all scenarios + assert sample_scenario_timeseries._selected_scenarios is None + + # Set to a subset + subset_index = sample_scenario_index[:2] # First two scenarios + sample_scenario_timeseries.set_selection(scenarios=subset_index) + assert sample_scenario_timeseries._selected_scenarios.equals(subset_index) + + # Active data should reflect the subset + assert sample_scenario_timeseries.selected_data.equals( + sample_scenario_timeseries.stored_data.sel(scenario=subset_index) + ) + + # Clear selection + sample_scenario_timeseries.clear_selection(timesteps=False, scenarios=True) + assert sample_scenario_timeseries._selected_scenarios is None + + def test_all_equal_with_scenarios(self, sample_timesteps, sample_scenario_index): + """Test all_equal property with scenarios.""" + # All values equal across all scenarios + equal_data = np.full((3, 5), 5) # All values are 5 + equal_dataarray = xr.DataArray( + data=equal_data, + coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, + dims=['scenario', 'time'] + ) + ts_equal = TimeSeries(equal_dataarray, 'Equal Scenario Series') + assert ts_equal.all_equal is True + + # Equal within each scenario but different between scenarios + per_scenario_equal = np.array([ + [5, 5, 5, 5, 5], # baseline - all 5 + [10, 10, 10, 10, 10], # high_demand - all 10 + [15, 15, 15, 15, 15] # low_price - all 15 + ]) + per_scenario_dataarray = xr.DataArray( + data=per_scenario_equal, + coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, + dims=['scenario', 'time'] + ) + ts_per_scenario = TimeSeries(per_scenario_dataarray, 'Per-Scenario Equal Series') + assert ts_per_scenario.all_equal is False + + def test_arithmetic_with_scenarios(self, sample_scenario_timeseries, sample_timesteps, sample_scenario_index): + """Test arithmetic operations with scenarios.""" + # Create a second TimeSeries with scenarios + data2 = np.ones((3, 5)) # All ones + second_dataarray = xr.DataArray( + data=data2, + coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, + dims=['scenario', 'time'] + ) + ts2 = TimeSeries(second_dataarray, 'Second Series') + + # Test operations between two scenario TimeSeries objects + result = sample_scenario_timeseries + ts2 + assert result.shape == (3, 5) + assert result.dims == ('scenario', 'time') + + # First scenario values should be increased by 1 + baseline_original = sample_scenario_timeseries.sel(scenario='baseline').values + baseline_result = result.sel(scenario='baseline').values + assert np.array_equal(baseline_result, baseline_original + 1) + + +class TestTimeSeriesAllocator: + """Test suite for TimeSeriesCollection class.""" def test_initialization(self, sample_timesteps): """Test basic initialization.""" - collection = TimeSeriesCollection(sample_timesteps) + allocator = TimeSeriesCollection(sample_timesteps) - assert collection.all_timesteps.equals(sample_timesteps) - assert len(collection.all_timesteps_extra) == len(sample_timesteps) + 1 - assert isinstance(collection.all_hours_per_timestep, xr.DataArray) - assert len(collection) == 0 + assert allocator.timesteps.equals(sample_timesteps) + assert len(allocator.timesteps_extra) == len(sample_timesteps) + 1 + assert isinstance(allocator.hours_per_timestep, xr.DataArray) + assert len(allocator._time_series) == 0 def test_initialization_with_custom_hours(self, sample_timesteps): """Test initialization with custom hour settings.""" # Test with last timestep duration last_timestep_hours = 12 - collection = TimeSeriesCollection(sample_timesteps, hours_of_last_timestep=last_timestep_hours) + allocator = TimeSeriesCollection(sample_timesteps, hours_of_last_timestep=last_timestep_hours) # Verify the last timestep duration - extra_step_delta = collection.all_timesteps_extra[-1] - collection.all_timesteps_extra[-2] + extra_step_delta = allocator.timesteps_extra[-1] - allocator.timesteps_extra[-2] assert extra_step_delta == pd.Timedelta(hours=last_timestep_hours) # Test with previous timestep duration hours_per_step = 8 - collection2 = TimeSeriesCollection(sample_timesteps, hours_of_previous_timesteps=hours_per_step) + allocator2 = TimeSeriesCollection(sample_timesteps, hours_of_previous_timesteps=hours_per_step) - assert collection2.hours_of_previous_timesteps == hours_per_step + assert allocator2.hours_of_previous_timesteps == hours_per_step - def test_create_time_series(self, sample_collection): - """Test creating time series.""" + def test_add_time_series(self, sample_allocator, sample_timesteps): + """Test adding time series.""" # Test scalar - ts1 = sample_collection.create_time_series(42, 'scalar_series') + ts1 = sample_allocator.add_time_series('scalar_series', 42) assert ts1.name == 'scalar_series' - assert np.all(ts1.active_data.values == 42) + assert np.all(ts1.selected_data.values == 42) # Test numpy array data = np.array([1, 2, 3, 4, 5]) - ts2 = sample_collection.create_time_series(data, 'array_series') - assert np.array_equal(ts2.active_data.values, data) + ts2 = sample_allocator.add_time_series('array_series', data) + assert np.array_equal(ts2.selected_data.values, data) - # Test with TimeSeriesData - ts3 = sample_collection.create_time_series(TimeSeriesData(10, agg_weight=0.7), 'weighted_series') - assert ts3.aggregation_weight == 0.7 + # Test with existing TimeSeries + existing_ts = TimeSeries.from_datasource(10, 'original_name', sample_timesteps, aggregation_weight=0.7) + ts3 = sample_allocator.add_time_series('weighted_series', existing_ts) + assert ts3.name == 'weighted_series' # Name changed + assert ts3.aggregation_weight == 0.7 # Weight preserved # Test with extra timestep - ts4 = sample_collection.create_time_series(5, 'extra_series', needs_extra_timestep=True) - assert ts4.needs_extra_timestep - assert len(ts4.active_data) == len(sample_collection.timesteps_extra) + ts4 = sample_allocator.add_time_series('extra_series', 5, has_extra_timestep=True) + assert ts4.name == 'extra_series' + assert ts4.has_extra_timestep + assert len(ts4.selected_data) == len(sample_allocator.timesteps_extra) # Test duplicate name - with pytest.raises(ValueError, match='already exists'): - sample_collection.create_time_series(1, 'scalar_series') + with pytest.raises(KeyError, match='already exists'): + sample_allocator.add_time_series('scalar_series', 1) - def test_access_time_series(self, populated_collection): + def test_access_time_series(self, sample_allocator): """Test accessing time series.""" + # Add a few time series + sample_allocator.add_time_series('series1', 42) + sample_allocator.add_time_series('series2', np.array([1, 2, 3, 4, 5])) + # Test __getitem__ - ts = populated_collection['varying_series'] - assert ts.name == 'varying_series' + ts = sample_allocator['series1'] + assert ts.name == 'series1' # Test __contains__ with string - assert 'constant_series' in populated_collection - assert 'nonexistent_series' not in populated_collection + assert 'series1' in sample_allocator + assert 'nonexistent_series' not in sample_allocator # Test __contains__ with TimeSeries object - assert populated_collection['varying_series'] in populated_collection - - # Test __iter__ - names = [ts.name for ts in populated_collection] - assert len(names) == 6 - assert 'varying_series' in names + assert sample_allocator['series2'] in sample_allocator # Test access to non-existent series - with pytest.raises(KeyError): - populated_collection['nonexistent_series'] - - def test_constants_and_non_constants(self, populated_collection): - """Test constants and non_constants properties.""" - # Test constants - constants = populated_collection.constants - assert len(constants) == 4 # constant_series, group1_series1, group1_series2, weighted_series - assert all(ts.all_equal for ts in constants) - - # Test non_constants - non_constants = populated_collection.non_constants - assert len(non_constants) == 2 # varying_series, extra_timestep_series - assert all(not ts.all_equal for ts in non_constants) - - # Test modifying a series changes the results - populated_collection['constant_series'].stored_data = np.array([1, 2, 3, 4, 5]) - updated_constants = populated_collection.constants - assert len(updated_constants) == 3 # One less constant - assert 'constant_series' not in [ts.name for ts in updated_constants] - - def test_timesteps_properties(self, populated_collection, sample_timesteps): - """Test timestep-related properties.""" - # Test default (all) timesteps - assert populated_collection.timesteps.equals(sample_timesteps) - assert len(populated_collection.timesteps_extra) == len(sample_timesteps) + 1 - - # Test activating a subset - subset = sample_timesteps[1:3] - populated_collection.activate_timesteps(subset) - - assert populated_collection.timesteps.equals(subset) - assert len(populated_collection.timesteps_extra) == len(subset) + 1 - - # Check that time series were updated - assert populated_collection['varying_series'].active_timesteps.equals(subset) - assert populated_collection['extra_timestep_series'].active_timesteps.equals( - populated_collection.timesteps_extra - ) - - # Test reset - populated_collection.reset() - assert populated_collection.timesteps.equals(sample_timesteps) + with pytest.raises(ValueError): + sample_allocator['nonexistent_series'] - def test_to_dataframe_and_dataset(self, populated_collection): - """Test conversion to DataFrame and Dataset.""" - # Test to_dataset - ds = populated_collection.to_dataset() - assert isinstance(ds, xr.Dataset) - assert len(ds.data_vars) == 6 + def test_selection_propagation(self, sample_allocator, sample_timesteps): + """Test that selections propagate to TimeSeries.""" + # Add a few time series + ts1 = sample_allocator.add_time_series('series1', 42) + ts2 = sample_allocator.add_time_series('series2', np.array([1, 2, 3, 4, 5])) + ts3 = sample_allocator.add_time_series('series3', 5, has_extra_timestep=True) - # Test to_dataframe with different filters - df_all = populated_collection.to_dataframe(filtered='all') - assert len(df_all.columns) == 6 + # Initially no selections + assert ts1._selected_timesteps is None + assert ts2._selected_timesteps is None + assert ts3._selected_timesteps is None - df_constant = populated_collection.to_dataframe(filtered='constant') - assert len(df_constant.columns) == 4 + # Apply selection + subset_timesteps = sample_timesteps[1:3] + sample_allocator.set_selection(timesteps=subset_timesteps) - df_non_constant = populated_collection.to_dataframe(filtered='non_constant') - assert len(df_non_constant.columns) == 2 + # Check selection propagated to regular time series + assert ts1._selected_timesteps.equals(subset_timesteps) + assert ts2._selected_timesteps.equals(subset_timesteps) - # Test invalid filter - with pytest.raises(ValueError): - populated_collection.to_dataframe(filtered='invalid') - - def test_calculate_aggregation_weights(self, populated_collection): - """Test aggregation weight calculation.""" - weights = populated_collection.calculate_aggregation_weights() - - # Group weights should be 0.5 each (1/2) - assert populated_collection.group_weights['group1'] == 0.5 - - # Series in group1 should have weight 0.5 - assert weights['group1_series1'] == 0.5 - assert weights['group1_series2'] == 0.5 - - # Series with explicit weight should have that weight - assert weights['weighted_series'] == 0.5 - - # Series without group or weight should have weight 1 - assert weights['constant_series'] == 1 - - def test_insert_new_data(self, populated_collection, sample_timesteps): - """Test inserting new data.""" - # Create new data - new_data = pd.DataFrame( - { - 'constant_series': [100, 100, 100, 100, 100], - 'varying_series': [5, 10, 15, 20, 25], - # extra_timestep_series is omitted to test partial updates - }, - index=sample_timesteps, - ) + # Check selection with extra timestep + assert ts3._selected_timesteps is not None + assert len(ts3._selected_timesteps) == len(subset_timesteps) + 1 - # Insert data - populated_collection.insert_new_data(new_data) + # Clear selection + sample_allocator.clear_selection() - # Verify updates - assert np.all(populated_collection['constant_series'].active_data.values == 100) - assert np.array_equal(populated_collection['varying_series'].active_data.values, np.array([5, 10, 15, 20, 25])) + # Check selection cleared + assert ts1._selected_timesteps is None + assert ts2._selected_timesteps is None + assert ts3._selected_timesteps is None - # Series not in the DataFrame should be unchanged - assert np.array_equal( - populated_collection['extra_timestep_series'].active_data.values[:-1], np.array([1, 2, 3, 4, 5]) - ) + def test_update_time_series(self, sample_allocator): + """Test updating a time series.""" + # Add a time series + ts = sample_allocator.add_time_series('series', 42) - # Test with mismatched index - bad_index = pd.date_range('2023-02-01', periods=5, freq='D', name='time') - bad_data = pd.DataFrame({'constant_series': [1, 1, 1, 1, 1]}, index=bad_index) - - with pytest.raises(ValueError, match='must match collection timesteps'): - populated_collection.insert_new_data(bad_data) - - def test_restore_data(self, populated_collection): - """Test restoring original data.""" - # Capture original data - original_values = {name: ts.stored_data.copy() for name, ts in populated_collection.time_series_data.items()} - - # Modify data - new_data = pd.DataFrame( - { - name: np.ones(len(populated_collection.timesteps)) * 999 - for name in populated_collection.time_series_data - if not populated_collection[name].needs_extra_timestep - }, - index=populated_collection.timesteps, - ) + # Update it + sample_allocator.update_time_series('series', np.array([1, 2, 3, 4, 5])) - populated_collection.insert_new_data(new_data) + # Check update was applied + assert np.array_equal(ts.selected_data.values, np.array([1, 2, 3, 4, 5])) - # Verify data was changed - assert np.all(populated_collection['constant_series'].active_data.values == 999) + # Test updating non-existent series + with pytest.raises(KeyError): + sample_allocator.update_time_series('nonexistent', 42) - # Restore data - populated_collection.restore_data() + def test_as_dataset(self, sample_allocator): + """Test as_dataset method.""" + # Add some time series + sample_allocator.add_time_series('series1', 42) + sample_allocator.add_time_series('series2', np.array([1, 2, 3, 4, 5])) - # Verify data was restored - for name, original in original_values.items(): - restored = populated_collection[name].stored_data - assert np.array_equal(restored.values, original.values) + # Get dataset + ds = sample_allocator.as_dataset(with_extra_timestep=False) - def test_class_method_with_uniform_timesteps(self): - """Test the with_uniform_timesteps class method.""" - collection = TimeSeriesCollection.with_uniform_timesteps( - start_time=pd.Timestamp('2023-01-01'), periods=24, freq='H', hours_per_step=1 - ) + # Check dataset contents + assert isinstance(ds, xr.Dataset) + assert 'series1' in ds + assert 'series2' in ds + assert np.all(ds['series1'].values == 42) + assert np.array_equal(ds['series2'].values, np.array([1, 2, 3, 4, 5])) - assert len(collection.timesteps) == 24 - assert collection.hours_of_previous_timesteps == 1 - assert (collection.timesteps[1] - collection.timesteps[0]) == pd.Timedelta(hours=1) - - def test_hours_per_timestep(self, populated_collection): - """Test hours_per_timestep calculation.""" - # Standard case - uniform timesteps - hours = populated_collection.hours_per_timestep.values - assert np.allclose(hours, 24) # Default is daily timesteps - - # Create non-uniform timesteps - non_uniform_times = pd.DatetimeIndex( - [ - pd.Timestamp('2023-01-01'), - pd.Timestamp('2023-01-02'), - pd.Timestamp('2023-01-03 12:00:00'), # 1.5 days from previous - pd.Timestamp('2023-01-04'), # 0.5 days from previous - pd.Timestamp('2023-01-06'), # 2 days from previous - ], - name='time', - ) - collection = TimeSeriesCollection(non_uniform_times) - hours = collection.hours_per_timestep.values +class TestTimeSeriesAllocatorWithScenarios: + """Test suite for TimeSeriesCollection with scenarios.""" - # Expected hours between timestamps - expected = np.array([24, 36, 12, 48, 48]) - assert np.allclose(hours, expected) + def test_initialization_with_scenarios(self, sample_timesteps, sample_scenario_index): + """Test initialization with scenarios.""" + allocator = TimeSeriesCollection(sample_timesteps, scenarios=sample_scenario_index) - def test_validation_and_errors(self, sample_timesteps): - """Test validation and error handling.""" - # Test non-DatetimeIndex - with pytest.raises(TypeError, match='must be a pandas DatetimeIndex'): - TimeSeriesCollection(pd.Index([1, 2, 3, 4, 5])) + assert allocator.timesteps.equals(sample_timesteps) + assert allocator.scenarios.equals(sample_scenario_index) + assert len(allocator._time_series) == 0 - # Test too few timesteps - with pytest.raises(ValueError, match='must contain at least 2 timestamps'): - TimeSeriesCollection(pd.DatetimeIndex([pd.Timestamp('2023-01-01')], name='time')) + def test_add_time_series_with_scenarios(self, sample_scenario_allocator): + """Test creating time series with scenarios.""" + # Test scalar (broadcasts to all scenarios) + ts1 = sample_scenario_allocator.add_time_series('scalar_series', 42) + assert ts1._has_scenarios + assert ts1.name == 'scalar_series' + assert ts1.selected_data.shape == (3, 5) # 3 scenarios, 5 timesteps + assert np.all(ts1.selected_data.values == 42) - # Test invalid active_timesteps - collection = TimeSeriesCollection(sample_timesteps) - invalid_timesteps = pd.date_range('2024-01-01', periods=3, freq='D', name='time') + # Test 1D array (broadcasts to all scenarios) + data = np.array([1, 2, 3, 4, 5]) + ts2 = sample_scenario_allocator.add_time_series('array_series', data) + assert ts2._has_scenarios + assert ts2.selected_data.shape == (3, 5) + # Each scenario should have the same values + for scenario in sample_scenario_allocator.scenarios: + assert np.array_equal(ts2.sel(scenario=scenario).values, data) + + # Test 2D array (one row per scenario) + data_2d = np.array([ + [10, 20, 30, 40, 50], + [15, 25, 35, 45, 55], + [5, 15, 25, 35, 45] + ]) + ts3 = sample_scenario_allocator.add_time_series('scenario_specific_series', data_2d) + assert ts3._has_scenarios + assert ts3.selected_data.shape == (3, 5) + # Each scenario should have its own values + assert np.array_equal(ts3.sel(scenario='baseline').values, data_2d[0]) + assert np.array_equal(ts3.sel(scenario='high_demand').values, data_2d[1]) + assert np.array_equal(ts3.sel(scenario='low_price').values, data_2d[2]) + + def test_selection_propagation_with_scenarios(self, sample_scenario_allocator, sample_timesteps, sample_scenario_index): + """Test scenario selection propagation.""" + # Add some time series + ts1 = sample_scenario_allocator.add_time_series('series1', 42) + ts2 = sample_scenario_allocator.add_time_series('series2', np.array([1, 2, 3, 4, 5])) + + # Initial state - no selections + assert ts1._selected_scenarios is None + assert ts2._selected_scenarios is None + + # Select scenarios + subset_scenarios = sample_scenario_index[:2] + sample_scenario_allocator.set_selection(scenarios=subset_scenarios) + + # Check selections propagated + assert ts1._selected_scenarios.equals(subset_scenarios) + assert ts2._selected_scenarios.equals(subset_scenarios) + + # Check data is filtered + assert ts1.selected_data.shape == (2, 5) # 2 scenarios, 5 timesteps + assert ts2.selected_data.shape == (2, 5) + + # Apply combined selection + subset_timesteps = sample_timesteps[1:3] + sample_scenario_allocator.set_selection(timesteps=subset_timesteps, scenarios=subset_scenarios) + + # Check combined selection applied + assert ts1._selected_timesteps.equals(subset_timesteps) + assert ts1._selected_scenarios.equals(subset_scenarios) + assert ts1.selected_data.shape == (2, 2) # 2 scenarios, 2 timesteps + + # Clear selections + sample_scenario_allocator.clear_selection() + assert ts1._selected_timesteps is None + assert ts1._selected_scenarios is None + assert ts1.selected_data.shape == (3, 5) # Back to full shape + + def test_as_dataset_with_scenarios(self, sample_scenario_allocator): + """Test as_dataset method with scenarios.""" + # Add some time series + sample_scenario_allocator.add_time_series('scalar_series', 42) + sample_scenario_allocator.add_time_series( + 'varying_series', + np.array([ + [10, 20, 30, 40, 50], + [15, 25, 35, 45, 55], + [5, 15, 25, 35, 45] + ]) + ) - with pytest.raises(ValueError, match='must be a subset'): - collection.activate_timesteps(invalid_timesteps) + # Get dataset + ds = sample_scenario_allocator.as_dataset(with_extra_timestep=False) + + # Check dataset dimensions + assert 'scenario' in ds.dims + assert 'time' in ds.dims + assert ds.dims['scenario'] == 3 + assert ds.dims['time'] == 5 + + # Check dataset variables + assert 'scalar_series' in ds + assert 'varying_series' in ds + + # Check values + assert np.all(ds['scalar_series'].values == 42) + baseline_values = ds['varying_series'].sel(scenario='baseline').values + assert np.array_equal(baseline_values, np.array([10, 20, 30, 40, 50])) + + def test_contains_and_iteration(self, sample_scenario_allocator): + """Test __contains__ and __iter__ methods.""" + # Add some time series + ts1 = sample_scenario_allocator.add_time_series('series1', 42) + sample_scenario_allocator.add_time_series('series2', 10) + + # Test __contains__ + assert 'series1' in sample_scenario_allocator + assert ts1 in sample_scenario_allocator + assert 'nonexistent' not in sample_scenario_allocator + + # Test behavior with invalid type + with pytest.raises(TypeError): + assert 42 in sample_scenario_allocator + + def test_update_time_series_with_scenarios(self, sample_scenario_allocator, sample_scenario_index): + """Test updating a time series with scenarios.""" + # Add a time series + ts = sample_scenario_allocator.add_time_series('series', 42) + assert ts._has_scenarios + assert np.all(ts.selected_data.values == 42) + + # Update with scenario-specific data + new_data = np.array([ + [1, 2, 3, 4, 5], + [6, 7, 8, 9, 10], + [11, 12, 13, 14, 15] + ]) + sample_scenario_allocator.update_time_series('series', new_data) + + # Check update was applied + assert np.array_equal(ts.selected_data.values, new_data) + assert ts._has_scenarios + + # Check scenario-specific values + assert np.array_equal(ts.sel(scenario='baseline').values, new_data[0]) + assert np.array_equal(ts.sel(scenario='high_demand').values, new_data[1]) + assert np.array_equal(ts.sel(scenario='low_price').values, new_data[2]) + + +if __name__ == '__main__': + pytest.main() From 8c4a45bf59773e72858ff4a01058d0619ba97a6c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 1 Apr 2025 18:42:26 +0200 Subject: [PATCH 002/183] Feature/scenarios Transform data and update type hints (#215) * Update TImeSeries to work with only scenario data * Get TImeSeriesCollection for Scenario data without time index * Simpliefy dataconverter * Drop support for pandas dataframe and Series for now * Remove test for pandas * ruff check * remove weird file * Update methods to create timeseries in FLowSystem * Bugfix * Add new Datatypes * Rename NumericData to TimestepData * Update Datatypes * Update Datatypes * Update create_time_series() * Add dimension data to Piece interfaces * Update transform_data() * Modify how time dimension is determined in Piecewise * Update OnOffParameters * Update typehints * Update Flow * Update Storage * Update typehints * Update Storage * Bugfix * Bugfix * Make sure TImeSeries are only created if needed * Bugfix * Bugfix * Bugfix and improve * Use function to get the coords of the linopy model * Updae method to determine what coords to use * Bugfix --- flixopt/components.py | 81 ++-- flixopt/core.py | 749 ++++++++++++------------------- flixopt/effects.py | 36 +- flixopt/elements.py | 47 +- flixopt/features.py | 38 +- flixopt/flow_system.py | 67 ++- flixopt/interface.py | 142 ++++-- flixopt/io.py | 2 +- flixopt/structure.py | 33 +- flixopt/utils.py | 8 - site/release-notes/_template.txt | 32 -- tests/run_all_tests.py | 2 +- tests/test_dataconverter.py | 344 +------------- 13 files changed, 590 insertions(+), 991 deletions(-) delete mode 100644 site/release-notes/_template.txt diff --git a/flixopt/components.py b/flixopt/components.py index 2a69c6165..4726ca0f4 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -9,7 +9,7 @@ import numpy as np from . import utils -from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeries +from .core import TimestepData, PlausibilityError, Scalar, TimeSeries, ScenarioData from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion @@ -34,7 +34,7 @@ def __init__( inputs: List[Flow], outputs: List[Flow], on_off_parameters: OnOffParameters = None, - conversion_factors: List[Dict[str, NumericDataTS]] = None, + conversion_factors: List[Dict[str, TimestepData]] = None, piecewise_conversion: Optional[PiecewiseConversion] = None, meta_data: Optional[Dict] = None, ): @@ -92,6 +92,7 @@ def transform_data(self, flow_system: 'FlowSystem'): if self.conversion_factors: self.conversion_factors = self._transform_conversion_factors(flow_system) if self.piecewise_conversion: + self.piecewise_conversion.has_time_dim = True self.piecewise_conversion.transform_data(flow_system, f'{self.label_full}|PiecewiseConversion') def _transform_conversion_factors(self, flow_system: 'FlowSystem') -> List[Dict[str, TimeSeries]]: @@ -124,14 +125,14 @@ def __init__( charging: Flow, discharging: Flow, capacity_in_flow_hours: Union[Scalar, InvestParameters], - relative_minimum_charge_state: NumericData = 0, - relative_maximum_charge_state: NumericData = 1, - initial_charge_state: Union[Scalar, Literal['lastValueOfSim']] = 0, - minimal_final_charge_state: Optional[Scalar] = None, - maximal_final_charge_state: Optional[Scalar] = None, - eta_charge: NumericData = 1, - eta_discharge: NumericData = 1, - relative_loss_per_hour: NumericData = 0, + relative_minimum_charge_state: TimestepData = 0, + relative_maximum_charge_state: TimestepData = 1, + initial_charge_state: Union[ScenarioData, Literal['lastValueOfSim']] = 0, + minimal_final_charge_state: Optional[ScenarioData] = None, + maximal_final_charge_state: Optional[ScenarioData] = None, + eta_charge: TimestepData = 1, + eta_discharge: TimestepData = 1, + relative_loss_per_hour: TimestepData = 0, prevent_simultaneous_charge_and_discharge: bool = True, meta_data: Optional[Dict] = None, ): @@ -172,16 +173,16 @@ def __init__( self.charging = charging self.discharging = discharging self.capacity_in_flow_hours = capacity_in_flow_hours - self.relative_minimum_charge_state: NumericDataTS = relative_minimum_charge_state - self.relative_maximum_charge_state: NumericDataTS = relative_maximum_charge_state + self.relative_minimum_charge_state: TimestepData = relative_minimum_charge_state + self.relative_maximum_charge_state: TimestepData = relative_maximum_charge_state self.initial_charge_state = initial_charge_state self.minimal_final_charge_state = minimal_final_charge_state self.maximal_final_charge_state = maximal_final_charge_state - self.eta_charge: NumericDataTS = eta_charge - self.eta_discharge: NumericDataTS = eta_discharge - self.relative_loss_per_hour: NumericDataTS = relative_loss_per_hour + self.eta_charge: TimestepData = eta_charge + self.eta_discharge: TimestepData = eta_discharge + self.relative_loss_per_hour: TimestepData = relative_loss_per_hour self.prevent_simultaneous_charge_and_discharge = prevent_simultaneous_charge_and_discharge def create_model(self, model: SystemModel) -> 'StorageModel': @@ -206,14 +207,28 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: self.relative_loss_per_hour = flow_system.create_time_series( f'{self.label_full}|relative_loss_per_hour', self.relative_loss_per_hour ) + if self.initial_charge_state != 'lastValueOfSim': + self.initial_charge_state = flow_system.create_time_series( + f'{self.label_full}|initial_charge_state', self.initial_charge_state, has_time_dim=False + ) + self.minimal_final_charge_state = flow_system.create_time_series( + f'{self.label_full}|minimal_final_charge_state', self.minimal_final_charge_state, has_time_dim=False + ) + self.maximal_final_charge_state = flow_system.create_time_series( + f'{self.label_full}|maximal_final_charge_state', self.maximal_final_charge_state, has_time_dim=False + ) if isinstance(self.capacity_in_flow_hours, InvestParameters): - self.capacity_in_flow_hours.transform_data(flow_system) + self.capacity_in_flow_hours.transform_data(flow_system, f'{self.label_full}|InvestParameters') def _plausibility_checks(self) -> None: """ Check for infeasible or uncommon combinations of parameters """ - if utils.is_number(self.initial_charge_state): + if isinstance(self.initial_charge_state, str) and not self.initial_charge_state == 'lastValueOfSim': + raise PlausibilityError( + f'initial_charge_state has undefined value: {self.initial_charge_state}' + ) + else: if isinstance(self.capacity_in_flow_hours, InvestParameters): if self.capacity_in_flow_hours.fixed_size is None: maximum_capacity = self.capacity_in_flow_hours.maximum_size @@ -229,20 +244,18 @@ def _plausibility_checks(self) -> None: minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) # initial capacity <= allowed max for minimum_size: maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) + #TODO: index=1 ??? I think index 0 - if self.initial_charge_state > maximum_inital_capacity: + if (self.initial_charge_state > maximum_inital_capacity).any(): raise ValueError( f'{self.label_full}: {self.initial_charge_state=} ' f'is above allowed maximum charge_state {maximum_inital_capacity}' ) - if self.initial_charge_state < minimum_inital_capacity: + if (self.initial_charge_state < minimum_inital_capacity).any(): raise ValueError( f'{self.label_full}: {self.initial_charge_state=} ' f'is below allowed minimum charge_state {minimum_inital_capacity}' ) - elif self.initial_charge_state != 'lastValueOfSim': - raise ValueError(f'{self.label_full}: {self.initial_charge_state=} has an invalid value') - @register_class_for_io class Transmission(Component): @@ -259,8 +272,8 @@ def __init__( out1: Flow, in2: Optional[Flow] = None, out2: Optional[Flow] = None, - relative_losses: Optional[NumericDataTS] = None, - absolute_losses: Optional[NumericDataTS] = None, + relative_losses: Optional[TimestepData] = None, + absolute_losses: Optional[TimestepData] = None, on_off_parameters: OnOffParameters = None, prevent_simultaneous_flows_in_both_directions: bool = True, meta_data: Optional[Dict] = None, @@ -454,12 +467,12 @@ def do_modeling(self): lb, ub = self.absolute_charge_state_bounds self.charge_state = self.add( self._model.add_variables( - lower=lb, upper=ub, coords=self._model.coords_extra, name=f'{self.label_full}|charge_state' + lower=lb, upper=ub, coords=self._model.get_coords(extra_timestep=True), name=f'{self.label_full}|charge_state' ), 'charge_state', ) self.netto_discharge = self.add( - self._model.add_variables(coords=self._model.coords, name=f'{self.label_full}|netto_discharge'), + self._model.add_variables(coords=self._model.get_coords(), name=f'{self.label_full}|netto_discharge'), 'netto_discharge', ) # netto_discharge: @@ -511,24 +524,20 @@ def _initial_and_final_charge_state(self): name_short = 'initial_charge_state' name = f'{self.label_full}|{name_short}' - if utils.is_number(self.element.initial_charge_state): + if self.element.initial_charge_state == 'lastValueOfSim': self.add( self._model.add_constraints( - self.charge_state.isel(time=0) == self.element.initial_charge_state, name=name + self.charge_state.isel(time=0) == self.charge_state.isel(time=-1), name=name ), name_short, ) - elif self.element.initial_charge_state == 'lastValueOfSim': + else: self.add( self._model.add_constraints( - self.charge_state.isel(time=0) == self.charge_state.isel(time=-1), name=name + self.charge_state.isel(time=0) == self.element.initial_charge_state, name=name ), name_short, ) - else: # TODO: Validation in Storage Class, not in Model - raise PlausibilityError( - f'initial_charge_state has undefined value: {self.element.initial_charge_state}' - ) if self.element.maximal_final_charge_state is not None: self.add( @@ -549,7 +558,7 @@ def _initial_and_final_charge_state(self): ) @property - def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: + def absolute_charge_state_bounds(self) -> Tuple[TimestepData, TimestepData]: relative_lower_bound, relative_upper_bound = self.relative_charge_state_bounds if not isinstance(self.element.capacity_in_flow_hours, InvestParameters): return ( @@ -563,7 +572,7 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: ) @property - def relative_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: + def relative_charge_state_bounds(self) -> Tuple[TimestepData, TimestepData]: return ( self.element.relative_minimum_charge_state.selected_data, self.element.relative_maximum_charge_state.selected_data, diff --git a/flixopt/core.py b/flixopt/core.py index d2a8edd59..185236b3a 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -26,6 +26,12 @@ NumericDataTS = Union[NumericData, 'TimeSeriesData'] """Represents either standard numeric data or TimeSeriesData.""" +TimestepData = NumericData +"""Represents any form of numeric data that corresponds to timesteps.""" + +ScenarioData = NumericData +"""Represents any form of numeric data that corresponds to scenarios.""" + class PlausibilityError(Exception): """Error for a failing Plausibility check.""" @@ -41,568 +47,322 @@ class ConversionError(Exception): class DataConverter: """ - Converts various data types into xarray.DataArray with timesteps and optional scenarios dimensions. - - Supports: - - Scalar values (broadcast to all timesteps/scenarios) - - 1D arrays (mapped to timesteps, broadcast to scenarios if provided) - - 2D arrays (mapped to scenarios × timesteps if dimensions match) - - Series with time index (broadcast to scenarios if provided) - - DataFrames with time index and a single column (broadcast to scenarios if provided) - - Series/DataFrames with MultiIndex (scenario, time) - - Existing DataArrays - """ + Converts various data types into xarray.DataArray with optional time and scenario dimension. - #TODO: Allow DataFrame with scenarios as columns + Current implementation handles: + - Scalar values + - NumPy arrays + - xarray.DataArray + """ @staticmethod def as_dataarray( - data: NumericData, timesteps: pd.DatetimeIndex, scenarios: Optional[pd.Index] = None + data: TimestepData, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None ) -> xr.DataArray: """ - Convert data to xarray.DataArray with specified timesteps and optional scenarios dimensions. + Convert data to xarray.DataArray with specified dimensions. Args: - data: The data to convert (scalar, array, Series, DataFrame, or DataArray) - timesteps: DatetimeIndex representing the time dimension (must be named 'time') - scenarios: Optional Index representing scenarios (must be named 'scenario') + data: The data to convert (scalar, array, or DataArray) + timesteps: Optional DatetimeIndex for time dimension + scenarios: Optional Index for scenario dimension Returns: DataArray with the converted data - - Raises: - ValueError: If timesteps or scenarios are invalid - ConversionError: If the data cannot be converted to the expected dimensions """ - # Validate inputs - DataConverter._validate_timesteps(timesteps) - if scenarios is not None: - DataConverter._validate_scenarios(scenarios) - - # Determine dimensions and coordinates - coords, dims, expected_shape = DataConverter._get_dimensions(timesteps, scenarios) - - try: - # Convert different data types using specialized methods - if isinstance(data, (int, float, np.integer, np.floating)): - return DataConverter._convert_scalar(data, coords, dims) + # Prepare dimensions and coordinates + coords, dims = DataConverter._prepare_dimensions(timesteps, scenarios) - elif isinstance(data, pd.DataFrame): - return DataConverter._convert_dataframe(data, timesteps, scenarios, coords, dims) + # Select appropriate converter based on data type + if isinstance(data, (int, float, np.integer, np.floating)): + return DataConverter._convert_scalar(data, coords, dims) - elif isinstance(data, pd.Series): - return DataConverter._convert_series(data, timesteps, scenarios, coords, dims) + elif isinstance(data, xr.DataArray): + return DataConverter._convert_dataarray(data, coords, dims) - elif isinstance(data, np.ndarray): - return DataConverter._convert_ndarray(data, timesteps, scenarios, coords, dims, expected_shape) + elif isinstance(data, np.ndarray): + return DataConverter._convert_ndarray(data, coords, dims) - elif isinstance(data, xr.DataArray): - return DataConverter._convert_dataarray(data, timesteps, scenarios, coords, dims) - - else: - raise ConversionError(f'Unsupported type: {type(data).__name__}') - - except Exception as e: - if isinstance(e, ConversionError): - raise - raise ConversionError(f'Converting {type(data)} to DataArray raised an error: {str(e)}') from e + else: + raise ConversionError(f'Unsupported data type: {type(data).__name__}') @staticmethod - def _validate_timesteps(timesteps: pd.DatetimeIndex) -> None: + def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: """ - Validate that timesteps is a properly named non-empty DatetimeIndex. + Validate and prepare time index. Args: - timesteps: The DatetimeIndex to validate + timesteps: The time index to validate - Raises: - ValueError: If timesteps is not a non-empty DatetimeIndex - ConversionError: If timesteps is not named 'time' + Returns: + Validated time index """ if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: - raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') - if timesteps.name != 'time': - raise ConversionError(f'DatetimeIndex must be named "time", got {timesteps.name=}') + raise ConversionError('Timesteps must be a non-empty DatetimeIndex') - @staticmethod - def _validate_scenarios(scenarios: pd.Index) -> None: - """ - Validate that scenarios is a properly named non-empty Index. + if not timesteps.name == 'time': + raise ConversionError(f'Scenarios must be named "time", got "{timesteps.name}"') - Args: - scenarios: The Index to validate - - Raises: - ValueError: If scenarios is not a non-empty Index - ConversionError: If scenarios is not named 'scenario' - """ - if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: - raise ValueError(f'Scenarios must be a non-empty Index, got {type(scenarios).__name__}') - if scenarios.name != 'scenario': - raise ConversionError(f'Scenarios Index must be named "scenario", got {scenarios.name=}') + return timesteps @staticmethod - def _get_dimensions( - timesteps: pd.DatetimeIndex, scenarios: Optional[pd.Index] = None - ) -> Tuple[Dict[str, pd.Index], Tuple[str, ...], Tuple[int, ...]]: + def _validate_scenarios(scenarios: pd.Index) -> pd.Index: """ - Create the coordinates, dimensions, and expected shape for the output DataArray. + Validate and prepare scenario index. Args: - timesteps: The time index - scenarios: Optional scenario index - - Returns: - Tuple containing: - - Dict mapping dimension names to coordinate indexes - - Tuple of dimension names - - Tuple of expected shape - """ - if scenarios is not None: - coords = {'scenario': scenarios, 'time': timesteps} - dims = ('scenario', 'time') - expected_shape = (len(scenarios), len(timesteps)) - else: - coords = {'time': timesteps} - dims = ('time',) - expected_shape = (len(timesteps),) - - return coords, dims, expected_shape - - @staticmethod - def _convert_scalar( - data: Union[int, float, np.integer, np.floating], coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: + scenarios: The scenario index to validate """ - Convert a scalar value to a DataArray. + if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: + raise ConversionError('Scenarios must be a non-empty Index') - Args: - data: The scalar value to convert - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + if not scenarios.name == 'scenario': + raise ConversionError(f'Scenarios must be named "scenario", got "{scenarios.name}"') - Returns: - DataArray with the scalar value broadcast to all coordinates - """ - return xr.DataArray(data, coords=coords, dims=dims) + return scenarios @staticmethod - def _convert_dataframe( - df: pd.DataFrame, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - ) -> xr.DataArray: + def _prepare_dimensions( + timesteps: Optional[pd.DatetimeIndex], scenarios: Optional[pd.Index] + ) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: """ - Convert a DataFrame to a DataArray. + Prepare coordinates and dimensions for the DataArray. Args: - df: The DataFrame to convert - timesteps: The time index + timesteps: Optional time index scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names Returns: - DataArray created from the DataFrame - - Raises: - ConversionError: If the DataFrame cannot be converted to the expected dimensions - """ - # Case 1: DataFrame with MultiIndex (scenario, time) - if ( - isinstance(df.index, pd.MultiIndex) - and len(df.index.names) == 2 - and 'scenario' in df.index.names - and 'time' in df.index.names - and scenarios is not None - ): - return DataConverter._convert_multi_index_dataframe(df, timesteps, scenarios, coords, dims) - - # Case 2: Standard DataFrame with time index - elif not isinstance(df.index, pd.MultiIndex): - return DataConverter._convert_standard_dataframe(df, timesteps, scenarios, coords, dims) - - else: - raise ConversionError(f'Unsupported DataFrame index structure: {df}') - - @staticmethod - def _convert_multi_index_dataframe( - df: pd.DataFrame, - timesteps: pd.DatetimeIndex, - scenarios: pd.Index, - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - ) -> xr.DataArray: + Tuple of (coordinates dict, dimensions tuple) """ - Convert a DataFrame with MultiIndex (scenario, time) to a DataArray. + # Validate inputs if provided + if timesteps is not None: + timesteps = DataConverter._validate_timesteps(timesteps) - Args: - df: The DataFrame with MultiIndex to convert - timesteps: The time index - scenarios: The scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names - - Returns: - DataArray created from the MultiIndex DataFrame + if scenarios is not None: + scenarios = DataConverter._validate_scenarios(scenarios) - Raises: - ConversionError: If the DataFrame's index doesn't match expected or has multiple columns - """ - # Validate that the index contains the expected values - if not set(df.index.get_level_values('time')).issubset(set(timesteps)): - raise ConversionError("DataFrame time index doesn't match or isn't a subset of timesteps") - if not set(df.index.get_level_values('scenario')).issubset(set(scenarios)): - raise ConversionError("DataFrame scenario index doesn't match or isn't a subset of scenarios") + # Build coordinates and dimensions + coords = {} + dims = [] - # Ensure single column - if len(df.columns) != 1: - raise ConversionError('DataFrame must have exactly one column') + if scenarios is not None: + coords['scenario'] = scenarios + dims.append('scenario') - # Reindex to ensure complete coverage and correct order - multi_idx = pd.MultiIndex.from_product([scenarios, timesteps], names=['scenario', 'time']) - reindexed = df.reindex(multi_idx).iloc[:, 0] + if timesteps is not None: + coords['time'] = timesteps + dims.append('time') - # Reshape to 2D array - reshaped = reindexed.values.reshape(len(scenarios), len(timesteps)) - return xr.DataArray(reshaped, coords=coords, dims=dims) + return coords, tuple(dims) @staticmethod - def _convert_standard_dataframe( - df: pd.DataFrame, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], + def _convert_scalar( + data: Union[int, float, np.integer, np.floating], coords: Dict[str, pd.Index], dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert a standard DataFrame with time index to a DataArray. + Convert a scalar value to a DataArray. Args: - df: The DataFrame to convert - timesteps: The time index - scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The scalar value + coords: Coordinate dictionary + dims: Dimension names Returns: - DataArray created from the DataFrame - - Raises: - ConversionError: If the DataFrame's index doesn't match timesteps or has multiple columns + DataArray with the scalar value """ - if not df.index.equals(timesteps): - raise ConversionError("DataFrame index doesn't match timesteps index") - if len(df.columns) != 1: - raise ConversionError('DataFrame must have exactly one column') - - # Get values - values = df.values.flatten() - - if scenarios is not None: - # Broadcast to scenarios dimension - values = np.tile(values, (len(scenarios), 1)) - - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(data, coords=coords, dims=dims) @staticmethod - def _convert_series( - series: pd.Series, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - ) -> xr.DataArray: + def _convert_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: """ - Convert a Series to a DataArray. + Convert an existing DataArray to desired dimensions. Args: - series: The Series to convert - timesteps: The time index - scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The source DataArray + coords: Target coordinates + dims: Target dimensions Returns: - DataArray created from the Series - - Raises: - ConversionError: If the Series cannot be converted to the expected dimensions - """ - # Case 1: Series with MultiIndex (scenario, time) - if ( - isinstance(series.index, pd.MultiIndex) - and len(series.index.names) == 2 - and 'scenario' in series.index.names - and 'time' in series.index.names - and scenarios is not None - ): - return DataConverter._convert_multi_index_series(series, timesteps, scenarios, coords, dims) - - # Case 2: Standard Series with time index - elif not isinstance(series.index, pd.MultiIndex): - return DataConverter._convert_standard_series(series, timesteps, scenarios, coords, dims) - - else: - raise ConversionError('Unsupported Series index structure') + DataArray with the target dimensions + """ + # No dimensions case + if len(dims) == 0: + if data.size != 1: + raise ConversionError('When converting to dimensionless DataArray, source must be scalar') + return xr.DataArray(data.values.item()) + + # Check if data already has matching dimensions + if set(data.dims) == set(dims): + # Check if coordinates match + is_compatible = True + for dim in dims: + if dim in data.dims and not np.array_equal(data.coords[dim].values, coords[dim].values): + is_compatible = False + break + + if is_compatible: + # Return existing DataArray if compatible + return data.copy(deep=True) + + # Handle dimension broadcasting + if len(data.dims) == 1 and len(dims) == 2: + # Single dimension to two dimensions + if data.dims[0] == 'time' and 'scenario' in dims: + # Broadcast time dimension to include scenarios + return DataConverter._broadcast_time_to_scenarios(data, coords, dims) + + elif data.dims[0] == 'scenario' and 'time' in dims: + # Broadcast scenario dimension to include time + return DataConverter._broadcast_scenario_to_time(data, coords, dims) + + raise ConversionError(f'Cannot convert {data.dims} to {dims}') @staticmethod - def _convert_multi_index_series( - series: pd.Series, - timesteps: pd.DatetimeIndex, - scenarios: pd.Index, - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], + def _broadcast_time_to_scenarios( + data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert a Series with MultiIndex (scenario, time) to a DataArray. + Broadcast a time-only DataArray to include scenarios. Args: - series: The Series with MultiIndex to convert - timesteps: The time index - scenarios: The scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The time-indexed DataArray + coords: Target coordinates + dims: Target dimensions Returns: - DataArray created from the MultiIndex Series - - Raises: - ConversionError: If the Series' index doesn't match expected + DataArray with time and scenario dimensions """ - # Validate that the index contains the expected values - if not set(series.index.get_level_values('time')).issubset(set(timesteps)): - raise ConversionError("Series time index doesn't match or isn't a subset of timesteps") - if not set(series.index.get_level_values('scenario')).issubset(set(scenarios)): - raise ConversionError("Series scenario index doesn't match or isn't a subset of scenarios") + # Check compatibility + if not np.array_equal(data.coords['time'].values, coords['time'].values): + raise ConversionError("Source time coordinates don't match target time coordinates") - # Reindex to ensure complete coverage and correct order - multi_idx = pd.MultiIndex.from_product([scenarios, timesteps], names=['scenario', 'time']) - reindexed = series.reindex(multi_idx) - - # Reshape to 2D array - reshaped = reindexed.values.reshape(len(scenarios), len(timesteps)) - return xr.DataArray(reshaped, coords=coords, dims=dims) + # Broadcast values + values = np.tile(data.values, (len(coords['scenario']), 1)) + return xr.DataArray(values, coords=coords, dims=dims) @staticmethod - def _convert_standard_series( - series: pd.Series, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], + def _broadcast_scenario_to_time( + data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert a standard Series with time index to a DataArray. + Broadcast a scenario-only DataArray to include time. Args: - series: The Series to convert - timesteps: The time index - scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The scenario-indexed DataArray + coords: Target coordinates + dims: Target dimensions Returns: - DataArray created from the Series - - Raises: - ConversionError: If the Series' index doesn't match timesteps + DataArray with time and scenario dimensions """ - if not series.index.equals(timesteps): - raise ConversionError("Series index doesn't match timesteps index") - - # Get values - values = series.values - - if scenarios is not None: - # Broadcast to scenarios dimension - values = np.tile(values, (len(scenarios), 1)) + # Check compatibility + if not np.array_equal(data.coords['scenario'].values, coords['scenario'].values): + raise ConversionError("Source scenario coordinates don't match target scenario coordinates") + # Broadcast values + values = np.repeat(data.values[:, np.newaxis], len(coords['time']), axis=1) return xr.DataArray(values, coords=coords, dims=dims) @staticmethod - def _convert_ndarray( - arr: np.ndarray, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - expected_shape: Tuple[int, ...], - ) -> xr.DataArray: + def _convert_ndarray(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: """ - Convert a numpy array to a DataArray. + Convert a NumPy array to a DataArray. Args: - arr: The numpy array to convert - timesteps: The time index - scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names - expected_shape: Expected shape of the resulting array + data: The NumPy array + coords: Target coordinates + dims: Target dimensions Returns: - DataArray created from the numpy array - - Raises: - ConversionError: If the array cannot be converted to the expected dimensions - """ - # Case 1: With scenarios - array can be 1D or 2D - if scenarios is not None: - return DataConverter._convert_ndarray_with_scenarios( - arr, timesteps, scenarios, coords, dims, expected_shape - ) - - # Case 2: Without scenarios - array must be 1D - else: - return DataConverter._convert_ndarray_without_scenarios(arr, timesteps, coords, dims) - - @staticmethod - def _convert_ndarray_with_scenarios( - arr: np.ndarray, - timesteps: pd.DatetimeIndex, - scenarios: pd.Index, - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - expected_shape: Tuple[int, ...], - ) -> xr.DataArray: + DataArray from the NumPy array """ - Convert a numpy array to a DataArray with scenarios dimension. - - Args: - arr: The numpy array to convert - timesteps: The time index - scenarios: The scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names - expected_shape: Expected shape (scenarios, timesteps) + # Handle dimensionless case + if len(dims) == 0: + if data.size != 1: + raise ConversionError('Without dimensions, can only convert scalar arrays') + return xr.DataArray(data.item()) - Returns: - DataArray created from the numpy array + # Handle single dimension + elif len(dims) == 1: + return DataConverter._convert_ndarray_single_dim(data, coords, dims) - Raises: - ConversionError: If the array dimensions don't match expected - """ - if arr.ndim == 1: - # 1D array should match timesteps and be broadcast to scenarios - if arr.shape[0] != len(timesteps): - raise ConversionError(f"1D array length {arr.shape[0]} doesn't match timesteps length {len(timesteps)}") - # Broadcast to scenarios - values = np.tile(arr, (len(scenarios), 1)) - return xr.DataArray(values, coords=coords, dims=dims) - - elif arr.ndim == 2: - # 2D array should match (scenarios, timesteps) - if arr.shape != expected_shape: - raise ConversionError(f"2D array shape {arr.shape} doesn't match expected shape {expected_shape}") - return xr.DataArray(arr, coords=coords, dims=dims) + # Handle two dimensions + elif len(dims) == 2: + return DataConverter._convert_ndarray_two_dims(data, coords, dims) else: - raise ConversionError(f'Array must be 1D or 2D, got {arr.ndim}D') - - @staticmethod - def _convert_ndarray_without_scenarios( - arr: np.ndarray, timesteps: pd.DatetimeIndex, coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: - """ - Convert a numpy array to a DataArray without scenarios dimension. - - Args: - arr: The numpy array to convert - timesteps: The time index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names - - Returns: - DataArray created from the numpy array - - Raises: - ConversionError: If the array isn't 1D or doesn't match timesteps length - """ - if arr.ndim != 1: - raise ConversionError(f'Without scenarios, array must be 1D, got {arr.ndim}D') - if arr.shape[0] != len(timesteps): - raise ConversionError(f"Array shape {arr.shape} doesn't match expected length {len(timesteps)}") - return xr.DataArray(arr, coords=coords, dims=dims) + raise ConversionError('Maximum 2 dimensions supported') @staticmethod - def _convert_dataarray( - da: xr.DataArray, - timesteps: pd.DatetimeIndex, - scenarios: Optional[pd.Index], - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], + def _convert_ndarray_single_dim( + data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert an existing DataArray to a new DataArray with the desired dimensions. + Convert a NumPy array to a single-dimension DataArray. Args: - da: The DataArray to convert - timesteps: The time index - scenarios: Optional scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The NumPy array + coords: Target coordinates + dims: Target dimensions (length 1) Returns: - New DataArray with the specified coordinates and dimensions - - Raises: - ConversionError: If the DataArray dimensions don't match expected + DataArray with single dimension """ - # Case 1: DataArray with only time dimension when scenarios are provided - if scenarios is not None and set(da.dims) == {'time'}: - return DataConverter._broadcast_time_only_dataarray(da, timesteps, scenarios, coords, dims) - - # Case 2: DataArray dimensions should match expected - elif set(da.dims) != set(dims): - raise ConversionError(f"DataArray dimensions {da.dims} don't match expected {dims}") + dim_name = dims[0] + dim_length = len(coords[dim_name]) - # Validate dimensions sizes - for dim in dims: - if not np.array_equal(da.coords[dim].values, coords[dim].values): - raise ConversionError(f"DataArray dimension '{dim}' doesn't match expected {coords[dim]}") - - # Create a new DataArray with our coordinates to ensure consistency - result = xr.DataArray(da.values.copy(), coords=coords, dims=dims) - return result + if data.ndim == 1: + # 1D array must match dimension length + if data.shape[0] != dim_length: + raise ConversionError(f"Array length {data.shape[0]} doesn't match {dim_name} length {dim_length}") + return xr.DataArray(data, coords=coords, dims=dims) + else: + raise ConversionError(f'Expected 1D array for single dimension, got {data.ndim}D') @staticmethod - def _broadcast_time_only_dataarray( - da: xr.DataArray, - timesteps: pd.DatetimeIndex, - scenarios: pd.Index, - coords: Dict[str, pd.Index], - dims: Tuple[str, ...], - ) -> xr.DataArray: + def _convert_ndarray_two_dims(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: """ - Broadcast a time-only DataArray to include the scenarios dimension. + Convert a NumPy array to a two-dimension DataArray. Args: - da: The DataArray with only time dimension - timesteps: The time index - scenarios: The scenario index - coords: Dictionary mapping dimension names to coordinate indexes - dims: Tuple of dimension names + data: The NumPy array + coords: Target coordinates + dims: Target dimensions (length 2) Returns: - DataArray with the data broadcast to include scenarios dimension + DataArray with two dimensions + """ + scenario_length = len(coords['scenario']) + time_length = len(coords['time']) + + if data.ndim == 1: + # For 1D array, create 2D array based on which dimension it matches + if data.shape[0] == time_length: + # Broadcast across scenarios + values = np.tile(data, (scenario_length, 1)) + return xr.DataArray(values, coords=coords, dims=dims) + elif data.shape[0] == scenario_length: + # Broadcast across time + values = np.repeat(data[:, np.newaxis], time_length, axis=1) + return xr.DataArray(values, coords=coords, dims=dims) + else: + raise ConversionError(f"1D array length {data.shape[0]} doesn't match either dimension") - Raises: - ConversionError: If the DataArray time coordinates aren't compatible with timesteps - """ - # Ensure the time dimension is compatible - if not np.array_equal(da.coords['time'].values, timesteps.values): - raise ConversionError("DataArray time coordinates aren't compatible with timesteps") + elif data.ndim == 2: + # For 2D array, shape must match dimensions + expected_shape = (scenario_length, time_length) + if data.shape != expected_shape: + raise ConversionError(f"2D array shape {data.shape} doesn't match expected shape {expected_shape}") + return xr.DataArray(data, coords=coords, dims=dims) - # Broadcast to scenarios - values = np.tile(da.values.copy(), (len(scenarios), 1)) - return xr.DataArray(values, coords=coords, dims=dims) + else: + raise ConversionError(f'Expected 1D or 2D array for two dimensions, got {data.ndim}D') class TimeSeriesData: # TODO: Move to Interface.py - def __init__(self, data: NumericData, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): + def __init__(self, data: TimestepData, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): """ timeseries class for transmit timeseries AND special characteristics of timeseries, i.g. to define weights needed in calculation_type 'aggregated' @@ -744,11 +504,8 @@ def __init__( has_extra_timestep: Whether this series requires an extra timestep Raises: - ValueError: If data doesn't have a 'time' index or has unsupported dimensions + ValueError: If data has unsupported dimensions """ - if 'time' not in data.indexes: - raise ValueError(f'DataArray must have a "time" index. Got {data.indexes}') - allowed_dims = {'time', 'scenario'} if not set(data.dims).issubset(allowed_dims): raise ValueError(f'DataArray dimensions must be subset of {allowed_dims}. Got {data.dims}') @@ -766,8 +523,9 @@ def __init__( self._selected_timesteps: Optional[pd.DatetimeIndex] = None self._selected_scenarios: Optional[pd.Index] = None - # Flag for whether this series has scenarios - self._has_scenarios = 'scenario' in data.dims + # Flag for whether this series has various dimensions + self.has_time_dim = 'time' in data.dims + self.has_scenario_dim = 'scenario' in data.dims def reset(self) -> None: """ @@ -836,16 +594,18 @@ def selected_data(self) -> xr.DataArray: return self._stored_data.sel(**self._valid_selector) @property - def active_timesteps(self) -> pd.DatetimeIndex: - """Get the current active timesteps.""" + def active_timesteps(self) -> Optional[pd.DatetimeIndex]: + """Get the current active timesteps, or None if no time dimension.""" + if not self.has_time_dim: + return None if self._selected_timesteps is None: return self._stored_data.indexes['time'] return self._selected_timesteps @property def active_scenarios(self) -> Optional[pd.Index]: - """Get the current active scenarios.""" - if not self._has_scenarios: + """Get the current active scenarios, or None if no scenario dimension.""" + if not self.has_scenario_dim: return None if self._selected_scenarios is None: return self._stored_data.indexes['scenario'] @@ -865,8 +625,8 @@ def update_stored_data(self, value: xr.DataArray) -> None: """ new_data = DataConverter.as_dataarray( value, - timesteps=self.active_timesteps, - scenarios=self.active_scenarios if self._has_scenarios else None + timesteps=self.active_timesteps if self.has_time_dim else None, + scenarios=self.active_scenarios if self.has_scenario_dim else None, ) # Skip if data is unchanged to avoid overwriting backup @@ -883,15 +643,26 @@ def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> Non self._selected_scenarios = None def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: - if timesteps is None: - self.clear_selection(timesteps=True, scenarios=False) - else: - self._selected_timesteps = timesteps + """ + Set active subset for timesteps and scenarios. - if scenarios is None: - self.clear_selection(timesteps=False, scenarios=True) - else: - self._selected_scenarios = scenarios + Args: + timesteps: Timesteps to activate, or None to clear. Ignored if series has no time dimension. + scenarios: Scenarios to activate, or None to clear. Ignored if series has no scenario dimension. + """ + # Only update timesteps if the series has time dimension + if self.has_time_dim: + if timesteps is None: + self.clear_selection(timesteps=True, scenarios=False) + else: + self._selected_timesteps = timesteps + + # Only update scenarios if the series has scenario dimension + if self.has_scenario_dim: + if scenarios is None: + self.clear_selection(timesteps=False, scenarios=True) + else: + self._selected_scenarios = scenarios @property def sel(self): @@ -906,8 +677,17 @@ def isel(self): @property def _valid_selector(self) -> Dict[str, pd.Index]: """Get the current selection as a dictionary.""" - full_selection = {'time': self._selected_timesteps, 'scenario': self._selected_scenarios} - return {dim: sel for dim, sel in full_selection.items() if dim in self._stored_data.dims and sel is not None} + selector = {} + + # Only include time in selector if series has time dimension + if self.has_time_dim and self._selected_timesteps is not None: + selector['time'] = self._selected_timesteps + + # Only include scenario in selector if series has scenario dimension + if self.has_scenario_dim and self._selected_scenarios is not None: + selector['scenario'] = self._selected_scenarios + + return selector def _apply_operation(self, other, op): """Apply an operation between this TimeSeries and another object.""" @@ -1042,6 +822,8 @@ def add_time_series( self, name: str, data: Union[NumericDataTS, TimeSeries], + has_time_dim: bool = True, + has_scenario_dim: bool = True, aggregation_weight: Optional[float] = None, aggregation_group: Optional[str] = None, has_extra_timestep: bool = False, @@ -1052,6 +834,8 @@ def add_time_series( Args: name: Name of the time series data: Data for the time series (can be raw data or an existing TimeSeries) + has_time_dim: Whether the TimeSeries has a time dimension + has_scenario_dim: Whether the TimeSeries has a scenario dimension aggregation_weight: Weight used for aggregation aggregation_group: Group name for shared aggregation weighting has_extra_timestep: Whether this series needs an extra timestep @@ -1061,9 +845,16 @@ def add_time_series( """ if name in self._time_series: raise KeyError(f"TimeSeries '{name}' already exists in allocator") + if not has_time_dim and has_extra_timestep: + raise ValueError('A not time-indexed TimeSeries cannot have an extra timestep') # Choose which timesteps to use - target_timesteps = self.timesteps_extra if has_extra_timestep else self.timesteps + if has_time_dim: + target_timesteps = self.timesteps_extra if has_extra_timestep else self.timesteps + else: + target_timesteps = None + + target_scenarios = self.scenarios if has_scenario_dim else None # Create or adapt the TimeSeries object if isinstance(data, TimeSeries): @@ -1071,7 +862,7 @@ def add_time_series( time_series = data # Update the stored data to use our timesteps and scenarios data_array = DataConverter.as_dataarray( - time_series.stored_data, timesteps=target_timesteps, scenarios=self.scenarios + time_series.stored_data, timesteps=target_timesteps, scenarios=target_scenarios ) time_series = TimeSeries( data=data_array, @@ -1086,7 +877,7 @@ def add_time_series( data=data, name=name, timesteps=target_timesteps, - scenarios=self.scenarios, + scenarios=target_scenarios, aggregation_weight=aggregation_weight, aggregation_group=aggregation_group, has_extra_timestep=has_extra_timestep, @@ -1163,7 +954,7 @@ def as_dataset(self, with_extra_timestep: bool = True, with_constants: bool = Tr Args: with_extra_timestep: Whether to exclude the extra timesteps. - Effectively, this removes the last timestep for certain TImeSeries, but mitigates the presence of NANs in others. + Effectively, this removes the last timestep for certain TimeSeries, but mitigates the presence of NANs in others. with_constants: Whether to exclude TimeSeries with a constant value from the dataset. """ if self.scenarios is None: @@ -1212,10 +1003,16 @@ def scenarios(self) -> Optional[pd.Index]: def _propagate_selection_to_time_series(self) -> None: """Apply the current selection to all TimeSeries objects.""" for ts_name, ts in self._time_series.items(): - timesteps = self._selected_timesteps_extra if ts_name in self._has_extra_timestep else self._selected_timesteps + if ts.has_time_dim: + timesteps = ( + self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps + ) + else: + timesteps = None + ts.set_selection( timesteps=timesteps, - scenarios=self._selected_scenarios + scenarios=self.scenarios if ts.has_scenario_dim else None ) def __getitem__(self, name: str) -> TimeSeries: @@ -1245,7 +1042,7 @@ def __iter__(self) -> Iterator[TimeSeries]: """Iterate over TimeSeries objects.""" return iter(self._time_series.values()) - def update_time_series(self, name: str, data: NumericData) -> TimeSeries: + def update_time_series(self, name: str, data: TimestepData) -> TimeSeries: """ Update an existing TimeSeries with new data. @@ -1265,11 +1062,17 @@ def update_time_series(self, name: str, data: NumericData) -> TimeSeries: # Get the TimeSeries ts = self._time_series[name] + # Determine which timesteps to use if the series has a time dimension + if ts.has_time_dim: + target_timesteps = self.timesteps_extra if name in self._has_extra_timestep else self.timesteps + else: + target_timesteps = None + # Convert data to proper format data_array = DataConverter.as_dataarray( data, - self.timesteps_extra if name in self._has_extra_timestep else self.timesteps, - self.scenarios + timesteps=target_timesteps, + scenarios=self.scenarios if ts.has_scenario_dim else None ) # Update the TimeSeries diff --git a/flixopt/effects.py b/flixopt/effects.py index 9b5ea41d6..e834e339e 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -13,7 +13,7 @@ import numpy as np import pandas as pd -from .core import NumericData, NumericDataTS, Scalar, TimeSeries, TimeSeriesCollection +from .core import TimestepData, NumericDataTS, Scalar, TimeSeries, TimeSeriesCollection, ScenarioData, TimestepData from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -38,16 +38,16 @@ def __init__( meta_data: Optional[Dict] = None, is_standard: bool = False, is_objective: bool = False, - specific_share_to_other_effects_operation: Optional['EffectValuesUser'] = None, - specific_share_to_other_effects_invest: Optional['EffectValuesUser'] = None, - minimum_operation: Optional[Scalar] = None, - maximum_operation: Optional[Scalar] = None, - minimum_invest: Optional[Scalar] = None, - maximum_invest: Optional[Scalar] = None, + specific_share_to_other_effects_operation: Optional['EffectValuesUserTimestep'] = None, + specific_share_to_other_effects_invest: Optional['EffectValuesUserScenario'] = None, + minimum_operation: Optional[ScenarioData] = None, + maximum_operation: Optional[ScenarioData] = None, + minimum_invest: Optional[ScenarioData] = None, + maximum_invest: Optional[ScenarioData] = None, minimum_operation_per_hour: Optional[NumericDataTS] = None, maximum_operation_per_hour: Optional[NumericDataTS] = None, - minimum_total: Optional[Scalar] = None, - maximum_total: Optional[Scalar] = None, + minimum_total: Optional[ScenarioData] = None, + maximum_total: Optional[ScenarioData] = None, ): """ Args: @@ -76,10 +76,10 @@ def __init__( self.description = description self.is_standard = is_standard self.is_objective = is_objective - self.specific_share_to_other_effects_operation: EffectValuesUser = ( + self.specific_share_to_other_effects_operation: EffectValuesUserTimestep = ( specific_share_to_other_effects_operation or {} ) - self.specific_share_to_other_effects_invest: EffectValuesUser = specific_share_to_other_effects_invest or {} + self.specific_share_to_other_effects_invest: EffectValuesUserTimestep = specific_share_to_other_effects_invest or {} self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation self.minimum_operation_per_hour: NumericDataTS = minimum_operation_per_hour @@ -171,11 +171,12 @@ def do_modeling(self): EffectValuesExpr = Dict[str, linopy.LinearExpression] # Used to create Shares EffectTimeSeries = Dict[str, TimeSeries] # Used internally to index values EffectValuesDict = Dict[str, NumericDataTS] # How effect values are stored -EffectValuesUser = Union[NumericDataTS, Dict[str, NumericDataTS]] # User-specified Shares to Effects -""" This datatype is used to define the share to an effect by a certain attribute. """ -EffectValuesUserScalar = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects -""" This datatype is used to define the share to an effect by a certain attribute. Only scalars are allowed. """ +EffectValuesUserScenario = Union[ScenarioData, Dict[str, ScenarioData]] +""" This datatype is used to define the share to an effect for every scenario. """ + +EffectValuesUserTimestep = Union[TimestepData, Dict[str, TimestepData]] +""" This datatype is used to define the share to an effect for every timestep. """ class EffectCollection: @@ -207,7 +208,10 @@ def add_effects(self, *effects: Effect) -> None: self._effects[effect.label] = effect logger.info(f'Registered new Effect: {effect.label}') - def create_effect_values_dict(self, effect_values_user: EffectValuesUser) -> Optional[EffectValuesDict]: + def create_effect_values_dict( + self, + effect_values_user: Union[EffectValuesUserScenario, EffectValuesUserTimestep] + ) -> Optional[EffectValuesDict]: """ Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type. diff --git a/flixopt/elements.py b/flixopt/elements.py index 95536b910..b6de8c7c2 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,8 +10,8 @@ import numpy as np from .config import CONFIG -from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection -from .effects import EffectValuesUser +from .core import TimestepData, NumericDataTS, PlausibilityError, Scalar, ScenarioData +from .effects import EffectValuesUserTimestep from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters from .structure import Element, ElementModel, SystemModel, register_class_for_io @@ -148,16 +148,16 @@ def __init__( label: str, bus: str, size: Union[Scalar, InvestParameters] = None, - fixed_relative_profile: Optional[NumericDataTS] = None, - relative_minimum: NumericDataTS = 0, - relative_maximum: NumericDataTS = 1, - effects_per_flow_hour: Optional[EffectValuesUser] = None, + fixed_relative_profile: Optional[TimestepData] = None, + relative_minimum: TimestepData = 0, + relative_maximum: TimestepData = 1, + effects_per_flow_hour: Optional[EffectValuesUserTimestep] = None, on_off_parameters: Optional[OnOffParameters] = None, - flow_hours_total_max: Optional[Scalar] = None, - flow_hours_total_min: Optional[Scalar] = None, - load_factor_min: Optional[Scalar] = None, - load_factor_max: Optional[Scalar] = None, - previous_flow_rate: Optional[NumericData] = None, + flow_hours_total_max: Optional[ScenarioData] = None, + flow_hours_total_min: Optional[ScenarioData] = None, + load_factor_min: Optional[ScenarioData] = None, + load_factor_max: Optional[ScenarioData] = None, + previous_flow_rate: Optional[ScenarioData] = None, meta_data: Optional[Dict] = None, ): r""" @@ -240,10 +240,23 @@ def transform_data(self, flow_system: 'FlowSystem'): self.effects_per_flow_hour = flow_system.create_effect_time_series( self.label_full, self.effects_per_flow_hour, 'per_flow_hour' ) + self.flow_hours_total_max = flow_system.create_time_series( + f'{self.label_full}|flow_hours_total_max', self.flow_hours_total_max, has_time_dim=False + ) + self.flow_hours_total_min = flow_system.create_time_series( + f'{self.label_full}|flow_hours_total_min', self.flow_hours_total_min, has_time_dim=False + ) + self.load_factor_max = flow_system.create_time_series( + f'{self.label_full}|load_factor_max', self.load_factor_max, has_time_dim=False + ) + self.load_factor_min = flow_system.create_time_series( + f'{self.label_full}|load_factor_min', self.load_factor_min, has_time_dim=False + ) + if self.on_off_parameters is not None: self.on_off_parameters.transform_data(flow_system, self.label_full) if isinstance(self.size, InvestParameters): - self.size.transform_data(flow_system) + self.size.transform_data(flow_system, self.label_full) def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: infos = super().infos(use_numpy, use_element_label) @@ -308,7 +321,7 @@ def do_modeling(self): self._model.add_variables( lower=self.absolute_flow_rate_bounds[0] if self.element.on_off_parameters is None else 0, upper=self.absolute_flow_rate_bounds[1], - coords=self._model.coords, + coords=self._model.get_coords(), name=f'{self.label_full}|flow_rate', ), 'flow_rate', @@ -414,7 +427,7 @@ def _create_bounds_for_load_factor(self): ) @property - def absolute_flow_rate_bounds(self) -> Tuple[NumericData, NumericData]: + def absolute_flow_rate_bounds(self) -> Tuple[TimestepData, TimestepData]: """Returns absolute flow rate bounds. Important for OnOffModel""" relative_minimum, relative_maximum = self.relative_flow_rate_bounds size = self.element.size @@ -425,7 +438,7 @@ def absolute_flow_rate_bounds(self) -> Tuple[NumericData, NumericData]: return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size @property - def relative_flow_rate_bounds(self) -> Tuple[NumericData, NumericData]: + def relative_flow_rate_bounds(self) -> Tuple[TimestepData, TimestepData]: """Returns relative flow rate bounds.""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: @@ -454,11 +467,11 @@ def do_modeling(self) -> None: self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.selected_data ) self.excess_input = self.add( - self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_input'), + self._model.add_variables(lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_input'), 'excess_input', ) self.excess_output = self.add( - self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_output'), + self._model.add_variables(lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_output'), 'excess_output', ) eq_bus_balance.lhs -= -self.excess_input + self.excess_output diff --git a/flixopt/features.py b/flixopt/features.py index 32c382486..7b92396fd 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -11,7 +11,7 @@ from . import utils from .config import CONFIG -from .core import NumericData, Scalar, TimeSeries +from .core import TimestepData, Scalar, TimeSeries from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects from .structure import Model, SystemModel @@ -27,7 +27,7 @@ def __init__( label_of_element: str, parameters: InvestParameters, defining_variable: [linopy.Variable], - relative_bounds_of_defining_variable: Tuple[NumericData, NumericData], + relative_bounds_of_defining_variable: Tuple[TimestepData, TimestepData], label: Optional[str] = None, on_variable: Optional[linopy.Variable] = None, ): @@ -205,8 +205,8 @@ def __init__( on_off_parameters: OnOffParameters, label_of_element: str, defining_variables: List[linopy.Variable], - defining_bounds: List[Tuple[NumericData, NumericData]], - previous_values: List[Optional[NumericData]], + defining_bounds: List[Tuple[TimestepData, TimestepData]], + previous_values: List[Optional[TimestepData]], label: Optional[str] = None, ): """ @@ -246,7 +246,7 @@ def do_modeling(self): self._model.add_variables( name=f'{self.label_full}|on', binary=True, - coords=self._model.coords, + coords=self._model.get_coords(), ), 'on', ) @@ -275,7 +275,7 @@ def do_modeling(self): self._model.add_variables( name=f'{self.label_full}|off', binary=True, - coords=self._model.coords, + coords=self._model.get_coords(), ), 'off', ) @@ -303,12 +303,12 @@ def do_modeling(self): if self.parameters.use_switch_on: self.switch_on = self.add( - self._model.add_variables(binary=True, name=f'{self.label_full}|switch_on', coords=self._model.coords), + self._model.add_variables(binary=True, name=f'{self.label_full}|switch_on', coords=self._model.get_coords()), 'switch_on', ) self.switch_off = self.add( - self._model.add_variables(binary=True, name=f'{self.label_full}|switch_off', coords=self._model.coords), + self._model.add_variables(binary=True, name=f'{self.label_full}|switch_off', coords=self._model.get_coords()), 'switch_off', ) @@ -451,7 +451,7 @@ def _get_duration_in_hours( self._model.add_variables( lower=0, upper=maximum_duration.selected_data if maximum_duration is not None else mega, - coords=self._model.coords, + coords=self._model.get_coords(), name=f'{self.label_full}|{variable_name}', ), variable_name, @@ -623,7 +623,7 @@ def previous_consecutive_off_hours(self) -> Scalar: return self.compute_consecutive_duration(self.previous_off_values, self._model.hours_per_step) @staticmethod - def compute_previous_on_states(previous_values: List[Optional[NumericData]], epsilon: float = 1e-5) -> np.ndarray: + def compute_previous_on_states(previous_values: List[Optional[TimestepData]], epsilon: float = 1e-5) -> np.ndarray: """ Computes the previous 'on' states {0, 1} of defining variables as a binary array from their previous values. @@ -647,7 +647,7 @@ def compute_previous_on_states(previous_values: List[Optional[NumericData]], eps @staticmethod def compute_consecutive_duration( - binary_values: NumericData, hours_per_timestep: Union[int, float, np.ndarray] + binary_values: TimestepData, hours_per_timestep: Union[int, float, np.ndarray] ) -> Scalar: """ Computes the final consecutive duration in State 'on' (=1) in hours, from a binary. @@ -716,7 +716,7 @@ def do_modeling(self): self._model.add_variables( binary=True, name=f'{self.label_full}|inside_piece', - coords=self._model.coords if self._as_time_series else None, + coords=self._model.get_coords(time_dim=self._as_time_series), ), 'inside_piece', ) @@ -726,7 +726,7 @@ def do_modeling(self): lower=0, upper=1, name=f'{self.label_full}|lambda0', - coords=self._model.coords if self._as_time_series else None, + coords=self._model.get_coords(time_dim=self._as_time_series), ), 'lambda0', ) @@ -736,7 +736,7 @@ def do_modeling(self): lower=0, upper=1, name=f'{self.label_full}|lambda1', - coords=self._model.coords if self._as_time_series else None, + coords=self._model.get_coords(time_dim=self._as_time_series), ), 'lambda1', ) @@ -820,7 +820,7 @@ def do_modeling(self): elif self._zero_point is True: self.zero_point = self.add( self._model.add_variables( - coords=self._model.coords, binary=True, name=f'{self.label_full}|zero_point' + coords=self._model.get_coords(), binary=True, name=f'{self.label_full}|zero_point' ), 'zero_point', ) @@ -847,8 +847,8 @@ def __init__( label_full: Optional[str] = None, total_max: Optional[Scalar] = None, total_min: Optional[Scalar] = None, - max_per_hour: Optional[NumericData] = None, - min_per_hour: Optional[NumericData] = None, + max_per_hour: Optional[TimestepData] = None, + min_per_hour: Optional[TimestepData] = None, ): super().__init__(model, label_of_element=label_of_element, label=label, label_full=label_full) if not shares_are_time_series: # If the condition is True @@ -891,7 +891,7 @@ def do_modeling(self): upper=np.inf if (self._max_per_hour is None) else np.multiply(self._max_per_hour, self._model.hours_per_step), - coords=self._model.coords, + coords=self._model.get_coords(), name=f'{self.label_full}|total_per_timestep', ), 'total_per_timestep', @@ -929,7 +929,7 @@ def add_share( if isinstance(expression, linopy.LinearExpression) and expression.ndim == 0 or not isinstance(expression, linopy.LinearExpression) - else self._model.coords, + else self._model.get_coords(), #TODO: Add logic on what coords to use name=f'{name}->{self.label_full}', ), name, diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index e39d71e94..a4705371c 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,10 +16,10 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import NumericData, NumericDataTS, TimeSeries, TimeSeriesCollection, TimeSeriesData -from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser +from .core import TimestepData, TimeSeries, TimeSeriesCollection, TimeSeriesData, Scalar +from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUserScenario, EffectValuesUserTimestep from .elements import Bus, Component, Flow -from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation +from .structure import CLASS_REGISTRY, Element, SystemModel if TYPE_CHECKING: import pyvis @@ -277,44 +277,71 @@ def transform_data(self): def create_time_series( self, name: str, - data: Optional[Union[NumericData, TimeSeriesData, TimeSeries]], + data: Optional[Union[TimestepData, TimeSeriesData, TimeSeries]], + has_time_dim: bool = True, + has_scenario_dim: bool = True, has_extra_timestep: bool = False, - ) -> Optional[TimeSeries]: + ) -> Optional[Union[Scalar, TimeSeries]]: """ Tries to create a TimeSeries from NumericData Data and adds it to the time_series_collection If the data already is a TimeSeries, nothing happens and the TimeSeries gets reset and returned If the data is a TimeSeriesData, it is converted to a TimeSeries, and the aggregation weights are applied. If the data is None, nothing happens. + + Args: + name: The name of the TimeSeries + data: The data to create a TimeSeries from + has_time_dim: Whether the data has a time dimension + has_scenario_dim: Whether the data has a scenario dimension + has_extra_timestep: Whether the data has an extra timestep """ + if not has_time_dim and not has_scenario_dim: + raise ValueError("At least one of the dimensions must be present") if data is None: return None - elif isinstance(data, TimeSeries): + + if not has_time_dim and self.time_series_collection.scenarios is None: + return data + + if isinstance(data, TimeSeries): data.restore_data() if data in self.time_series_collection: return data return self.time_series_collection.add_time_series( - data=data.selected_data, name=name, has_extra_timestep=has_extra_timestep + data=data.selected_data, + name=name, + has_time_dim=has_time_dim, + has_scenario_dim=has_scenario_dim, + has_extra_timestep=has_extra_timestep, ) elif isinstance(data, TimeSeriesData): data.label = name return self.time_series_collection.add_time_series( data=data.data, name=name, + has_time_dim=has_time_dim, + has_scenario_dim=has_scenario_dim, has_extra_timestep=has_extra_timestep, aggregation_weight=data.agg_weight, aggregation_group=data.agg_group ) return self.time_series_collection.add_time_series( - data=data, name=name, has_extra_timestep=has_extra_timestep + data=data, + name=name, + has_time_dim=has_time_dim, + has_scenario_dim=has_scenario_dim, + has_extra_timestep=has_extra_timestep, ) def create_effect_time_series( self, label_prefix: Optional[str], - effect_values: EffectValuesUser, + effect_values: Union[EffectValuesUserScenario, EffectValuesUserTimestep], label_suffix: Optional[str] = None, - ) -> Optional[EffectTimeSeries]: + has_time_dim: bool = True, + has_scenario_dim: bool = True, + ) -> Optional[Union[EffectTimeSeries, EffectValuesDict]]: """ Transform EffectValues to EffectTimeSeries. Creates a TimeSeries for each key in the nested_values dictionary, using the value as the data. @@ -322,13 +349,31 @@ def create_effect_time_series( The resulting label of the TimeSeries is the label of the parent_element, followed by the label of the Effect in the nested_values and the label_suffix. If the key in the EffectValues is None, the alias 'Standard_Effect' is used + + Args: + label_prefix: Prefix for the TimeSeries name + effect_values: Dictionary of EffectValues + label_suffix: Suffix for the TimeSeries name + has_time_dim: Whether the data has a time dimension + has_scenario_dim: Whether the data has a scenario dimension """ + if not has_time_dim and not has_scenario_dim: + raise ValueError("At least one of the dimensions must be present") + effect_values: Optional[EffectValuesDict] = self.effects.create_effect_values_dict(effect_values) if effect_values is None: return None + if not has_time_dim and self.time_series_collection.scenarios is None: + return effect_values + return { - effect: self.create_time_series('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) + effect: self.create_time_series( + name='|'.join(filter(None, [label_prefix, effect, label_suffix])), + data=value, + has_time_dim=has_time_dim, + has_scenario_dim=has_scenario_dim, + ) for effect, value in effect_values.items() } diff --git a/flixopt/interface.py b/flixopt/interface.py index f9dbeb518..f57362ee3 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,11 +7,11 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from .config import CONFIG -from .core import NumericData, NumericDataTS, Scalar +from .core import TimestepData, NumericDataTS, Scalar, ScenarioData from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports - from .effects import EffectValuesUser, EffectValuesUserScalar + from .effects import EffectValuesUserScenario, EffectValuesUserTimestep from .flow_system import FlowSystem @@ -20,7 +20,7 @@ @register_class_for_io class Piece(Interface): - def __init__(self, start: NumericData, end: NumericData): + def __init__(self, start: TimestepData, end: TimestepData): """ Define a Piece, which is part of a Piecewise object. @@ -30,10 +30,21 @@ def __init__(self, start: NumericData, end: NumericData): """ self.start = start self.end = end + self.has_time_dim = False def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - self.start = flow_system.create_time_series(f'{name_prefix}|start', self.start) - self.end = flow_system.create_time_series(f'{name_prefix}|end', self.end) + self.start = flow_system.create_time_series( + name=f'{name_prefix}|start', + data=self.start, + has_time_dim=self.has_time_dim, + has_scenario_dim=True + ) + self.end = flow_system.create_time_series( + name=f'{name_prefix}|end', + data=self.end, + has_time_dim=self.has_time_dim, + has_scenario_dim=True + ) @register_class_for_io @@ -46,6 +57,17 @@ def __init__(self, pieces: List[Piece]): pieces: The pieces of the piecewise. """ self.pieces = pieces + self._has_time_dim = False + + @property + def has_time_dim(self): + return self._has_time_dim + + @has_time_dim.setter + def has_time_dim(self, value): + self._has_time_dim = value + for piece in self.pieces: + piece.has_time_dim = value def __len__(self): return len(self.pieces) @@ -73,6 +95,18 @@ def __init__(self, piecewises: Dict[str, Piecewise]): piecewises: Dict of Piecewises defining the conversion factors. flow labels as keys, piecewise as values """ self.piecewises = piecewises + self._has_time_dim = True + self.has_time_dim = True # Inital propagation + + @property + def has_time_dim(self): + return self._has_time_dim + + @has_time_dim.setter + def has_time_dim(self, value): + self._has_time_dim = value + for piecewise in self.piecewises.values(): + piecewise.has_time_dim = value def items(self): return self.piecewises.items() @@ -94,12 +128,24 @@ def __init__(self, piecewise_origin: Piecewise, piecewise_shares: Dict[str, Piec """ self.piecewise_origin = piecewise_origin self.piecewise_shares = piecewise_shares + self._has_time_dim = False + self.has_time_dim = False # Inital propagation + + @property + def has_time_dim(self): + return self._has_time_dim + + @has_time_dim.setter + def has_time_dim(self, value): + self._has_time_dim = value + self.piecewise_origin.has_time_dim = value + for piecewise in self.piecewise_shares.values(): + piecewise.has_time_dim = value def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - raise NotImplementedError('PiecewiseEffects is not yet implemented for non scalar shares') - # self.piecewise_origin.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects|origin') - # for name, piecewise in self.piecewise_shares.items(): - # piecewise.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects|{name}') + self.piecewise_origin.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects|origin') + for effect, piecewise in self.piecewise_shares.items(): + piecewise.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects|{effect}') @register_class_for_io @@ -110,14 +156,14 @@ class InvestParameters(Interface): def __init__( self, - fixed_size: Optional[Union[int, float]] = None, - minimum_size: Union[int, float] = 0, # TODO: Use EPSILON? - maximum_size: Optional[Union[int, float]] = None, + fixed_size: Optional[Scalar] = None, + minimum_size: Scalar = 0, # TODO: Use EPSILON? + maximum_size: Optional[Scalar] = None, optional: bool = True, # Investition ist weglassbar - fix_effects: Optional['EffectValuesUserScalar'] = None, - specific_effects: Optional['EffectValuesUserScalar'] = None, # costs per Flow-Unit/Storage-Size/... + fix_effects: Optional['EffectValuesUserScenario'] = None, + specific_effects: Optional['EffectValuesUserScenario'] = None, # costs per Flow-Unit/Storage-Size/... piecewise_effects: Optional[PiecewiseEffects] = None, - divest_effects: Optional['EffectValuesUserScalar'] = None, + divest_effects: Optional['EffectValuesUserScenario'] = None, ): """ Args: @@ -144,19 +190,40 @@ def __init__( minimum_size: Min nominal value (only if: size_is_fixed = False). maximum_size: Max nominal value (only if: size_is_fixed = False). """ - self.fix_effects: EffectValuesUser = fix_effects or {} - self.divest_effects: EffectValuesUser = divest_effects or {} + self.fix_effects: EffectValuesUserScenario = fix_effects or {} + self.divest_effects: EffectValuesUserScenario = divest_effects or {} self.fixed_size = fixed_size self.optional = optional - self.specific_effects: EffectValuesUser = specific_effects or {} + self.specific_effects: EffectValuesUserScenario = specific_effects or {} self.piecewise_effects = piecewise_effects self._minimum_size = minimum_size self._maximum_size = maximum_size or CONFIG.modeling.BIG # default maximum - def transform_data(self, flow_system: 'FlowSystem'): - self.fix_effects = flow_system.effects.create_effect_values_dict(self.fix_effects) - self.divest_effects = flow_system.effects.create_effect_values_dict(self.divest_effects) - self.specific_effects = flow_system.effects.create_effect_values_dict(self.specific_effects) + def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): + self.fix_effects = flow_system.create_effect_time_series( + label_prefix=name_prefix, + effect_values=self.fix_effects, + label_suffix='fix_effects', + has_time_dim=False, + has_scenario_dim=True, + ) + self.divest_effects = flow_system.create_effect_time_series( + label_prefix=name_prefix, + effect_values=self.divest_effects, + label_suffix='divest_effects', + has_time_dim=False, + has_scenario_dim=True, + ) + self.specific_effects = flow_system.create_effect_time_series( + label_prefix=name_prefix, + effect_values=self.specific_effects, + label_suffix='specific_effects', + has_time_dim=False, + has_scenario_dim=True, + ) + if self.piecewise_effects is not None: + self.piecewise_effects.has_time_dim=False + self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') @property def minimum_size(self): @@ -171,15 +238,15 @@ def maximum_size(self): class OnOffParameters(Interface): def __init__( self, - effects_per_switch_on: Optional['EffectValuesUser'] = None, - effects_per_running_hour: Optional['EffectValuesUser'] = None, - on_hours_total_min: Optional[int] = None, - on_hours_total_max: Optional[int] = None, - consecutive_on_hours_min: Optional[NumericData] = None, - consecutive_on_hours_max: Optional[NumericData] = None, - consecutive_off_hours_min: Optional[NumericData] = None, - consecutive_off_hours_max: Optional[NumericData] = None, - switch_on_total_max: Optional[int] = None, + effects_per_switch_on: Optional['EffectValuesUserTimestep'] = None, + effects_per_running_hour: Optional['EffectValuesUserTimestep'] = None, + on_hours_total_min: Optional[ScenarioData] = None, + on_hours_total_max: Optional[ScenarioData] = None, + consecutive_on_hours_min: Optional[TimestepData] = None, + consecutive_on_hours_max: Optional[TimestepData] = None, + consecutive_off_hours_min: Optional[TimestepData] = None, + consecutive_off_hours_max: Optional[TimestepData] = None, + switch_on_total_max: Optional[ScenarioData] = None, force_switch_on: bool = False, ): """ @@ -202,8 +269,8 @@ def __init__( switch_on_total_max: max nr of switchOn operations force_switch_on: force creation of switch on variable, even if there is no switch_on_total_max """ - self.effects_per_switch_on: EffectValuesUser = effects_per_switch_on or {} - self.effects_per_running_hour: EffectValuesUser = effects_per_running_hour or {} + self.effects_per_switch_on: EffectValuesUserTimestep = effects_per_switch_on or {} + self.effects_per_running_hour: EffectValuesUserTimestep = effects_per_running_hour or {} self.on_hours_total_min: Scalar = on_hours_total_min self.on_hours_total_max: Scalar = on_hours_total_max self.consecutive_on_hours_min: NumericDataTS = consecutive_on_hours_min @@ -232,6 +299,15 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.consecutive_off_hours_max = flow_system.create_time_series( f'{name_prefix}|consecutive_off_hours_max', self.consecutive_off_hours_max ) + self.on_hours_total_max = flow_system.create_time_series( + f'{name_prefix}|on_hours_total_max', self.on_hours_total_max, has_time_dim=False + ) + self.on_hours_total_min = flow_system.create_time_series( + f'{name_prefix}|on_hours_total_min', self.on_hours_total_min, has_time_dim=False + ) + self.switch_on_total_max = flow_system.create_time_series( + f'{name_prefix}|switch_on_total_max', self.switch_on_total_max, has_time_dim=False + ) @property def use_off(self) -> bool: diff --git a/flixopt/io.py b/flixopt/io.py index 5cc353836..adaf52f55 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -23,7 +23,7 @@ def replace_timeseries(obj, mode: Literal['name', 'stats', 'data'] = 'name'): return [replace_timeseries(v, mode) for v in obj] elif isinstance(obj, TimeSeries): # Adjust this based on the actual class if obj.all_equal: - return obj.selected_data.values[0].item() + return obj.selected_data.values.max().item() elif mode == 'name': return f'::::{obj.name}' elif mode == 'stats': diff --git a/flixopt/structure.py b/flixopt/structure.py index 2e136c652..7306c97d5 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from rich.pretty import Pretty from .config import CONFIG -from .core import NumericData, Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData +from .core import TimestepData, Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -98,13 +98,32 @@ def hours_per_step(self): def hours_of_previous_timesteps(self): return self.time_series_collection.hours_of_previous_timesteps - @property - def coords(self) -> Tuple[pd.DatetimeIndex]: - return (self.time_series_collection.timesteps,) + def get_coords( + self, + scenario_dim = True, + time_dim = True, + extra_timestep = False + ) -> Optional[Union[Tuple[pd.Index], Tuple[pd.Index, pd.Index]]]: + """ + Returns the coordinates of the model - @property - def coords_extra(self) -> Tuple[pd.DatetimeIndex]: - return (self.time_series_collection.timesteps_extra,) + Args: + scenario_dim: If True, the scenario dimension is included in the coordinates + time_dim: If True, the time dimension is included in the coordinates + extra_timestep: If True, the extra timesteps are used instead of the regular timesteps + + Returns: + The coordinates of the model. Might also be None if no scenarios are present and time_dim is False + """ + scenarios = self.time_series_collection.scenarios + timesteps = self.time_series_collection.timesteps if not extra_timestep else self.time_series_collection.timesteps_extra + if scenarios is None: + if time_dim: + return (timesteps,) + return None + if scenario_dim and not time_dim: + return (scenarios,) + return scenarios, timesteps class Interface: diff --git a/flixopt/utils.py b/flixopt/utils.py index bb6e8ec40..6b5d88693 100644 --- a/flixopt/utils.py +++ b/flixopt/utils.py @@ -11,14 +11,6 @@ logger = logging.getLogger('flixopt') -def is_number(number_alias: Union[int, float, str]): - """Returns True is string is a number.""" - try: - float(number_alias) - return True - except ValueError: - return False - def round_floats(obj, decimals=2): if isinstance(obj, dict): diff --git a/site/release-notes/_template.txt b/site/release-notes/_template.txt deleted file mode 100644 index fe85a0554..000000000 --- a/site/release-notes/_template.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Release v{version} - -**Release Date:** YYYY-MM-DD - -## What's New - -* Feature 1 - Description -* Feature 2 - Description - -## Improvements - -* Improvement 1 - Description -* Improvement 2 - Description - -## Bug Fixes - -* Fixed issue with X -* Resolved problem with Y - -## Breaking Changes - -* Change 1 - Migration instructions -* Change 2 - Migration instructions - -## Deprecations - -* Feature X will be removed in v{next_version} - -## Dependencies - -* Added dependency X v1.2.3 -* Updated dependency Y to v2.0.0 \ No newline at end of file diff --git a/tests/run_all_tests.py b/tests/run_all_tests.py index 5597a47f3..83b6dfacf 100644 --- a/tests/run_all_tests.py +++ b/tests/run_all_tests.py @@ -7,4 +7,4 @@ import pytest if __name__ == '__main__': - pytest.main(['test_functional.py', '--disable-warnings']) + pytest.main(['test_integration.py', '--disable-warnings']) diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 579de9c00..0466f3a2e 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -48,48 +48,6 @@ def test_scalar_conversion(self, sample_time_index): result = DataConverter.as_dataarray(np.float32(42.5), sample_time_index) assert np.all(result.values == 42.5) - def test_series_conversion(self, sample_time_index): - """Test converting a pandas Series.""" - # Test with integer values - series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) - result = DataConverter.as_dataarray(series, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, series.values) - - # Test with float values - series = pd.Series([1.1, 2.2, 3.3, 4.4, 5.5], index=sample_time_index) - result = DataConverter.as_dataarray(series, sample_time_index) - assert np.array_equal(result.values, series.values) - - # Test with mixed NA values - series = pd.Series([1, np.nan, 3, None, 5], index=sample_time_index) - result = DataConverter.as_dataarray(series, sample_time_index) - assert np.array_equal(np.isnan(result.values), np.isnan(series.values)) - assert np.array_equal(result.values[~np.isnan(result.values)], series.values[~np.isnan(series.values)]) - - def test_dataframe_conversion(self, sample_time_index): - """Test converting a pandas DataFrame.""" - # Test with a single-column DataFrame - df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) - result = DataConverter.as_dataarray(df, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values.flatten(), df['A'].values) - - # Test with float values - df = pd.DataFrame({'A': [1.1, 2.2, 3.3, 4.4, 5.5]}, index=sample_time_index) - result = DataConverter.as_dataarray(df, sample_time_index) - assert np.array_equal(result.values.flatten(), df['A'].values) - - # Test with NA values - df = pd.DataFrame({'A': [1, np.nan, 3, None, 5]}, index=sample_time_index) - result = DataConverter.as_dataarray(df, sample_time_index) - assert np.array_equal(np.isnan(result.values), np.isnan(df['A'].values)) - assert np.array_equal(result.values[~np.isnan(result.values)], df['A'].values[~np.isnan(df['A'].values)]) - def test_ndarray_conversion(self, sample_time_index): """Test converting a numpy ndarray.""" # Test with integer 1D array @@ -153,158 +111,6 @@ def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): result = DataConverter.as_dataarray(42.5, sample_time_index, sample_scenario_index) assert np.all(result.values == 42.5) - def test_series_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting Series with scenario dimension.""" - # Create time series data - series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) - - # Convert with scenario dimension - result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) - - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') - - # Values should be broadcast to all scenarios - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(scenario_slice.values, series.values) - - # Test with series containing NaN - series = pd.Series([1, np.nan, 3, np.nan, 5], index=sample_time_index) - result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) - - # Each scenario should have the same pattern of NaNs - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(np.isnan(scenario_slice.values), np.isnan(series.values)) - assert np.array_equal( - scenario_slice.values[~np.isnan(scenario_slice.values)], series.values[~np.isnan(series.values)] - ) - - def test_multi_index_series(self, sample_time_index, sample_scenario_index, multi_index): - """Test converting a Series with MultiIndex (scenario, time).""" - # Create a MultiIndex Series with scenario-specific values - values = [ - # baseline scenario - 10, - 20, - 30, - 40, - 50, - # high_demand scenario - 15, - 25, - 35, - 45, - 55, - # low_price scenario - 5, - 15, - 25, - 35, - 45, - ] - series_multi = pd.Series(values, index=multi_index) - - # Convert the MultiIndex Series - result = DataConverter.as_dataarray(series_multi, sample_time_index, sample_scenario_index) - - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') - - # Check values for each scenario - baseline_values = result.sel(scenario='baseline').values - assert np.array_equal(baseline_values, [10, 20, 30, 40, 50]) - - high_demand_values = result.sel(scenario='high_demand').values - assert np.array_equal(high_demand_values, [15, 25, 35, 45, 55]) - - low_price_values = result.sel(scenario='low_price').values - assert np.array_equal(low_price_values, [5, 15, 25, 35, 45]) - - # Test with some missing values in the MultiIndex - incomplete_index = multi_index[:-2] # Remove last two entries - incomplete_values = values[:-2] # Remove corresponding values - incomplete_series = pd.Series(incomplete_values, index=incomplete_index) - - result = DataConverter.as_dataarray(incomplete_series, sample_time_index, sample_scenario_index) - - # The last value of low_price scenario should be NaN - assert np.isnan(result.sel(scenario='low_price').values[-1]) - - def test_dataframe_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting DataFrame with scenario dimension.""" - # Create a single-column DataFrame - df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) - - # Convert with scenario dimension - result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) - - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') - - # Values should be broadcast to all scenarios - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(scenario_slice.values, df['A'].values) - - def test_multi_index_dataframe(self, sample_time_index, sample_scenario_index, multi_index): - """Test converting a DataFrame with MultiIndex (scenario, time).""" - # Create a MultiIndex DataFrame with scenario-specific values - values = [ - # baseline scenario - 10, - 20, - 30, - 40, - 50, - # high_demand scenario - 15, - 25, - 35, - 45, - 55, - # low_price scenario - 5, - 15, - 25, - 35, - 45, - ] - df_multi = pd.DataFrame({'A': values}, index=multi_index) - - # Convert the MultiIndex DataFrame - result = DataConverter.as_dataarray(df_multi, sample_time_index, sample_scenario_index) - - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') - - # Check values for each scenario - baseline_values = result.sel(scenario='baseline').values - assert np.array_equal(baseline_values, [10, 20, 30, 40, 50]) - - high_demand_values = result.sel(scenario='high_demand').values - assert np.array_equal(high_demand_values, [15, 25, 35, 45, 55]) - - low_price_values = result.sel(scenario='low_price').values - assert np.array_equal(low_price_values, [5, 15, 25, 35, 45]) - - # Test with missing values - incomplete_index = multi_index[:-2] # Remove last two entries - incomplete_values = values[:-2] # Remove corresponding values - incomplete_df = pd.DataFrame({'A': incomplete_values}, index=incomplete_index) - - result = DataConverter.as_dataarray(incomplete_df, sample_time_index, sample_scenario_index) - - # The last value of low_price scenario should be NaN - assert np.isnan(result.sel(scenario='low_price').values[-1]) - - # Test with multiple columns (should raise error) - df_multi_col = pd.DataFrame({'A': values, 'B': [v * 2 for v in values]}, index=multi_index) - - with pytest.raises(ConversionError): - DataConverter.as_dataarray(df_multi_col, sample_time_index, sample_scenario_index) - def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index): """Test converting 1D array with scenario dimension (broadcasting).""" # Create 1D array matching timesteps length @@ -391,12 +197,12 @@ def test_time_index_validation(self): # Test with empty index empty_index = pd.DatetimeIndex([], name='time') - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, empty_index) # Test with non-DatetimeIndex wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, wrong_type_index) def test_scenario_index_validation(self, sample_time_index): @@ -408,11 +214,11 @@ def test_scenario_index_validation(self, sample_time_index): # Test with empty scenario index empty_index = pd.Index([], name='scenario') - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, sample_time_index, empty_index) # Test with non-Index scenario - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, sample_time_index, ['baseline', 'high_demand']) def test_invalid_data_types(self, sample_time_index, sample_scenario_index): @@ -572,46 +378,9 @@ def test_all_nan_data(self, sample_time_index, sample_scenario_index): assert np.all(np.isnan(result.values)) # Series of all NaNs - all_nan_series = pd.Series([np.nan, np.nan, np.nan, np.nan, np.nan], index=sample_time_index) - result = DataConverter.as_dataarray(all_nan_series, sample_time_index, sample_scenario_index) + result = DataConverter.as_dataarray(np.array([np.nan, np.nan, np.nan, np.nan, np.nan]), sample_time_index, sample_scenario_index) assert np.all(np.isnan(result.values)) - def test_subset_index_multiindex(self, sample_time_index, sample_scenario_index): - """Test handling of MultiIndex Series/DataFrames with subset of expected indices.""" - # Create a subset of the expected indexes - subset_time = sample_time_index[1:4] # Middle subset - subset_scenarios = sample_scenario_index[0:2] # First two scenarios - - # Create MultiIndex with subset - subset_multi_index = pd.MultiIndex.from_product([subset_scenarios, subset_time], names=['scenario', 'time']) - - # Create Series with subset of data - values = [ - # baseline (3 values) - 20, - 30, - 40, - # high_demand (3 values) - 25, - 35, - 45, - ] - subset_series = pd.Series(values, index=subset_multi_index) - - # Convert and test - result = DataConverter.as_dataarray(subset_series, sample_time_index, sample_scenario_index) - - # Shape should be full size - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - - # Check values - present values should match - assert result.sel(scenario='baseline', time=subset_time[0]).item() == 20 - assert result.sel(scenario='high_demand', time=subset_time[1]).item() == 35 - - # Missing values should be NaN - assert np.isnan(result.sel(scenario='baseline', time=sample_time_index[0]).item()) - assert np.isnan(result.sel(scenario='low_price', time=sample_time_index[2]).item()) - def test_mixed_data_types(self, sample_time_index, sample_scenario_index): """Test conversion of mixed integer and float data.""" # Create array with mixed types @@ -632,77 +401,6 @@ def test_mixed_data_types(self, sample_time_index, sample_scenario_index): class TestFunctionalUseCase: """Tests for realistic use cases combining multiple features.""" - def test_multiindex_with_nans_and_partial_data(self, sample_time_index, sample_scenario_index): - """Test MultiIndex Series with partial data and NaN values.""" - # Create a MultiIndex Series with missing values and partial coverage - time_subset = sample_time_index[1:4] # Middle 3 timestamps only - - # Build index with holes - idx_tuples = [] - for scenario in sample_scenario_index: - for time in time_subset: - # Skip some combinations to create holes - if scenario == 'baseline' and time == time_subset[0]: - continue - if scenario == 'high_demand' and time == time_subset[2]: - continue - idx_tuples.append((scenario, time)) - - partial_idx = pd.MultiIndex.from_tuples(idx_tuples, names=['scenario', 'time']) - - # Create values with some NaNs - values = [ - # baseline (2 values, skipping first) - 30, - 40, - # high_demand (2 values, skipping last) - 25, - 35, - # low_price (3 values) - 15, - np.nan, - 35, - ] - - # Create Series - partial_series = pd.Series(values, index=partial_idx) - - # Convert and test - result = DataConverter.as_dataarray(partial_series, sample_time_index, sample_scenario_index) - - # Shape should be full size - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - - # Check specific values - assert result.sel(scenario='baseline', time=time_subset[1]).item() == 30 - assert result.sel(scenario='high_demand', time=time_subset[0]).item() == 25 - assert np.isnan(result.sel(scenario='low_price', time=time_subset[1]).item()) - - # All skipped combinations should be NaN - assert np.isnan(result.sel(scenario='baseline', time=time_subset[0]).item()) - assert np.isnan(result.sel(scenario='high_demand', time=time_subset[2]).item()) - - # First and last timestamps should all be NaN (not in original subset) - assert np.all(np.isnan(result.sel(time=sample_time_index[0]).values)) - assert np.all(np.isnan(result.sel(time=sample_time_index[-1]).values)) - - def test_scenario_broadcast_with_nan_values(self, sample_time_index, sample_scenario_index): - """Test broadcasting a Series with NaN values to scenarios.""" - # Create Series with some NaN values - series = pd.Series([1, np.nan, 3, np.nan, 5], index=sample_time_index) - - # Convert with scenario broadcasting - result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) - - # All scenarios should have the same pattern of NaN values - for scenario in sample_scenario_index: - scenario_data = result.sel(scenario=scenario) - assert np.isnan(scenario_data[1].item()) - assert np.isnan(scenario_data[3].item()) - assert scenario_data[0].item() == 1 - assert scenario_data[2].item() == 3 - assert scenario_data[4].item() == 5 - def test_large_dataset(self, sample_scenario_index): """Test with a larger dataset to ensure performance.""" # Create a larger timestep array (e.g., hourly for a year) @@ -814,34 +512,6 @@ def test_preserving_scenario_order(self, sample_time_index): assert np.array_equal(result.sel(scenario='scenario1').values, data[1]) assert np.array_equal(result.sel(scenario='scenario2').values, data[2]) - def test_multiindex_reindexing(self, sample_time_index): - """Test reindexing of MultiIndex Series.""" - # Create scenarios with intentional different order - scenarios = pd.Index(['z_scenario', 'a_scenario', 'm_scenario'], name='scenario') - - # Create MultiIndex with different order than the target - source_scenarios = pd.Index(['a_scenario', 'm_scenario', 'z_scenario'], name='scenario') - multi_idx = pd.MultiIndex.from_product([source_scenarios, sample_time_index], names=['scenario', 'time']) - - # Create values - order should match the source index - values = [] - for i, _ in enumerate(source_scenarios): - values.extend([i * 10 + j for j in range(1, len(sample_time_index) + 1)]) - - # Create Series - series = pd.Series(values, index=multi_idx) - - # Convert using the target scenario order - result = DataConverter.as_dataarray(series, sample_time_index, scenarios) - - # Verify scenario order matches the target - assert list(result.coords['scenario'].values) == list(scenarios) - - # Verify values are correctly indexed - assert np.array_equal(result.sel(scenario='a_scenario').values, [1, 2, 3, 4, 5]) - assert np.array_equal(result.sel(scenario='m_scenario').values, [11, 12, 13, 14, 15]) - assert np.array_equal(result.sel(scenario='z_scenario').values, [21, 22, 23, 24, 25]) - if __name__ == '__main__': pytest.main() @@ -879,12 +549,12 @@ def test_time_index_validation(): # Test with empty index empty_index = pd.DatetimeIndex([], name='time') - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, empty_index) # Test with non-DatetimeIndex wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') - with pytest.raises(ValueError): + with pytest.raises(ConversionError): DataConverter.as_dataarray(42, wrong_type_index) From 99057901f13bcdcf48c82b9962e73135c4445782 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:33:36 +0200 Subject: [PATCH 003/183] Feature/scenarios effects (#216) * Update ShareAllocationModel * Update EffectModel * Update Objective * Improve float conversion in main results * Update dims in ShareAllocationModel * Update dims in ShareAllocationModel * Specify dimensions for ShareAllocationModel * Improve logic to get coords * Bugfix * Apply a scaling factor to the objective if ycenarios are used * Bugfix mein results: always as floats and list of floats * Add scenarios to calculation.py * Improve timestep indexing. Now adjust duration for each timestep accordingly to the new index * Improve validation of Indexes and Bugfix calculate_hours_per_timestep() * Add example # Changes: - change how main results are converted to floats - Improved logic to get coords for vars and constraints - Add scaling factor for the objective to have a better scaled model - Change tilmestep indexing to also update the hours_per_timestep accordingly - Added minimal example --- examples/04_Scenarios/scenario_example.py | 122 ++++++++++++++++++ flixopt/calculation.py | 36 +++--- flixopt/core.py | 143 ++++++++++++++++++---- flixopt/effects.py | 51 +++++--- flixopt/features.py | 40 +++--- flixopt/structure.py | 17 ++- flixopt/utils.py | 6 + 7 files changed, 336 insertions(+), 79 deletions(-) create mode 100644 examples/04_Scenarios/scenario_example.py diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py new file mode 100644 index 000000000..d834ff5f0 --- /dev/null +++ b/examples/04_Scenarios/scenario_example.py @@ -0,0 +1,122 @@ +""" +This script shows how to use the flixopt framework to model a simple energy system. +""" + +import numpy as np +import pandas as pd +from rich.pretty import pprint # Used for pretty printing + +import flixopt as fx + +if __name__ == '__main__': + # --- Create Time Series Data --- + # Heat demand profile (e.g., kW) over time and corresponding power prices + heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], + [30, 0, 100, 118, 125, 20, 20, 20, 20]]) + power_prices = np.ones(9) * 0.08 + + # Create datetime array starting from '2020-01-01' for the given time period + timesteps = pd.date_range('2020-01-01', periods=9, freq='h') + scenarios = pd.Index(['Base Case', 'High Demand']) + flow_system = fx.FlowSystem(timesteps=timesteps, scenarios=scenarios) + + # --- Define Energy Buses --- + # These represent nodes, where the used medias are balanced (electricity, heat, and gas) + flow_system.add_elements(fx.Bus(label='Strom'), fx.Bus(label='Fernwärme'), fx.Bus(label='Gas')) + + # --- Define Effects (Objective and CO2 Emissions) --- + # Cost effect: used as the optimization objective --> minimizing costs + costs = fx.Effect( + label='costs', + unit='€', + description='Kosten', + is_standard=True, # standard effect: no explicit value needed for costs + is_objective=True, # Minimizing costs as the optimization objective + ) + + # CO2 emissions effect with an associated cost impact + CO2 = fx.Effect( + label='CO2', + unit='kg', + description='CO2_e-Emissionen', + specific_share_to_other_effects_operation={costs.label: 0.2}, + maximum_operation_per_hour=1000, # Max CO2 emissions per hour + ) + + # --- Define Flow System Components --- + # Boiler: Converts fuel (gas) into thermal energy (heat) + boiler = fx.linear_converters.Boiler( + label='Boiler', + eta=0.5, + Q_th=fx.Flow(label='Q_th', bus='Fernwärme', size=50, relative_minimum=0.1, relative_maximum=1), + Q_fu=fx.Flow(label='Q_fu', bus='Gas'), + ) + + # Combined Heat and Power (CHP): Generates both electricity and heat from fuel + chp = fx.linear_converters.CHP( + label='CHP', + eta_th=0.5, + eta_el=0.4, + P_el=fx.Flow('P_el', bus='Strom', size=60, relative_minimum=5 / 60), + Q_th=fx.Flow('Q_th', bus='Fernwärme'), + Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + + # Storage: Energy storage system with charging and discharging capabilities + storage = fx.Storage( + label='Storage', + charging=fx.Flow('Q_th_load', bus='Fernwärme', size=1000), + discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1000), + capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), + initial_charge_state=0, # Initial storage state: empty + relative_maximum_charge_state=np.array([80, 70, 80, 80, 80, 80, 80, 80, 80, 80]) * 0.01, + eta_charge=0.9, + eta_discharge=1, # Efficiency factors for charging/discharging + relative_loss_per_hour=0.08, # 8% loss per hour. Absolute loss depends on current charge state + prevent_simultaneous_charge_and_discharge=True, # Prevent charging and discharging at the same time + ) + + # Heat Demand Sink: Represents a fixed heat demand profile + heat_sink = fx.Sink( + label='Heat Demand', + sink=fx.Flow(label='Q_th_Last', bus='Fernwärme', size=1, fixed_relative_profile=heat_demand_per_h), + ) + + # Gas Source: Gas tariff source with associated costs and CO2 emissions + gas_source = fx.Source( + label='Gastarif', + source=fx.Flow(label='Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={costs.label: 0.04, CO2.label: 0.3}), + ) + + # Power Sink: Represents the export of electricity to the grid + power_sink = fx.Sink( + label='Einspeisung', sink=fx.Flow(label='P_el', bus='Strom', effects_per_flow_hour=-1 * power_prices) + ) + + # --- Build the Flow System --- + # Add all defined components and effects to the flow system + flow_system.add_elements(costs, CO2, boiler, storage, chp, heat_sink, gas_source, power_sink) + + # Visualize the flow system for validation purposes + flow_system.plot_network(show=True) + + # --- Define and Run Calculation --- + # Create a calculation object to model the Flow System + calculation = fx.FullCalculation(name='Sim1', flow_system=flow_system) + calculation.do_modeling() # Translate the model to a solvable form, creating equations and Variables + + # --- Solve the Calculation and Save Results --- + calculation.solve(fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=30)) + + # --- Analyze Results --- + calculation.results['Fernwärme'].plot_node_balance_pie() + calculation.results['Fernwärme'].plot_node_balance() + calculation.results['Storage'].plot_node_balance() + calculation.results.plot_heatmap('CHP(Q_th)|flow_rate') + + # Convert the results for the storage component to a dataframe and display + df = calculation.results['Storage'].node_balance_with_charge_state() + print(df) + + # Save results to file for later usage + calculation.results.to_file() diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 03cf8b9a6..2dbb6af19 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -44,19 +44,22 @@ def __init__( name: str, flow_system: FlowSystem, active_timesteps: Optional[pd.DatetimeIndex] = None, + selected_scenarios: Optional[pd.Index] = None, folder: Optional[pathlib.Path] = None, ): """ Args: name: name of calculation flow_system: flow_system which should be calculated - active_timesteps: list with indices, which should be used for calculation. If None, then all timesteps are used. + active_timesteps: timesteps which should be used for calculation. If None, then all timesteps are used. + selected_scenarios: scenarios which should be used for calculation. If None, then all scenarios are used. folder: folder where results should be saved. If None, then the current working directory is used. """ self.name = name self.flow_system = flow_system self.model: Optional[SystemModel] = None self.active_timesteps = active_timesteps + self.selected_scenarios = selected_scenarios self.durations = {'modeling': 0.0, 'solving': 0.0, 'saving': 0.0} self.folder = pathlib.Path.cwd() / 'results' if folder is None else pathlib.Path(folder) @@ -73,48 +76,49 @@ def __init__( @property def main_results(self) -> Dict[str, Union[Scalar, Dict]]: from flixopt.features import InvestmentModel - - return { + main_results = { 'Objective': self.model.objective.value, - 'Penalty': float(self.model.effects.penalty.total.solution.values), + 'Penalty': self.model.effects.penalty.total.solution.values, 'Effects': { f'{effect.label} [{effect.unit}]': { - 'operation': float(effect.model.operation.total.solution.values), - 'invest': float(effect.model.invest.total.solution.values), - 'total': float(effect.model.total.solution.values), + 'operation': effect.model.operation.total.solution.values, + 'invest': effect.model.invest.total.solution.values, + 'total': effect.model.total.solution.values, } for effect in self.flow_system.effects }, 'Invest-Decisions': { 'Invested': { - model.label_of_element: float(model.size.solution) + model.label_of_element: model.size.solution for component in self.flow_system.components.values() for model in component.model.all_sub_models - if isinstance(model, InvestmentModel) and float(model.size.solution) >= CONFIG.modeling.EPSILON + if isinstance(model, InvestmentModel) and model.size.solution >= CONFIG.modeling.EPSILON }, 'Not invested': { - model.label_of_element: float(model.size.solution) + model.label_of_element: model.size.solution for component in self.flow_system.components.values() for model in component.model.all_sub_models - if isinstance(model, InvestmentModel) and float(model.size.solution) < CONFIG.modeling.EPSILON + if isinstance(model, InvestmentModel) and model.size.solution < CONFIG.modeling.EPSILON }, }, 'Buses with excess': [ { bus.label_full: { - 'input': float(np.sum(bus.model.excess_input.solution.values)), - 'output': float(np.sum(bus.model.excess_output.solution.values)), + 'input': np.sum(bus.model.excess_input.solution.values), + 'output': np.sum(bus.model.excess_output.solution.values), } } for bus in self.flow_system.buses.values() if bus.with_excess and ( - float(np.sum(bus.model.excess_input.solution.values)) > 1e-3 - or float(np.sum(bus.model.excess_output.solution.values)) > 1e-3 + np.sum(bus.model.excess_input.solution.values) > 1e-3 + or np.sum(bus.model.excess_output.solution.values) > 1e-3 ) ], } + return utils.round_floats(main_results) + @property def summary(self): return { @@ -184,7 +188,7 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma def _activate_time_series(self): self.flow_system.transform_data() self.flow_system.time_series_collection.set_selection( - timesteps=self.active_timesteps + timesteps=self.active_timesteps, scenarios=self.selected_scenarios ) diff --git a/flixopt/core.py b/flixopt/core.py index 185236b3a..ac62bc33a 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -795,17 +795,19 @@ def __init__( hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] = None, ): """Initialize a TimeSeriesCollection.""" - self._validate_timesteps(timesteps) + self._full_timesteps = self._validate_timesteps(timesteps) + self._full_scenarios = self._validate_scenarios(scenarios) + + self._full_timesteps_extra = self._create_timesteps_with_extra( + self._full_timesteps, + self._calculate_hours_of_final_timestep(self._full_timesteps, hours_of_final_timestep=hours_of_last_timestep) + ) + self._full_hours_per_timestep = self.calculate_hours_per_timestep(self._full_timesteps_extra, self._full_scenarios) + self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps ) #TODO: Make dynamic - self._full_timesteps = timesteps - self._full_timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - self._full_hours_per_timestep = self.calculate_hours_per_timestep(self._full_timesteps_extra) - - self._full_scenarios = scenarios - # Series that need extra timestep self._has_extra_timestep: set = set() @@ -940,13 +942,13 @@ def _update_selected_timesteps(self, timesteps: Optional[pd.DatetimeIndex]) -> N self._selected_hours_per_timestep = None return - self._validate_timesteps(timesteps, self._full_timesteps) - - self._selected_timesteps = timesteps - self._selected_hours_per_timestep = self._full_hours_per_timestep.sel(time=timesteps) + self._selected_timesteps = self._validate_timesteps(timesteps, self._full_timesteps) self._selected_timesteps_extra = self._create_timesteps_with_extra( - timesteps, self._selected_hours_per_timestep.isel(time=-1).max().item() + timesteps, + self._calculate_hours_of_final_timestep(timesteps, self._full_timesteps) ) + self._selected_hours_per_timestep = self.calculate_hours_per_timestep(self._selected_timesteps_extra, + self._selected_scenarios) def as_dataset(self, with_extra_timestep: bool = True, with_constants: bool = True) -> xr.Dataset: """ @@ -1108,7 +1110,10 @@ def _calculate_group_weights(self) -> Dict[str, float]: return {group: 1 / count for group, count in group_counts.items()} @staticmethod - def _validate_timesteps(timesteps: pd.DatetimeIndex, present_timesteps: Optional[pd.DatetimeIndex] = None): + def _validate_timesteps( + timesteps: pd.DatetimeIndex, + present_timesteps: Optional[pd.DatetimeIndex] = None + ) -> pd.DatetimeIndex: """ Validate timesteps format and rename if needed. Args: @@ -1131,7 +1136,7 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex, present_timesteps: Optional # Ensure timesteps has the required name if timesteps.name != 'time': - logger.warning('Renamed timesteps to "time" (was "%s")', timesteps.name) + logger.debug('Renamed timesteps to "time" (was "%s")', timesteps.name) timesteps.name = 'time' # Ensure timesteps is sorted @@ -1146,19 +1151,56 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex, present_timesteps: Optional if present_timesteps is not None and not set(timesteps).issubset(set(present_timesteps)): raise ValueError('timesteps must be a subset of present_timesteps') + return timesteps + + @staticmethod + def _validate_scenarios( + scenarios: pd.Index, + present_scenarios: Optional[pd.Index] = None + ) -> Optional[pd.Index]: + """ + Validate scenario format and rename if needed. + Args: + scenarios: The scenarios to validate + present_scenarios: The present_scenarios that are present in the dataset + + Raises: + ValueError: If timesteps is not a pandas DatetimeIndex + ValueError: If timesteps is not at least 2 timestamps + ValueError: If timesteps has a different name than 'time' + ValueError: If timesteps is not sorted + ValueError: If timesteps contains duplicates + ValueError: If timesteps is not a subset of present_timesteps + """ + if scenarios is None: + return None + + if not isinstance(scenarios, pd.Index): + logger.warning('Converting scenarios to pandas.Index') + scenarios = pd.Index(scenarios, name='scenario') + + if len(scenarios) < 2: + logger.warning('scenarios must contain at least 2 scenarios') + raise ValueError('timesteps must contain at least 2 timestamps') + + # Ensure timesteps has the required name + if scenarios.name != 'scenario': + logger.debug('Renamed scenarios to "scneario" (was "%s")', scenarios.name) + scenarios.name = 'scenario' + + # Ensure timesteps is a subset of present_timesteps + if present_scenarios is not None and not set(scenarios).issubset(set(present_scenarios)): + raise ValueError('scenarios must be a subset of present_scenarios') + + return scenarios + @staticmethod def _create_timesteps_with_extra( - timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] + timesteps: pd.DatetimeIndex, + hours_of_last_timestep: float ) -> pd.DatetimeIndex: """Create timesteps with an extra step at the end.""" - if hours_of_last_timestep is not None: - # Create the extra timestep using the specified duration - last_date = pd.DatetimeIndex([timesteps[-1] + pd.Timedelta(hours=hours_of_last_timestep)], name='time') - else: - # Use the last interval as the extra timestep duration - last_date = pd.DatetimeIndex([timesteps[-1] + (timesteps[-1] - timesteps[-2])], name='time') - - # Combine with original timesteps + last_date = pd.DatetimeIndex([timesteps[-1] + pd.Timedelta(hours=hours_of_last_timestep)], name='time') return pd.DatetimeIndex(timesteps.append(last_date), name='time') @staticmethod @@ -1174,14 +1216,61 @@ def _calculate_hours_of_previous_timesteps( return first_interval.total_seconds() / 3600 # Convert to hours @staticmethod - def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: + def _calculate_hours_of_final_timestep( + timesteps: pd.DatetimeIndex, + timesteps_superset: Optional[pd.DatetimeIndex] = None, + hours_of_final_timestep: Optional[float] = None, + ) -> float: + """ + Calculate duration of the final timestep. + If timesteps_subset is provided, the final timestep is calculated for this subset. + The hours_of_final_timestep is only used if the final timestep cant be determined from the timesteps. + + Args: + timesteps: The full timesteps + timesteps_subset: The subset of timesteps + hours_of_final_timestep: The duration of the final timestep, if already known + + Returns: + The duration of the final timestep in hours + + Raises: + ValueError: If the provided timesteps_subset does not end before the timesteps superset + """ + if timesteps_superset is None: + if hours_of_final_timestep is not None: + return hours_of_final_timestep + return (timesteps[-1] - timesteps[-2]) / pd.Timedelta(hours=1) + + final_timestep = timesteps[-1] + + if timesteps_superset[-1] == final_timestep: + if hours_of_final_timestep is not None: + return hours_of_final_timestep + return (timesteps_superset[-1] - timesteps_superset[-2]) / pd.Timedelta(hours=1) + + elif timesteps_superset[-1] <= final_timestep: + raise ValueError(f'The provided timesteps ({timesteps}) end ' + f'after the provided timesteps_superset ({timesteps_superset})') + else: + # Get the first timestep in the superset that is after the final timestep of the subset + extra_timestep = timesteps_superset[timesteps_superset > final_timestep].min() + return (extra_timestep - final_timestep) / pd.Timedelta(hours=1) + + @staticmethod + def calculate_hours_per_timestep( + timesteps_extra: pd.DatetimeIndex, + scenarios: Optional[pd.Index] = None + ) -> xr.DataArray: """Calculate duration of each timestep.""" # Calculate differences between consecutive timestamps hours_per_step = np.diff(timesteps_extra) / pd.Timedelta(hours=1) - return xr.DataArray( - data=hours_per_step, coords={'time': timesteps_extra[:-1]}, dims=('time',), name='hours_per_step' - ) + return DataConverter.as_dataarray( + hours_per_step, + timesteps=timesteps_extra[:-1], + scenarios=scenarios, + ).rename('hours_per_step') def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10, by_scenario: bool = False) -> str: diff --git a/flixopt/effects.py b/flixopt/effects.py index e834e339e..e15fa16b1 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -118,10 +118,11 @@ def __init__(self, model: SystemModel, element: Effect): self.total: Optional[linopy.Variable] = None self.invest: ShareAllocationModel = self.add( ShareAllocationModel( - self._model, - False, - self.label_of_element, - 'invest', + model=self._model, + has_time_dim=False, + has_scenario_dim=True, + label_of_element=self.label_of_element, + label='invest', label_full=f'{self.label_full}(invest)', total_max=self.element.maximum_invest, total_min=self.element.minimum_invest, @@ -130,10 +131,11 @@ def __init__(self, model: SystemModel, element: Effect): self.operation: ShareAllocationModel = self.add( ShareAllocationModel( - self._model, - True, - self.label_of_element, - 'operation', + model=self._model, + has_time_dim=True, + has_scenario_dim=True, + label_of_element=self.label_of_element, + label='operation', label_full=f'{self.label_full}(operation)', total_max=self.element.maximum_operation, total_min=self.element.minimum_operation, @@ -154,7 +156,7 @@ def do_modeling(self): self._model.add_variables( lower=self.element.minimum_total if self.element.minimum_total is not None else -np.inf, upper=self.element.maximum_total if self.element.maximum_total is not None else np.inf, - coords=None, + coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|total', ), 'total', @@ -162,7 +164,7 @@ def do_modeling(self): self.add( self._model.add_constraints( - self.total == self.operation.total.sum() + self.invest.total.sum(), name=f'{self.label_full}|total' + self.total == self.operation.total + self.invest.total, name=f'{self.label_full}|total' ), 'total', ) @@ -350,29 +352,42 @@ def add_share_to_effects( ) -> None: for effect, expression in expressions.items(): if target == 'operation': - self.effects[effect].model.operation.add_share(name, expression) + self.effects[effect].model.operation.add_share( + name, + expression, + has_time_dim=True, + has_scenario_dim=True, + ) elif target == 'invest': - self.effects[effect].model.invest.add_share(name, expression) + self.effects[effect].model.invest.add_share( + name, + expression, + has_time_dim=False, + has_scenario_dim=True, + ) else: raise ValueError(f'Target {target} not supported!') def add_share_to_penalty(self, name: str, expression: linopy.LinearExpression) -> None: if expression.ndim != 0: raise TypeError(f'Penalty shares must be scalar expressions! ({expression.ndim=})') - self.penalty.add_share(name, expression) + self.penalty.add_share(name, expression, has_time_dim=False, has_scenario_dim=False) def do_modeling(self): for effect in self.effects: effect.create_model(self._model) self.penalty = self.add( - ShareAllocationModel(self._model, shares_are_time_series=False, label_of_element='Penalty') + ShareAllocationModel(self._model, has_time_dim=False, has_scenario_dim=False, label_of_element='Penalty') ) for model in [effect.model for effect in self.effects] + [self.penalty]: model.do_modeling() self._add_share_between_effects() - - self._model.add_objective(self.effects.objective_effect.model.total + self.penalty.total) + scaling_factor = len(self._model.time_series_collection.scenarios) if self._model.time_series_collection.scenarios is not None else 1 + self._model.add_objective( + (self.effects.objective_effect.model.total / scaling_factor).sum() + + (self.penalty.total / scaling_factor).sum() + ) def _add_share_between_effects(self): for origin_effect in self.effects: @@ -381,10 +396,14 @@ def _add_share_between_effects(self): self.effects[target_effect].model.operation.add_share( origin_effect.model.operation.label_full, origin_effect.model.operation.total_per_timestep * time_series.selected_data, + has_time_dim=True, + has_scenario_dim=True, ) # 2. invest: -> hier ist es Scalar (share) for target_effect, factor in origin_effect.specific_share_to_other_effects_invest.items(): self.effects[target_effect].model.invest.add_share( origin_effect.model.invest.label_full, origin_effect.model.invest.total * factor, + has_time_dim=False, + has_scenario_dim=True, ) diff --git a/flixopt/features.py b/flixopt/features.py index 7b92396fd..a9f50aba6 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -841,7 +841,8 @@ class ShareAllocationModel(Model): def __init__( self, model: SystemModel, - shares_are_time_series: bool, + has_time_dim: bool, + has_scenario_dim: bool, label_of_element: Optional[str] = None, label: Optional[str] = None, label_full: Optional[str] = None, @@ -851,9 +852,9 @@ def __init__( min_per_hour: Optional[TimestepData] = None, ): super().__init__(model, label_of_element=label_of_element, label=label, label_full=label_full) - if not shares_are_time_series: # If the condition is True + if not has_time_dim: # If the condition is True assert max_per_hour is None and min_per_hour is None, ( - 'Both max_per_hour and min_per_hour cannot be used when shares_are_time_series is False' + 'Both max_per_hour and min_per_hour cannot be used when has_time_dim is False' ) self.total_per_timestep: Optional[linopy.Variable] = None self.total: Optional[linopy.Variable] = None @@ -864,7 +865,8 @@ def __init__( self._eq_total: Optional[linopy.Constraint] = None # Parameters - self._shares_are_time_series = shares_are_time_series + self._has_time_dim = has_time_dim + self._has_scenario_dim = has_scenario_dim self._total_max = total_max if total_min is not None else np.inf self._total_min = total_min if total_min is not None else -np.inf self._max_per_hour = max_per_hour if max_per_hour is not None else np.inf @@ -873,7 +875,10 @@ def __init__( def do_modeling(self): self.total = self.add( self._model.add_variables( - lower=self._total_min, upper=self._total_max, coords=None, name=f'{self.label_full}|total' + lower=self._total_min, + upper=self._total_max, + coords=self._model.get_coords(time_dim=False, scenario_dim=self._has_scenario_dim), + name=f'{self.label_full}|total' ), 'total', ) @@ -882,16 +887,16 @@ def do_modeling(self): self._model.add_constraints(self.total == 0, name=f'{self.label_full}|total'), 'total' ) - if self._shares_are_time_series: + if self._has_time_dim: self.total_per_timestep = self.add( self._model.add_variables( lower=-np.inf if (self._min_per_hour is None) - else np.multiply(self._min_per_hour, self._model.hours_per_step), + else self._min_per_hour * self._model.hours_per_step, upper=np.inf if (self._max_per_hour is None) - else np.multiply(self._max_per_hour, self._model.hours_per_step), - coords=self._model.get_coords(), + else self._max_per_hour * self._model.hours_per_step, + coords=self._model.get_coords(time_dim=True, scenario_dim=self._has_scenario_dim), name=f'{self.label_full}|total_per_timestep', ), 'total_per_timestep', @@ -903,12 +908,14 @@ def do_modeling(self): ) # Add it to the total - self._eq_total.lhs -= self.total_per_timestep.sum() + self._eq_total.lhs -= self.total_per_timestep.sum(dim='time') def add_share( self, name: str, expression: linopy.LinearExpression, + has_time_dim: bool, + has_scenario_dim: bool, ): """ Add a share to the share allocation model. If the share already exists, the expression is added to the existing share. @@ -920,16 +927,17 @@ def add_share( name: The name of the share. expression: The expression of the share. Added to the right hand side of the constraint. """ + if has_time_dim and not self._has_time_dim: + raise ValueError('Cannot add share with time_dim=True to a model without time_dim') + if has_scenario_dim and not self._has_scenario_dim: + raise ValueError('Cannot add share with scenario_dim=True to a model without scenario_dim') + if name in self.shares: self.share_constraints[name].lhs -= expression else: self.shares[name] = self.add( self._model.add_variables( - coords=None - if isinstance(expression, linopy.LinearExpression) - and expression.ndim == 0 - or not isinstance(expression, linopy.LinearExpression) - else self._model.get_coords(), #TODO: Add logic on what coords to use + coords=self._model.get_coords(time_dim=has_time_dim, scenario_dim=has_scenario_dim), name=f'{name}->{self.label_full}', ), name, @@ -937,7 +945,7 @@ def add_share( self.share_constraints[name] = self.add( self._model.add_constraints(self.shares[name] == expression, name=f'{name}->{self.label_full}'), name ) - if self.shares[name].ndim == 0: + if not has_time_dim: self._eq_total.lhs -= self.shares[name] else: self._eq_total_per_timestep.lhs -= self.shares[name] diff --git a/flixopt/structure.py b/flixopt/structure.py index 7306c97d5..91fc6c1d0 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -115,15 +115,24 @@ def get_coords( Returns: The coordinates of the model. Might also be None if no scenarios are present and time_dim is False """ + if not scenario_dim and not time_dim: + return None scenarios = self.time_series_collection.scenarios timesteps = self.time_series_collection.timesteps if not extra_timestep else self.time_series_collection.timesteps_extra - if scenarios is None: - if time_dim: + + if scenario_dim and time_dim: + if scenarios is None: return (timesteps,) - return None + return scenarios, timesteps + if scenario_dim and not time_dim: + if scenarios is None: + return None return (scenarios,) - return scenarios, timesteps + if time_dim and not scenario_dim: + return (timesteps,) + + raise ValueError(f'Cannot get coordinates with both {scenario_dim=} and {time_dim=}') class Interface: diff --git a/flixopt/utils.py b/flixopt/utils.py index 6b5d88693..542f87942 100644 --- a/flixopt/utils.py +++ b/flixopt/utils.py @@ -19,6 +19,12 @@ def round_floats(obj, decimals=2): return [round_floats(v, decimals) for v in obj] elif isinstance(obj, float): return round(obj, decimals) + elif isinstance(obj, int): + return obj + elif isinstance(obj, np.ndarray): + return np.round(obj, decimals).tolist() + elif isinstance(obj, xr.DataArray): + return obj.round(decimals).values.tolist() return obj From 24af8c60ba5fc2ce008af89e8dc45cefd0344a98 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Apr 2025 14:38:08 +0200 Subject: [PATCH 004/183] ruff check and format --- examples/04_Scenarios/scenario_example.py | 3 +- flixopt/calculation.py | 1 + flixopt/components.py | 18 +++--- flixopt/core.py | 53 +++++++---------- flixopt/effects.py | 16 +++-- flixopt/elements.py | 10 +++- flixopt/features.py | 21 ++++--- flixopt/flow_system.py | 17 ++++-- flixopt/interface.py | 14 ++--- flixopt/structure.py | 11 ++-- flixopt/utils.py | 1 - tests/test_dataconverter.py | 4 +- tests/test_timeseries.py | 71 +++++++++++------------ 13 files changed, 119 insertions(+), 121 deletions(-) diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index d834ff5f0..a004d1851 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -11,8 +11,7 @@ if __name__ == '__main__': # --- Create Time Series Data --- # Heat demand profile (e.g., kW) over time and corresponding power prices - heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], - [30, 0, 100, 118, 125, 20, 20, 20, 20]]) + heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], [30, 0, 100, 118, 125, 20, 20, 20, 20]]) power_prices = np.ones(9) * 0.08 # Create datetime array starting from '2020-01-01' for the given time period diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 2dbb6af19..962d2c95f 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -76,6 +76,7 @@ def __init__( @property def main_results(self) -> Dict[str, Union[Scalar, Dict]]: from flixopt.features import InvestmentModel + main_results = { 'Objective': self.model.objective.value, 'Penalty': self.model.effects.penalty.total.solution.values, diff --git a/flixopt/components.py b/flixopt/components.py index 4726ca0f4..69b0fe47a 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -9,7 +9,7 @@ import numpy as np from . import utils -from .core import TimestepData, PlausibilityError, Scalar, TimeSeries, ScenarioData +from .core import PlausibilityError, Scalar, ScenarioData, TimeSeries, TimestepData from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion @@ -225,9 +225,7 @@ def _plausibility_checks(self) -> None: Check for infeasible or uncommon combinations of parameters """ if isinstance(self.initial_charge_state, str) and not self.initial_charge_state == 'lastValueOfSim': - raise PlausibilityError( - f'initial_charge_state has undefined value: {self.initial_charge_state}' - ) + raise PlausibilityError(f'initial_charge_state has undefined value: {self.initial_charge_state}') else: if isinstance(self.capacity_in_flow_hours, InvestParameters): if self.capacity_in_flow_hours.fixed_size is None: @@ -244,7 +242,7 @@ def _plausibility_checks(self) -> None: minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) # initial capacity <= allowed max for minimum_size: maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) - #TODO: index=1 ??? I think index 0 + # TODO: index=1 ??? I think index 0 if (self.initial_charge_state > maximum_inital_capacity).any(): raise ValueError( @@ -257,6 +255,7 @@ def _plausibility_checks(self) -> None: f'is below allowed minimum charge_state {minimum_inital_capacity}' ) + @register_class_for_io class Transmission(Component): # TODO: automatic on-Value in Flows if loss_abs @@ -427,7 +426,9 @@ def do_modeling(self): self.add( self._model.add_constraints( sum([flow.model.flow_rate * conv_factors[flow.label].selected_data for flow in used_inputs]) - == sum([flow.model.flow_rate * conv_factors[flow.label].selected_data for flow in used_outputs]), + == sum( + [flow.model.flow_rate * conv_factors[flow.label].selected_data for flow in used_outputs] + ), name=f'{self.label_full}|conversion_{i}', ) ) @@ -467,7 +468,10 @@ def do_modeling(self): lb, ub = self.absolute_charge_state_bounds self.charge_state = self.add( self._model.add_variables( - lower=lb, upper=ub, coords=self._model.get_coords(extra_timestep=True), name=f'{self.label_full}|charge_state' + lower=lb, + upper=ub, + coords=self._model.get_coords(extra_timestep=True), + name=f'{self.label_full}|charge_state', ), 'charge_state', ) diff --git a/flixopt/core.py b/flixopt/core.py index ac62bc33a..68d1ddaad 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -787,6 +787,7 @@ class TimeSeriesCollection: Provides a way to store time series data and work with subsets of dimensions that automatically update all references when changed. """ + def __init__( self, timesteps: pd.DatetimeIndex, @@ -800,13 +801,17 @@ def __init__( self._full_timesteps_extra = self._create_timesteps_with_extra( self._full_timesteps, - self._calculate_hours_of_final_timestep(self._full_timesteps, hours_of_final_timestep=hours_of_last_timestep) + self._calculate_hours_of_final_timestep( + self._full_timesteps, hours_of_final_timestep=hours_of_last_timestep + ), + ) + self._full_hours_per_timestep = self.calculate_hours_per_timestep( + self._full_timesteps_extra, self._full_scenarios ) - self._full_hours_per_timestep = self.calculate_hours_per_timestep(self._full_timesteps_extra, self._full_scenarios) self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps - ) #TODO: Make dynamic + ) # TODO: Make dynamic # Series that need extra timestep self._has_extra_timestep: set = set() @@ -944,11 +949,11 @@ def _update_selected_timesteps(self, timesteps: Optional[pd.DatetimeIndex]) -> N self._selected_timesteps = self._validate_timesteps(timesteps, self._full_timesteps) self._selected_timesteps_extra = self._create_timesteps_with_extra( - timesteps, - self._calculate_hours_of_final_timestep(timesteps, self._full_timesteps) + timesteps, self._calculate_hours_of_final_timestep(timesteps, self._full_timesteps) + ) + self._selected_hours_per_timestep = self.calculate_hours_per_timestep( + self._selected_timesteps_extra, self._selected_scenarios ) - self._selected_hours_per_timestep = self.calculate_hours_per_timestep(self._selected_timesteps_extra, - self._selected_scenarios) def as_dataset(self, with_extra_timestep: bool = True, with_constants: bool = True) -> xr.Dataset: """ @@ -1006,16 +1011,11 @@ def _propagate_selection_to_time_series(self) -> None: """Apply the current selection to all TimeSeries objects.""" for ts_name, ts in self._time_series.items(): if ts.has_time_dim: - timesteps = ( - self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps - ) + timesteps = self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps else: timesteps = None - ts.set_selection( - timesteps=timesteps, - scenarios=self.scenarios if ts.has_scenario_dim else None - ) + ts.set_selection(timesteps=timesteps, scenarios=self.scenarios if ts.has_scenario_dim else None) def __getitem__(self, name: str) -> TimeSeries: """ @@ -1072,9 +1072,7 @@ def update_time_series(self, name: str, data: TimestepData) -> TimeSeries: # Convert data to proper format data_array = DataConverter.as_dataarray( - data, - timesteps=target_timesteps, - scenarios=self.scenarios if ts.has_scenario_dim else None + data, timesteps=target_timesteps, scenarios=self.scenarios if ts.has_scenario_dim else None ) # Update the TimeSeries @@ -1111,8 +1109,7 @@ def _calculate_group_weights(self) -> Dict[str, float]: @staticmethod def _validate_timesteps( - timesteps: pd.DatetimeIndex, - present_timesteps: Optional[pd.DatetimeIndex] = None + timesteps: pd.DatetimeIndex, present_timesteps: Optional[pd.DatetimeIndex] = None ) -> pd.DatetimeIndex: """ Validate timesteps format and rename if needed. @@ -1154,10 +1151,7 @@ def _validate_timesteps( return timesteps @staticmethod - def _validate_scenarios( - scenarios: pd.Index, - present_scenarios: Optional[pd.Index] = None - ) -> Optional[pd.Index]: + def _validate_scenarios(scenarios: pd.Index, present_scenarios: Optional[pd.Index] = None) -> Optional[pd.Index]: """ Validate scenario format and rename if needed. Args: @@ -1195,10 +1189,7 @@ def _validate_scenarios( return scenarios @staticmethod - def _create_timesteps_with_extra( - timesteps: pd.DatetimeIndex, - hours_of_last_timestep: float - ) -> pd.DatetimeIndex: + def _create_timesteps_with_extra(timesteps: pd.DatetimeIndex, hours_of_last_timestep: float) -> pd.DatetimeIndex: """Create timesteps with an extra step at the end.""" last_date = pd.DatetimeIndex([timesteps[-1] + pd.Timedelta(hours=hours_of_last_timestep)], name='time') return pd.DatetimeIndex(timesteps.append(last_date), name='time') @@ -1250,8 +1241,9 @@ def _calculate_hours_of_final_timestep( return (timesteps_superset[-1] - timesteps_superset[-2]) / pd.Timedelta(hours=1) elif timesteps_superset[-1] <= final_timestep: - raise ValueError(f'The provided timesteps ({timesteps}) end ' - f'after the provided timesteps_superset ({timesteps_superset})') + raise ValueError( + f'The provided timesteps ({timesteps}) end after the provided timesteps_superset ({timesteps_superset})' + ) else: # Get the first timestep in the superset that is after the final timestep of the subset extra_timestep = timesteps_superset[timesteps_superset > final_timestep].min() @@ -1259,8 +1251,7 @@ def _calculate_hours_of_final_timestep( @staticmethod def calculate_hours_per_timestep( - timesteps_extra: pd.DatetimeIndex, - scenarios: Optional[pd.Index] = None + timesteps_extra: pd.DatetimeIndex, scenarios: Optional[pd.Index] = None ) -> xr.DataArray: """Calculate duration of each timestep.""" # Calculate differences between consecutive timestamps diff --git a/flixopt/effects.py b/flixopt/effects.py index e15fa16b1..226e59a0f 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -11,9 +11,8 @@ import linopy import numpy as np -import pandas as pd -from .core import TimestepData, NumericDataTS, Scalar, TimeSeries, TimeSeriesCollection, ScenarioData, TimestepData +from .core import NumericDataTS, ScenarioData, TimeSeries, TimeSeriesCollection, TimestepData from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -79,7 +78,9 @@ def __init__( self.specific_share_to_other_effects_operation: EffectValuesUserTimestep = ( specific_share_to_other_effects_operation or {} ) - self.specific_share_to_other_effects_invest: EffectValuesUserTimestep = specific_share_to_other_effects_invest or {} + self.specific_share_to_other_effects_invest: EffectValuesUserTimestep = ( + specific_share_to_other_effects_invest or {} + ) self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation self.minimum_operation_per_hour: NumericDataTS = minimum_operation_per_hour @@ -211,8 +212,7 @@ def add_effects(self, *effects: Effect) -> None: logger.info(f'Registered new Effect: {effect.label}') def create_effect_values_dict( - self, - effect_values_user: Union[EffectValuesUserScenario, EffectValuesUserTimestep] + self, effect_values_user: Union[EffectValuesUserScenario, EffectValuesUserTimestep] ) -> Optional[EffectValuesDict]: """ Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type. @@ -383,7 +383,11 @@ def do_modeling(self): model.do_modeling() self._add_share_between_effects() - scaling_factor = len(self._model.time_series_collection.scenarios) if self._model.time_series_collection.scenarios is not None else 1 + scaling_factor = ( + len(self._model.time_series_collection.scenarios) + if self._model.time_series_collection.scenarios is not None + else 1 + ) self._model.add_objective( (self.effects.objective_effect.model.total / scaling_factor).sum() + (self.penalty.total / scaling_factor).sum() diff --git a/flixopt/elements.py b/flixopt/elements.py index b6de8c7c2..a98edff9d 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import TimestepData, NumericDataTS, PlausibilityError, Scalar, ScenarioData +from .core import NumericDataTS, PlausibilityError, Scalar, ScenarioData, TimestepData from .effects import EffectValuesUserTimestep from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters @@ -467,11 +467,15 @@ def do_modeling(self) -> None: self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.selected_data ) self.excess_input = self.add( - self._model.add_variables(lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_input'), + self._model.add_variables( + lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_input' + ), 'excess_input', ) self.excess_output = self.add( - self._model.add_variables(lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_output'), + self._model.add_variables( + lower=0, coords=self._model.get_coords(), name=f'{self.label_full}|excess_output' + ), 'excess_output', ) eq_bus_balance.lhs -= -self.excess_input + self.excess_output diff --git a/flixopt/features.py b/flixopt/features.py index a9f50aba6..a122a9dac 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -9,9 +9,8 @@ import linopy import numpy as np -from . import utils from .config import CONFIG -from .core import TimestepData, Scalar, TimeSeries +from .core import Scalar, TimeSeries, TimestepData from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects from .structure import Model, SystemModel @@ -303,12 +302,16 @@ def do_modeling(self): if self.parameters.use_switch_on: self.switch_on = self.add( - self._model.add_variables(binary=True, name=f'{self.label_full}|switch_on', coords=self._model.get_coords()), + self._model.add_variables( + binary=True, name=f'{self.label_full}|switch_on', coords=self._model.get_coords() + ), 'switch_on', ) self.switch_off = self.add( - self._model.add_variables(binary=True, name=f'{self.label_full}|switch_off', coords=self._model.get_coords()), + self._model.add_variables( + binary=True, name=f'{self.label_full}|switch_off', coords=self._model.get_coords() + ), 'switch_off', ) @@ -878,7 +881,7 @@ def do_modeling(self): lower=self._total_min, upper=self._total_max, coords=self._model.get_coords(time_dim=False, scenario_dim=self._has_scenario_dim), - name=f'{self.label_full}|total' + name=f'{self.label_full}|total', ), 'total', ) @@ -890,12 +893,8 @@ def do_modeling(self): if self._has_time_dim: self.total_per_timestep = self.add( self._model.add_variables( - lower=-np.inf - if (self._min_per_hour is None) - else self._min_per_hour * self._model.hours_per_step, - upper=np.inf - if (self._max_per_hour is None) - else self._max_per_hour * self._model.hours_per_step, + lower=-np.inf if (self._min_per_hour is None) else self._min_per_hour * self._model.hours_per_step, + upper=np.inf if (self._max_per_hour is None) else self._max_per_hour * self._model.hours_per_step, coords=self._model.get_coords(time_dim=True, scenario_dim=self._has_scenario_dim), name=f'{self.label_full}|total_per_timestep', ), diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index a4705371c..6985572c1 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,8 +16,15 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import TimestepData, TimeSeries, TimeSeriesCollection, TimeSeriesData, Scalar -from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUserScenario, EffectValuesUserTimestep +from .core import Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData +from .effects import ( + Effect, + EffectCollection, + EffectTimeSeries, + EffectValuesDict, + EffectValuesUserScenario, + EffectValuesUserTimestep, +) from .elements import Bus, Component, Flow from .structure import CLASS_REGISTRY, Element, SystemModel @@ -296,7 +303,7 @@ def create_time_series( has_extra_timestep: Whether the data has an extra timestep """ if not has_time_dim and not has_scenario_dim: - raise ValueError("At least one of the dimensions must be present") + raise ValueError('At least one of the dimensions must be present') if data is None: return None @@ -324,7 +331,7 @@ def create_time_series( has_scenario_dim=has_scenario_dim, has_extra_timestep=has_extra_timestep, aggregation_weight=data.agg_weight, - aggregation_group=data.agg_group + aggregation_group=data.agg_group, ) return self.time_series_collection.add_time_series( data=data, @@ -358,7 +365,7 @@ def create_effect_time_series( has_scenario_dim: Whether the data has a scenario dimension """ if not has_time_dim and not has_scenario_dim: - raise ValueError("At least one of the dimensions must be present") + raise ValueError('At least one of the dimensions must be present') effect_values: Optional[EffectValuesDict] = self.effects.create_effect_values_dict(effect_values) if effect_values is None: diff --git a/flixopt/interface.py b/flixopt/interface.py index f57362ee3..b6eb80c54 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from .config import CONFIG -from .core import TimestepData, NumericDataTS, Scalar, ScenarioData +from .core import NumericDataTS, Scalar, ScenarioData, TimestepData from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports @@ -34,16 +34,10 @@ def __init__(self, start: TimestepData, end: TimestepData): def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.start = flow_system.create_time_series( - name=f'{name_prefix}|start', - data=self.start, - has_time_dim=self.has_time_dim, - has_scenario_dim=True + name=f'{name_prefix}|start', data=self.start, has_time_dim=self.has_time_dim, has_scenario_dim=True ) self.end = flow_system.create_time_series( - name=f'{name_prefix}|end', - data=self.end, - has_time_dim=self.has_time_dim, - has_scenario_dim=True + name=f'{name_prefix}|end', data=self.end, has_time_dim=self.has_time_dim, has_scenario_dim=True ) @@ -222,7 +216,7 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): has_scenario_dim=True, ) if self.piecewise_effects is not None: - self.piecewise_effects.has_time_dim=False + self.piecewise_effects.has_time_dim = False self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') @property diff --git a/flixopt/structure.py b/flixopt/structure.py index 91fc6c1d0..37b02b122 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from rich.pretty import Pretty from .config import CONFIG -from .core import TimestepData, Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData +from .core import Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -99,10 +99,7 @@ def hours_of_previous_timesteps(self): return self.time_series_collection.hours_of_previous_timesteps def get_coords( - self, - scenario_dim = True, - time_dim = True, - extra_timestep = False + self, scenario_dim=True, time_dim=True, extra_timestep=False ) -> Optional[Union[Tuple[pd.Index], Tuple[pd.Index, pd.Index]]]: """ Returns the coordinates of the model @@ -118,7 +115,9 @@ def get_coords( if not scenario_dim and not time_dim: return None scenarios = self.time_series_collection.scenarios - timesteps = self.time_series_collection.timesteps if not extra_timestep else self.time_series_collection.timesteps_extra + timesteps = ( + self.time_series_collection.timesteps if not extra_timestep else self.time_series_collection.timesteps_extra + ) if scenario_dim and time_dim: if scenarios is None: diff --git a/flixopt/utils.py b/flixopt/utils.py index 542f87942..3e65328a4 100644 --- a/flixopt/utils.py +++ b/flixopt/utils.py @@ -11,7 +11,6 @@ logger = logging.getLogger('flixopt') - def round_floats(obj, decimals=2): if isinstance(obj, dict): return {k: round_floats(v, decimals) for k, v in obj.items()} diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 0466f3a2e..a023b8e58 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -378,7 +378,9 @@ def test_all_nan_data(self, sample_time_index, sample_scenario_index): assert np.all(np.isnan(result.values)) # Series of all NaNs - result = DataConverter.as_dataarray(np.array([np.nan, np.nan, np.nan, np.nan, np.nan]), sample_time_index, sample_scenario_index) + result = DataConverter.as_dataarray( + np.array([np.nan, np.nan, np.nan, np.nan, np.nan]), sample_time_index, sample_scenario_index + ) assert np.all(np.isnan(result.values)) def test_mixed_data_types(self, sample_time_index, sample_scenario_index): diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index 50136536b..d64c13d85 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -111,7 +111,9 @@ def test_reset(self, sample_timeseries, sample_timesteps): def test_restore_data(self, sample_timeseries, simple_dataarray): """Test restore_data method.""" # Modify the stored data - new_data = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) + new_data = xr.DataArray( + [1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time'] + ) # Store original data for comparison original_data = sample_timeseries.stored_data @@ -227,7 +229,9 @@ def test_all_equal(self, sample_timesteps): def test_arithmetic_operations(self, sample_timeseries): """Test arithmetic operations.""" # Create a second TimeSeries for testing - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) + data2 = xr.DataArray( + [1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time'] + ) ts2 = TimeSeries(data2, 'Second Series') # Test operations between two TimeSeries objects @@ -284,7 +288,9 @@ def test_numpy_ufunc(self, sample_timeseries): ) # Test with two TimeSeries objects - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time']) + data2 = xr.DataArray( + [1, 2, 3, 4, 5], coords={'time': sample_timeseries.stored_data.coords['time']}, dims=['time'] + ) ts2 = TimeSeries(data2, 'Second Series') assert np.array_equal( @@ -311,15 +317,15 @@ def sample_scenario_index(): @pytest.fixture def simple_scenario_dataarray(sample_timesteps, sample_scenario_index): """Create a DataArray with both scenario and time dimensions.""" - data = np.array([ - [10, 20, 30, 40, 50], # baseline - [15, 25, 35, 45, 55], # high_demand - [5, 15, 25, 35, 45] # low_price - ]) + data = np.array( + [ + [10, 20, 30, 40, 50], # baseline + [15, 25, 35, 45, 55], # high_demand + [5, 15, 25, 35, 45], # low_price + ] + ) return xr.DataArray( - data=data, - coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, - dims=['scenario', 'time'] + data=data, coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, dims=['scenario', 'time'] ) @@ -412,21 +418,23 @@ def test_all_equal_with_scenarios(self, sample_timesteps, sample_scenario_index) equal_dataarray = xr.DataArray( data=equal_data, coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, - dims=['scenario', 'time'] + dims=['scenario', 'time'], ) ts_equal = TimeSeries(equal_dataarray, 'Equal Scenario Series') assert ts_equal.all_equal is True # Equal within each scenario but different between scenarios - per_scenario_equal = np.array([ - [5, 5, 5, 5, 5], # baseline - all 5 - [10, 10, 10, 10, 10], # high_demand - all 10 - [15, 15, 15, 15, 15] # low_price - all 15 - ]) + per_scenario_equal = np.array( + [ + [5, 5, 5, 5, 5], # baseline - all 5 + [10, 10, 10, 10, 10], # high_demand - all 10 + [15, 15, 15, 15, 15], # low_price - all 15 + ] + ) per_scenario_dataarray = xr.DataArray( data=per_scenario_equal, coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, - dims=['scenario', 'time'] + dims=['scenario', 'time'], ) ts_per_scenario = TimeSeries(per_scenario_dataarray, 'Per-Scenario Equal Series') assert ts_per_scenario.all_equal is False @@ -436,9 +444,7 @@ def test_arithmetic_with_scenarios(self, sample_scenario_timeseries, sample_time # Create a second TimeSeries with scenarios data2 = np.ones((3, 5)) # All ones second_dataarray = xr.DataArray( - data=data2, - coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, - dims=['scenario', 'time'] + data=data2, coords={'scenario': sample_scenario_index, 'time': sample_timesteps}, dims=['scenario', 'time'] ) ts2 = TimeSeries(second_dataarray, 'Second Series') @@ -624,11 +630,7 @@ def test_add_time_series_with_scenarios(self, sample_scenario_allocator): assert np.array_equal(ts2.sel(scenario=scenario).values, data) # Test 2D array (one row per scenario) - data_2d = np.array([ - [10, 20, 30, 40, 50], - [15, 25, 35, 45, 55], - [5, 15, 25, 35, 45] - ]) + data_2d = np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]) ts3 = sample_scenario_allocator.add_time_series('scenario_specific_series', data_2d) assert ts3._has_scenarios assert ts3.selected_data.shape == (3, 5) @@ -637,7 +639,9 @@ def test_add_time_series_with_scenarios(self, sample_scenario_allocator): assert np.array_equal(ts3.sel(scenario='high_demand').values, data_2d[1]) assert np.array_equal(ts3.sel(scenario='low_price').values, data_2d[2]) - def test_selection_propagation_with_scenarios(self, sample_scenario_allocator, sample_timesteps, sample_scenario_index): + def test_selection_propagation_with_scenarios( + self, sample_scenario_allocator, sample_timesteps, sample_scenario_index + ): """Test scenario selection propagation.""" # Add some time series ts1 = sample_scenario_allocator.add_time_series('series1', 42) @@ -679,12 +683,7 @@ def test_as_dataset_with_scenarios(self, sample_scenario_allocator): # Add some time series sample_scenario_allocator.add_time_series('scalar_series', 42) sample_scenario_allocator.add_time_series( - 'varying_series', - np.array([ - [10, 20, 30, 40, 50], - [15, 25, 35, 45, 55], - [5, 15, 25, 35, 45] - ]) + 'varying_series', np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]) ) # Get dataset @@ -728,11 +727,7 @@ def test_update_time_series_with_scenarios(self, sample_scenario_allocator, samp assert np.all(ts.selected_data.values == 42) # Update with scenario-specific data - new_data = np.array([ - [1, 2, 3, 4, 5], - [6, 7, 8, 9, 10], - [11, 12, 13, 14, 15] - ]) + new_data = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]) sample_scenario_allocator.update_time_series('series', new_data) # Check update was applied From 379252330ebd264b09147de924d45ee35691938b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Apr 2025 16:36:40 +0200 Subject: [PATCH 005/183] Fix coords in constraints and variables --- flixopt/elements.py | 8 ++++---- flixopt/features.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index a98edff9d..78dada129 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -361,7 +361,7 @@ def do_modeling(self): self._model.add_variables( lower=self.element.flow_hours_total_min if self.element.flow_hours_total_min is not None else -np.inf, upper=self.element.flow_hours_total_max if self.element.flow_hours_total_max is not None else np.inf, - coords=None, + coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|total_flow_hours', ), 'total_flow_hours', @@ -369,7 +369,7 @@ def do_modeling(self): self.add( self._model.add_constraints( - self.total_flow_hours == (self.flow_rate * self._model.hours_per_step).sum(), + self.total_flow_hours == (self.flow_rate * self._model.hours_per_step).sum('time'), name=f'{self.label_full}|total_flow_hours', ), 'total_flow_hours', @@ -399,7 +399,7 @@ def _create_bounds_for_load_factor(self): # eq: var_sumFlowHours <= size * dt_tot * load_factor_max if self.element.load_factor_max is not None: name_short = 'load_factor_max' - flow_hours_per_size_max = self._model.hours_per_step.sum() * self.element.load_factor_max + flow_hours_per_size_max = self._model.hours_per_step.sum('time') * self.element.load_factor_max size = self.element.size if self._investment is None else self._investment.size if self._investment is not None: @@ -414,7 +414,7 @@ def _create_bounds_for_load_factor(self): # eq: size * sum(dt)* load_factor_min <= var_sumFlowHours if self.element.load_factor_min is not None: name_short = 'load_factor_min' - flow_hours_per_size_min = self._model.hours_per_step.sum() * self.element.load_factor_min + flow_hours_per_size_min = self._model.hours_per_step.sum('time') * self.element.load_factor_min size = self.element.size if self._investment is None else self._investment.size if self._investment is not None: diff --git a/flixopt/features.py b/flixopt/features.py index a122a9dac..317c0d36f 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -254,6 +254,7 @@ def do_modeling(self): self._model.add_variables( lower=self.parameters.on_hours_total_min if self.parameters.on_hours_total_min is not None else 0, upper=self.parameters.on_hours_total_max if self.parameters.on_hours_total_max is not None else np.inf, + coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|on_hours_total', ), 'on_hours_total', @@ -261,7 +262,7 @@ def do_modeling(self): self.add( self._model.add_constraints( - self.total_on_hours == (self.on * self._model.hours_per_step).sum(), + self.total_on_hours == (self.on * self._model.hours_per_step).sum('time'), name=f'{self.label_full}|on_hours_total', ), 'on_hours_total', @@ -437,7 +438,7 @@ def _get_duration_in_hours( """ assert binary_variable is not None, f'Duration Variable of {self.label_full} must be defined to add constraints' - mega = self._model.hours_per_step.sum() + previous_duration + mega = self._model.hours_per_step.sum('time') + previous_duration if maximum_duration is not None: first_step_max: Scalar = maximum_duration.isel(time=0) @@ -582,7 +583,7 @@ def _add_switch_constraints(self): # eq: nrSwitchOn = sum(SwitchOn(t)) self.add( self._model.add_constraints( - self.switch_on_nr == self.switch_on.sum(), name=f'{self.label_full}|switch_on_nr' + self.switch_on_nr == self.switch_on.sum('time'), name=f'{self.label_full}|switch_on_nr' ), 'switch_on_nr', ) @@ -973,7 +974,12 @@ def __init__( def do_modeling(self): self.shares = { - effect: self.add(self._model.add_variables(coords=None, name=f'{self.label_full}|{effect}'), f'{effect}') + effect: self.add( + self._model.add_variables( + coords=self._model.get_coords(time_dim=False), + name=f'{self.label_full}|{effect}' + ), + f'{effect}') for effect in self._piecewise_shares } From dcc86f48ea887ede6e03378ab35da6d5e19083c4 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Apr 2025 19:54:47 +0200 Subject: [PATCH 006/183] Feature/scenarios results (#220) # Improvements - Add scenarios to CalculationResults - Add Scenarios to all plotting options, defaulting to the first scenario available - Add the scenario to the plot title and filename if scenario is used - Improve `filter_solution`: Filter by time steps, scenarios and variable dims - Add options `mode` to `node_balance()` and corresponding plotting methods, to allow to get/plot the flow hours instead of the flow_rate. The plot_pie() always does that - Improve docstrings in general --- flixopt/io.py | 2 +- flixopt/results.py | 279 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 227 insertions(+), 54 deletions(-) diff --git a/flixopt/io.py b/flixopt/io.py index adaf52f55..1ef9578e5 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -44,7 +44,7 @@ def insert_dataarray(obj, ds: xr.Dataset): return [insert_dataarray(v, ds) for v in obj] elif isinstance(obj, str) and obj.startswith('::::'): da = ds[obj[4:]] - if da.isel(time=-1).isnull(): + if 'time' in da.dims and da.isel(time=-1).isnull().any().item(): return da.isel(time=slice(0, -1)) return da else: diff --git a/flixopt/results.py b/flixopt/results.py index d9eb5a654..bbf2dcd7a 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -161,6 +161,7 @@ def __init__( self.timesteps_extra = self.solution.indexes['time'] self.hours_per_timestep = TimeSeriesCollection.calculate_hours_per_timestep(self.timesteps_extra) + self.scenarios = self.solution.indexes['scenario'] if 'scenario' in self.solution.indexes else None def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults']: if key in self.components: @@ -196,19 +197,38 @@ def constraints(self) -> linopy.Constraints: return self.model.constraints def filter_solution( - self, variable_dims: Optional[Literal['scalar', 'time']] = None, element: Optional[str] = None + self, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + element: Optional[str] = None, + timesteps: Optional[pd.DatetimeIndex] = None, + scenarios: Optional[pd.Index] = None, ) -> xr.Dataset: """ Filter the solution to a specific variable dimension and element. If no element is specified, all elements are included. Args: - variable_dims: The dimension of the variables to filter for. + variable_dims: The dimension of which to get variables from. + - 'scalar': Get scalar variables (without dimensions) + - 'time': Get time-dependent variables (with a time dimension) + - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) + - 'timeonly': Get time-dependent variables (with ONLY a time dimension) element: The element to filter for. + timesteps: Optional time indexes to select. Can be: + - pd.DatetimeIndex: Multiple timesteps + - str/pd.Timestamp: Single timestep + Defaults to all available timesteps. + scenarios: Optional scenario indexes to select. Can be: + - pd.Index: Multiple scenarios + - str/int: Single scenario (int is treated as a label, not an index position) + Defaults to all available scenarios. """ - if element is not None: - return filter_dataset(self[element].solution, variable_dims) - return filter_dataset(self.solution, variable_dims) + return filter_dataset( + self.solution if element is None else self[element].solution, + variable_dims=variable_dims, + timesteps=timesteps, + scenarios=scenarios, + ) def plot_heatmap( self, @@ -219,10 +239,32 @@ def plot_heatmap( save: Union[bool, pathlib.Path] = False, show: bool = True, engine: plotting.PlottingEngine = 'plotly', + scenario: Optional[Union[str, int]] = None, ) -> Union[plotly.graph_objs.Figure, Tuple[plt.Figure, plt.Axes]]: + """ + Plots a heatmap of the solution of a variable. + + Args: + variable_name: The name of the variable to plot. + heatmap_timeframes: The timeframes to use for the heatmap. + heatmap_timesteps_per_frame: The timesteps per frame to use for the heatmap. + color_map: The color map to use for the heatmap. + save: Whether to save the plot or not. If a path is provided, the plot will be saved at that location. + show: Whether to show the plot or not. + engine: The engine to use for plotting. Can be either 'plotly' or 'matplotlib'. + scenario: The scenario to plot. Defaults to the first scenario. Has no effect without scenarios present + """ + dataarray = self.solution[variable_name] + + scenario_suffix = '' + if 'scenario' in dataarray.indexes: + chosen_scenario = scenario or self.scenarios[0] + dataarray = dataarray.sel(scenario=chosen_scenario).drop_vars('scenario') + scenario_suffix = f'--{chosen_scenario}' + return plot_heatmap( - dataarray=self.solution[variable_name], - name=variable_name, + dataarray=dataarray, + name=f'{variable_name}{scenario_suffix}', folder=self.folder, heatmap_timeframes=heatmap_timeframes, heatmap_timesteps_per_frame=heatmap_timesteps_per_frame, @@ -345,14 +387,37 @@ def constraints(self) -> linopy.Constraints: raise ValueError('The linopy model is not available.') return self._calculation_results.model.constraints[self._variable_names] - def filter_solution(self, variable_dims: Optional[Literal['scalar', 'time']] = None) -> xr.Dataset: + def filter_solution( + self, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + timesteps: Optional[pd.DatetimeIndex] = None, + scenarios: Optional[pd.Index] = None, + ) -> xr.Dataset: """ - Filter the solution of the element by dimension. + Filter the solution to a specific variable dimension and element. + If no element is specified, all elements are included. Args: - variable_dims: The dimension of the variables to filter for. + variable_dims: The dimension of which to get variables from. + - 'scalar': Get scalar variables (without dimensions) + - 'time': Get time-dependent variables (with a time dimension) + - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) + - 'timeonly': Get time-dependent variables (with ONLY a time dimension) + timesteps: Optional time indexes to select. Can be: + - pd.DatetimeIndex: Multiple timesteps + - str/pd.Timestamp: Single timestep + Defaults to all available timesteps. + scenarios: Optional scenario indexes to select. Can be: + - pd.Index: Multiple scenarios + - str/int: Single scenario (int is treated as a label, not an index position) + Defaults to all available scenarios. """ - return filter_dataset(self.solution, variable_dims) + return filter_dataset( + self.solution, + variable_dims=variable_dims, + timesteps=timesteps, + scenarios=scenarios, + ) class _NodeResults(_ElementResults): @@ -386,28 +451,46 @@ def plot_node_balance( show: bool = True, colors: plotting.ColorType = 'viridis', engine: plotting.PlottingEngine = 'plotly', + scenario: Optional[Union[str, int]] = None, + mode: Literal["flow_rate", "flow_hours"] = 'flow_rate', + drop_suffix: bool = True, ) -> Union[plotly.graph_objs.Figure, Tuple[plt.Figure, plt.Axes]]: """ Plots the node balance of the Component or Bus. Args: save: Whether to save the plot or not. If a path is provided, the plot will be saved at that location. show: Whether to show the plot or not. + colors: The colors to use for the plot. See `flixopt.plotting.ColorType` for options. engine: The engine to use for plotting. Can be either 'plotly' or 'matplotlib'. + scenario: The scenario to plot. Defaults to the first scenario. Has no effect without scenarios present + mode: The mode to use for the dataset. Can be 'flow_rate' or 'flow_hours'. + - 'flow_rate': Returns the flow_rates of the Node. + - 'flow_hours': Returns the flow_hours of the Node. [flow_hours(t) = flow_rate(t) * dt(t)]. Renames suffixes to |flow_hours. + drop_suffix: Whether to drop the suffix from the variable names. """ + ds = self.node_balance(with_last_timestep=True, mode=mode, drop_suffix=drop_suffix) + + title = f'{self.label} (flow rates)' if mode == 'flow_rate' else f'{self.label} (flow hours)' + + if 'scenario' in ds.indexes: + chosen_scenario = scenario or self._calculation_results.scenarios[0] + ds = ds.sel(scenario=chosen_scenario).drop_vars('scenario') + title = f'{title} - {chosen_scenario}' + if engine == 'plotly': figure_like = plotting.with_plotly( - self.node_balance(with_last_timestep=True).to_dataframe(), + ds.to_dataframe(), colors=colors, mode='area', - title=f'Flow rates of {self.label}', + title=title, ) default_filetype = '.html' elif engine == 'matplotlib': figure_like = plotting.with_matplotlib( - self.node_balance(with_last_timestep=True).to_dataframe(), + ds.to_dataframe(), colors=colors, mode='bar', - title=f'Flow rates of {self.label}', + title=title, ) default_filetype = '.png' else: @@ -415,7 +498,7 @@ def plot_node_balance( return plotting.export_figure( figure_like=figure_like, - default_path=self._calculation_results.folder / f'{self.label} (flow rates)', + default_path=self._calculation_results.folder / title, default_filetype=default_filetype, user_path=None if isinstance(save, bool) else pathlib.Path(save), show=show, @@ -430,6 +513,8 @@ def plot_node_balance_pie( save: Union[bool, pathlib.Path] = False, show: bool = True, engine: plotting.PlottingEngine = 'plotly', + scenario: Optional[Union[str, int]] = None, + drop_suffix: bool = True, ) -> plotly.graph_objects.Figure: """ Plots a pie chart of the flow hours of the inputs and outputs of buses or components. @@ -441,32 +526,45 @@ def plot_node_balance_pie( save: Whether to save the figure. show: Whether to show the figure. engine: Plotting engine to use. Only 'plotly' is implemented atm. + scenario: If scenarios are present: The scenario to plot. If None, the first scenario is used. + drop_suffix: Whether to drop the suffix from the variable names. """ - inputs = ( - sanitize_dataset( - ds=self.solution[self.inputs], - threshold=1e-5, - drop_small_vars=True, - zero_small_values=True, - ) - * self._calculation_results.hours_per_timestep + inputs = sanitize_dataset( + ds=self.solution[self.inputs], + threshold=1e-5, + drop_small_vars=True, + zero_small_values=True, ) - outputs = ( - sanitize_dataset( - ds=self.solution[self.outputs], - threshold=1e-5, - drop_small_vars=True, - zero_small_values=True, - ) - * self._calculation_results.hours_per_timestep + outputs = sanitize_dataset( + ds=self.solution[self.outputs], + threshold=1e-5, + drop_small_vars=True, + zero_small_values=True, ) + inputs = inputs.sum('time') + outputs = outputs.sum('time') + + title = f'{self.label} (total flow hours)' + + if 'scenario' in inputs.indexes: + chosen_scenario = scenario or self._calculation_results.scenarios[0] + inputs = inputs.sel(scenario=chosen_scenario).drop_vars('scenario') + outputs = outputs.sel(scenario=chosen_scenario).drop_vars('scenario') + title = f'{title} - {chosen_scenario}' + + if drop_suffix: + inputs = inputs.rename_vars({var: var.split('|flow_rate')[0] for var in inputs}) + outputs = outputs.rename_vars({var: var.split('|flow_rate')[0] for var in outputs}) + else: + inputs = inputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in inputs}) + outputs = outputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in outputs}) if engine == 'plotly': figure_like = plotting.dual_pie_with_plotly( - inputs.to_dataframe().sum(), - outputs.to_dataframe().sum(), + data_left=inputs.to_pandas(), + data_right=outputs.to_pandas(), colors=colors, - title=f'Flow hours of {self.label}', + title=title, text_info=text_info, subtitles=('Inputs', 'Outputs'), legend_title='Flows', @@ -476,10 +574,10 @@ def plot_node_balance_pie( elif engine == 'matplotlib': logger.debug('Parameter text_info is not supported for matplotlib') figure_like = plotting.dual_pie_with_matplotlib( - inputs.to_dataframe().sum(), - outputs.to_dataframe().sum(), + data_left=inputs.to_pandas(), + data_right=outputs.to_pandas(), colors=colors, - title=f'Total flow hours of {self.label}', + title=title, subtitles=('Inputs', 'Outputs'), legend_title='Flows', lower_percentage_group=lower_percentage_group, @@ -490,7 +588,7 @@ def plot_node_balance_pie( return plotting.export_figure( figure_like=figure_like, - default_path=self._calculation_results.folder / f'{self.label} (total flow hours)', + default_path=self._calculation_results.folder / title, default_filetype=default_filetype, user_path=None if isinstance(save, bool) else pathlib.Path(save), show=show, @@ -503,9 +601,29 @@ def node_balance( negate_outputs: bool = False, threshold: Optional[float] = 1e-5, with_last_timestep: bool = False, + mode: Literal["flow_rate", "flow_hours"] = 'flow_rate', + drop_suffix: bool = False, ) -> xr.Dataset: + """ + Returns a dataset with the node balance of the Component or Bus. + Args: + negate_inputs: Whether to negate the input flow_rates of the Node. + negate_outputs: Whether to negate the output flow_rates of the Node. + threshold: The threshold for small values. Variables with all values below the threshold are dropped. + with_last_timestep: Whether to include the last timestep in the dataset. + mode: The mode to use for the dataset. Can be 'flow_rate' or 'flow_hours'. + - 'flow_rate': Returns the flow_rates of the Node. + - 'flow_hours': Returns the flow_hours of the Node. [flow_hours(t) = flow_rate(t) * dt(t)]. Renames suffixes to |flow_hours. + drop_suffix: Whether to drop the suffix from the variable names. + """ + ds = self.solution[self.inputs + self.outputs] + if drop_suffix: + ds = ds.rename_vars({var: var.split('|flow_hours')[0] for var in ds.data_vars}) + if mode == 'flow_hours': + ds = ds * self._calculation_results.hours_per_timestep + ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) return sanitize_dataset( - ds=self.solution[self.inputs + self.outputs], + ds=ds, threshold=threshold, timesteps=self._calculation_results.timesteps_extra if with_last_timestep else None, negate=( @@ -548,6 +666,7 @@ def plot_charge_state( show: bool = True, colors: plotting.ColorType = 'viridis', engine: plotting.PlottingEngine = 'plotly', + scenario: Optional[Union[str, int]] = None, ) -> plotly.graph_objs.Figure: """ Plots the charge state of a Storage. @@ -556,6 +675,7 @@ def plot_charge_state( show: Whether to show the plot or not. colors: The c engine: Plotting engine to use. Only 'plotly' is implemented atm. + scenario: The scenario to plot. Defaults to the first scenario. Has no effect without scenarios present Raises: ValueError: If the Component is not a Storage. @@ -568,16 +688,26 @@ def plot_charge_state( if not self.is_storage: raise ValueError(f'Cant plot charge_state. "{self.label}" is not a storage') + ds = self.node_balance(with_last_timestep=True) + charge_state = self.charge_state + + scenario_suffix = '' + if 'scenario' in ds.indexes: + chosen_scenario = scenario or self._calculation_results.scenarios[0] + ds = ds.sel(scenario=chosen_scenario).drop_vars('scenario') + charge_state = charge_state.sel(scenario=chosen_scenario).drop_vars('scenario') + scenario_suffix = f'--{chosen_scenario}' + fig = plotting.with_plotly( - self.node_balance(with_last_timestep=True).to_dataframe(), + ds.to_dataframe(), colors=colors, mode='area', - title=f'Operation Balance of {self.label}', + title=f'Operation Balance of {self.label}{scenario_suffix}', ) # TODO: Use colors for charge state? - charge_state = self.charge_state.to_dataframe() + charge_state = charge_state.to_dataframe() fig.add_trace( plotly.graph_objs.Scatter( x=charge_state.index, y=charge_state.values.flatten(), mode='lines', name=self._charge_state @@ -586,7 +716,7 @@ def plot_charge_state( return plotting.export_figure( fig, - default_path=self._calculation_results.folder / f'{self.label} (charge state)', + default_path=self._calculation_results.folder / f'{self.label} (charge state){scenario_suffix}', default_filetype='.html', user_path=None if isinstance(save, bool) else pathlib.Path(save), show=show, @@ -878,21 +1008,64 @@ def sanitize_dataset( def filter_dataset( ds: xr.Dataset, - variable_dims: Optional[Literal['scalar', 'time']] = None, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + timesteps: Optional[Union[pd.DatetimeIndex, str, pd.Timestamp]] = None, + scenarios: Optional[Union[pd.Index, str, int]] = None, ) -> xr.Dataset: """ - Filters a dataset by its dimensions. + Filters a dataset by its dimensions and optionally selects specific indexes. Args: ds: The dataset to filter. - variable_dims: The dimension of the variables to filter for. + variable_dims: The dimension of which to get variables from. + - 'scalar': Get scalar variables (without dimensions) + - 'time': Get time-dependent variables (with a time dimension) + - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) + - 'timeonly': Get time-dependent variables (with ONLY a time dimension) + timesteps: Optional time indexes to select. Can be: + - pd.DatetimeIndex: Multiple timesteps + - str/pd.Timestamp: Single timestep + Defaults to all available timesteps. + scenarios: Optional scenario indexes to select. Can be: + - pd.Index: Multiple scenarios + - str/int: Single scenario (int is treated as a label, not an index position) + Defaults to all available scenarios. + + Returns: + Filtered dataset with specified variables and indexes. """ + # Return the full dataset if all dimension types are included if variable_dims is None: - return ds - - if variable_dims == 'scalar': - return ds[[name for name, da in ds.data_vars.items() if len(da.dims) == 0]] + pass + elif variable_dims == 'scalar': + ds = ds[[v for v in ds.data_vars if not ds[v].dims]] elif variable_dims == 'time': - return ds[[name for name, da in ds.data_vars.items() if 'time' in da.dims]] + ds = ds[[v for v in ds.data_vars if 'time' in ds[v].dims]] + elif variable_dims == 'scenario': + ds = ds[[v for v in ds.data_vars if ds[v].dims == ('scenario',)]] + elif variable_dims == 'timeonly': + ds = ds[[v for v in ds.data_vars if ds[v].dims == ('time',)]] else: - raise ValueError(f'Not allowed value for "filter_dataset()": {variable_dims=}') + raise ValueError(f'Unknown variable_dims "{variable_dims}" for filter_dataset') + + # Handle time selection if needed + if timesteps is not None and 'time' in ds.dims: + try: + ds = ds.sel(time=timesteps) + except KeyError: + available_times = set(ds.indexes['time']) + requested_times = set([timesteps]) if not isinstance(timesteps, pd.Index) else set(timesteps) + missing_times = requested_times - available_times + raise ValueError(f'Timesteps not found in dataset: {missing_times}. Available times: {available_times}') + + # Handle scenario selection if needed + if scenarios is not None and 'scenario' in ds.dims: + try: + ds = ds.sel(scenario=scenarios) + except KeyError: + available_scenarios = set(ds.indexes['scenario']) + requested_scenarios = set([scenarios]) if not isinstance(scenarios, pd.Index) else set(scenarios) + missing_scenarios = requested_scenarios - available_scenarios + raise ValueError(f'Scenarios not found in dataset: {missing_scenarios}. Available scenarios: {available_scenarios}') + + return ds From bd1d2b6c076bb4b956a024cd767daf7f693aaca7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:10:09 +0200 Subject: [PATCH 007/183] ruff check and format --- flixopt/features.py | 6 +++--- flixopt/results.py | 16 ++++++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/flixopt/features.py b/flixopt/features.py index 317c0d36f..e12c0b20f 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -976,10 +976,10 @@ def do_modeling(self): self.shares = { effect: self.add( self._model.add_variables( - coords=self._model.get_coords(time_dim=False), - name=f'{self.label_full}|{effect}' + coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|{effect}' ), - f'{effect}') + f'{effect}', + ) for effect in self._piecewise_shares } diff --git a/flixopt/results.py b/flixopt/results.py index bbf2dcd7a..757adb790 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -452,7 +452,7 @@ def plot_node_balance( colors: plotting.ColorType = 'viridis', engine: plotting.PlottingEngine = 'plotly', scenario: Optional[Union[str, int]] = None, - mode: Literal["flow_rate", "flow_hours"] = 'flow_rate', + mode: Literal['flow_rate', 'flow_hours'] = 'flow_rate', drop_suffix: bool = True, ) -> Union[plotly.graph_objs.Figure, Tuple[plt.Figure, plt.Axes]]: """ @@ -601,7 +601,7 @@ def node_balance( negate_outputs: bool = False, threshold: Optional[float] = 1e-5, with_last_timestep: bool = False, - mode: Literal["flow_rate", "flow_hours"] = 'flow_rate', + mode: Literal['flow_rate', 'flow_hours'] = 'flow_rate', drop_suffix: bool = False, ) -> xr.Dataset: """ @@ -1052,20 +1052,24 @@ def filter_dataset( if timesteps is not None and 'time' in ds.dims: try: ds = ds.sel(time=timesteps) - except KeyError: + except KeyError as e: available_times = set(ds.indexes['time']) requested_times = set([timesteps]) if not isinstance(timesteps, pd.Index) else set(timesteps) missing_times = requested_times - available_times - raise ValueError(f'Timesteps not found in dataset: {missing_times}. Available times: {available_times}') + raise ValueError( + f'Timesteps not found in dataset: {missing_times}. Available times: {available_times}' + ) from e # Handle scenario selection if needed if scenarios is not None and 'scenario' in ds.dims: try: ds = ds.sel(scenario=scenarios) - except KeyError: + except KeyError as e: available_scenarios = set(ds.indexes['scenario']) requested_scenarios = set([scenarios]) if not isinstance(scenarios, pd.Index) else set(scenarios) missing_scenarios = requested_scenarios - available_scenarios - raise ValueError(f'Scenarios not found in dataset: {missing_scenarios}. Available scenarios: {available_scenarios}') + raise ValueError( + f'Scenarios not found in dataset: {missing_scenarios}. Available scenarios: {available_scenarios}' + ) from e return ds From 28a46dc8ce9060237b1f2df416210521165d4800 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Apr 2025 20:48:01 +0200 Subject: [PATCH 008/183] Feature/scenarios dims order (#219) Make time to always be the first dimension, improving output and consistency across the code --- examples/04_Scenarios/scenario_example.py | 3 ++- flixopt/core.py | 14 +++++++------- flixopt/results.py | 20 ++++++++++++++++---- flixopt/structure.py | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index a004d1851..8e3349a4a 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -11,7 +11,8 @@ if __name__ == '__main__': # --- Create Time Series Data --- # Heat demand profile (e.g., kW) over time and corresponding power prices - heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], [30, 0, 100, 118, 125, 20, 20, 20, 20]]) + heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], + [30, 0, 100, 118, 125, 20, 20, 20, 20]]).T power_prices = np.ones(9) * 0.08 # Create datetime array starting from '2020-01-01' for the given time period diff --git a/flixopt/core.py b/flixopt/core.py index 68d1ddaad..386a1d873 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -146,14 +146,14 @@ def _prepare_dimensions( coords = {} dims = [] - if scenarios is not None: - coords['scenario'] = scenarios - dims.append('scenario') - if timesteps is not None: coords['time'] = timesteps dims.append('time') + if scenarios is not None: + coords['scenario'] = scenarios + dims.append('scenario') + return coords, tuple(dims) @staticmethod @@ -340,18 +340,18 @@ def _convert_ndarray_two_dims(data: np.ndarray, coords: Dict[str, pd.Index], dim # For 1D array, create 2D array based on which dimension it matches if data.shape[0] == time_length: # Broadcast across scenarios - values = np.tile(data, (scenario_length, 1)) + values = np.repeat(data[:, np.newaxis], scenario_length, axis=1) return xr.DataArray(values, coords=coords, dims=dims) elif data.shape[0] == scenario_length: # Broadcast across time - values = np.repeat(data[:, np.newaxis], time_length, axis=1) + values = np.tile(data, (time_length, 1)) return xr.DataArray(values, coords=coords, dims=dims) else: raise ConversionError(f"1D array length {data.shape[0]} doesn't match either dimension") elif data.ndim == 2: # For 2D array, shape must match dimensions - expected_shape = (scenario_length, time_length) + expected_shape = (time_length, scenario_length) if data.shape != expected_shape: raise ConversionError(f"2D array shape {data.shape} doesn't match expected shape {expected_shape}") return xr.DataArray(data, coords=coords, dims=dims) diff --git a/flixopt/results.py b/flixopt/results.py index 757adb790..280f20d0d 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -198,7 +198,7 @@ def constraints(self) -> linopy.Constraints: def filter_solution( self, - variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, element: Optional[str] = None, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None, @@ -213,6 +213,7 @@ def filter_solution( - 'time': Get time-dependent variables (with a time dimension) - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) - 'timeonly': Get time-dependent variables (with ONLY a time dimension) + - 'scenarioonly': Get scenario-dependent variables (with ONLY a scenario dimension) element: The element to filter for. timesteps: Optional time indexes to select. Can be: - pd.DatetimeIndex: Multiple timesteps @@ -389,7 +390,7 @@ def constraints(self) -> linopy.Constraints: def filter_solution( self, - variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None, ) -> xr.Dataset: @@ -403,6 +404,7 @@ def filter_solution( - 'time': Get time-dependent variables (with a time dimension) - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) - 'timeonly': Get time-dependent variables (with ONLY a time dimension) + - 'scenarioonly': Get scenario-dependent variables (with ONLY a scenario dimension) timesteps: Optional time indexes to select. Can be: - pd.DatetimeIndex: Multiple timesteps - str/pd.Timestamp: Single timestep @@ -559,6 +561,13 @@ def plot_node_balance_pie( inputs = inputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in inputs}) outputs = outputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in outputs}) + scenario_suffix = '' + if 'scenario' in inputs.indexes: + chosen_scenario = scenario or self._calculation_results.scenarios[0] + inputs = inputs.sel(scenario=chosen_scenario).drop_vars('scenario') + outputs = outputs.sel(scenario=chosen_scenario).drop_vars('scenario') + scenario_suffix = f'--{chosen_scenario}' + if engine == 'plotly': figure_like = plotting.dual_pie_with_plotly( data_left=inputs.to_pandas(), @@ -1008,7 +1017,7 @@ def sanitize_dataset( def filter_dataset( ds: xr.Dataset, - variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly']] = None, + variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, timesteps: Optional[Union[pd.DatetimeIndex, str, pd.Timestamp]] = None, scenarios: Optional[Union[pd.Index, str, int]] = None, ) -> xr.Dataset: @@ -1022,6 +1031,7 @@ def filter_dataset( - 'time': Get time-dependent variables (with a time dimension) - 'scenario': Get scenario-dependent variables (with ONLY a scenario dimension) - 'timeonly': Get time-dependent variables (with ONLY a time dimension) + - 'scenarioonly': Get scenario-dependent variables (with ONLY a scenario dimension) timesteps: Optional time indexes to select. Can be: - pd.DatetimeIndex: Multiple timesteps - str/pd.Timestamp: Single timestep @@ -1042,9 +1052,11 @@ def filter_dataset( elif variable_dims == 'time': ds = ds[[v for v in ds.data_vars if 'time' in ds[v].dims]] elif variable_dims == 'scenario': - ds = ds[[v for v in ds.data_vars if ds[v].dims == ('scenario',)]] + ds = ds[[v for v in ds.data_vars if 'scenario' in ds[v].dims]] elif variable_dims == 'timeonly': ds = ds[[v for v in ds.data_vars if ds[v].dims == ('time',)]] + elif variable_dims == 'scenarioonly': + ds = ds[[v for v in ds.data_vars if ds[v].dims == ('scenario',)]] else: raise ValueError(f'Unknown variable_dims "{variable_dims}" for filter_dataset') diff --git a/flixopt/structure.py b/flixopt/structure.py index 37b02b122..7282d3b7c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -122,7 +122,7 @@ def get_coords( if scenario_dim and time_dim: if scenarios is None: return (timesteps,) - return scenarios, timesteps + return timesteps, scenarios if scenario_dim and not time_dim: if scenarios is None: From 479c1eb4f1c62cba5cfeaa689f258c9808144a0d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:44:02 +0200 Subject: [PATCH 009/183] Bugfix main results --- flixopt/calculation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 962d2c95f..5329bc0f9 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -105,15 +105,15 @@ def main_results(self) -> Dict[str, Union[Scalar, Dict]]: 'Buses with excess': [ { bus.label_full: { - 'input': np.sum(bus.model.excess_input.solution.values), - 'output': np.sum(bus.model.excess_output.solution.values), + 'input': bus.model.excess_input.solution.sum('time'), + 'output': bus.model.excess_output.solution.sum('time'), } } for bus in self.flow_system.buses.values() if bus.with_excess and ( - np.sum(bus.model.excess_input.solution.values) > 1e-3 - or np.sum(bus.model.excess_output.solution.values) > 1e-3 + bus.model.excess_input.solution.sum() > 1e-3 + or bus.model.excess_output.solution.sum() > 1e-3 ) ], } From a611672c39e7a0a824ee421e4c72af6d493c8c6b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:47:58 +0200 Subject: [PATCH 010/183] Remove code duplicate --- flixopt/results.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index 280f20d0d..ae54b9e2e 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -561,13 +561,6 @@ def plot_node_balance_pie( inputs = inputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in inputs}) outputs = outputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in outputs}) - scenario_suffix = '' - if 'scenario' in inputs.indexes: - chosen_scenario = scenario or self._calculation_results.scenarios[0] - inputs = inputs.sel(scenario=chosen_scenario).drop_vars('scenario') - outputs = outputs.sel(scenario=chosen_scenario).drop_vars('scenario') - scenario_suffix = f'--{chosen_scenario}' - if engine == 'plotly': figure_like = plotting.dual_pie_with_plotly( data_left=inputs.to_pandas(), From 9a8724e94c2d53a0ea9a13fce5bd14474087ffc8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:57:37 +0200 Subject: [PATCH 011/183] Feature/scenarios invest (#227) Add different modes to handle the size per scenario. --- .../Mathematical Notation/Investment.md | 115 ++++++++++++++++++ flixopt/calculation.py | 4 +- flixopt/components.py | 4 +- flixopt/elements.py | 4 +- flixopt/features.py | 88 +++++++++++++- flixopt/interface.py | 56 +++++---- 6 files changed, 235 insertions(+), 36 deletions(-) create mode 100644 docs/user-guide/Mathematical Notation/Investment.md diff --git a/docs/user-guide/Mathematical Notation/Investment.md b/docs/user-guide/Mathematical Notation/Investment.md new file mode 100644 index 000000000..64467261d --- /dev/null +++ b/docs/user-guide/Mathematical Notation/Investment.md @@ -0,0 +1,115 @@ +# Investments + +## Current state +$$ +\beta_{\text{invest}} \cdot \text{max}(\epsilon, \text V^{\text L}) \leq V \leq \beta_{\text{invest}} \cdot \text V^{\text U} +$$ +With: +- $V$ = size +- $V^{\text L}$ = minimum size +- $V^{\text U}$ = maximum size +- $\epsilon$ = epsilon, a small number (such as $1e^{-5}$) +- $\beta_{invest} \in {0,1}$ = wether the size is invested or not + +_Please edit the use cases as needed_ +## Quickfix 1: Optimize the single best size overall +### Single variable +This is already possible and should be, as this is a needed use case +An additional factor to when the size is actually available might me practical (Which indicates the (fixed) time of investment) +## Math +$$ +V(p) = V * a(p) +$$ +with: +- $V$ = size +- $a(p)$ = factor for availlability per period + +Factor $a(p)$ is simply multiplied with relative minimum or maximum(t). This is already possible by doing this yourself. +Effectively, the relative minimum or maximum are altered before using the same constraiints as before. +THis might lead to some issues regariding minimum_load factor, or others, as the size is not 0 in a scenario where the component cant produce. +**Therefore this might not be the best choice. See (#Variable per Scenario) + +## Variable per Scenario +- **size** and **invest** as a variable per period $V(s)$ and $\beta_{invest}(s)$ +- with scenario $s \in S$ + +### Usecase 1: Optimize the size for each Scenario independently +Restrictions are seperatly for each scenario +No changes needed. This could be the default behaviour. + +### Usecase 2: Optimize ONE size for ALL scenarios +The size is the same globally, but not a scalar, but a variable per scenario $V(s)$ +#### 2a: The same size in all scenarios +$$ +V(s) = V(s') \quad \forall s,s' \in S +$$ + +With: +- $V(s)$ and $V(s')$ = size +- $S$ = set of scenarios + +#### 2b: The same size, but can be 0 prior to the first increment +- Find the Optimal time of investment. +- Force an investment in a certain scenario (parameter optional as a list/array ob booleans) +- Combine optional and minimum/maximum size to force an investment inside a range if scenarios + +$$ +\beta_{\text{invest}}(s) \leq \beta_{\text{invest}}(s+1) \quad \forall s \in \{1,2,\ldots,S-1\} +$$ + +$$ +V(s') - V(s) \leq M \cdot (2 - \beta_{\text{invest}}(s) - \beta_{\text{invest}}(s')) \quad \forall s, s' \in S +$$ +$$ +V(s') - V(s) \geq M \cdot (2 - \beta_{\text{invest}}(s) - \beta_{\text{invest}}(s')) \quad \forall s, s' \in S +$$ + +This could be the default behaviour. (which would be consistent with other variables) + + +### Switch + +$$ +\begin{aligned} +& \text{SWITCH}_s \in \{0,1\} \quad \forall s \in \{1,2,\ldots,S\} \\ +& \sum_{s=1}^{S} \text{SWITCH}_s = 1 \\ +& \beta_{\text{invest}}(s) = \sum_{s'=1}^{s} \text{SWITCH}_{s'} \quad \forall s \in \{1,2,\ldots,S\} \\ +\end{aligned} +$$ + +$$ +\begin{aligned} +& V(s) \leq V_{\text{actual}} \quad \forall s \in \{1,2,\ldots,S\} \\ +& V(s) \geq V_{\text{actual}} - M \cdot (1 - \beta_{\text{invest}}(s)) \quad \forall s \in \{1,2,\ldots,S\} +\end{aligned} +$$ + + + + +### Usecase 3: Find the best scenario to increment the size (Timing of the investment) +The size can only increment once (based on a starting point). This allows to optimize the timing of an investment. +#### Math +Treat $\beta_{invest}$ like an ON/OFF variable, and introduce a SwitchOn, that can only be active once. + +*Thoughts:* +- Treating $\beta_{invest}$ like an ON/OFF variable suggest using the already presentconstraints linked to On/OffModel +- The timing could be constraint to be first in scenario x, or last in scenario y +- Restrict the number of consecutive scenarios +THis might needs the OnOffModel to be more generic (HOURS). Further, the span between scenarios needs to be weighted (like dt_in_hours), or the scenarios need to be measureable (integers) + + +### Others + +#### Usecase 4: Only increase/decrease the size +Start from a certain size. For each scenario, the size can increase, but never decrease. (Or the other way around). +This would mean that a size expansion is possible, + +#### Usecase 5: Restrict the increment in size per scenario +Restrict how much the size can increase/decrease for in scenario, based on the prior scenario. + + + + + +Many more are possible diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 5329bc0f9..2024739ea 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -93,13 +93,13 @@ def main_results(self) -> Dict[str, Union[Scalar, Dict]]: model.label_of_element: model.size.solution for component in self.flow_system.components.values() for model in component.model.all_sub_models - if isinstance(model, InvestmentModel) and model.size.solution >= CONFIG.modeling.EPSILON + if isinstance(model, InvestmentModel) and model.size.solution.max() >= CONFIG.modeling.EPSILON }, 'Not invested': { model.label_of_element: model.size.solution for component in self.flow_system.components.values() for model in component.model.all_sub_models - if isinstance(model, InvestmentModel) and model.size.solution < CONFIG.modeling.EPSILON + if isinstance(model, InvestmentModel) and model.size.solution.max() < CONFIG.modeling.EPSILON }, }, 'Buses with excess': [ diff --git a/flixopt/components.py b/flixopt/components.py index 69b0fe47a..598ff06ab 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -578,8 +578,8 @@ def absolute_charge_state_bounds(self) -> Tuple[TimestepData, TimestepData]: @property def relative_charge_state_bounds(self) -> Tuple[TimestepData, TimestepData]: return ( - self.element.relative_minimum_charge_state.selected_data, - self.element.relative_maximum_charge_state.selected_data, + self.element.relative_minimum_charge_state, + self.element.relative_maximum_charge_state, ) diff --git a/flixopt/elements.py b/flixopt/elements.py index 78dada129..aa1c8e69b 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -434,8 +434,8 @@ def absolute_flow_rate_bounds(self) -> Tuple[TimestepData, TimestepData]: if not isinstance(size, InvestParameters): return relative_minimum * size, relative_maximum * size if size.fixed_size is not None: - return relative_minimum * size.fixed_size, relative_maximum * size.fixed_size - return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size + return size.fixed_size * relative_minimum, size.fixed_size * relative_maximum + return size.minimum_size * relative_minimum, size.maximum_size * relative_maximum @property def relative_flow_rate_bounds(self) -> Tuple[TimestepData, TimestepData]: diff --git a/flixopt/features.py b/flixopt/features.py index e12c0b20f..3d2984393 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -33,6 +33,7 @@ def __init__( super().__init__(model, label_of_element, label) self.size: Optional[Union[Scalar, linopy.Variable]] = None self.is_invested: Optional[linopy.Variable] = None + self.scenario_of_investment: Optional[linopy.Variable] = None self.piecewise_effects: Optional[PiecewiseEffectsModel] = None @@ -45,16 +46,18 @@ def do_modeling(self): if self.parameters.fixed_size and not self.parameters.optional: self.size = self.add( self._model.add_variables( - lower=self.parameters.fixed_size, upper=self.parameters.fixed_size, name=f'{self.label_full}|size' + lower=self.parameters.fixed_size, upper=self.parameters.fixed_size, name=f'{self.label_full}|size', + coords=self._model.get_coords(time_dim=False), ), 'size', ) else: self.size = self.add( self._model.add_variables( - lower=0 if self.parameters.optional else self.parameters.minimum_size, - upper=self.parameters.maximum_size, + lower=0 if self.parameters.optional else self.parameters.minimum_size*1, + upper=self.parameters.maximum_size*1, name=f'{self.label_full}|size', + coords=self._model.get_coords(time_dim=False), ), 'size', ) @@ -62,11 +65,19 @@ def do_modeling(self): # Optional if self.parameters.optional: self.is_invested = self.add( - self._model.add_variables(binary=True, name=f'{self.label_full}|is_invested'), 'is_invested' + self._model.add_variables( + binary=True, + name=f'{self.label_full}|is_invested', + coords=self._model.get_coords(time_dim=False), + ), + 'is_invested', ) self._create_bounds_for_optional_investment() + if self._model.time_series_collection.scenarios is not None: + self._create_bounds_for_scenarios() + # Bounds for defining variable self._create_bounds_for_defining_variable() @@ -181,7 +192,7 @@ def _create_bounds_for_defining_variable(self): # ... mit mega = relative_maximum * maximum_size # äquivalent zu:. # eq: - defining_variable(t) + mega * On(t) + size * relative_minimum(t) <= + mega - mega = lb_relative * self.parameters.maximum_size + mega = self.parameters.maximum_size * lb_relative on = self._on_variable self.add( self._model.add_constraints( @@ -191,6 +202,73 @@ def _create_bounds_for_defining_variable(self): ) # anmerkung: Glg bei Spezialfall relative_minimum = 0 redundant zu OnOff ?? + def _create_bounds_for_scenarios(self): + if self.parameters.size_per_scenario == 'equal': + self.add( + self._model.add_constraints( + self.size.isel(scenario=slice(None, -1)) == self.size.isel(scenario=slice(1, None)), + name=f'{self.label_full}|equalize_size_per_scenario', + ), + 'equalize_size_per_scenario', + ) + elif self.parameters.size_per_scenario == 'increment_once': + if not self.parameters.optional: + raise ValueError('Increment once can only be used if the Investment is optional') + + self.scenario_of_investment = self.add( + self._model.add_variables( + binary=True, + name=f'{self.label_full}|scenario_of_investment', + coords=self._model.get_coords(time_dim=False), + ), + 'scenario_of_investment', + ) + + # eq: scenario_of_investment(t) = is_invested(t) - is_invested(t-1) + self.add( + self._model.add_constraints( + self.scenario_of_investment.isel(scenario=slice(1, None)) + == self.is_invested.isel(scenario=slice(1, None)) - self.is_invested.isel(scenario=slice(None, -1)), + name=f'{self.label_full}|scenario_of_investment', + ), + 'scenario_of_investment', + ) + + # eq: scenario_of_investment(t=0) = is_invested(t=0) + self.add( + self._model.add_constraints( + self.scenario_of_investment.isel(scenario=0) + == self.is_invested.isel(scenario=0), + name=f'{self.label_full}|initial_scenario_of_investment', + ), + 'initial_scenario_of_investment', + ) + + big_m = self.parameters.maximum_size.isel(scenario=slice(1, None)) + + self.add( + self._model.add_constraints( + self.size.isel(scenario=slice(1, None)) - self.size.isel(scenario=slice(None, -1)) + <= self.scenario_of_investment.isel(scenario=slice(1, None)) * big_m, + name=f'{self.label_full}|invest_once_1a', + ), + 'invest_once_1a', + ) + + self.add( + self._model.add_constraints( + self.size.isel(scenario=slice(1, None)) - self.size.isel(scenario=slice(None, -1)) + >= self.scenario_of_investment.isel(scenario=slice(1, None)) * big_m, + name=f'{self.label_full}|invest_once_1b', + ), + 'invest_once_1b', + ) + + elif self.parameters.size_per_scenario == 'individual': + pass + else: + raise ValueError(f'Invalid value for size_per_scenario: {self.parameters.size_per_scenario}') + class OnOffModel(Model): """ diff --git a/flixopt/interface.py b/flixopt/interface.py index b6eb80c54..2bece9943 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -4,7 +4,7 @@ """ import logging -from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union +from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union, Literal from .config import CONFIG from .core import NumericDataTS, Scalar, ScenarioData, TimestepData @@ -150,14 +150,15 @@ class InvestParameters(Interface): def __init__( self, - fixed_size: Optional[Scalar] = None, - minimum_size: Scalar = 0, # TODO: Use EPSILON? - maximum_size: Optional[Scalar] = None, + fixed_size: Optional[ScenarioData] = None, + minimum_size: ScenarioData = 0, # TODO: Use EPSILON? + maximum_size: Optional[ScenarioData] = None, optional: bool = True, # Investition ist weglassbar fix_effects: Optional['EffectValuesUserScenario'] = None, specific_effects: Optional['EffectValuesUserScenario'] = None, # costs per Flow-Unit/Storage-Size/... piecewise_effects: Optional[PiecewiseEffects] = None, divest_effects: Optional['EffectValuesUserScenario'] = None, + size_per_scenario: Literal['equal', 'individual', 'increment_once'] = 'equal', ): """ Args: @@ -168,30 +169,24 @@ def __init__( specific_effects: Specific costs, e.g., in €/kW_nominal or €/m²_nominal. Example: {costs: 3, CO2: 0.3} with costs and CO2 representing an Object of class Effect (Attention: Annualize costs to chosen period!) - piecewise_effects: Linear piecewise relation [invest_pieces, cost_pieces]. - Example 1: - [ [5, 25, 25, 100], # size in kW - {costs: [50,250,250,800], # € - PE: [5, 25, 25, 100] # kWh_PrimaryEnergy - } - ] - Example 2 (if only standard-effect): - [ [5, 25, 25, 100], # kW # size in kW - [50,250,250,800] # value for standart effect, typically € - ] # € - (Attention: Annualize costs to chosen period!) - (Args 'specific_effects' and 'fix_effects' can be used in parallel to Investsizepieces) - minimum_size: Min nominal value (only if: size_is_fixed = False). - maximum_size: Max nominal value (only if: size_is_fixed = False). + piecewise_effects: Define the effects of the investment as a piecewise function of the size of the investment. + minimum_size: Minimum possible size of the investment. + maximum_size: Maximum possible size of the investment. + size_per_scenario: How to treat the size in each scenario + - 'equal': Equalize the size of all scenarios + - 'individual': Optimize the size of each scenario individually + - 'increment_once': Allow the size to increase only once. This is useful if the scenarios are related to + different periods (years, months). Tune the timing by setting the maximum size to 0 in the first scenarios. """ - self.fix_effects: EffectValuesUserScenario = fix_effects or {} - self.divest_effects: EffectValuesUserScenario = divest_effects or {} + self.fix_effects: EffectValuesUserScenario = fix_effects if fix_effects is not None else {} + self.divest_effects: EffectValuesUserScenario = divest_effects if divest_effects is not None else {} self.fixed_size = fixed_size self.optional = optional - self.specific_effects: EffectValuesUserScenario = specific_effects or {} + self.specific_effects: EffectValuesUserScenario = specific_effects if specific_effects is not None else {} self.piecewise_effects = piecewise_effects self._minimum_size = minimum_size - self._maximum_size = maximum_size or CONFIG.modeling.BIG # default maximum + self._maximum_size = CONFIG.modeling.BIG if maximum_size is None else maximum_size # default maximum + self.size_per_scenario = size_per_scenario def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.fix_effects = flow_system.create_effect_time_series( @@ -219,13 +214,24 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.piecewise_effects.has_time_dim = False self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') + self._minimum_size = flow_system.create_time_series( + f'{name_prefix}|minimum_size', self.minimum_size, has_time_dim=False, has_scenario_dim=True + ) + self._maximum_size = flow_system.create_time_series( + f'{name_prefix}|maximum_size', self.maximum_size, has_time_dim=False, has_scenario_dim=True + ) + if self.fixed_size is not None: + self.fixed_size = flow_system.create_time_series( + f'{name_prefix}|fixed_size', self.fixed_size, has_time_dim=False, has_scenario_dim=True + ) + @property def minimum_size(self): - return self.fixed_size or self._minimum_size + return self.fixed_size if self.fixed_size is not None else self._minimum_size @property def maximum_size(self): - return self.fixed_size or self._maximum_size + return self.fixed_size if self.fixed_size is not None else self._maximum_size @register_class_for_io From 39f9e4bbaacb52676f30fbfeb3be90630e466e7c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 15:03:05 +0200 Subject: [PATCH 012/183] Feature/scenarios weights (#228) * Simplify InvestmentModel * Add Scenario Weights to the SystemModel --- flixopt/effects.py | 10 +++------- flixopt/features.py | 27 +++++++++------------------ flixopt/flow_system.py | 8 +++++++- flixopt/structure.py | 19 +++++++++++++++++++ 4 files changed, 38 insertions(+), 26 deletions(-) diff --git a/flixopt/effects.py b/flixopt/effects.py index 226e59a0f..0cf165d66 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -383,14 +383,10 @@ def do_modeling(self): model.do_modeling() self._add_share_between_effects() - scaling_factor = ( - len(self._model.time_series_collection.scenarios) - if self._model.time_series_collection.scenarios is not None - else 1 - ) + self._model.add_objective( - (self.effects.objective_effect.model.total / scaling_factor).sum() - + (self.penalty.total / scaling_factor).sum() + (self.effects.objective_effect.model.total * self._model.scenario_weights).sum() + + self.penalty.total.sum() ) def _add_share_between_effects(self): diff --git a/flixopt/features.py b/flixopt/features.py index 3d2984393..425f9382a 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -43,24 +43,15 @@ def __init__( self.parameters = parameters def do_modeling(self): - if self.parameters.fixed_size and not self.parameters.optional: - self.size = self.add( - self._model.add_variables( - lower=self.parameters.fixed_size, upper=self.parameters.fixed_size, name=f'{self.label_full}|size', - coords=self._model.get_coords(time_dim=False), - ), - 'size', - ) - else: - self.size = self.add( - self._model.add_variables( - lower=0 if self.parameters.optional else self.parameters.minimum_size*1, - upper=self.parameters.maximum_size*1, - name=f'{self.label_full}|size', - coords=self._model.get_coords(time_dim=False), - ), - 'size', - ) + self.size = self.add( + self._model.add_variables( + lower=0 if self.parameters.optional else self.parameters.minimum_size*1, + upper=self.parameters.maximum_size*1, + name=f'{self.label_full}|size', + coords=self._model.get_coords(time_dim=False), + ), + 'size', + ) # Optional if self.parameters.optional: diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 6985572c1..a36a14af1 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,7 +16,7 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData +from .core import Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData, ScenarioData from .effects import ( Effect, EffectCollection, @@ -45,6 +45,7 @@ def __init__( scenarios: Optional[pd.Index] = None, hours_of_last_timestep: Optional[float] = None, hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, + scenario_weights: Optional[ScenarioData] = None, ): """ Args: @@ -55,6 +56,7 @@ def __init__( If None, the first time increment of time_series is used. This is needed to calculate previous durations (for example consecutive_on_hours). If you use an array, take care that its long enough to cover all previous values! + scenario_weights: The weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. """ self.time_series_collection = TimeSeriesCollection( timesteps=timesteps, @@ -62,6 +64,7 @@ def __init__( hours_of_last_timestep=hours_of_last_timestep, hours_of_previous_timesteps=hours_of_previous_timesteps, ) + self.scenario_weights = scenario_weights # defaults: self.components: Dict[str, Component] = {} @@ -278,6 +281,9 @@ def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, def transform_data(self): if not self._connected: self._connect_network() + self.scenario_weights = self.create_time_series( + 'scenario_weights', self.scenario_weights, has_time_dim=False, has_scenario_dim=True + ) for element in self.all_elements.values(): element.transform_data(self) diff --git a/flixopt/structure.py b/flixopt/structure.py index 7282d3b7c..6830dbb1c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -58,6 +58,7 @@ def __init__(self, flow_system: 'FlowSystem'): self.flow_system = flow_system self.time_series_collection = flow_system.time_series_collection self.effects: Optional[EffectCollectionModel] = None + self.scenario_weights = self._calculate_scenario_weights(flow_system.scenario_weights) def do_modeling(self): self.effects = self.flow_system.effects.create_model(self) @@ -69,6 +70,24 @@ def do_modeling(self): for bus_model in bus_models: # Buses after Components, because FlowModels are created in ComponentModels bus_model.do_modeling() + def _calculate_scenario_weights(self, weights: Optional[TimeSeries] = None) -> xr.DataArray: + """Calculates the weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. + If no scenarios are present, s single weight of 1 is returned. + """ + if weights is not None and not isinstance(weights, TimeSeries): + raise TypeError(f'Weights must be a TimeSeries or None, got {type(weights)}') + if self.time_series_collection.scenarios is None: + return xr.DataArray(1) + if weights is None: + weights = xr.DataArray( + np.ones(len(self.time_series_collection.scenarios)), + coords={'scenario': self.time_series_collection.scenarios} + ) + elif isinstance(weights, TimeSeries): + weights = weights.selected_data + + return weights / weights.sum() + @property def solution(self): solution = super().solution From 75cb399799c1c239c24237b56c7d39d5a69abdc1 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 17:16:49 +0200 Subject: [PATCH 013/183] Feature/scenarios tests pandas (#229) Integrate Pandas datatypes into Conversion and update tests --- examples/04_Scenarios/scenario_example.py | 13 +- flixopt/core.py | 139 ++++++++++++- flixopt/structure.py | 3 + tests/test_dataconverter.py | 235 ++++++++++++++++++---- tests/test_timeseries.py | 48 ++--- 5 files changed, 359 insertions(+), 79 deletions(-) diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index 8e3349a4a..03c2a5be0 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -9,15 +9,16 @@ import flixopt as fx if __name__ == '__main__': - # --- Create Time Series Data --- - # Heat demand profile (e.g., kW) over time and corresponding power prices - heat_demand_per_h = np.array([[30, 0, 90, 110, 110, 20, 20, 20, 20], - [30, 0, 100, 118, 125, 20, 20, 20, 20]]).T - power_prices = np.ones(9) * 0.08 - # Create datetime array starting from '2020-01-01' for the given time period timesteps = pd.date_range('2020-01-01', periods=9, freq='h') scenarios = pd.Index(['Base Case', 'High Demand']) + + # --- Create Time Series Data --- + # Heat demand profile (e.g., kW) over time and corresponding power prices + heat_demand_per_h = pd.DataFrame({'Base Case':[30, 0, 90, 110, 110, 20, 20, 20, 20], + 'High Demand':[30, 0, 100, 118, 125, 20, 20, 20, 20]}, index=timesteps) + power_prices = np.array([0.08, 0.09]) + flow_system = fx.FlowSystem(timesteps=timesteps, scenarios=scenarios) # --- Define Energy Buses --- diff --git a/flixopt/core.py b/flixopt/core.py index 386a1d873..304048201 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -83,6 +83,12 @@ def as_dataarray( elif isinstance(data, np.ndarray): return DataConverter._convert_ndarray(data, coords, dims) + elif isinstance(data, pd.Series): + return DataConverter._convert_series(data, coords, dims) + + elif isinstance(data, pd.DataFrame): + return DataConverter._convert_dataframe(data, coords, dims) + else: raise ConversionError(f'Unsupported data type: {type(data).__name__}') @@ -171,6 +177,8 @@ def _convert_scalar( Returns: DataArray with the scalar value """ + if isinstance(data, (np.integer, np.floating)): + data = data.item() return xr.DataArray(data, coords=coords, dims=dims) @staticmethod @@ -192,7 +200,7 @@ def _convert_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tu raise ConversionError('When converting to dimensionless DataArray, source must be scalar') return xr.DataArray(data.values.item()) - # Check if data already has matching dimensions + # Check if data already has matching dimensions and coordinates if set(data.dims) == set(dims): # Check if coordinates match is_compatible = True @@ -202,8 +210,13 @@ def _convert_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tu break if is_compatible: - # Return existing DataArray if compatible - return data.copy(deep=True) + # Ensure dimensions are in the correct order + if data.dims != dims: + # Transpose to get dimensions in the right order + return data.transpose(*dims).copy(deep=True) + else: + # Return existing DataArray if compatible and order is correct + return data.copy(deep=True) # Handle dimension broadcasting if len(data.dims) == 1 and len(dims) == 2: @@ -216,8 +229,9 @@ def _convert_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tu # Broadcast scenario dimension to include time return DataConverter._broadcast_scenario_to_time(data, coords, dims) - raise ConversionError(f'Cannot convert {data.dims} to {dims}') - + raise ConversionError( + f'Cannot convert {data.dims} to {dims}. Source coordinates: {data.coords}, Target coordinates: {coords}' + ) @staticmethod def _broadcast_time_to_scenarios( data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] @@ -239,7 +253,7 @@ def _broadcast_time_to_scenarios( # Broadcast values values = np.tile(data.values, (len(coords['scenario']), 1)) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) @staticmethod def _broadcast_scenario_to_time( @@ -262,7 +276,7 @@ def _broadcast_scenario_to_time( # Broadcast values values = np.repeat(data.values[:, np.newaxis], len(coords['time']), axis=1) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) @staticmethod def _convert_ndarray(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: @@ -359,6 +373,113 @@ def _convert_ndarray_two_dims(data: np.ndarray, coords: Dict[str, pd.Index], dim else: raise ConversionError(f'Expected 1D or 2D array for two dimensions, got {data.ndim}D') + @staticmethod + def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """ + Convert pandas Series to xarray DataArray. + + Args: + data: pandas Series to convert + coords: Target coordinates + dims: Target dimensions + + Returns: + DataArray from the pandas Series + """ + # Handle single dimension case + if len(dims) == 1: + dim_name = dims[0] + + # Check if series index matches the dimension + if data.index.equals(coords[dim_name]): + return xr.DataArray(data.values.copy(), coords=coords, dims=dims) + else: + raise ConversionError( + f"Series index doesn't match {dim_name} coordinates.\n" + f'Series index: {data.index}\n' + f'Target {dim_name} coordinates: {coords[dim_name]}' + ) + + # Handle two dimensions case + elif len(dims) == 2: + # Check if dimensions are time and scenario + if dims != ('time', 'scenario'): + raise ConversionError( + f'Two-dimensional conversion only supports time and scenario dimensions, got {dims}' + ) + + # Case 1: Series is indexed by time + if data.index.equals(coords['time']): + # Broadcast across scenarios + values = np.tile(data.values[:, np.newaxis], (1, len(coords['scenario']))) + return xr.DataArray(values.copy(), coords=coords, dims=dims) + + # Case 2: Series is indexed by scenario + elif data.index.equals(coords['scenario']): + # Broadcast across time + values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) + return xr.DataArray(values.copy(), coords=coords, dims=dims) + + else: + raise ConversionError( + "Series index must match either 'time' or 'scenario' coordinates.\n" + f'Series index: {data.index}\n' + f'Target time coordinates: {coords["time"]}\n' + f'Target scenario coordinates: {coords["scenario"]}' + ) + + else: + raise ConversionError(f'Maximum 2 dimensions supported, got {len(dims)}') + + @staticmethod + def _convert_dataframe(data: pd.DataFrame, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """ + Convert pandas DataFrame to xarray DataArray. + Only allows time as index and scenarios as columns. + + Args: + data: pandas DataFrame to convert + coords: Target coordinates + dims: Target dimensions + + Returns: + DataArray from the pandas DataFrame + """ + # Single dimension case + if len(dims) == 1: + # If DataFrame has one column, treat it like a Series + if len(data.columns) == 1: + series = data.iloc[:, 0] + return DataConverter._convert_series(series, coords, dims) + + raise ConversionError( + f'When converting DataFrame to single-dimension DataArray, DataFrame must have exactly one column, got {len(data.columns)}' + ) + + # Two dimensions case + elif len(dims) == 2: + # Check if dimensions are time and scenario + if dims != ('time', 'scenario'): + raise ConversionError( + f'Two-dimensional conversion only supports time and scenario dimensions, got {dims}' + ) + + # DataFrame must have time as index and scenarios as columns + if data.index.equals(coords['time']) and data.columns.equals(coords['scenario']): + # Create DataArray with proper dimension order + return xr.DataArray(data.values.copy(), coords=coords, dims=dims) + else: + raise ConversionError( + 'DataFrame must have time as index and scenarios as columns.\n' + f'DataFrame index: {data.index}\n' + f'DataFrame columns: {data.columns}\n' + f'Target time coordinates: {coords["time"]}\n' + f'Target scenario coordinates: {coords["scenario"]}' + ) + + else: + raise ConversionError(f'Maximum 2 dimensions supported, got {len(dims)}') + class TimeSeriesData: # TODO: Move to Interface.py @@ -913,8 +1034,8 @@ def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> Non if scenarios: self._selected_scenarios = None - # Apply the selection to all TimeSeries objects - self._propagate_selection_to_time_series() + for ts in self._time_series.values(): + ts.clear_selection(timesteps=timesteps, scenarios=scenarios) def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: """ diff --git a/flixopt/structure.py b/flixopt/structure.py index 6830dbb1c..a1c9ffa0d 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -583,6 +583,9 @@ def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_la return copy_and_convert_datatypes(data.selected_data, use_numpy, use_element_label) elif isinstance(data, TimeSeriesData): return copy_and_convert_datatypes(data.data, use_numpy, use_element_label) + elif isinstance(data, (pd.Series, pd.DataFrame)): + #TODO: This can be improved + return copy_and_convert_datatypes(data.values, use_numpy, use_element_label) elif isinstance(data, Interface): if use_element_label and isinstance(data, Element): diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index a023b8e58..61adcb284 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -101,8 +101,8 @@ def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): result = DataConverter.as_dataarray(42, sample_time_index, sample_scenario_index) assert isinstance(result, xr.DataArray) - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + assert result.dims == ('time', 'scenario') assert np.all(result.values == 42) assert set(result.coords['scenario'].values) == set(sample_scenario_index.values) assert set(result.coords['time'].values) == set(sample_time_index.values) @@ -119,8 +119,8 @@ def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index) # Convert with scenarios result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) - assert result.dims == ('scenario', 'time') + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + assert result.dims == ('time', 'scenario') # Each scenario should have the same values (broadcasting) for scenario in sample_scenario_index: @@ -139,10 +139,10 @@ def test_2d_array_with_scenarios(self, sample_time_index, sample_scenario_index) ) # Convert to DataArray - result = DataConverter.as_dataarray(arr_2d, sample_time_index, sample_scenario_index) + result = DataConverter.as_dataarray(arr_2d.T, sample_time_index, sample_scenario_index) - assert result.shape == (3, 5) - assert result.dims == ('scenario', 'time') + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') # Check that each scenario has correct values assert np.array_equal(result.sel(scenario='baseline').values, arr_2d[0]) @@ -161,28 +161,181 @@ def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index # Test conversion result = DataConverter.as_dataarray(original, sample_time_index, sample_scenario_index) - assert result.shape == (3, 5) - assert result.dims == ('scenario', 'time') - assert np.array_equal(result.values, original.values) + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, original.values.T) # Ensure it's a copy - result.loc['baseline'] = 999 + result.loc[:, 'baseline'] = 999 assert original.sel(scenario='baseline')[0].item() == 1 # Original should be unchanged - def test_time_only_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test broadcasting a time-only DataArray to scenarios.""" - # Create a DataArray with only time dimension - time_only = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) - # Convert with scenarios - should broadcast to all scenarios - result = DataConverter.as_dataarray(time_only, sample_time_index, sample_scenario_index) +class TestSeriesConversion: + """Tests for converting pandas Series to DataArray.""" + + def test_series_single_dimension(self, sample_time_index): + """Test converting a pandas Series with time index.""" + # Create a Series with matching time index + series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) + + # Convert and check + result = DataConverter.as_dataarray(series, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, series.values) + assert np.array_equal(result.coords['time'].values, sample_time_index.values) + + # Test with scenario index + scenario_index = pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') + series = pd.Series([100, 200, 300], index=scenario_index) + + result = DataConverter.as_dataarray(series, scenarios=scenario_index) + assert result.shape == (3,) + assert result.dims == ('scenario',) + assert np.array_equal(result.values, series.values) + assert np.array_equal(result.coords['scenario'].values, scenario_index.values) + + def test_series_mismatched_index(self, sample_time_index): + """Test converting a Series with mismatched index.""" + # Create Series with different time index + different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + series = pd.Series([10, 20, 30, 40, 50], index=different_times) + + # Should raise error for mismatched index + with pytest.raises(ConversionError): + DataConverter.as_dataarray(series, sample_time_index) + + def test_series_broadcast_to_scenarios(self, sample_time_index, sample_scenario_index): + """Test broadcasting a time-indexed Series across scenarios.""" + # Create a Series with time index + series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) + + # Convert with scenarios + result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) - assert result.shape == (3, 5) - assert result.dims == ('scenario', 'time') + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') - # Each scenario should have same values + # Check broadcasting - each scenario should have the same values for scenario in sample_scenario_index: - assert np.array_equal(result.sel(scenario=scenario).values, time_only.values) + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(scenario_slice.values, series.values) + + def test_series_broadcast_to_time(self, sample_time_index, sample_scenario_index): + """Test broadcasting a scenario-indexed Series across time.""" + # Create a Series with scenario index + series = pd.Series([100, 200, 300], index=sample_scenario_index) + + # Convert with time + result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + + # Check broadcasting - each time should have the same scenario values + for i, time in enumerate(sample_time_index): + time_slice = result.sel(time=time) + assert np.array_equal(time_slice.values, series.values) + + def test_series_dimension_order(self, sample_time_index, sample_scenario_index): + """Test that dimension order is respected with Series conversions.""" + # Create custom dimensions tuple with reversed order + dims = ('scenario', 'time',) + coords = {'time': sample_time_index, 'scenario': sample_scenario_index} + + # Time-indexed series + series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) + with pytest.raises(ConversionError, match="only supports time and scenario dimensions"): + _ = DataConverter._convert_series(series, coords, dims) + + # Scenario-indexed series + series = pd.Series([100, 200, 300], index=sample_scenario_index) + with pytest.raises(ConversionError, match="only supports time and scenario dimensions"): + _ = DataConverter._convert_series(series, coords, dims) + + +class TestDataFrameConversion: + """Tests for converting pandas DataFrame to DataArray.""" + + def test_dataframe_single_column(self, sample_time_index): + """Test converting a DataFrame with a single column.""" + # Create DataFrame with one column + df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + + # Convert and check + result = DataConverter.as_dataarray(df, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, df['value'].values) + + def test_dataframe_multi_column_fails(self, sample_time_index): + """Test that converting a multi-column DataFrame to 1D fails.""" + # Create DataFrame with multiple columns + df = pd.DataFrame({'val1': [10, 20, 30, 40, 50], 'val2': [15, 25, 35, 45, 55]}, index=sample_time_index) + + # Should raise error + with pytest.raises(ConversionError): + DataConverter.as_dataarray(df, sample_time_index) + + def test_dataframe_time_scenario(self, sample_time_index, sample_scenario_index): + """Test converting a DataFrame with time index and scenario columns.""" + # Create DataFrame with time as index and scenarios as columns + data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} + df = pd.DataFrame(data, index=sample_time_index) + + # Make sure columns are named properly + df.columns.name = 'scenario' + + # Convert and check + result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, df.values) + + # Check values for specific scenarios + assert np.array_equal(result.sel(scenario='baseline').values, df['baseline'].values) + assert np.array_equal(result.sel(scenario='high_demand').values, df['high_demand'].values) + + def test_dataframe_mismatched_coordinates(self, sample_time_index, sample_scenario_index): + """Test conversion fails with mismatched coordinates.""" + # Create DataFrame with different time index + different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} + df = pd.DataFrame(data, index=different_times) + df.columns = sample_scenario_index + + # Should raise error + with pytest.raises(ConversionError): + DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + + # Create DataFrame with different scenario columns + different_scenarios = pd.Index(['scenario1', 'scenario2', 'scenario3'], name='scenario') + data = {'scenario1': [10, 20, 30, 40, 50], 'scenario2': [15, 25, 35, 45, 55], 'scenario3': [5, 15, 25, 35, 45]} + df = pd.DataFrame(data, index=sample_time_index) + df.columns = different_scenarios + + # Should raise error + with pytest.raises(ConversionError): + DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + + def test_ensure_copy(self, sample_time_index, sample_scenario_index): + """Test that the returned DataArray is a copy.""" + # Create DataFrame + data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} + df = pd.DataFrame(data, index=sample_time_index) + df.columns = sample_scenario_index + + # Convert + result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + + # Modify the result + result.loc[dict(time=sample_time_index[0], scenario='baseline')] = 999 + + # Original should be unchanged + assert df.loc[sample_time_index[0], 'baseline'] == 10 class TestInvalidInputs: @@ -314,8 +467,8 @@ def test_single_timestep(self, sample_scenario_index): # With scenarios result_with_scenarios = DataConverter.as_dataarray(42, single_timestep, sample_scenario_index) - assert result_with_scenarios.shape == (len(sample_scenario_index), 1) - assert result_with_scenarios.dims == ('scenario', 'time') + assert result_with_scenarios.shape == (1, len(sample_scenario_index)) + assert result_with_scenarios.dims == ('time', 'scenario') def test_single_scenario(self, sample_time_index): """Test with a single scenario.""" @@ -324,19 +477,19 @@ def test_single_scenario(self, sample_time_index): # Scalar conversion with single scenario result = DataConverter.as_dataarray(42, sample_time_index, single_scenario) - assert result.shape == (1, len(sample_time_index)) - assert result.dims == ('scenario', 'time') + assert result.shape == (len(sample_time_index), 1) + assert result.dims == ('time', 'scenario') # Array conversion with single scenario arr = np.array([1, 2, 3, 4, 5]) result_arr = DataConverter.as_dataarray(arr, sample_time_index, single_scenario) - assert result_arr.shape == (1, 5) + assert result_arr.shape == (5, 1) assert np.array_equal(result_arr.sel(scenario='baseline').values, arr) # 2D array with single scenario arr_2d = np.array([[1, 2, 3, 4, 5]]) # Note the extra dimension - result_arr_2d = DataConverter.as_dataarray(arr_2d, sample_time_index, single_scenario) - assert result_arr_2d.shape == (1, 5) + result_arr_2d = DataConverter.as_dataarray(arr_2d.T, sample_time_index, single_scenario) + assert result_arr_2d.shape == (5, 1) assert np.array_equal(result_arr_2d.sel(scenario='baseline').values, arr_2d[0]) def test_different_scenario_order(self, sample_time_index): @@ -352,7 +505,7 @@ def test_different_scenario_order(self, sample_time_index): [6, 7, 8, 9, 10], # b [11, 12, 13, 14, 15], # c ] - ) + ).T result1 = DataConverter.as_dataarray(data, sample_time_index, scenarios1) assert np.array_equal(result1.sel(scenario='a').values, [1, 2, 3, 4, 5]) @@ -374,7 +527,7 @@ def test_all_nan_data(self, sample_time_index, sample_scenario_index): # With scenarios result = DataConverter.as_dataarray(all_nan_array, sample_time_index, sample_scenario_index) - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) assert np.all(np.isnan(result.values)) # Series of all NaNs @@ -417,11 +570,11 @@ def test_large_dataset(self, sample_scenario_index): large_data = np.random.rand(len(sample_scenario_index), len(large_timesteps)) # Convert and check - result = DataConverter.as_dataarray(large_data, large_timesteps, sample_scenario_index) + result = DataConverter.as_dataarray(large_data.T, large_timesteps, sample_scenario_index) - assert result.shape == (len(sample_scenario_index), len(large_timesteps)) - assert result.dims == ('scenario', 'time') - assert np.array_equal(result.values, large_data) + assert result.shape == (len(large_timesteps), len(sample_scenario_index)) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, large_data.T) class TestMultiScenarioArrayConversion: @@ -432,7 +585,7 @@ def test_1d_array_broadcasting(self, sample_time_index, sample_scenario_index): arr_1d = np.array([1, 2, 3, 4, 5]) result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) # Each scenario should have identical values for i, scenario in enumerate(sample_scenario_index): @@ -451,15 +604,15 @@ def test_2d_array_different_shapes(self, sample_time_index): single_scenario = pd.Index(['baseline'], name='scenario') arr_1_scenario = np.array([[1, 2, 3, 4, 5]]) - result = DataConverter.as_dataarray(arr_1_scenario, sample_time_index, single_scenario) - assert result.shape == (1, len(sample_time_index)) + result = DataConverter.as_dataarray(arr_1_scenario.T, sample_time_index, single_scenario) + assert result.shape == (len(sample_time_index), 1) # Test with 2 scenarios two_scenarios = pd.Index(['baseline', 'high_demand'], name='scenario') arr_2_scenarios = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) - result = DataConverter.as_dataarray(arr_2_scenarios, sample_time_index, two_scenarios) - assert result.shape == (2, len(sample_time_index)) + result = DataConverter.as_dataarray(arr_2_scenarios.T, sample_time_index, two_scenarios) + assert result.shape == (len(sample_time_index), 2) assert np.array_equal(result.sel(scenario='baseline').values, arr_2_scenarios[0]) assert np.array_equal(result.sel(scenario='high_demand').values, arr_2_scenarios[1]) @@ -474,7 +627,7 @@ def test_array_handling_edge_cases(self, sample_time_index, sample_scenario_inde bool_array = np.array([True, False, True, False, True]) result = DataConverter.as_dataarray(bool_array, sample_time_index, sample_scenario_index) assert result.dtype == bool - assert result.shape == (len(sample_scenario_index), len(sample_time_index)) + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) # Test with array containing infinite values inf_array = np.array([1, np.inf, 3, -np.inf, 5]) @@ -504,7 +657,7 @@ def test_preserving_scenario_order(self, sample_time_index): ) # Convert to DataArray - result = DataConverter.as_dataarray(data, sample_time_index, scenarios) + result = DataConverter.as_dataarray(data.T, sample_time_index, scenarios) # Verify order of scenarios is preserved assert list(result.coords['scenario'].values) == list(scenarios) diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index d64c13d85..8237cf293 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -66,7 +66,7 @@ def test_initialization_validation(self, sample_timesteps): """Test validation during initialization.""" # Test missing time dimension invalid_data = xr.DataArray([1, 2, 3], dims=['invalid_dim']) - with pytest.raises(ValueError, match='must have a "time" index'): + with pytest.raises(ValueError, match='DataArray dimensions must be subset of'): TimeSeries(invalid_data, name='Invalid Series') # Test multi-dimensional data @@ -356,7 +356,7 @@ def test_initialization_with_scenarios(self, simple_scenario_dataarray): # Check basic properties assert ts.name == 'Scenario Series' - assert ts._has_scenarios is True + assert ts.has_scenario_dim is True assert ts._selected_scenarios is None # No selection initially # Check data initialization @@ -615,29 +615,29 @@ def test_add_time_series_with_scenarios(self, sample_scenario_allocator): """Test creating time series with scenarios.""" # Test scalar (broadcasts to all scenarios) ts1 = sample_scenario_allocator.add_time_series('scalar_series', 42) - assert ts1._has_scenarios + assert ts1.has_scenario_dim assert ts1.name == 'scalar_series' - assert ts1.selected_data.shape == (3, 5) # 3 scenarios, 5 timesteps + assert ts1.selected_data.shape == (5, 3) # 5 timesteps, 3 scenarios assert np.all(ts1.selected_data.values == 42) # Test 1D array (broadcasts to all scenarios) data = np.array([1, 2, 3, 4, 5]) ts2 = sample_scenario_allocator.add_time_series('array_series', data) - assert ts2._has_scenarios - assert ts2.selected_data.shape == (3, 5) + assert ts2.has_scenario_dim + assert ts2.selected_data.shape == (5, 3) # Each scenario should have the same values for scenario in sample_scenario_allocator.scenarios: assert np.array_equal(ts2.sel(scenario=scenario).values, data) # Test 2D array (one row per scenario) - data_2d = np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]) + data_2d = np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]).T ts3 = sample_scenario_allocator.add_time_series('scenario_specific_series', data_2d) - assert ts3._has_scenarios - assert ts3.selected_data.shape == (3, 5) + assert ts3.has_scenario_dim + assert ts3.selected_data.shape == (5, 3) # Each scenario should have its own values - assert np.array_equal(ts3.sel(scenario='baseline').values, data_2d[0]) - assert np.array_equal(ts3.sel(scenario='high_demand').values, data_2d[1]) - assert np.array_equal(ts3.sel(scenario='low_price').values, data_2d[2]) + assert np.array_equal(ts3.sel(scenario='baseline').values, data_2d[:,0]) + assert np.array_equal(ts3.sel(scenario='high_demand').values, data_2d[:,1]) + assert np.array_equal(ts3.sel(scenario='low_price').values, data_2d[:,2]) def test_selection_propagation_with_scenarios( self, sample_scenario_allocator, sample_timesteps, sample_scenario_index @@ -660,8 +660,8 @@ def test_selection_propagation_with_scenarios( assert ts2._selected_scenarios.equals(subset_scenarios) # Check data is filtered - assert ts1.selected_data.shape == (2, 5) # 2 scenarios, 5 timesteps - assert ts2.selected_data.shape == (2, 5) + assert ts1.selected_data.shape == (5, 2) # 5 timesteps, 2 scenarios + assert ts2.selected_data.shape == (5, 2) # Apply combined selection subset_timesteps = sample_timesteps[1:3] @@ -670,20 +670,22 @@ def test_selection_propagation_with_scenarios( # Check combined selection applied assert ts1._selected_timesteps.equals(subset_timesteps) assert ts1._selected_scenarios.equals(subset_scenarios) - assert ts1.selected_data.shape == (2, 2) # 2 scenarios, 2 timesteps + assert ts1.selected_data.shape == (2, 2) # 2 timesteps, 2 scenarios # Clear selections sample_scenario_allocator.clear_selection() assert ts1._selected_timesteps is None + assert ts1.active_timesteps.equals(sample_scenario_allocator.timesteps) assert ts1._selected_scenarios is None - assert ts1.selected_data.shape == (3, 5) # Back to full shape + assert ts1.active_scenarios.equals(sample_scenario_allocator.scenarios) + assert ts1.selected_data.shape == (5, 3) # Back to full shape def test_as_dataset_with_scenarios(self, sample_scenario_allocator): """Test as_dataset method with scenarios.""" # Add some time series sample_scenario_allocator.add_time_series('scalar_series', 42) sample_scenario_allocator.add_time_series( - 'varying_series', np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]) + 'varying_series', np.array([[10, 20, 30, 40, 50], [15, 25, 35, 45, 55], [5, 15, 25, 35, 45]]).T ) # Get dataset @@ -723,21 +725,21 @@ def test_update_time_series_with_scenarios(self, sample_scenario_allocator, samp """Test updating a time series with scenarios.""" # Add a time series ts = sample_scenario_allocator.add_time_series('series', 42) - assert ts._has_scenarios + assert ts.has_scenario_dim assert np.all(ts.selected_data.values == 42) # Update with scenario-specific data - new_data = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]) + new_data = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]).T sample_scenario_allocator.update_time_series('series', new_data) # Check update was applied assert np.array_equal(ts.selected_data.values, new_data) - assert ts._has_scenarios + assert ts.has_scenario_dim # Check scenario-specific values - assert np.array_equal(ts.sel(scenario='baseline').values, new_data[0]) - assert np.array_equal(ts.sel(scenario='high_demand').values, new_data[1]) - assert np.array_equal(ts.sel(scenario='low_price').values, new_data[2]) + assert np.array_equal(ts.sel(scenario='baseline').values, new_data[:,0]) + assert np.array_equal(ts.sel(scenario='high_demand').values, new_data[:,1]) + assert np.array_equal(ts.sel(scenario='low_price').values, new_data[:,2]) if __name__ == '__main__': From 26bc4478ccb0d96fc743f7d7c69f2d68ead1f0eb Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 9 Apr 2025 17:42:11 +0200 Subject: [PATCH 014/183] ruff check --- flixopt/flow_system.py | 2 +- flixopt/interface.py | 2 +- tests/test_dataconverter.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index a36a14af1..d62f018bf 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,7 +16,7 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData, ScenarioData +from .core import Scalar, ScenarioData, TimeSeries, TimeSeriesCollection, TimeSeriesData, TimestepData from .effects import ( Effect, EffectCollection, diff --git a/flixopt/interface.py b/flixopt/interface.py index 2bece9943..a7b254fb6 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -4,7 +4,7 @@ """ import logging -from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union, Literal +from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union from .config import CONFIG from .core import NumericDataTS, Scalar, ScenarioData, TimestepData diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 61adcb284..a50754301 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -234,7 +234,7 @@ def test_series_broadcast_to_time(self, sample_time_index, sample_scenario_index assert result.dims == ('time', 'scenario') # Check broadcasting - each time should have the same scenario values - for i, time in enumerate(sample_time_index): + for time in sample_time_index: time_slice = result.sel(time=time) assert np.array_equal(time_slice.values, series.values) From 6eeea72548210e7adbd898767019f34f6f229551 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:34:11 +0200 Subject: [PATCH 015/183] Bugfix plausibility in Storage --- flixopt/components.py | 59 ++++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 358b66e1b..30c562543 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -226,36 +226,37 @@ def _plausibility_checks(self) -> None: Check for infeasible or uncommon combinations of parameters """ super()._plausibility_checks() - if isinstance(self.initial_charge_state, str) and not self.initial_charge_state == 'lastValueOfSim': - raise PlausibilityError(f'initial_charge_state has undefined value: {self.initial_charge_state}') - else: - if isinstance(self.capacity_in_flow_hours, InvestParameters): - if self.capacity_in_flow_hours.fixed_size is None: - maximum_capacity = self.capacity_in_flow_hours.maximum_size - minimum_capacity = self.capacity_in_flow_hours.minimum_size - else: - maximum_capacity = self.capacity_in_flow_hours.fixed_size - minimum_capacity = self.capacity_in_flow_hours.fixed_size + if isinstance(self.initial_charge_state, str): + if self.initial_charge_state != 'lastValueOfSim': + raise PlausibilityError(f'initial_charge_state has undefined value: {self.initial_charge_state}') + return + if isinstance(self.capacity_in_flow_hours, InvestParameters): + if self.capacity_in_flow_hours.fixed_size is None: + maximum_capacity = self.capacity_in_flow_hours.maximum_size + minimum_capacity = self.capacity_in_flow_hours.minimum_size else: - maximum_capacity = self.capacity_in_flow_hours - minimum_capacity = self.capacity_in_flow_hours - - # initial capacity >= allowed min for maximum_size: - minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) - # initial capacity <= allowed max for minimum_size: - maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) - # TODO: index=1 ??? I think index 0 - - if (self.initial_charge_state > maximum_inital_capacity).any(): - raise ValueError( - f'{self.label_full}: {self.initial_charge_state=} ' - f'is above allowed maximum charge_state {maximum_inital_capacity}' - ) - if (self.initial_charge_state < minimum_inital_capacity).any(): - raise ValueError( - f'{self.label_full}: {self.initial_charge_state=} ' - f'is below allowed minimum charge_state {minimum_inital_capacity}' - ) + maximum_capacity = self.capacity_in_flow_hours.fixed_size + minimum_capacity = self.capacity_in_flow_hours.fixed_size + else: + maximum_capacity = self.capacity_in_flow_hours + minimum_capacity = self.capacity_in_flow_hours + + # initial capacity >= allowed min for maximum_size: + minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) + # initial capacity <= allowed max for minimum_size: + maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) + # TODO: index=1 ??? I think index 0 + + if (self.initial_charge_state > maximum_inital_capacity).any(): + raise ValueError( + f'{self.label_full}: {self.initial_charge_state=} ' + f'is above allowed maximum charge_state {maximum_inital_capacity}' + ) + if (self.initial_charge_state < minimum_inital_capacity).any(): + raise ValueError( + f'{self.label_full}: {self.initial_charge_state=} ' + f'is below allowed minimum charge_state {minimum_inital_capacity}' + ) @register_class_for_io From ecf64d2b74503b2dc1ba8a363d72916d4f05d527 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:40:01 +0200 Subject: [PATCH 016/183] Bugfix check in Storage Model --- flixopt/components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/components.py b/flixopt/components.py index 30c562543..32b28308e 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -533,7 +533,7 @@ def _initial_and_final_charge_state(self): name_short = 'initial_charge_state' name = f'{self.label_full}|{name_short}' - if self.element.initial_charge_state == 'lastValueOfSim': + if isinstance(self.element.initial_charge_state, str): self.add( self._model.add_constraints( self.charge_state.isel(time=0) == self.charge_state.isel(time=-1), name=name From 091ab71b3103f40d81dd5df8a08130e7815af78b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:40:08 +0200 Subject: [PATCH 017/183] Improve example --- examples/04_Scenarios/scenario_example.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index 03c2a5be0..c68d1bbe5 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -49,7 +49,7 @@ boiler = fx.linear_converters.Boiler( label='Boiler', eta=0.5, - Q_th=fx.Flow(label='Q_th', bus='Fernwärme', size=50, relative_minimum=0.1, relative_maximum=1), + Q_th=fx.Flow(label='Q_th', bus='Fernwärme', size=50, relative_minimum=0.1, relative_maximum=1, on_off_parameters=fx.OnOffParameters()), Q_fu=fx.Flow(label='Q_fu', bus='Gas'), ) @@ -58,7 +58,7 @@ label='CHP', eta_th=0.5, eta_el=0.4, - P_el=fx.Flow('P_el', bus='Strom', size=60, relative_minimum=5 / 60), + P_el=fx.Flow('P_el', bus='Strom', size=60, relative_minimum=5 / 60, on_off_parameters=fx.OnOffParameters()), Q_th=fx.Flow('Q_th', bus='Fernwärme'), Q_fu=fx.Flow('Q_fu', bus='Gas'), ) From 0a7e3367fe659b2d8d204ccf0465195ace2c31f9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 11 Apr 2025 17:43:38 +0200 Subject: [PATCH 018/183] ruff check --- flixopt/elements.py | 2 +- flixopt/features.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index b5b4f2344..3cc775674 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import TimestepData, PlausibilityError, Scalar, ScenarioData, TimestepData +from .core import PlausibilityError, Scalar, ScenarioData, TimestepData from .effects import EffectValuesUserTimestep from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters diff --git a/flixopt/features.py b/flixopt/features.py index 52b49e960..0ccc7be2a 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import Scalar, TimeSeries, TimestepData, ScenarioData +from .core import Scalar, ScenarioData, TimeSeries, TimestepData from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel From 8b5d2fcc06869df575f8b37bc848b98777a82764 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 14 Apr 2025 18:20:09 +0200 Subject: [PATCH 019/183] Simplifying the investment with scenarios, by a simpler approach --- flixopt/features.py | 71 ++++++++++++++------------------------------ flixopt/interface.py | 27 +++++++++++++---- 2 files changed, 43 insertions(+), 55 deletions(-) diff --git a/flixopt/features.py b/flixopt/features.py index 0ccc7be2a..17ef9928a 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -194,7 +194,10 @@ def _create_bounds_for_defining_variable(self): # anmerkung: Glg bei Spezialfall relative_minimum = 0 redundant zu OnOff ?? def _create_bounds_for_scenarios(self): - if self.parameters.size_per_scenario == 'equal': + if self.parameters.investment_scenarios == 'individual': + return + + if self.parameters.investment_scenarios is None: self.add( self._model.add_constraints( self.size.isel(scenario=slice(None, -1)) == self.size.isel(scenario=slice(1, None)), @@ -202,64 +205,34 @@ def _create_bounds_for_scenarios(self): ), 'equalize_size_per_scenario', ) - elif self.parameters.size_per_scenario == 'increment_once': - if not self.parameters.optional: - raise ValueError('Increment once can only be used if the Investment is optional') - - self.scenario_of_investment = self.add( - self._model.add_variables( - binary=True, - name=f'{self.label_full}|scenario_of_investment', - coords=self._model.get_coords(time_dim=False), - ), - 'scenario_of_investment', - ) - - # eq: scenario_of_investment(t) = is_invested(t) - is_invested(t-1) - self.add( - self._model.add_constraints( - self.scenario_of_investment.isel(scenario=slice(1, None)) - == self.is_invested.isel(scenario=slice(1, None)) - self.is_invested.isel(scenario=slice(None, -1)), - name=f'{self.label_full}|scenario_of_investment', - ), - 'scenario_of_investment', - ) - - # eq: scenario_of_investment(t=0) = is_invested(t=0) - self.add( - self._model.add_constraints( - self.scenario_of_investment.isel(scenario=0) - == self.is_invested.isel(scenario=0), - name=f'{self.label_full}|initial_scenario_of_investment', - ), - 'initial_scenario_of_investment', - ) + return + if not isinstance(self.parameters.investment_scenarios, list): + raise ValueError(f'Invalid value for investment_scenarios: {self.parameters.investment_scenarios}') + if not all(scenario in self._model.time_series_collection.scenarios for scenario in self.parameters.investment_scenarios): + raise ValueError(f'Some scenarios in investment_scenarios are not present in the time_series_collection: {self.parameters.investment_scenarios}') - big_m = self.parameters.maximum_size.isel(scenario=slice(1, None)) + investment_scenarios = self._model.time_series_collection.scenarios.intersection(self.parameters.investment_scenarios) + no_investment_scenarios = self._model.time_series_collection.scenarios.difference(self.parameters.investment_scenarios) + # eq: size(s) = size(s') for s, s' in investment_scenarios + if len(investment_scenarios) > 1: self.add( self._model.add_constraints( - self.size.isel(scenario=slice(1, None)) - self.size.isel(scenario=slice(None, -1)) - <= self.scenario_of_investment.isel(scenario=slice(1, None)) * big_m, - name=f'{self.label_full}|invest_once_1a', - ), - 'invest_once_1a', + self.size.sel(scenario=investment_scenarios[:-1]) == self.size.sel(scenario=investment_scenarios[1:]), + name=f'{self.label_full}|investment_scenarios', + ), + 'investment_scenarios', ) + if len(no_investment_scenarios) >= 1: self.add( self._model.add_constraints( - self.size.isel(scenario=slice(1, None)) - self.size.isel(scenario=slice(None, -1)) - >= self.scenario_of_investment.isel(scenario=slice(1, None)) * big_m, - name=f'{self.label_full}|invest_once_1b', - ), - 'invest_once_1b', + self.size.sel(scenario=no_investment_scenarios) == 0, + name=f'{self.label_full}|no_investment_scenarios', + ), + 'no_investment_scenarios', ) - elif self.parameters.size_per_scenario == 'individual': - pass - else: - raise ValueError(f'Invalid value for size_per_scenario: {self.parameters.size_per_scenario}') - class StateModel(Model): """ diff --git a/flixopt/interface.py b/flixopt/interface.py index 6ede2f2b9..a4936e844 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -158,7 +158,7 @@ def __init__( specific_effects: Optional['EffectValuesUserScenario'] = None, # costs per Flow-Unit/Storage-Size/... piecewise_effects: Optional[PiecewiseEffects] = None, divest_effects: Optional['EffectValuesUserScenario'] = None, - size_per_scenario: Literal['equal', 'individual', 'increment_once'] = 'equal', + investment_scenarios: Optional[Union[Literal['individual'], List[Union[int, str]]]] = None, ): """ Args: @@ -172,11 +172,10 @@ def __init__( piecewise_effects: Define the effects of the investment as a piecewise function of the size of the investment. minimum_size: Minimum possible size of the investment. maximum_size: Maximum possible size of the investment. - size_per_scenario: How to treat the size in each scenario - - 'equal': Equalize the size of all scenarios + investment_scenarios: For which scenarios to optimize the size for. - 'individual': Optimize the size of each scenario individually - - 'increment_once': Allow the size to increase only once. This is useful if the scenarios are related to - different periods (years, months). Tune the timing by setting the maximum size to 0 in the first scenarios. + - List of scenario names: Optimize the size for the passed scenario names (equal size in all). All other scenarios will have the size 0. + - None: Equals to a list of all scenarios (default) """ self.fix_effects: EffectValuesUserScenario = fix_effects if fix_effects is not None else {} self.divest_effects: EffectValuesUserScenario = divest_effects if divest_effects is not None else {} @@ -186,9 +185,10 @@ def __init__( self.piecewise_effects = piecewise_effects self._minimum_size = minimum_size if minimum_size is not None else CONFIG.modeling.EPSILON self._maximum_size = maximum_size if maximum_size is not None else CONFIG.modeling.BIG # default maximum - self.size_per_scenario = size_per_scenario + self.investment_scenarios = investment_scenarios def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): + self._plausibility_checks(flow_system) self.fix_effects = flow_system.create_effect_time_series( label_prefix=name_prefix, effect_values=self.fix_effects, @@ -225,6 +225,21 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): f'{name_prefix}|fixed_size', self.fixed_size, has_time_dim=False, has_scenario_dim=True ) + def _plausibility_checks(self, flow_system): + if isinstance(self.investment_scenarios, list): + if not set(self.investment_scenarios).issubset(flow_system.time_series_collection.scenarios): + raise ValueError( + f'Some scenarios in investment_scenarios are not present in the time_series_collection: ' + f'{set(self.investment_scenarios) - set(flow_system.time_series_collection.scenarios)}' + ) + if self.investment_scenarios is not None: + if not self.optional: + if self.minimum_size is not None or self.fixed_size is not None: + logger.warning( + f'When using investment_scenarios, minimum_size and fixed_size should only ne used if optional is True.' + f'Otherwise the investment cannot be 0 incertain scenarios while being non-zero in others.' + ) + @property def minimum_size(self): return self.fixed_size if self.fixed_size is not None else self._minimum_size From f28db64ddb0dc3f8d2074896e1a0a55608342968 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:30:58 +0200 Subject: [PATCH 020/183] Simplifying the investment with scenarios --- flixopt/features.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flixopt/features.py b/flixopt/features.py index 17ef9928a..31c1dbadb 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -209,7 +209,9 @@ def _create_bounds_for_scenarios(self): if not isinstance(self.parameters.investment_scenarios, list): raise ValueError(f'Invalid value for investment_scenarios: {self.parameters.investment_scenarios}') if not all(scenario in self._model.time_series_collection.scenarios for scenario in self.parameters.investment_scenarios): - raise ValueError(f'Some scenarios in investment_scenarios are not present in the time_series_collection: {self.parameters.investment_scenarios}') + raise ValueError(f'Some scenarios in investment_scenarios are not present in the time_series_collection: ' + f'{self.parameters.investment_scenarios}. This might be due to selecting a subset of ' + f'all scenarios, which is not yet supported.') investment_scenarios = self._model.time_series_collection.scenarios.intersection(self.parameters.investment_scenarios) no_investment_scenarios = self._model.time_series_collection.scenarios.difference(self.parameters.investment_scenarios) From 8696430dff19e0f969df320ae8114516cf54e873 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:31:10 +0200 Subject: [PATCH 021/183] Bugfix in scenario selection --- flixopt/core.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 14b95ea5a..e1c3c361d 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -1109,7 +1109,7 @@ def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: if scenarios is None: self.clear_selection(timesteps=False, scenarios=True) else: - self._selected_scenarios = scenarios + self._selected_scenarios = self._validate_scenarios(scenarios) # Apply the selection to all TimeSeries objects self._propagate_selection_to_time_series() @@ -1350,10 +1350,6 @@ def _validate_scenarios(scenarios: pd.Index, present_scenarios: Optional[pd.Inde logger.warning('Converting scenarios to pandas.Index') scenarios = pd.Index(scenarios, name='scenario') - if len(scenarios) < 2: - logger.warning('scenarios must contain at least 2 scenarios') - raise ValueError('timesteps must contain at least 2 timestamps') - # Ensure timesteps has the required name if scenarios.name != 'scenario': logger.debug('Renamed scenarios to "scneario" (was "%s")', scenarios.name) From 2d3f0ada33061ad9fa66266b0df3fd2742ac68df Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 14 Apr 2025 19:40:11 +0200 Subject: [PATCH 022/183] ruff check --- flixopt/interface.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/interface.py b/flixopt/interface.py index a4936e844..4b7634e96 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -236,8 +236,8 @@ def _plausibility_checks(self, flow_system): if not self.optional: if self.minimum_size is not None or self.fixed_size is not None: logger.warning( - f'When using investment_scenarios, minimum_size and fixed_size should only ne used if optional is True.' - f'Otherwise the investment cannot be 0 incertain scenarios while being non-zero in others.' + 'When using investment_scenarios, minimum_size and fixed_size should only ne used if optional is True.' + 'Otherwise the investment cannot be 0 incertain scenarios while being non-zero in others.' ) @property From c730b87dfcca74be20730c061598146580fff4f3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:35:16 +0200 Subject: [PATCH 023/183] Scenarios/io (#244) * Add scenarios to io of flow_system.py * Add test for io of scenarios * Fix tests and docstrings (#242) * Bugfix testing fixture * Bugfix tests and add new tests to check for previous states/flow_rates * Bugfix tests and add new tests to check for previous states/flow_rates * Add comment for previous flow_rates * Add comment for OnOffParameters in Component and LinearConverter * Bugfix io --- flixopt/flow_system.py | 15 ++++++++- tests/conftest.py | 74 ++++++++++++++++++++++++++++++++++++++++++ tests/test_io.py | 3 +- 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 7b83b2005..591b55e06 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -64,7 +64,9 @@ def __init__( hours_of_last_timestep=hours_of_last_timestep, hours_of_previous_timesteps=hours_of_previous_timesteps, ) - self.scenario_weights = scenario_weights + self.scenario_weights = self.create_time_series( + 'scenario_weights', scenario_weights, has_time_dim=False, has_scenario_dim=True + ) # defaults: self.components: Dict[str, Component] = {} @@ -79,10 +81,15 @@ def from_dataset(cls, ds: xr.Dataset): timesteps_extra = pd.DatetimeIndex(ds.attrs['timesteps_extra'], name='time') hours_of_last_timestep = TimeSeriesCollection.calculate_hours_per_timestep(timesteps_extra).isel(time=-1).item() + scenarios = pd.Index(ds.attrs['scenarios'], name='scenario') if ds.attrs.get('scenarios') is not None else None + scenario_weights = fx_io.insert_dataarray(ds.attrs['scenario_weights'], ds) + flow_system = FlowSystem( timesteps=timesteps_extra[:-1], hours_of_last_timestep=hours_of_last_timestep, hours_of_previous_timesteps=ds.attrs['hours_of_previous_timesteps'], + scenarios=scenarios, + scenario_weights=scenario_weights, ) structure = fx_io.insert_dataarray({key: ds.attrs[key] for key in ['components', 'buses', 'effects']}, ds) @@ -103,11 +110,15 @@ def from_dict(cls, data: Dict) -> 'FlowSystem': """ timesteps_extra = pd.DatetimeIndex(data['timesteps_extra'], name='time') hours_of_last_timestep = TimeSeriesCollection.calculate_hours_per_timestep(timesteps_extra).isel(time=-1).item() + scenarios = pd.Index(data['scenarios'], name='scenario') if data.get('scenarios') is not None else None + scenario_weights = data.get('scenario_weights').selected_data if data.get('scenario_weights') is not None else None flow_system = FlowSystem( timesteps=timesteps_extra[:-1], hours_of_last_timestep=hours_of_last_timestep, hours_of_previous_timesteps=data['hours_of_previous_timesteps'], + scenarios=scenarios, + scenario_weights=scenario_weights, ) flow_system.add_elements(*[Bus.from_dict(bus) for bus in data['buses'].values()]) @@ -183,6 +194,8 @@ def as_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: }, 'timesteps_extra': [date.isoformat() for date in self.time_series_collection.timesteps_extra], 'hours_of_previous_timesteps': self.time_series_collection.hours_of_previous_timesteps, + 'scenarios': self.time_series_collection.scenarios.tolist() if self.time_series_collection.scenarios is not None else None, + 'scenario_weights': self.scenario_weights, } if data_mode == 'data': return fx_io.replace_timeseries(data, 'data') diff --git a/tests/conftest.py b/tests/conftest.py index 5399be72a..b2ceb1c1d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -121,6 +121,80 @@ def simple_flow_system() -> fx.FlowSystem: return flow_system +@pytest.fixture +def simple_flow_system_scenarios() -> fx.FlowSystem: + """ + Create a simple energy system for testing + """ + base_thermal_load = np.array([30.0, 0.0, 90.0, 110, 110, 20, 20, 20, 20]) + base_electrical_price = np.array([0.08, 0.1, 0.15]) + base_timesteps = pd.date_range('2020-01-01', periods=9, freq='h', name='time') + # Define effects + costs = fx.Effect('costs', '€', 'Kosten', is_standard=True, is_objective=True) + co2 = fx.Effect( + 'CO2', + 'kg', + 'CO2_e-Emissionen', + specific_share_to_other_effects_operation={costs.label: 0.2}, + maximum_operation_per_hour=1000, + ) + + # Create components + boiler = fx.linear_converters.Boiler( + 'Boiler', + eta=0.5, + Q_th=fx.Flow( + 'Q_th', + bus='Fernwärme', + size=50, + relative_minimum=5 / 50, + relative_maximum=1, + on_off_parameters=fx.OnOffParameters(), + ), + Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + + chp = fx.linear_converters.CHP( + 'CHP_unit', + eta_th=0.5, + eta_el=0.4, + P_el=fx.Flow('P_el', bus='Strom', size=60, relative_minimum=5 / 60, on_off_parameters=fx.OnOffParameters()), + Q_th=fx.Flow('Q_th', bus='Fernwärme'), + Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + + storage = fx.Storage( + 'Speicher', + charging=fx.Flow('Q_th_load', bus='Fernwärme', size=1e4), + discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1e4), + capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), + initial_charge_state=0, + relative_maximum_charge_state=1 / 100 * np.array([80.0, 70.0, 80.0, 80, 80, 80, 80, 80, 80, 80]), + eta_charge=0.9, + eta_discharge=1, + relative_loss_per_hour=0.08, + prevent_simultaneous_charge_and_discharge=True, + ) + + heat_load = fx.Sink( + 'Wärmelast', sink=fx.Flow('Q_th_Last', bus='Fernwärme', size=1, fixed_relative_profile=base_thermal_load) + ) + + gas_tariff = fx.Source( + 'Gastarif', source=fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': 0.04, 'CO2': 0.3}) + ) + + electricity_feed_in = fx.Sink( + 'Einspeisung', sink=fx.Flow('P_el', bus='Strom', effects_per_flow_hour=-1 * base_electrical_price) + ) + + # Create flow system + flow_system = fx.FlowSystem(base_timesteps, scenarios=pd.Index(['A', 'B', 'C']), scenario_weights=np.array([0.5, 0.25, 0.25])) + flow_system.add_elements(fx.Bus('Strom'), fx.Bus('Fernwärme'), fx.Bus('Gas')) + flow_system.add_elements(storage, costs, co2, boiler, heat_load, gas_tariff, electricity_feed_in, chp) + + return flow_system + @pytest.fixture def basic_flow_system() -> fx.FlowSystem: diff --git a/tests/test_io.py b/tests/test_io.py index 2e6c61ccf..2b3a03399 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -11,10 +11,11 @@ flow_system_long, flow_system_segments_of_flows_2, simple_flow_system, + simple_flow_system_scenarios, ) -@pytest.fixture(params=[flow_system_base, flow_system_segments_of_flows_2, simple_flow_system, flow_system_long]) +@pytest.fixture(params=[flow_system_base, simple_flow_system_scenarios, flow_system_segments_of_flows_2, simple_flow_system, flow_system_long]) def flow_system(request): fs = request.getfixturevalue(request.param.__name__) if isinstance(fs, fx.FlowSystem): From 50bb559f813d6a6e0e40864a10763dfc8fa10cbb Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:42:10 +0200 Subject: [PATCH 024/183] Scenarios/testing (#246) * Add tests for scenario calculations * utility method to get TimeSeries as a DataArray --- examples/04_Scenarios/scenario_example.py | 2 +- flixopt/components.py | 2 +- flixopt/core.py | 100 ++++--- flixopt/effects.py | 22 +- flixopt/elements.py | 22 +- flixopt/features.py | 30 +- tests/test_scenarios.py | 333 ++++++++++++++++++++++ tests/test_timeseries.py | 8 +- 8 files changed, 423 insertions(+), 96 deletions(-) create mode 100644 tests/test_scenarios.py diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index c68d1bbe5..3edb6e7c0 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -19,7 +19,7 @@ 'High Demand':[30, 0, 100, 118, 125, 20, 20, 20, 20]}, index=timesteps) power_prices = np.array([0.08, 0.09]) - flow_system = fx.FlowSystem(timesteps=timesteps, scenarios=scenarios) + flow_system = fx.FlowSystem(timesteps=timesteps, scenarios=scenarios, scenario_weights=np.array([0.5, 0.6])) # --- Define Energy Buses --- # These represent nodes, where the used medias are balanced (electricity, heat, and gas) diff --git a/flixopt/components.py b/flixopt/components.py index d471dcf6e..1b745b54c 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -211,7 +211,7 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: self.relative_loss_per_hour = flow_system.create_time_series( f'{self.label_full}|relative_loss_per_hour', self.relative_loss_per_hour ) - if self.initial_charge_state != 'lastValueOfSim': + if not isinstance(self.initial_charge_state, str): self.initial_charge_state = flow_system.create_time_series( f'{self.label_full}|initial_charge_state', self.initial_charge_state, has_time_dim=False ) diff --git a/flixopt/core.py b/flixopt/core.py index e1c3c361d..850e01c04 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -653,7 +653,7 @@ def reset(self) -> None: Reset selections to include all timesteps and scenarios. This is equivalent to clearing all selections. """ - self.clear_selection() + self.set_selection(None, None) def restore_data(self) -> None: """ @@ -755,13 +755,7 @@ def update_stored_data(self, value: xr.DataArray) -> None: return self._stored_data = new_data - self.clear_selection() # Reset selections to full dataset - - def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> None: - if timesteps: - self._selected_timesteps = None - if scenarios: - self._selected_scenarios = None + self.set_selection(None, None) # Reset selections to full dataset def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: """ @@ -773,15 +767,15 @@ def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: """ # Only update timesteps if the series has time dimension if self.has_time_dim: - if timesteps is None: - self.clear_selection(timesteps=True, scenarios=False) + if timesteps is None or timesteps.equals(self._stored_data.indexes['time']): + self._selected_timesteps = None else: self._selected_timesteps = timesteps # Only update scenarios if the series has scenario dimension if self.has_scenario_dim: - if scenarios is None: - self.clear_selection(timesteps=False, scenarios=True) + if scenarios is None or scenarios.equals(self._stored_data.indexes['scenario']): + self._selected_scenarios = None else: self._selected_scenarios = scenarios @@ -1077,22 +1071,6 @@ def add_time_series( # Return the TimeSeries object return time_series - def clear_selection(self, timesteps: bool = True, scenarios: bool = True) -> None: - """ - Clear selection for timesteps and/or scenarios. - - Args: - timesteps: Whether to clear timesteps selection - scenarios: Whether to clear scenarios selection - """ - if timesteps: - self._update_selected_timesteps(timesteps=None) - if scenarios: - self._selected_scenarios = None - - for ts in self._time_series.values(): - ts.clear_selection(timesteps=timesteps, scenarios=scenarios) - def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None) -> None: """ Set active subset for timesteps and scenarios. @@ -1102,35 +1080,30 @@ def set_selection(self, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: scenarios: Scenarios to activate, or None to clear """ if timesteps is None: - self.clear_selection(timesteps=True, scenarios=False) + self._selected_timesteps = None + self._selected_timesteps_extra = None else: - self._update_selected_timesteps(timesteps) + self._selected_timesteps = self._validate_timesteps(timesteps, self._full_timesteps) + self._selected_timesteps_extra = self._create_timesteps_with_extra( + timesteps, self._calculate_hours_of_final_timestep(timesteps, self._full_timesteps) + ) if scenarios is None: - self.clear_selection(timesteps=False, scenarios=True) + self._selected_scenarios = None else: - self._selected_scenarios = self._validate_scenarios(scenarios) + self._selected_scenarios = self._validate_scenarios(scenarios, self._full_scenarios) - # Apply the selection to all TimeSeries objects - self._propagate_selection_to_time_series() + self._selected_hours_per_timestep = self.calculate_hours_per_timestep(self.timesteps_extra, self.scenarios) - def _update_selected_timesteps(self, timesteps: Optional[pd.DatetimeIndex]) -> None: - """ - Updates the timestep and related metrics (timesteps_extra, hours_per_timestep) based on the current selection. - """ - if timesteps is None: - self._selected_timesteps = None - self._selected_timesteps_extra = None - self._selected_hours_per_timestep = None - return + # Apply the selection to all TimeSeries objects + for ts_name, ts in self._time_series.items(): + if ts.has_time_dim: + timesteps = self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps + else: + timesteps = None - self._selected_timesteps = self._validate_timesteps(timesteps, self._full_timesteps) - self._selected_timesteps_extra = self._create_timesteps_with_extra( - timesteps, self._calculate_hours_of_final_timestep(timesteps, self._full_timesteps) - ) - self._selected_hours_per_timestep = self.calculate_hours_per_timestep( - self._selected_timesteps_extra, self._selected_scenarios - ) + ts.set_selection(timesteps=timesteps, scenarios=self.scenarios if ts.has_scenario_dim else None) + self._propagate_selection_to_time_series() def as_dataset(self, with_extra_timestep: bool = True, with_constants: bool = True) -> xr.Dataset: """ @@ -1188,7 +1161,7 @@ def _propagate_selection_to_time_series(self) -> None: """Apply the current selection to all TimeSeries objects.""" for ts_name, ts in self._time_series.items(): if ts.has_time_dim: - timesteps = self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps + timesteps = self.timesteps_extra if ts_name in self._has_extra_timestep else self.timesteps else: timesteps = None @@ -1482,3 +1455,28 @@ def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10, by_ std = data.std().item() return f'{mean:{format_spec}} (mean), {median:{format_spec}} (median), {min_val:{format_spec}} (min), {max_val:{format_spec}} (max), {std:{format_spec}} (std)' + + +def extract_data( + data: Optional[Union[int, float, xr.DataArray, TimeSeries]], + if_none: Any = None +) -> Any: + """ + Convert data to xr.DataArray. + + Args: + data: The data to convert (scalar, array, or DataArray) + if_none: The value to return if data is None + + Returns: + DataArray with the converted data, or the value specified by if_none + """ + if data is None: + return if_none + if isinstance(data, TimeSeries): + return data.selected_data + if isinstance(data, xr.DataArray): + return data + if isinstance(data, (int, float, np.integer, np.floating)): + return xr.DataArray(data) + raise TypeError(f'Unsupported data type: {type(data).__name__}') \ No newline at end of file diff --git a/flixopt/effects.py b/flixopt/effects.py index 0cf165d66..2da561a36 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -12,7 +12,7 @@ import linopy import numpy as np -from .core import NumericDataTS, ScenarioData, TimeSeries, TimeSeriesCollection, TimestepData +from .core import NumericDataTS, ScenarioData, TimeSeries, TimeSeriesCollection, TimestepData, extract_data from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -125,8 +125,8 @@ def __init__(self, model: SystemModel, element: Effect): label_of_element=self.label_of_element, label='invest', label_full=f'{self.label_full}(invest)', - total_max=self.element.maximum_invest, - total_min=self.element.minimum_invest, + total_max=extract_data(self.element.maximum_invest), + total_min=extract_data(self.element.minimum_invest), ) ) @@ -138,14 +138,10 @@ def __init__(self, model: SystemModel, element: Effect): label_of_element=self.label_of_element, label='operation', label_full=f'{self.label_full}(operation)', - total_max=self.element.maximum_operation, - total_min=self.element.minimum_operation, - min_per_hour=self.element.minimum_operation_per_hour.selected_data - if self.element.minimum_operation_per_hour is not None - else None, - max_per_hour=self.element.maximum_operation_per_hour.selected_data - if self.element.maximum_operation_per_hour is not None - else None, + total_max=extract_data(self.element.maximum_operation), + total_min=extract_data(self.element.minimum_operation), + min_per_hour=extract_data(self.element.minimum_operation_per_hour), + max_per_hour=extract_data(self.element.maximum_operation_per_hour), ) ) @@ -155,8 +151,8 @@ def do_modeling(self): self.total = self.add( self._model.add_variables( - lower=self.element.minimum_total if self.element.minimum_total is not None else -np.inf, - upper=self.element.maximum_total if self.element.maximum_total is not None else np.inf, + lower=extract_data(self.element.minimum_total, if_none=-np.inf), + upper=extract_data(self.element.maximum_total, if_none=np.inf), coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|total', ), diff --git a/flixopt/elements.py b/flixopt/elements.py index 80085cd0c..2ff49567e 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import PlausibilityError, Scalar, ScenarioData, TimestepData +from .core import PlausibilityError, Scalar, ScenarioData, TimestepData, extract_data from .effects import EffectValuesUserTimestep from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters @@ -375,8 +375,8 @@ def do_modeling(self): self.total_flow_hours = self.add( self._model.add_variables( - lower=self.element.flow_hours_total_min if self.element.flow_hours_total_min is not None else 0, - upper=self.element.flow_hours_total_max if self.element.flow_hours_total_max is not None else np.inf, + lower=extract_data(self.element.flow_hours_total_min, 0), + upper=extract_data(self.element.flow_hours_total_max, np.inf), coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|total_flow_hours', ), @@ -456,16 +456,16 @@ def flow_rate_lower_bound_relative(self) -> TimestepData: """Returns the lower bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: - return self.element.relative_minimum.selected_data - return fixed_profile.selected_data + return extract_data(self.element.relative_minimum) + return extract_data(fixed_profile) @property def flow_rate_upper_bound_relative(self) -> TimestepData: """ Returns the upper bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: - return self.element.relative_maximum.selected_data - return fixed_profile.selected_data + return extract_data(self.element.relative_maximum) + return extract_data(fixed_profile) @property def flow_rate_lower_bound(self) -> TimestepData: @@ -478,8 +478,8 @@ def flow_rate_lower_bound(self) -> TimestepData: if isinstance(self.element.size, InvestParameters): if self.element.size.optional: return 0 - return self.flow_rate_lower_bound_relative * self.element.size.minimum_size - return self.flow_rate_lower_bound_relative * self.element.size + return self.flow_rate_lower_bound_relative * extract_data(self.element.size.minimum_size) + return self.flow_rate_lower_bound_relative * extract_data(self.element.size) @property def flow_rate_upper_bound(self) -> TimestepData: @@ -488,8 +488,8 @@ def flow_rate_upper_bound(self) -> TimestepData: Further constraining might be done in OnOffModel and InvestmentModel """ if isinstance(self.element.size, InvestParameters): - return self.flow_rate_upper_bound_relative * self.element.size.maximum_size - return self.flow_rate_upper_bound_relative * self.element.size + return self.flow_rate_upper_bound_relative * extract_data(self.element.size.maximum_size) + return self.flow_rate_upper_bound_relative * extract_data(self.element.size) class BusModel(ElementModel): diff --git a/flixopt/features.py b/flixopt/features.py index 31c1dbadb..b8243d794 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import Scalar, ScenarioData, TimeSeries, TimestepData +from .core import Scalar, ScenarioData, TimeSeries, TimestepData, extract_data from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel @@ -45,8 +45,8 @@ def __init__( def do_modeling(self): self.size = self.add( self._model.add_variables( - lower=0 if self.parameters.optional else self.parameters.minimum_size*1, - upper=self.parameters.maximum_size*1, + lower=0 if self.parameters.optional else extract_data(self.parameters.minimum_size), + upper=extract_data(self.parameters.maximum_size), name=f'{self.label_full}|size', coords=self._model.get_coords(time_dim=False), ), @@ -295,8 +295,8 @@ def do_modeling(self): self.total_on_hours = self.add( self._model.add_variables( - lower=self._on_hours_total_min, - upper=self._on_hours_total_max, + lower=extract_data(self._on_hours_total_min), + upper=extract_data(self._on_hours_total_max), coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|on_hours_total', ), @@ -440,7 +440,7 @@ def do_modeling(self): # Create count variable for number of switches self.switch_on_nr = self.add( self._model.add_variables( - upper=self._switch_on_max, + upper=extract_data(self._switch_on_max), lower=0, name=f'{self.label_full}|switch_on_nr', ), @@ -534,7 +534,7 @@ def do_modeling(self): self.duration = self.add( self._model.add_variables( lower=0, - upper=self._maximum_duration if self._maximum_duration is not None else mega, + upper=extract_data(self._maximum_duration, mega), coords=self._model.get_coords(), name=f'{self.label_full}|hours', ), @@ -588,7 +588,7 @@ def do_modeling(self): ) # Handle initial condition - if 0 < self.previous_duration < self._minimum_duration.isel(time=0): + if 0 < self.previous_duration < self._minimum_duration.isel(time=0).max(): self.add( self._model.add_constraints( self._state_variable.isel(time=0) == 1, name=f'{self.label_full}|initial_minimum' @@ -613,7 +613,7 @@ def previous_duration(self) -> Scalar: """Computes the previous duration of the state variable""" #TODO: Allow for other/dynamic timestep resolutions return ConsecutiveStateModel.compute_consecutive_hours_in_state( - self._previous_states, self._model.hours_per_step.isel(time=0).item() + self._previous_states, self._model.hours_per_step.isel(time=0).values.flatten()[0] ) @staticmethod @@ -715,8 +715,8 @@ def do_modeling(self): defining_bounds=self._defining_bounds, previous_values=self._previous_values, use_off=self.parameters.use_off, - on_hours_total_min=self.parameters.on_hours_total_min, - on_hours_total_max=self.parameters.on_hours_total_max, + on_hours_total_min=extract_data(self.parameters.on_hours_total_min), + on_hours_total_max=extract_data(self.parameters.on_hours_total_max), effects_per_running_hour=self.parameters.effects_per_running_hour, ) self.add(self.state_model) @@ -965,8 +965,8 @@ def __init__( label_of_element: Optional[str] = None, label: Optional[str] = None, label_full: Optional[str] = None, - total_max: Optional[Scalar] = None, - total_min: Optional[Scalar] = None, + total_max: Optional[ScenarioData] = None, + total_min: Optional[ScenarioData] = None, max_per_hour: Optional[TimestepData] = None, min_per_hour: Optional[TimestepData] = None, ): @@ -1009,8 +1009,8 @@ def do_modeling(self): if self._has_time_dim: self.total_per_timestep = self.add( self._model.add_variables( - lower=-np.inf if (self._min_per_hour is None) else self._min_per_hour * self._model.hours_per_step, - upper=np.inf if (self._max_per_hour is None) else self._max_per_hour * self._model.hours_per_step, + lower=-np.inf if (self._min_per_hour is None) else extract_data(self._min_per_hour) * self._model.hours_per_step, + upper=np.inf if (self._max_per_hour is None) else extract_data(self._max_per_hour) * self._model.hours_per_step, coords=self._model.get_coords(time_dim=True, scenario_dim=self._has_scenario_dim), name=f'{self.label_full}|total_per_timestep', ), diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py new file mode 100644 index 000000000..5b9105a68 --- /dev/null +++ b/tests/test_scenarios.py @@ -0,0 +1,333 @@ +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from linopy.testing import assert_linequal +import xarray as xr +import pytest +import flixopt as fx + +from flixopt.commons import Effect, FullCalculation, InvestParameters, Sink, Source, Storage, TimeSeriesData, solvers +from flixopt.elements import Bus, Flow +from flixopt.flow_system import FlowSystem + +from .conftest import create_linopy_model, create_calculation_and_solve + + +@pytest.fixture +def test_system(): + """Create a basic test system with scenarios.""" + # Create a two-day time index with hourly resolution + timesteps = pd.date_range( + "2023-01-01", periods=48, freq="h", name="time" + ) + + # Create two scenarios + scenarios = pd.Index(["Scenario A", "Scenario B"], name="scenario") + + # Create scenario weights as TimeSeriesData + # Using TimeSeriesData to avoid conversion issues + scenario_weights = TimeSeriesData(np.array([0.7, 0.3])) + + # Create a flow system with scenarios + flow_system = FlowSystem( + timesteps=timesteps, + scenarios=scenarios, + scenario_weights=scenario_weights # Use TimeSeriesData for weights + ) + + # Create demand profiles that differ between scenarios + # Scenario A: Higher demand in first day, lower in second day + # Scenario B: Lower demand in first day, higher in second day + demand_profile_a = np.concatenate([ + np.sin(np.linspace(0, 2*np.pi, 24)) * 5 + 10, # Day 1, max ~15 + np.sin(np.linspace(0, 2*np.pi, 24)) * 2 + 5 # Day 2, max ~7 + ]) + + demand_profile_b = np.concatenate([ + np.sin(np.linspace(0, 2*np.pi, 24)) * 2 + 5, # Day 1, max ~7 + np.sin(np.linspace(0, 2*np.pi, 24)) * 5 + 10 # Day 2, max ~15 + ]) + + # Stack the profiles into a 2D array (time, scenario) + demand_profiles = np.column_stack([demand_profile_a, demand_profile_b]) + + # Create the necessary model elements + # Create buses + electricity_bus = Bus("Electricity") + + # Create a demand sink with scenario-dependent profiles + demand = Flow( + label="Demand", + bus=electricity_bus.label_full, + fixed_relative_profile=demand_profiles + ) + demand_sink = Sink("Demand", sink=demand) + + # Create a power source with investment option + power_gen = Flow( + label="Generation", + bus=electricity_bus.label_full, + size=InvestParameters( + minimum_size=0, + maximum_size=20, + specific_effects={"Costs": 100} # €/kW + ), + effects_per_flow_hour={"Costs": 20} # €/MWh + ) + generator = Source("Generator", source=power_gen) + + # Create a storage for electricity + storage_charge = Flow( + label="Charge", + bus=electricity_bus.label_full, + size=10 + ) + storage_discharge = Flow( + label="Discharge", + bus=electricity_bus.label_full, + size=10 + ) + storage = Storage( + label="Battery", + charging=storage_charge, + discharging=storage_discharge, + capacity_in_flow_hours=InvestParameters( + minimum_size=0, + maximum_size=50, + specific_effects={"Costs": 50} # €/kWh + ), + eta_charge=0.95, + eta_discharge=0.95, + initial_charge_state="lastValueOfSim" + ) + + # Create effects and objective + cost_effect = Effect( + label="Costs", + unit="€", + description="Total costs", + is_standard=True, + is_objective=True + ) + + # Add all elements to the flow system + flow_system.add_elements( + electricity_bus, + generator, + demand_sink, + storage, + cost_effect + ) + + # Return the created system and its components + return { + "flow_system": flow_system, + "timesteps": timesteps, + "scenarios": scenarios, + "electricity_bus": electricity_bus, + "demand": demand, + "demand_sink": demand_sink, + "generator": generator, + "power_gen": power_gen, + "storage": storage, + "storage_charge": storage_charge, + "storage_discharge": storage_discharge, + "cost_effect": cost_effect + } + +@pytest.fixture +def flow_system_complex_scenarios() -> fx.FlowSystem: + """ + Helper method to create a base model with configurable parameters + """ + thermal_load = np.array([30, 0, 90, 110, 110, 20, 20, 20, 20]) + electrical_load = np.array([40, 40, 40, 40, 40, 40, 40, 40, 40]) + flow_system = fx.FlowSystem(pd.date_range('2020-01-01', periods=9, freq='h', name='time'), + pd.Index(['A', 'B', 'C'], name='scenario')) + # Define the components and flow_system + flow_system.add_elements( + fx.Effect('costs', '€', 'Kosten', is_standard=True, is_objective=True), + fx.Effect('CO2', 'kg', 'CO2_e-Emissionen', specific_share_to_other_effects_operation={'costs': 0.2}), + fx.Effect('PE', 'kWh_PE', 'Primärenergie', maximum_total=3.5e3), + fx.Bus('Strom'), + fx.Bus('Fernwärme'), + fx.Bus('Gas'), + fx.Sink('Wärmelast', sink=fx.Flow('Q_th_Last', 'Fernwärme', size=1, fixed_relative_profile=thermal_load)), + fx.Source( + 'Gastarif', source=fx.Flow('Q_Gas', 'Gas', size=1000, effects_per_flow_hour={'costs': 0.04, 'CO2': 0.3}) + ), + fx.Sink('Einspeisung', sink=fx.Flow('P_el', 'Strom', effects_per_flow_hour=-1 * electrical_load)), + ) + + boiler = fx.linear_converters.Boiler( + 'Kessel', + eta=0.5, + on_off_parameters=fx.OnOffParameters(effects_per_running_hour={'costs': 0, 'CO2': 1000}), + Q_th=fx.Flow( + 'Q_th', + bus='Fernwärme', + load_factor_max=1.0, + load_factor_min=0.1, + relative_minimum=5 / 50, + relative_maximum=1, + previous_flow_rate=50, + size=fx.InvestParameters( + fix_effects=1000, fixed_size=50, optional=False, specific_effects={'costs': 10, 'PE': 2} + ), + on_off_parameters=fx.OnOffParameters( + on_hours_total_min=0, + on_hours_total_max=1000, + consecutive_on_hours_max=10, + consecutive_on_hours_min=1, + consecutive_off_hours_max=10, + effects_per_switch_on=0.01, + switch_on_total_max=1000, + ), + flow_hours_total_max=1e6, + ), + Q_fu=fx.Flow('Q_fu', bus='Gas', size=200, relative_minimum=0, relative_maximum=1), + ) + + invest_speicher = fx.InvestParameters( + fix_effects=0, + piecewise_effects=fx.PiecewiseEffects( + piecewise_origin=fx.Piecewise([fx.Piece(5, 25), fx.Piece(25, 100)]), + piecewise_shares={ + 'costs': fx.Piecewise([fx.Piece(50, 250), fx.Piece(250, 800)]), + 'PE': fx.Piecewise([fx.Piece(5, 25), fx.Piece(25, 100)]), + }, + ), + optional=False, + specific_effects={'costs': 0.01, 'CO2': 0.01}, + minimum_size=0, + maximum_size=1000, + ) + speicher = fx.Storage( + 'Speicher', + charging=fx.Flow('Q_th_load', bus='Fernwärme', size=1e4), + discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1e4), + capacity_in_flow_hours=invest_speicher, + initial_charge_state=0, + maximal_final_charge_state=10, + eta_charge=0.9, + eta_discharge=1, + relative_loss_per_hour=0.08, + prevent_simultaneous_charge_and_discharge=True, + ) + + flow_system.add_elements(boiler, speicher) + + return flow_system + + +@pytest.fixture +def flow_system_piecewise_conversion_scenarios(flow_system_complex_scenarios) -> fx.FlowSystem: + """ + Use segments/Piecewise with numeric data + """ + flow_system = flow_system_complex_scenarios + + flow_system.add_elements( + fx.LinearConverter( + 'KWK', + inputs=[fx.Flow('Q_fu', bus='Gas')], + outputs=[ + fx.Flow('P_el', bus='Strom', size=60, relative_maximum=55, previous_flow_rate=10), + fx.Flow('Q_th', bus='Fernwärme'), + ], + piecewise_conversion=fx.PiecewiseConversion( + { + 'P_el': fx.Piecewise( + [ + fx.Piece(np.linspace(5, 6, len(flow_system.time_series_collection.timesteps)), 30), + fx.Piece(40, np.linspace(60, 70, len(flow_system.time_series_collection.timesteps))), + ] + ), + 'Q_th': fx.Piecewise([fx.Piece(6, 35), fx.Piece(45, 100)]), + 'Q_fu': fx.Piecewise([fx.Piece(12, 70), fx.Piece(90, 200)]), + } + ), + on_off_parameters=fx.OnOffParameters(effects_per_switch_on=0.01), + ) + ) + + return flow_system + + +def test_scenario_weights(flow_system_piecewise_conversion_scenarios): + """Test that scenario weights are correctly used in the model.""" + scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) + flow_system_piecewise_conversion_scenarios.scenario_weights = weights + model = create_linopy_model(flow_system_piecewise_conversion_scenarios) + np.testing.assert_allclose(model.scenario_weights.values, weights) + assert_linequal(model.objective.expression, + (model.variables['costs|total'] * weights).sum() + model.variables['Penalty|total']) + assert np.isclose(model.scenario_weights.sum().item(), 1.0) + +def test_scenario_dimensions_in_variables(flow_system_piecewise_conversion_scenarios): + """Test that all time variables are correctly broadcasted to scenario dimensions.""" + model = create_linopy_model(flow_system_piecewise_conversion_scenarios) + for var in model.variables: + assert model.variables[var].dims in [('time', 'scenario'), ('scenario',), ()] + +def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): + """Test a full optimization with scenarios and verify results.""" + scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) + flow_system_piecewise_conversion_scenarios.scenario_weights = weights + calc = create_calculation_and_solve(flow_system_piecewise_conversion_scenarios, + solver=fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60), + name='test_full_scenario') + calc.results.to_file() + + res = fx.results.CalculationResults.from_file('results', 'test_full_scenario') + fx.FlowSystem.from_dataset(res.flow_system) + calc = create_calculation_and_solve( + flow_system_piecewise_conversion_scenarios, + solver=fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60), + name='test_full_scenario', + ) + +@pytest.mark.slow +def test_io_persistance(flow_system_piecewise_conversion_scenarios): + """Test a full optimization with scenarios and verify results.""" + scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) + flow_system_piecewise_conversion_scenarios.scenario_weights = weights + calc = create_calculation_and_solve(flow_system_piecewise_conversion_scenarios, + solver=fx.solvers.HighsSolver(mip_gap=0.001, time_limit_seconds=60), + name='test_full_scenario') + calc.results.to_file() + + res = fx.results.CalculationResults.from_file('results', 'test_full_scenario') + flow_system_2 = fx.FlowSystem.from_dataset(res.flow_system) + calc_2 = create_calculation_and_solve( + flow_system_2, + solver=fx.solvers.HighsSolver(mip_gap=0.001, time_limit_seconds=60), + name='test_full_scenario_2', + ) + + np.testing.assert_allclose(calc.results.objective, calc_2.results.objective, rtol=0.001) + + +def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): + flow_system = flow_system_piecewise_conversion_scenarios + scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) + flow_system_piecewise_conversion_scenarios.scenario_weights = weights + calc = fx.FullCalculation(flow_system=flow_system_piecewise_conversion_scenarios, + selected_scenarios=flow_system.time_series_collection.scenarios[0:2], + name='test_full_scenario') + calc.do_modeling() + calc.solve(fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60)) + + calc.results.to_file() + flow_system_2 = fx.FlowSystem.from_dataset(calc.results.flow_system) + + assert calc.results.solution.indexes['scenario'].equals(flow_system.time_series_collection.scenarios[0:2]) + + assert flow_system_2.time_series_collection.scenarios.equals(flow_system.time_series_collection.scenarios[0:2]) + + np.testing.assert_allclose(flow_system_2.scenario_weights.selected_data.values, weights[0:2]) + diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index e2432e784..237935e59 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -91,7 +91,7 @@ def test_selection_methods(self, sample_timeseries, sample_timesteps): assert sample_timeseries.selected_data.equals(sample_timeseries.stored_data.sel(time=subset_index)) # Clear selection - sample_timeseries.clear_selection() + sample_timeseries.set_selection() assert sample_timeseries._selected_timesteps is None assert sample_timeseries.selected_data.equals(sample_timeseries.stored_data) @@ -408,7 +408,7 @@ def test_scenario_selection(self, sample_scenario_timeseries, sample_scenario_in ) # Clear selection - sample_scenario_timeseries.clear_selection(timesteps=False, scenarios=True) + sample_scenario_timeseries.set_selection() assert sample_scenario_timeseries._selected_scenarios is None def test_all_equal_with_scenarios(self, sample_timesteps, sample_scenario_index): @@ -561,7 +561,7 @@ def test_selection_propagation(self, sample_allocator, sample_timesteps): assert len(ts3._selected_timesteps) == len(subset_timesteps) + 1 # Clear selection - sample_allocator.clear_selection() + sample_allocator.set_selection() # Check selection cleared assert ts1._selected_timesteps is None @@ -673,7 +673,7 @@ def test_selection_propagation_with_scenarios( assert ts1.selected_data.shape == (2, 2) # 2 timesteps, 2 scenarios # Clear selections - sample_scenario_allocator.clear_selection() + sample_scenario_allocator.set_selection() assert ts1._selected_timesteps is None assert ts1.active_timesteps.equals(sample_scenario_allocator.timesteps) assert ts1._selected_scenarios is None From 9ea8fba7fc8c4d9d46f76391b8fd3deceb93bcf9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:43:04 +0200 Subject: [PATCH 025/183] ruff check --- flixopt/core.py | 2 +- tests/test_scenarios.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 850e01c04..eab45e239 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -1479,4 +1479,4 @@ def extract_data( return data if isinstance(data, (int, float, np.integer, np.floating)): return xr.DataArray(data) - raise TypeError(f'Unsupported data type: {type(data).__name__}') \ No newline at end of file + raise TypeError(f'Unsupported data type: {type(data).__name__}') diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 5b9105a68..62b0d3e29 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -1,16 +1,16 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd -from linopy.testing import assert_linequal -import xarray as xr import pytest -import flixopt as fx +import xarray as xr +from linopy.testing import assert_linequal +import flixopt as fx from flixopt.commons import Effect, FullCalculation, InvestParameters, Sink, Source, Storage, TimeSeriesData, solvers from flixopt.elements import Bus, Flow from flixopt.flow_system import FlowSystem -from .conftest import create_linopy_model, create_calculation_and_solve +from .conftest import create_calculation_and_solve, create_linopy_model @pytest.fixture From 7f4fddb942bda850f8cdbd25c09e8ccf34951011 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Apr 2025 22:40:23 +0200 Subject: [PATCH 026/183] Bugfix in _create_bounds_for_scenarios() --- flixopt/features.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flixopt/features.py b/flixopt/features.py index b8243d794..4eeb46337 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -194,8 +194,10 @@ def _create_bounds_for_defining_variable(self): # anmerkung: Glg bei Spezialfall relative_minimum = 0 redundant zu OnOff ?? def _create_bounds_for_scenarios(self): - if self.parameters.investment_scenarios == 'individual': - return + if isinstance(self.parameters.investment_scenarios, str): + if self.parameters.investment_scenarios == 'individual': + return + raise ValueError(f'Invalid value for investment_scenarios: {self.parameters.investment_scenarios}') if self.parameters.investment_scenarios is None: self.add( From e2192daba3f20349bbf4d16482068aa26385ee46 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Apr 2025 09:46:52 +0200 Subject: [PATCH 027/183] exclude super long test --- tests/test_scenarios.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 62b0d3e29..76da9e7ac 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -289,7 +289,7 @@ def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): name='test_full_scenario', ) -@pytest.mark.slow +@pytest.skip def test_io_persistance(flow_system_piecewise_conversion_scenarios): """Test a full optimization with scenarios and verify results.""" scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios From 4ad86d5a3f7bf1ca2b47d97ec82349726c88023a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Apr 2025 10:10:15 +0200 Subject: [PATCH 028/183] exclude super long test - fix --- tests/test_scenarios.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 76da9e7ac..89b5feced 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -289,7 +289,7 @@ def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): name='test_full_scenario', ) -@pytest.skip +@pytest.mark.skip(reason="This test is taking too long with highs and is too big for gurobipy free") def test_io_persistance(flow_system_piecewise_conversion_scenarios): """Test a full optimization with scenarios and verify results.""" scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios @@ -330,4 +330,3 @@ def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): assert flow_system_2.time_series_collection.scenarios.equals(flow_system.time_series_collection.scenarios[0:2]) np.testing.assert_allclose(flow_system_2.scenario_weights.selected_data.values, weights[0:2]) - From 967174c1f33224f2fb06b15d318fe4340ee90323 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 17 Apr 2025 15:01:19 +0200 Subject: [PATCH 029/183] Bugfix plot_node_balance_pie() --- flixopt/results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index ae54b9e2e..f5765286a 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -532,13 +532,13 @@ def plot_node_balance_pie( drop_suffix: Whether to drop the suffix from the variable names. """ inputs = sanitize_dataset( - ds=self.solution[self.inputs], + ds=self.solution[self.inputs] * self._calculation_results.hours_per_timestep, threshold=1e-5, drop_small_vars=True, zero_small_values=True, ) outputs = sanitize_dataset( - ds=self.solution[self.outputs], + ds=self.solution[self.outputs] * self._calculation_results.hours_per_timestep, threshold=1e-5, drop_small_vars=True, zero_small_values=True, From d24b5e77a57a39b767843880008e5d1a18653a4a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:28:29 +0200 Subject: [PATCH 030/183] Scenarios/fixes (#252) * BUGFIX missing conversion to TimeSeries * BUGFIX missing conversion to TimeSeries * Bugfix node_balance with flow_hours: Negate correctly --- flixopt/effects.py | 24 +++++++++++++++++++++++- flixopt/results.py | 12 ++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/flixopt/effects.py b/flixopt/effects.py index 2da561a36..3d93ee76a 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -97,11 +97,33 @@ def transform_data(self, flow_system: 'FlowSystem'): self.maximum_operation_per_hour = flow_system.create_time_series( f'{self.label_full}|maximum_operation_per_hour', self.maximum_operation_per_hour, flow_system ) - self.specific_share_to_other_effects_operation = flow_system.create_effect_time_series( f'{self.label_full}|operation->', self.specific_share_to_other_effects_operation, 'operation' ) + self.minimum_operation = flow_system.create_time_series( + f'{self.label_full}|minimum_operation', self.minimum_operation, has_time_dim=False + ) + self.maximum_operation = flow_system.create_time_series( + f'{self.label_full}|maximum_operation', self.maximum_operation, has_time_dim=False + ) + self.minimum_invest = flow_system.create_time_series( + f'{self.label_full}|minimum_invest', self.minimum_invest, has_time_dim=False + ) + self.maximum_invest = flow_system.create_time_series( + f'{self.label_full}|maximum_invest', self.maximum_invest, has_time_dim=False + ) + self.minimum_total = flow_system.create_time_series( + f'{self.label_full}|minimum_total', self.minimum_total, has_time_dim=False, + ) + self.maximum_total = flow_system.create_time_series( + f'{self.label_full}|maximum_total', self.maximum_total, has_time_dim=False + ) + self.specific_share_to_other_effects_invest = flow_system.create_effect_time_series( + f'{self.label_full}|invest->', self.specific_share_to_other_effects_invest, 'invest', + has_time_dim=False + ) + def create_model(self, model: SystemModel) -> 'EffectModel': self._plausibility_checks() self.model = EffectModel(model, self) diff --git a/flixopt/results.py b/flixopt/results.py index f5765286a..b57af1fb7 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -621,10 +621,8 @@ def node_balance( ds = self.solution[self.inputs + self.outputs] if drop_suffix: ds = ds.rename_vars({var: var.split('|flow_hours')[0] for var in ds.data_vars}) - if mode == 'flow_hours': - ds = ds * self._calculation_results.hours_per_timestep - ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) - return sanitize_dataset( + + ds = sanitize_dataset( ds=ds, threshold=threshold, timesteps=self._calculation_results.timesteps_extra if with_last_timestep else None, @@ -639,6 +637,12 @@ def node_balance( ), ) + if mode == 'flow_hours': + ds = ds * self._calculation_results.hours_per_timestep + ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) + + return ds + class BusResults(_NodeResults): """Results for a Bus""" From a3c7d472cbaf514a79eba4f655186908da19c765 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:28:49 +0200 Subject: [PATCH 031/183] Scenarios/filter (#253) * Add containts and startswith to filter_solution --- flixopt/results.py | 90 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 23 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index b57af1fb7..1be662975 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -202,6 +202,8 @@ def filter_solution( element: Optional[str] = None, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None, + contains: Optional[Union[str, List[str]]] = None, + startswith: Optional[Union[str, List[str]]] = None, ) -> xr.Dataset: """ Filter the solution to a specific variable dimension and element. @@ -223,12 +225,18 @@ def filter_solution( - pd.Index: Multiple scenarios - str/int: Single scenario (int is treated as a label, not an index position) Defaults to all available scenarios. + contains: Filter variables that contain this string or strings. + If a list is provided, variables must contain ALL strings in the list. + startswith: Filter variables that start with this string or strings. + If a list is provided, variables must start with ANY of the strings in the list. """ return filter_dataset( self.solution if element is None else self[element].solution, variable_dims=variable_dims, timesteps=timesteps, scenarios=scenarios, + contains=contains, + startswith=startswith, ) def plot_heatmap( @@ -393,6 +401,8 @@ def filter_solution( variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None, + contains: Optional[Union[str, List[str]]] = None, + startswith: Optional[Union[str, List[str]]] = None, ) -> xr.Dataset: """ Filter the solution to a specific variable dimension and element. @@ -413,12 +423,18 @@ def filter_solution( - pd.Index: Multiple scenarios - str/int: Single scenario (int is treated as a label, not an index position) Defaults to all available scenarios. + contains: Filter variables that contain this string or strings. + If a list is provided, variables must contain ALL strings in the list. + startswith: Filter variables that start with this string or strings. + If a list is provided, variables must start with ANY of the strings in the list. """ return filter_dataset( self.solution, variable_dims=variable_dims, timesteps=timesteps, scenarios=scenarios, + contains=contains, + startswith=startswith, ) @@ -1017,9 +1033,11 @@ def filter_dataset( variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, timesteps: Optional[Union[pd.DatetimeIndex, str, pd.Timestamp]] = None, scenarios: Optional[Union[pd.Index, str, int]] = None, + contains: Optional[Union[str, List[str]]] = None, + startswith: Optional[Union[str, List[str]]] = None, ) -> xr.Dataset: """ - Filters a dataset by its dimensions and optionally selects specific indexes. + Filters a dataset by its dimensions, indexes, and with string filters for variable names. Args: ds: The dataset to filter. @@ -1037,32 +1055,58 @@ def filter_dataset( - pd.Index: Multiple scenarios - str/int: Single scenario (int is treated as a label, not an index position) Defaults to all available scenarios. + contains: Filter variables that contain this string or strings. + If a list is provided, variables must contain ALL strings in the list. + startswith: Filter variables that start with this string or strings. + If a list is provided, variables must start with ANY of the strings in the list. Returns: Filtered dataset with specified variables and indexes. """ - # Return the full dataset if all dimension types are included - if variable_dims is None: - pass - elif variable_dims == 'scalar': - ds = ds[[v for v in ds.data_vars if not ds[v].dims]] - elif variable_dims == 'time': - ds = ds[[v for v in ds.data_vars if 'time' in ds[v].dims]] - elif variable_dims == 'scenario': - ds = ds[[v for v in ds.data_vars if 'scenario' in ds[v].dims]] - elif variable_dims == 'timeonly': - ds = ds[[v for v in ds.data_vars if ds[v].dims == ('time',)]] - elif variable_dims == 'scenarioonly': - ds = ds[[v for v in ds.data_vars if ds[v].dims == ('scenario',)]] - else: - raise ValueError(f'Unknown variable_dims "{variable_dims}" for filter_dataset') + # First filter by dimensions + filtered_ds = ds.copy() + if variable_dims is not None: + if variable_dims == 'scalar': + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if not filtered_ds[v].dims]] + elif variable_dims == 'time': + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if 'time' in filtered_ds[v].dims]] + elif variable_dims == 'scenario': + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if 'scenario' in filtered_ds[v].dims]] + elif variable_dims == 'timeonly': + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if filtered_ds[v].dims == ('time',)]] + elif variable_dims == 'scenarioonly': + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if filtered_ds[v].dims == ('scenario',)]] + else: + raise ValueError(f'Unknown variable_dims "{variable_dims}" for filter_dataset') + + # Filter by 'contains' parameter + if contains is not None: + if isinstance(contains, str): + # Single string - keep variables that contain this string + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if contains in v]] + elif isinstance(contains, list) and all(isinstance(s, str) for s in contains): + # List of strings - keep variables that contain ALL strings in the list + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if all(s in v for s in contains)]] + else: + raise TypeError(f"'contains' must be a string or list of strings, got {type(contains)}") + + # Filter by 'startswith' parameter + if startswith is not None: + if isinstance(startswith, str): + # Single string - keep variables that start with this string + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if v.startswith(startswith)]] + elif isinstance(startswith, list) and all(isinstance(s, str) for s in startswith): + # List of strings - keep variables that start with ANY of the strings in the list + filtered_ds = filtered_ds[[v for v in filtered_ds.data_vars if any(v.startswith(s) for s in startswith)]] + else: + raise TypeError(f"'startswith' must be a string or list of strings, got {type(startswith)}") # Handle time selection if needed - if timesteps is not None and 'time' in ds.dims: + if timesteps is not None and 'time' in filtered_ds.dims: try: - ds = ds.sel(time=timesteps) + filtered_ds = filtered_ds.sel(time=timesteps) except KeyError as e: - available_times = set(ds.indexes['time']) + available_times = set(filtered_ds.indexes['time']) requested_times = set([timesteps]) if not isinstance(timesteps, pd.Index) else set(timesteps) missing_times = requested_times - available_times raise ValueError( @@ -1070,15 +1114,15 @@ def filter_dataset( ) from e # Handle scenario selection if needed - if scenarios is not None and 'scenario' in ds.dims: + if scenarios is not None and 'scenario' in filtered_ds.dims: try: - ds = ds.sel(scenario=scenarios) + filtered_ds = filtered_ds.sel(scenario=scenarios) except KeyError as e: - available_scenarios = set(ds.indexes['scenario']) + available_scenarios = set(filtered_ds.indexes['scenario']) requested_scenarios = set([scenarios]) if not isinstance(scenarios, pd.Index) else set(scenarios) missing_scenarios = requested_scenarios - available_scenarios raise ValueError( f'Scenarios not found in dataset: {missing_scenarios}. Available scenarios: {available_scenarios}' ) from e - return ds + return filtered_ds From 0977c1fffd20383aab98a45d88af0703f448ba8c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:32:50 +0200 Subject: [PATCH 032/183] Scenarios/drop suffix (#251) Drop suffixes in plots and add the option to drop suffixes to sanitize_dataset() --- flixopt/results.py | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index 1be662975..3a4989672 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -532,7 +532,6 @@ def plot_node_balance_pie( show: bool = True, engine: plotting.PlottingEngine = 'plotly', scenario: Optional[Union[str, int]] = None, - drop_suffix: bool = True, ) -> plotly.graph_objects.Figure: """ Plots a pie chart of the flow hours of the inputs and outputs of buses or components. @@ -552,12 +551,14 @@ def plot_node_balance_pie( threshold=1e-5, drop_small_vars=True, zero_small_values=True, + drop_suffix='|', ) outputs = sanitize_dataset( ds=self.solution[self.outputs] * self._calculation_results.hours_per_timestep, threshold=1e-5, drop_small_vars=True, zero_small_values=True, + drop_suffix='|', ) inputs = inputs.sum('time') outputs = outputs.sum('time') @@ -570,13 +571,6 @@ def plot_node_balance_pie( outputs = outputs.sel(scenario=chosen_scenario).drop_vars('scenario') title = f'{title} - {chosen_scenario}' - if drop_suffix: - inputs = inputs.rename_vars({var: var.split('|flow_rate')[0] for var in inputs}) - outputs = outputs.rename_vars({var: var.split('|flow_rate')[0] for var in outputs}) - else: - inputs = inputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in inputs}) - outputs = outputs.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in outputs}) - if engine == 'plotly': figure_like = plotting.dual_pie_with_plotly( data_left=inputs.to_pandas(), @@ -635,10 +629,12 @@ def node_balance( drop_suffix: Whether to drop the suffix from the variable names. """ ds = self.solution[self.inputs + self.outputs] - if drop_suffix: - ds = ds.rename_vars({var: var.split('|flow_hours')[0] for var in ds.data_vars}) - ds = sanitize_dataset( + if mode == 'flow_hours': + ds = ds * self._calculation_results.hours_per_timestep + ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) + + return sanitize_dataset( ds=ds, threshold=threshold, timesteps=self._calculation_results.timesteps_extra if with_last_timestep else None, @@ -651,14 +647,9 @@ def node_balance( if negate_inputs else None ), + drop_suffix='|' if drop_suffix else None, ) - if mode == 'flow_hours': - ds = ds * self._calculation_results.hours_per_timestep - ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) - - return ds - class BusResults(_NodeResults): """Results for a Bus""" @@ -976,6 +967,7 @@ def sanitize_dataset( negate: Optional[List[str]] = None, drop_small_vars: bool = True, zero_small_values: bool = False, + drop_suffix: Optional[str] = None, ) -> xr.Dataset: """ Sanitizes a dataset by handling small values (dropping or zeroing) and optionally reindexing the time axis. @@ -987,6 +979,7 @@ def sanitize_dataset( negate: The variables to negate. If None, no variables are negated. drop_small_vars: If True, drops variables where all values are below threshold. zero_small_values: If True, sets values below threshold to zero. + drop_suffix: Drop suffix of data var names. Split by the provided str. Returns: xr.Dataset: The sanitized dataset. @@ -1025,6 +1018,20 @@ def sanitize_dataset( if timesteps is not None and not ds.indexes['time'].equals(timesteps): ds = ds.reindex({'time': timesteps}, fill_value=np.nan) + if drop_suffix is not None: + if not isinstance(drop_suffix, str): + raise ValueError(f'Only pass str values to drop suffixes. Got {drop_suffix}') + unique_dict = {} + for var in ds.data_vars: + new_name = var.split(drop_suffix)[0] + + # If name already exists, keep original name + if new_name in unique_dict.values(): + unique_dict[var] = var + else: + unique_dict[var] = new_name + ds = ds.rename(unique_dict) + return ds From 5cf6e0eb070f7dc2d318d1050b04dc7e37e86989 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:05:35 +0200 Subject: [PATCH 033/183] Scenarios/bar plot (#254) * Add stacked bar style to plotting methods * Rename mode to style (line, bar, area, ...) --- .../example_calculation_types.py | 8 +-- examples/04_Scenarios/scenario_example.py | 4 +- flixopt/plotting.py | 34 ++++++------- flixopt/results.py | 50 +++++++++++-------- 4 files changed, 54 insertions(+), 42 deletions(-) diff --git a/examples/03_Calculation_types/example_calculation_types.py b/examples/03_Calculation_types/example_calculation_types.py index 97b18e3c0..3a15b4e28 100644 --- a/examples/03_Calculation_types/example_calculation_types.py +++ b/examples/03_Calculation_types/example_calculation_types.py @@ -194,28 +194,28 @@ def get_solutions(calcs: List, variable: str) -> xr.Dataset: # --- Plotting for comparison --- fx.plotting.with_plotly( get_solutions(calculations, 'Speicher|charge_state').to_dataframe(), - mode='line', + style='line', title='Charge State Comparison', ylabel='Charge state', ).write_html('results/Charge State.html') fx.plotting.with_plotly( get_solutions(calculations, 'BHKW2(Q_th)|flow_rate').to_dataframe(), - mode='line', + style='line', title='BHKW2(Q_th) Flow Rate Comparison', ylabel='Flow rate', ).write_html('results/BHKW2 Thermal Power.html') fx.plotting.with_plotly( get_solutions(calculations, 'costs(operation)|total_per_timestep').to_dataframe(), - mode='line', + style='line', title='Operation Cost Comparison', ylabel='Costs [€]', ).write_html('results/Operation Costs.html') fx.plotting.with_plotly( pd.DataFrame(get_solutions(calculations, 'costs(operation)|total_per_timestep').to_dataframe().sum()).T, - mode='bar', + style='bar', title='Total Cost Comparison', ylabel='Costs [€]', ).update_layout(barmode='group').write_html('results/Total Costs.html') diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index 3edb6e7c0..b9932a016 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -111,13 +111,15 @@ # --- Analyze Results --- calculation.results['Fernwärme'].plot_node_balance_pie() - calculation.results['Fernwärme'].plot_node_balance() + calculation.results['Fernwärme'].plot_node_balance(style='stacked_bar') calculation.results['Storage'].plot_node_balance() calculation.results.plot_heatmap('CHP(Q_th)|flow_rate') # Convert the results for the storage component to a dataframe and display df = calculation.results['Storage'].node_balance_with_charge_state() print(df) + calculation.results['Storage'].plot_charge_state(engine='matplotlib') # Save results to file for later usage calculation.results.to_file() + fig, ax = calculation.results['Storage'].plot_charge_state(engine='matplotlib') diff --git a/flixopt/plotting.py b/flixopt/plotting.py index e4c440aaf..9ea19a686 100644 --- a/flixopt/plotting.py +++ b/flixopt/plotting.py @@ -209,7 +209,7 @@ def process_colors( def with_plotly( data: pd.DataFrame, - mode: Literal['bar', 'line', 'area'] = 'area', + style: Literal['stacked_bar', 'line', 'area'] = 'area', colors: ColorType = 'viridis', title: str = '', ylabel: str = '', @@ -222,7 +222,7 @@ def with_plotly( Args: data: A DataFrame containing the data to plot, where the index represents time (e.g., hours), and each column represents a separate data series. - mode: The plotting mode. Use 'bar' for stacked bar charts, 'line' for stepped lines, + style: The plotting style. Use 'stacked_bar' for stacked bar charts, 'line' for stepped lines, or 'area' for stacked area charts. colors: Color specification, can be: - A string with a colorscale name (e.g., 'viridis', 'plasma') @@ -235,7 +235,7 @@ def with_plotly( Returns: A Plotly figure object containing the generated plot. """ - assert mode in ['bar', 'line', 'area'], f"'mode' must be one of {['bar', 'line', 'area']}" + assert style in ['stacked_bar', 'line', 'area'], f"'style' must be one of {['stacked_bar', 'line', 'area']}" if data.empty: return go.Figure() @@ -243,7 +243,7 @@ def with_plotly( fig = fig if fig is not None else go.Figure() - if mode == 'bar': + if style == 'stacked_bar': for i, column in enumerate(data.columns): fig.add_trace( go.Bar( @@ -255,22 +255,22 @@ def with_plotly( ) fig.update_layout( - barmode='relative' if mode == 'bar' else None, + barmode='relative' if style == 'stacked_bar' else None, bargap=0, # No space between bars bargroupgap=0, # No space between groups of bars ) - elif mode == 'line': + elif style == 'line': for i, column in enumerate(data.columns): fig.add_trace( go.Scatter( x=data.index, y=data[column], - mode='lines', + style='lines', name=column, line=dict(shape='hv', color=processed_colors[i]), ) ) - elif mode == 'area': + elif style == 'area': data = data.copy() data[(data > -1e-5) & (data < 1e-5)] = 0 # Preventing issues with plotting # Split columns into positive, negative, and mixed categories @@ -293,7 +293,7 @@ def with_plotly( go.Scatter( x=data.index, y=data[column], - mode='lines', + style='lines', name=column, line=dict(shape='hv', color=colors_stacked[column]), fill='tonexty', @@ -306,7 +306,7 @@ def with_plotly( go.Scatter( x=data.index, y=data[column], - mode='lines', + style='lines', name=column, line=dict(shape='hv', color=colors_stacked[column], dash='dash'), ) @@ -345,7 +345,7 @@ def with_plotly( def with_matplotlib( data: pd.DataFrame, - mode: Literal['bar', 'line'] = 'bar', + style: Literal['stacked_bar', 'line'] = 'stacked_bar', colors: ColorType = 'viridis', title: str = '', ylabel: str = '', @@ -360,7 +360,7 @@ def with_matplotlib( Args: data: A DataFrame containing the data to plot. The index should represent time (e.g., hours), and each column represents a separate data series. - mode: Plotting mode. Use 'bar' for stacked bar charts or 'line' for stepped lines. + style: Plotting style. Use 'stacked_bar' for stacked bar charts or 'line' for stepped lines. colors: Color specification, can be: - A string with a colormap name (e.g., 'viridis', 'plasma') - A list of color strings (e.g., ['#ff0000', '#00ff00']) @@ -376,19 +376,19 @@ def with_matplotlib( A tuple containing the Matplotlib figure and axes objects used for the plot. Notes: - - If `mode` is 'bar', bars are stacked for both positive and negative values. + - If `style` is 'stacked_bar', bars are stacked for both positive and negative values. Negative values are stacked separately without extra labels in the legend. - - If `mode` is 'line', stepped lines are drawn for each data series. + - If `style` is 'line', stepped lines are drawn for each data series. - The legend is placed below the plot to accommodate multiple data series. """ - assert mode in ['bar', 'line'], f"'mode' must be one of {['bar', 'line']} for matplotlib" + assert style in ['stacked_bar', 'line'], f"'style' must be one of {['stacked_bar', 'line']} for matplotlib" if fig is None or ax is None: fig, ax = plt.subplots(figsize=figsize) processed_colors = ColorProcessor(engine='matplotlib').process_colors(colors, list(data.columns)) - if mode == 'bar': + if style == 'stacked_bar': cumulative_positive = np.zeros(len(data)) cumulative_negative = np.zeros(len(data)) width = data.index.to_series().diff().dropna().min() # Minimum time difference @@ -419,7 +419,7 @@ def with_matplotlib( ) cumulative_negative += negative_values.values - elif mode == 'line': + elif style == 'line': for i, column in enumerate(data.columns): ax.step(data.index, data[column], where='post', color=processed_colors[i], label=column) diff --git a/flixopt/results.py b/flixopt/results.py index 3a4989672..47c47f288 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -471,6 +471,7 @@ def plot_node_balance( engine: plotting.PlottingEngine = 'plotly', scenario: Optional[Union[str, int]] = None, mode: Literal['flow_rate', 'flow_hours'] = 'flow_rate', + style: Literal['area', 'stacked_bar', 'line'] = 'stacked_bar', drop_suffix: bool = True, ) -> Union[plotly.graph_objs.Figure, Tuple[plt.Figure, plt.Axes]]: """ @@ -499,7 +500,7 @@ def plot_node_balance( figure_like = plotting.with_plotly( ds.to_dataframe(), colors=colors, - mode='area', + style=style, title=title, ) default_filetype = '.html' @@ -507,7 +508,7 @@ def plot_node_balance( figure_like = plotting.with_matplotlib( ds.to_dataframe(), colors=colors, - mode='bar', + style=style, title=title, ) default_filetype = '.png' @@ -679,6 +680,7 @@ def plot_charge_state( show: bool = True, colors: plotting.ColorType = 'viridis', engine: plotting.PlottingEngine = 'plotly', + style: Literal['area', 'stacked_bar', 'line'] = 'stacked_bar', scenario: Optional[Union[str, int]] = None, ) -> plotly.graph_objs.Figure: """ @@ -688,16 +690,12 @@ def plot_charge_state( show: Whether to show the plot or not. colors: The c engine: Plotting engine to use. Only 'plotly' is implemented atm. + style: The plotting mode for the flow_rate scenario: The scenario to plot. Defaults to the first scenario. Has no effect without scenarios present Raises: ValueError: If the Component is not a Storage. """ - if engine != 'plotly': - raise NotImplementedError( - f'Plotting engine "{engine}" not implemented for ComponentResults.plot_charge_state.' - ) - if not self.is_storage: raise ValueError(f'Cant plot charge_state. "{self.label}" is not a storage') @@ -710,22 +708,34 @@ def plot_charge_state( ds = ds.sel(scenario=chosen_scenario).drop_vars('scenario') charge_state = charge_state.sel(scenario=chosen_scenario).drop_vars('scenario') scenario_suffix = f'--{chosen_scenario}' + if engine == 'plotly': + fig = plotting.with_plotly( + ds.to_dataframe(), + colors=colors, + style=style, + title=f'Operation Balance of {self.label}{scenario_suffix}', + ) - fig = plotting.with_plotly( - ds.to_dataframe(), - colors=colors, - mode='area', - title=f'Operation Balance of {self.label}{scenario_suffix}', - ) - - # TODO: Use colors for charge state? + # TODO: Use colors for charge state? - charge_state = charge_state.to_dataframe() - fig.add_trace( - plotly.graph_objs.Scatter( - x=charge_state.index, y=charge_state.values.flatten(), mode='lines', name=self._charge_state + charge_state = charge_state.to_dataframe() + fig.add_trace( + plotly.graph_objs.Scatter( + x=charge_state.index, y=charge_state.values.flatten(), mode='lines', name=self._charge_state + ) ) - ) + elif engine=='matplotlib': + fig, ax = plotting.with_matplotlib( + ds.to_dataframe(), + colors=colors, + style=style, + title=f'Operation Balance of {self.label}{scenario_suffix}', + ) + + charge_state = charge_state.to_dataframe() + ax.plot(charge_state.index, charge_state.values.flatten(), label=self._charge_state) + fig.tight_layout() + fig = fig, ax return plotting.export_figure( fig, From 4cfa27fcf8e0a29e015759c23c8ee98db5a248f4 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:18:43 +0200 Subject: [PATCH 034/183] Bugfix plotting --- flixopt/plotting.py | 6 +++--- tests/test_plots.py | 8 ++++---- tests/test_results_plots.py | 7 +------ 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/flixopt/plotting.py b/flixopt/plotting.py index 9ea19a686..d5b4aef0d 100644 --- a/flixopt/plotting.py +++ b/flixopt/plotting.py @@ -265,7 +265,7 @@ def with_plotly( go.Scatter( x=data.index, y=data[column], - style='lines', + mode='lines', name=column, line=dict(shape='hv', color=processed_colors[i]), ) @@ -293,7 +293,7 @@ def with_plotly( go.Scatter( x=data.index, y=data[column], - style='lines', + mode='lines', name=column, line=dict(shape='hv', color=colors_stacked[column]), fill='tonexty', @@ -306,7 +306,7 @@ def with_plotly( go.Scatter( x=data.index, y=data[column], - style='lines', + mode='lines', name=column, line=dict(shape='hv', color=colors_stacked[column], dash='dash'), ) diff --git a/tests/test_plots.py b/tests/test_plots.py index 840b4e7b3..4e00f9a51 100644 --- a/tests/test_plots.py +++ b/tests/test_plots.py @@ -53,15 +53,15 @@ def get_sample_data( def test_bar_plots(self): data = self.get_sample_data(nr_of_columns=10, nr_of_periods=1, time_steps_per_period=24) - plotly.offline.plot(plotting.with_plotly(data, 'bar')) - plotting.with_matplotlib(data, 'bar') + plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar')) + plotting.with_matplotlib(data, 'stacked_bar') plt.show() data = self.get_sample_data( nr_of_columns=10, nr_of_periods=5, time_steps_per_period=24, drop_fraction_of_indices=0.3 ) - plotly.offline.plot(plotting.with_plotly(data, 'bar')) - plotting.with_matplotlib(data, 'bar') + plotly.offline.plot(plotting.with_plotly(data, 'stacked_bar')) + plotting.with_matplotlib(data, 'stacked_bar') plt.show() def test_line_plots(self): diff --git a/tests/test_results_plots.py b/tests/test_results_plots.py index 855944a48..fe8d27c3b 100644 --- a/tests/test_results_plots.py +++ b/tests/test_results_plots.py @@ -58,12 +58,7 @@ def test_results_plots(flow_system, plotting_engine, show, save, color_spec): ) results['Speicher'].plot_node_balance_pie(engine=plotting_engine, save=save, show=show, colors=color_spec) - - if plotting_engine == 'matplotlib': - with pytest.raises(NotImplementedError): - results['Speicher'].plot_charge_state(engine=plotting_engine) - else: - results['Speicher'].plot_charge_state(engine=plotting_engine) + results['Speicher'].plot_charge_state(engine=plotting_engine) plt.close('all') From 16fd74c306bb19d2788ea0b5a4cc2bfe87c66a8e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:45:11 +0200 Subject: [PATCH 035/183] Fix example_calculation_types.py --- examples/03_Calculation_types/example_calculation_types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/03_Calculation_types/example_calculation_types.py b/examples/03_Calculation_types/example_calculation_types.py index 3a15b4e28..e9f5604f9 100644 --- a/examples/03_Calculation_types/example_calculation_types.py +++ b/examples/03_Calculation_types/example_calculation_types.py @@ -215,13 +215,13 @@ def get_solutions(calcs: List, variable: str) -> xr.Dataset: fx.plotting.with_plotly( pd.DataFrame(get_solutions(calculations, 'costs(operation)|total_per_timestep').to_dataframe().sum()).T, - style='bar', + style='stacked_bar', title='Total Cost Comparison', ylabel='Costs [€]', ).update_layout(barmode='group').write_html('results/Total Costs.html') fx.plotting.with_plotly( - pd.DataFrame([calc.durations for calc in calculations], index=[calc.name for calc in calculations]), 'bar' + pd.DataFrame([calc.durations for calc in calculations], index=[calc.name for calc in calculations]), 'stacked_bar' ).update_layout(title='Duration Comparison', xaxis_title='Calculation type', yaxis_title='Time (s)').write_html( 'results/Speed Comparison.html' ) From 67d1716a6b7280373a1c4171471f042b4f7ca984 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 24 Apr 2025 20:45:03 +0200 Subject: [PATCH 036/183] Scenarios/fixes (#255) * Fix indexing issue with only one scenario * Bugfix Cooling Tower * Add option for balanced Storage Flows (equalize size of charging and discharging) * Add option for balanced Storage Flows * Change error to warning (non-fixed size with piecewise conversion AND fixed_flow_rate with OnOff) * Bugfix in DataConverter * BUGFIX: Typo (total_max/total_min in Effect) * Bugfix in node_balance() (negating did not work when using flow_hours mode --- flixopt/components.py | 28 ++++++++++++++++++++++++++-- flixopt/core.py | 5 ++++- flixopt/elements.py | 7 +++---- flixopt/features.py | 7 +------ flixopt/linear_converters.py | 6 +++--- flixopt/results.py | 12 +++++++----- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 1b745b54c..234418694 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -86,8 +86,8 @@ def _plausibility_checks(self) -> None: if self.piecewise_conversion: for flow in self.flows.values(): if isinstance(flow.size, InvestParameters) and flow.size.fixed_size is None: - raise PlausibilityError( - f'piecewise_conversion (in {self.label_full}) and variable size ' + logger.warning( + f'Piecewise_conversion (in {self.label_full}) and variable size ' f'(in flow {flow.label_full}) do not make sense together!' ) @@ -138,6 +138,7 @@ def __init__( eta_discharge: TimestepData = 1, relative_loss_per_hour: TimestepData = 0, prevent_simultaneous_charge_and_discharge: bool = True, + balanced: bool = False, meta_data: Optional[Dict] = None, ): """ @@ -163,6 +164,7 @@ def __init__( relative_loss_per_hour: loss per chargeState-Unit per hour. The default is 0. prevent_simultaneous_charge_and_discharge: If True, loading and unloading at the same time is not possible. Increases the number of binary variables, but is recommended for easier evaluation. The default is True. + balanced: Wether to equate the size of the charging and discharging flow. Only if not fixed. meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types. """ # TODO: fixed_relative_chargeState implementieren @@ -188,6 +190,7 @@ def __init__( self.eta_discharge: TimestepData = eta_discharge self.relative_loss_per_hour: TimestepData = relative_loss_per_hour self.prevent_simultaneous_charge_and_discharge = prevent_simultaneous_charge_and_discharge + self.balanced = balanced def create_model(self, model: SystemModel) -> 'StorageModel': self._plausibility_checks() @@ -261,6 +264,18 @@ def _plausibility_checks(self) -> None: f'is below allowed minimum charge_state {minimum_inital_capacity}' ) + if self.balanced: + if not isinstance(self.charging.size, InvestParameters) or not isinstance(self.discharging.size, InvestParameters): + raise PlausibilityError( + f'Balancing charging and discharging Flows in {self.label_full} ' + f'is only possible with Investments.') + if (self.charging.size.minimum_size > self.discharging.size.maximum_size or + self.charging.size.maximum_size < self.discharging.size.minimum_size): + raise PlausibilityError( + f'Balancing charging and discharging Flows in {self.label_full} need compatible minimum and maximum sizes.' + f'Got: {self.charging.size.minimum_size=}, {self.charging.size.maximum_size=} and ' + f'{self.charging.size.minimum_size=}, {self.charging.size.maximum_size=}.') + @register_class_for_io class Transmission(Component): @@ -531,6 +546,15 @@ def do_modeling(self): # Initial charge state self._initial_and_final_charge_state() + if self.element.balanced: + self.add( + self._model.add_constraints( + self.element.charging.model._investment.size * 1 == self.element.discharging.model._investment.size * 1, + name=f'{self.label_full}|balanced_sizes', + ), + 'balanced_sizes' + ) + def _initial_and_final_charge_state(self): if self.element.initial_charge_state is not None: name_short = 'initial_charge_state' diff --git a/flixopt/core.py b/flixopt/core.py index eab45e239..5d24e46e4 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -251,8 +251,11 @@ def _broadcast_time_to_scenarios( if not np.array_equal(data.coords['time'].values, coords['time'].values): raise ConversionError("Source time coordinates don't match target time coordinates") + if len(coords['scenario']) <= 1: + return data.copy(deep=True) + # Broadcast values - values = np.tile(data.values, (len(coords['scenario']), 1)) + values = np.tile(data.values, (len(coords['scenario']), 1)).T # Tile seems to be faster than repeat() return xr.DataArray(values.copy(), coords=coords, dims=dims) @staticmethod diff --git a/flixopt/elements.py b/flixopt/elements.py index 2ff49567e..7dda3e9cf 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -292,10 +292,9 @@ def _plausibility_checks(self) -> None: ) if self.fixed_relative_profile is not None and self.on_off_parameters is not None: - raise ValueError( - f'Flow {self.label} has both a fixed_relative_profile and an on_off_parameters. This is not supported. ' - f'Use relative_minimum and relative_maximum instead, ' - f'if you want to allow flows to be switched on and off.' + logger.warning( + f'Flow {self.label} has both a fixed_relative_profile and an on_off_parameters.' + f'This will allow the flow to be switched on and off, effectively differing from the fixed_flow_rate.' ) if (self.relative_minimum > 0).any() and self.on_off_parameters is None: diff --git a/flixopt/features.py b/flixopt/features.py index 4eeb46337..94231ccec 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -154,11 +154,6 @@ def _create_bounds_for_defining_variable(self): ), f'fix_{variable.name}', ) - if self._on_variable is not None: - raise ValueError( - f'Flow {self.label_full} has a fixed relative flow rate and an on_variable.' - f'This combination is currently not supported.' - ) return # eq: defining_variable(t) <= size * upper_bound(t) @@ -988,7 +983,7 @@ def __init__( # Parameters self._has_time_dim = has_time_dim self._has_scenario_dim = has_scenario_dim - self._total_max = total_max if total_min is not None else np.inf + self._total_max = total_max if total_max is not None else np.inf self._total_min = total_min if total_min is not None else -np.inf self._max_per_hour = max_per_hour if max_per_hour is not None else np.inf self._min_per_hour = min_per_hour if min_per_hour is not None else -np.inf diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 3fd032632..b096921f0 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -165,7 +165,7 @@ def __init__( label, inputs=[P_el, Q_th], outputs=[], - conversion_factors=[{P_el.label: 1, Q_th.label: -specific_electricity_demand}], + conversion_factors=[{P_el.label: -1, Q_th.label: specific_electricity_demand}], on_off_parameters=on_off_parameters, meta_data=meta_data, ) @@ -177,12 +177,12 @@ def __init__( @property def specific_electricity_demand(self): - return -self.conversion_factors[0][self.Q_th.label] + return self.conversion_factors[0][self.Q_th.label] @specific_electricity_demand.setter def specific_electricity_demand(self, value): check_bounds(value, 'specific_electricity_demand', self.label_full, 0, 1) - self.conversion_factors[0][self.Q_th.label] = -value + self.conversion_factors[0][self.Q_th.label] = value @register_class_for_io diff --git a/flixopt/results.py b/flixopt/results.py index 47c47f288..4f2c7d856 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -631,11 +631,7 @@ def node_balance( """ ds = self.solution[self.inputs + self.outputs] - if mode == 'flow_hours': - ds = ds * self._calculation_results.hours_per_timestep - ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) - - return sanitize_dataset( + ds = sanitize_dataset( ds=ds, threshold=threshold, timesteps=self._calculation_results.timesteps_extra if with_last_timestep else None, @@ -651,6 +647,12 @@ def node_balance( drop_suffix='|' if drop_suffix else None, ) + if mode == 'flow_hours': + ds = ds * self._calculation_results.hours_per_timestep + ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) + + return ds + class BusResults(_NodeResults): """Results for a Bus""" From b96802757cd4019ae858c242f2331e925129942f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 25 Apr 2025 12:32:31 +0200 Subject: [PATCH 037/183] Scenarios/effects (#256) * Add methods to track effect shares of components and Flows * Add option to include flows when retrieving effects * Add properties and methods to store effect results in a dataset * Reorder methods * Rename and improve docs * Bugfix test class name * Fix the Network algorithm to calculate the sum of parallel paths, and be independent on nr of nodes and complexity of the network * Add tests for the newtork chaining and the results of effect shares * Add methods to check for circular references * Add test to check for circular references * Update cycle checker to return the found cycles * Add checks in results to confirm effects are computed correctly * BUGFIX: Remove +1 from prior testing * Add option for grouped bars to plotting.with_plotly() and make lines of stacked bar plots invisible * Reconstruct FlowSystem in CalculationResults on demand. DEPRECATION in CalculationResults * ruff check * Bugfix: save flow_system data, not the flow_system * Update tests --- flixopt/effects.py | 197 +++++++++++++++++++-- flixopt/plotting.py | 30 +++- flixopt/results.py | 219 +++++++++++++++++++++-- tests/test_cycle_detection.py | 226 +++++++++++++++++++++++ tests/test_effect.py | 86 ++++++++- tests/test_effects_shares_summation.py | 236 +++++++++++++++++++++++++ tests/test_scenarios.py | 6 +- 7 files changed, 956 insertions(+), 44 deletions(-) create mode 100644 tests/test_cycle_detection.py create mode 100644 tests/test_effects_shares_summation.py diff --git a/flixopt/effects.py b/flixopt/effects.py index 3d93ee76a..914100362 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -7,10 +7,11 @@ import logging import warnings -from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union +from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Set, Tuple, Union import linopy import numpy as np +import xarray as xr from .core import NumericDataTS, ScenarioData, TimeSeries, TimeSeriesCollection, TimestepData, extract_data from .features import ShareAllocationModel @@ -268,26 +269,18 @@ def get_effect_label(eff: Union[Effect, str]) -> str: def _plausibility_checks(self) -> None: # Check circular loops in effects: - # TODO: Improve checks!! Only most basic case covered... + operation, invest = self.calculate_effect_share_factors() - def error_str(effect_label: str, share_ffect_label: str): - return ( - f' {effect_label} -> has share in: {share_ffect_label}\n' - f' {share_ffect_label} -> has share in: {effect_label}' - ) + operation_cycles = detect_cycles(tuples_to_adjacency_list([key for key in operation])) + invest_cycles = detect_cycles(tuples_to_adjacency_list([key for key in invest])) - for effect in self.effects.values(): - # Effekt darf nicht selber als Share in seinen ShareEffekten auftauchen: - # operation: - for target_effect in effect.specific_share_to_other_effects_operation.keys(): - assert effect not in self[target_effect].specific_share_to_other_effects_operation.keys(), ( - f'Error: circular operation-shares \n{error_str(target_effect.label, target_effect.label)}' - ) - # invest: - for target_effect in effect.specific_share_to_other_effects_invest.keys(): - assert effect not in self[target_effect].specific_share_to_other_effects_invest.keys(), ( - f'Error: circular invest-shares \n{error_str(target_effect.label, target_effect.label)}' - ) + if operation_cycles: + cycle_str = "\n".join([" -> ".join(cycle) for cycle in operation_cycles]) + raise ValueError(f'Error: circular operation-shares detected:\n{cycle_str}') + + if invest_cycles: + cycle_str = "\n".join([" -> ".join(cycle) for cycle in invest_cycles]) + raise ValueError(f'Error: circular invest-shares detected:\n{cycle_str}') def __getitem__(self, effect: Union[str, Effect]) -> 'Effect': """ @@ -351,6 +344,30 @@ def objective_effect(self, value: Effect) -> None: raise ValueError(f'An objective-effect already exists! ({self._objective_effect.label=})') self._objective_effect = value + def calculate_effect_share_factors(self) -> Tuple[ + Dict[Tuple[str, str], xr.DataArray], + Dict[Tuple[str, str], xr.DataArray], + ]: + shares_invest = {} + for name, effect in self.effects.items(): + if effect.specific_share_to_other_effects_invest: + shares_invest[name] = { + target: extract_data(data) + for target, data in effect.specific_share_to_other_effects_invest.items() + } + shares_invest = calculate_all_conversion_paths(shares_invest) + + shares_operation = {} + for name, effect in self.effects.items(): + if effect.specific_share_to_other_effects_operation: + shares_operation[name] = { + target: extract_data(data) + for target, data in effect.specific_share_to_other_effects_operation.items() + } + shares_operation = calculate_all_conversion_paths(shares_operation) + + return shares_operation, shares_invest + class EffectCollectionModel(Model): """ @@ -425,3 +442,145 @@ def _add_share_between_effects(self): has_time_dim=False, has_scenario_dim=True, ) + + +def calculate_all_conversion_paths( + conversion_dict: Dict[str, Dict[str, xr.DataArray]], +) -> Dict[Tuple[str, str], xr.DataArray]: + """ + Calculates all possible direct and indirect conversion factors between units/domains. + This function uses Breadth-First Search (BFS) to find all possible conversion paths + between different units or domains in a conversion graph. It computes both direct + conversions (explicitly provided in the input) and indirect conversions (derived + through intermediate units). + Args: + conversion_dict: A nested dictionary where: + - Outer keys represent origin units/domains + - Inner dictionaries map target units/domains to their conversion factors + - Conversion factors can be integers, floats, or numpy arrays + Returns: + A dictionary mapping (origin, target) tuples to their respective conversion factors. + Each key is a tuple of strings representing the origin and target units/domains. + Each value is the conversion factor (int, float, or numpy array) from origin to target. + """ + # Initialize the result dictionary to accumulate all paths + result = {} + + # Add direct connections to the result first + for origin, targets in conversion_dict.items(): + for target, factor in targets.items(): + result[(origin, target)] = factor + + # Track all paths by keeping path history to avoid cycles + # Iterate over each domain in the dictionary + for origin in conversion_dict: + # Keep track of visited paths to avoid repeating calculations + processed_paths = set() + # Use a queue with (current_domain, factor, path_history) + queue = [(origin, 1, [origin])] + + while queue: + current_domain, factor, path = queue.pop(0) + + # Skip if we've processed this exact path before + path_key = tuple(path) + if path_key in processed_paths: + continue + processed_paths.add(path_key) + + # Iterate over the neighbors of the current domain + for target, conversion_factor in conversion_dict.get(current_domain, {}).items(): + # Skip if target would create a cycle + if target in path: + continue + + # Calculate the indirect conversion factor + indirect_factor = factor * conversion_factor + new_path = path + [target] + + # Only consider paths starting at origin and ending at some target + if len(new_path) > 2 and new_path[0] == origin: + # Update the result dictionary - accumulate factors from different paths + if (origin, target) in result: + result[(origin, target)] = result[(origin, target)] + indirect_factor + else: + result[(origin, target)] = indirect_factor + + # Add new path to queue for further exploration + queue.append((target, indirect_factor, new_path)) + + return result + + +def detect_cycles(graph: Dict[str, List[str]]) -> List[List[str]]: + """ + Detects cycles in a directed graph using DFS. + + Args: + graph: Adjacency list representation of the graph + + Returns: + List of cycles found, where each cycle is a list of nodes + """ + # Track nodes in current recursion stack + visiting = set() + # Track nodes that have been fully explored + visited = set() + # Store all found cycles + cycles = [] + + def dfs_find_cycles(node, path=None): + if path is None: + path = [] + + # Current path to this node + current_path = path + [node] + # Add node to current recursion stack + visiting.add(node) + + # Check all neighbors + for neighbor in graph.get(node, []): + # If neighbor is in current path, we found a cycle + if neighbor in visiting: + # Get the cycle by extracting the relevant portion of the path + cycle_start = current_path.index(neighbor) + cycle = current_path[cycle_start:] + [neighbor] + cycles.append(cycle) + # If neighbor hasn't been fully explored, check it + elif neighbor not in visited: + dfs_find_cycles(neighbor, current_path) + + # Remove node from current path and mark as fully explored + visiting.remove(node) + visited.add(node) + + # Check each unvisited node + for node in graph: + if node not in visited: + dfs_find_cycles(node) + + return cycles + + +def tuples_to_adjacency_list(edges: List[Tuple[str, str]]) -> Dict[str, List[str]]: + """ + Converts a list of edge tuples (source, target) to an adjacency list representation. + + Args: + edges: List of (source, target) tuples representing directed edges + + Returns: + Dictionary mapping each source node to a list of its target nodes + """ + graph = {} + + for source, target in edges: + if source not in graph: + graph[source] = [] + graph[source].append(target) + + # Ensure target nodes with no outgoing edges are in the graph + if target not in graph: + graph[target] = [] + + return graph diff --git a/flixopt/plotting.py b/flixopt/plotting.py index d5b4aef0d..6a2170674 100644 --- a/flixopt/plotting.py +++ b/flixopt/plotting.py @@ -209,7 +209,7 @@ def process_colors( def with_plotly( data: pd.DataFrame, - style: Literal['stacked_bar', 'line', 'area'] = 'area', + style: Literal['stacked_bar', 'line', 'area', 'grouped_bar'] = 'stacked_bar', colors: ColorType = 'viridis', title: str = '', ylabel: str = '', @@ -235,7 +235,8 @@ def with_plotly( Returns: A Plotly figure object containing the generated plot. """ - assert style in ['stacked_bar', 'line', 'area'], f"'style' must be one of {['stacked_bar', 'line', 'area']}" + if style not in ['stacked_bar', 'line', 'area', 'grouped_bar']: + raise ValueError(f"'style' must be one of {['stacked_bar', 'line', 'area', 'grouped_bar']}") if data.empty: return go.Figure() @@ -250,14 +251,31 @@ def with_plotly( x=data.index, y=data[column], name=column, - marker=dict(color=processed_colors[i]), + marker=dict(color=processed_colors[i], + line=dict(width=0, color='rgba(0,0,0,0)')), #Transparent line with 0 width ) ) fig.update_layout( - barmode='relative' if style == 'stacked_bar' else None, - bargap=0, # No space between bars - bargroupgap=0, # No space between groups of bars + barmode='relative', + bargap=0.2, # No space between bars + bargroupgap=0, # No space between grouped bars + ) + if style == 'grouped_bar': + for i, column in enumerate(data.columns): + fig.add_trace( + go.Bar( + x=data.index, + y=data[column], + name=column, + marker=dict(color=processed_colors[i]) + ) + ) + + fig.update_layout( + barmode='group', + bargap=0.2, # No space between bars + bargroupgap=0, # space between grouped bars ) elif style == 'line': for i, column in enumerate(data.columns): diff --git a/flixopt/results.py b/flixopt/results.py index 4f2c7d856..0f300bbcb 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -92,7 +92,7 @@ def from_file(cls, folder: Union[str, pathlib.Path], name: str): return cls( solution=fx_io.load_dataset_from_netcdf(paths.solution), - flow_system=fx_io.load_dataset_from_netcdf(paths.flow_system), + flow_system_data=fx_io.load_dataset_from_netcdf(paths.flow_system), name=name, folder=folder, model=model, @@ -118,7 +118,7 @@ def from_calculation(cls, calculation: 'Calculation'): """ return cls( solution=calculation.model.solution, - flow_system=calculation.flow_system.as_dataset(constants_in_dataset=True), + flow_system_data=calculation.flow_system.as_dataset(constants_in_dataset=True), summary=calculation.summary, model=calculation.model, name=calculation.name, @@ -128,7 +128,7 @@ def from_calculation(cls, calculation: 'Calculation'): def __init__( self, solution: xr.Dataset, - flow_system: xr.Dataset, + flow_system_data: xr.Dataset, name: str, summary: Dict, folder: Optional[pathlib.Path] = None, @@ -137,14 +137,16 @@ def __init__( """ Args: solution: The solution of the optimization. - flow_system: The flow_system that was used to create the calculation as a datatset. + flow_system_data: The flow_system that was used to create the calculation as a datatset. name: The name of the calculation. summary: Information about the calculation, folder: The folder where the results are saved. model: The linopy model that was used to solve the calculation. + Deprecated: + flow_system: Use flow_system_data instead. """ self.solution = solution - self.flow_system = flow_system + self.flow_system_data = flow_system_data self.summary = summary self.name = name self.model = model @@ -163,6 +165,10 @@ def __init__( self.hours_per_timestep = TimeSeriesCollection.calculate_hours_per_timestep(self.timesteps_extra) self.scenarios = self.solution.indexes['scenario'] if 'scenario' in self.solution.indexes else None + self._effect_share_factors = None + self._flow_system = None + self._effects_per_component = {'operation': None, 'invest': None, 'total': None} + def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults']: if key in self.components: return self.components[key] @@ -196,6 +202,26 @@ def constraints(self) -> linopy.Constraints: raise ValueError('The linopy model is not available.') return self.model.constraints + @property + def effect_share_factors(self): + if self._effect_share_factors is None: + effect_share_factors = self.flow_system.effects.calculate_effect_share_factors() + self._effect_share_factors = {'operation': effect_share_factors[0], + 'invest': effect_share_factors[1]} + return self._effect_share_factors + + @property + def flow_system(self): + """ The restored flow_system that was used to create the calculation. + Contains all input parameters.""" + if self._flow_system is None: + from . import FlowSystem + current_logger_level = logger.getEffectiveLevel() + logger.setLevel(logging.CRITICAL) + self._flow_system = FlowSystem.from_dataset(self.flow_system_data) + logger.setLevel(current_logger_level) + return self._flow_system + def filter_solution( self, variable_dims: Optional[Literal['scalar', 'time', 'scenario', 'timeonly', 'scenarioonly']] = None, @@ -239,6 +265,178 @@ def filter_solution( startswith=startswith, ) + def get_effects_per_component(self, mode: Literal['operation', 'invest', 'total'] = 'total') -> xr.Dataset: + """Returns a dataset containing effect totals for each components (including their flows). + + Args: + mode: Which effects to contain. (operation, invest, total) + + Returns: + An xarray Dataset with an additional component dimension and effects as variables. + """ + if mode not in ['operation', 'invest', 'total']: + raise ValueError(f'Invalid mode {mode}') + if self._effects_per_component[mode] is None: + self._effects_per_component[mode] = self._create_effects_dataset(mode) + return self._effects_per_component[mode] + + def get_effect_shares( + self, + element: str, + effect: str, + mode: Optional[Literal['operation', 'invest']] = None, + include_flows: bool = False + ) -> xr.Dataset: + """Retrieves individual effect shares for a specific element and effect. + Either for operation, investment, or both modes combined. + Only includes the direct shares. + + Args: + element: The element identifier for which to retrieve effect shares. + effect: The effect identifier for which to retrieve shares. + mode: Optional. The mode to retrieve shares for. Can be 'operation', 'invest', + or None to retrieve both. Defaults to None. + + Returns: + An xarray Dataset containing the requested effect shares. If mode is None, + returns a merged Dataset containing both operation and investment shares. + + Raises: + ValueError: If the specified effect is not available or if mode is invalid. + """ + if effect not in self.effects: + raise ValueError(f'Effect {effect} is not available.') + + if mode is None: + return xr.merge([self.get_effect_shares(element=element, effect=effect, mode='operation', include_flows=include_flows), + self.get_effect_shares(element=element, effect=effect, mode='invest', include_flows=include_flows)]) + + if mode not in ['operation', 'invest']: + raise ValueError(f'Mode {mode} is not available. Choose between "operation" and "invest".') + + ds = xr.Dataset() + + label = f'{element}->{effect}({mode})' + if label in self.solution: + ds = xr.Dataset({label: self.solution[label]}) + + if include_flows: + if element not in self.components: + raise ValueError(f'Only use Components when retrieving Effects including flows. Got {element}') + flows = [label.split('|')[0] for label in self.components[element].inputs + self.components[element].outputs] + return xr.merge( + [ds] + [self.get_effect_shares(element=flow, effect=effect, mode=mode, include_flows=False) + for flow in flows] + ) + + return ds + + def _compute_effect_total( + self, + element: str, + effect: str, + mode: Literal['operation', 'invest', 'total'] = 'total', + include_flows: bool = False + ) -> xr.DataArray: + """Calculates the total effect for a specific element and effect. + + This method computes the total direct and indirect effects for a given element + and effect, considering the conversion factors between different effects. + + Args: + element: The element identifier for which to calculate total effects. + effect: The effect identifier to calculate. + mode: The calculation mode. Options are: + 'operation': Returns operation-specific effects. + 'invest': Returns investment-specific effects. + 'total': Returns the sum of operation effects (across all timesteps) + and investment effects. Defaults to 'total'. + + Returns: + An xarray DataArray containing the total effects, named with pattern + '{element}->{effect}' for mode='total' or '{element}->{effect}({mode})' + for other modes. + + Raises: + ValueError: If the specified effect is not available. + """ + if effect not in self.effects: + raise ValueError(f'Effect {effect} is not available.') + + if mode == 'total': + operation = self._compute_effect_total(element=element, effect=effect, mode='operation', include_flows=include_flows) + if len(operation.indexes) > 0: + operation = operation.sum('time') + return (operation + self._compute_effect_total(element=element, effect=effect, mode='invest', include_flows=include_flows) + ).rename(f'{element}->{effect}') + + total = xr.DataArray(0) + + relevant_conversion_factors = { + key[0]: value for key, value in self.effect_share_factors[mode].items() if key[1] == effect + } + relevant_conversion_factors[effect] = 1 # Share to itself is 1 + + for target_effect, conversion_factor in relevant_conversion_factors.items(): + label = f'{element}->{target_effect}({mode})' + if label in self.solution: + da = self.solution[label] + total = total + da * conversion_factor + + if include_flows: + if element not in self.components: + raise ValueError(f'Only use Components when retrieving Effects including flows. Got {element}') + flows = [label.split('|')[0] for label in + self.components[element].inputs + self.components[element].outputs] + for flow in flows: + label = f'{flow}->{target_effect}({mode})' + if label in self.solution: + da = self.solution[label] + total = total + da * conversion_factor + + return total.rename(f'{element}->{effect}({mode})') + + def _create_effects_dataset(self, mode: Literal['operation', 'invest', 'total'] = 'total') -> xr.Dataset: + """Creates a dataset containing effect totals for all components (including their flows). + The dataset does contain the direct as well as the indirect effects of each component. + + Args: + mode: The calculation mode ('operation', 'invest', or 'total'). + + Returns: + An xarray Dataset with components as a dimension and effects as variables. + """ + data_vars = {} + + for effect in self.effects: + # Create a list of DataArrays, one for each component + effect_arrays = [ + self._compute_effect_total(element=component, effect=effect, mode=mode, include_flows=True) + .expand_dims(component=[component]) # Add component dimension to each array + for component in list(self.components) + ] + + # Combine all components into one DataArray for this effect + if effect_arrays: + data_vars[effect] = xr.concat(effect_arrays, dim="component", coords='minimal') + + ds = xr.Dataset(data_vars) + + # For now include a test to ensure correctness + suffix = {'operation': '(operation)|total_per_timestep', + 'invest': '(invest)|total', + 'total': '|total', + } + for effect in self.effects: + label = f'{effect}{suffix[mode]}' + computed = ds.sum('component')[effect] + found = self.solution[label] + if not np.allclose(computed.values, found.fillna(0).values): + logger.critical(f'Results for {effect}({mode}) in effects_dataset doesnt match {label}\n' + f'{computed=}\n, {found=}') + + return ds + def plot_heatmap( self, variable_name: str, @@ -295,16 +493,9 @@ def plot_network( show: bool = False, ) -> 'pyvis.network.Network': """See flixopt.flow_system.FlowSystem.plot_network""" - try: - from .flow_system import FlowSystem - - flow_system = FlowSystem.from_dataset(self.flow_system) - except Exception as e: - logger.critical(f'Could not reconstruct the flow_system from dataset: {e}') - return None if path is None: path = self.folder / f'{self.name}--network.html' - return flow_system.plot_network(controls=controls, path=path, show=show) + return self.flow_system.plot_network(controls=controls, path=path, show=show) def to_file( self, @@ -337,7 +528,7 @@ def to_file( paths = fx_io.CalculationResultsPaths(folder, name) fx_io.save_dataset_to_netcdf(self.solution, paths.solution, compression=compression) - fx_io.save_dataset_to_netcdf(self.flow_system, paths.flow_system, compression=compression) + fx_io.save_dataset_to_netcdf(self.flow_system_data, paths.flow_system, compression=compression) with open(paths.summary, 'w', encoding='utf-8') as f: yaml.dump(self.summary, f, allow_unicode=True, sort_keys=False, indent=4, width=1000) diff --git a/tests/test_cycle_detection.py b/tests/test_cycle_detection.py new file mode 100644 index 000000000..71c775b99 --- /dev/null +++ b/tests/test_cycle_detection.py @@ -0,0 +1,226 @@ +import pytest + +from flixopt.effects import detect_cycles + + +def test_empty_graph(): + """Test that an empty graph has no cycles.""" + assert detect_cycles({}) == [] + + +def test_single_node(): + """Test that a graph with a single node and no edges has no cycles.""" + assert detect_cycles({"A": []}) == [] + + +def test_self_loop(): + """Test that a graph with a self-loop has a cycle.""" + cycles = detect_cycles({"A": ["A"]}) + assert len(cycles) == 1 + assert cycles[0] == ["A", "A"] + + +def test_simple_cycle(): + """Test that a simple cycle is detected.""" + graph = { + "A": ["B"], + "B": ["C"], + "C": ["A"] + } + cycles = detect_cycles(graph) + assert len(cycles) == 1 + assert cycles[0] == ["A", "B", "C", "A"] or cycles[0] == ["B", "C", "A", "B"] or cycles[0] == ["C", "A", "B", "C"] + + +def test_no_cycles(): + """Test that a directed acyclic graph has no cycles.""" + graph = { + "A": ["B", "C"], + "B": ["D", "E"], + "C": ["F"], + "D": [], + "E": [], + "F": [] + } + assert detect_cycles(graph) == [] + + +def test_multiple_cycles(): + """Test that a graph with multiple cycles is detected.""" + graph = { + "A": ["B", "D"], + "B": ["C"], + "C": ["A"], + "D": ["E"], + "E": ["D"] + } + cycles = detect_cycles(graph) + assert len(cycles) == 2 + + # Check that both cycles are detected (order might vary) + cycle_strings = [",".join(cycle) for cycle in cycles] + assert any("A,B,C,A" in s for s in cycle_strings) or any("B,C,A,B" in s for s in cycle_strings) or any( + "C,A,B,C" in s for s in cycle_strings) + assert any("D,E,D" in s for s in cycle_strings) or any("E,D,E" in s for s in cycle_strings) + + +def test_hidden_cycle(): + """Test that a cycle hidden deep in the graph is detected.""" + graph = { + "A": ["B", "C"], + "B": ["D"], + "C": ["E"], + "D": ["F"], + "E": ["G"], + "F": ["H"], + "G": ["I"], + "H": ["J"], + "I": ["K"], + "J": ["L"], + "K": ["M"], + "L": ["N"], + "M": ["N"], + "N": ["O"], + "O": ["P"], + "P": ["Q"], + "Q": ["O"] # Hidden cycle O->P->Q->O + } + cycles = detect_cycles(graph) + assert len(cycles) == 1 + + # Check that the O-P-Q cycle is detected + cycle = cycles[0] + assert "O" in cycle and "P" in cycle and "Q" in cycle + + # Check that they appear in the correct order + o_index = cycle.index("O") + p_index = cycle.index("P") + q_index = cycle.index("Q") + + # Check the cycle order is correct (allowing for different starting points) + cycle_len = len(cycle) + assert (p_index == (o_index + 1) % cycle_len and q_index == (p_index + 1) % cycle_len) or \ + (q_index == (o_index + 1) % cycle_len and p_index == (q_index + 1) % cycle_len) or \ + (o_index == (p_index + 1) % cycle_len and q_index == (o_index + 1) % cycle_len) + + +def test_disconnected_graph(): + """Test with a disconnected graph.""" + graph = { + "A": ["B"], + "B": ["C"], + "C": [], + "D": ["E"], + "E": ["F"], + "F": [] + } + assert detect_cycles(graph) == [] + + +def test_disconnected_graph_with_cycle(): + """Test with a disconnected graph containing a cycle in one component.""" + graph = { + "A": ["B"], + "B": ["C"], + "C": [], + "D": ["E"], + "E": ["F"], + "F": ["D"] # Cycle in D->E->F->D + } + cycles = detect_cycles(graph) + assert len(cycles) == 1 + + # Check that the D-E-F cycle is detected + cycle = cycles[0] + assert "D" in cycle and "E" in cycle and "F" in cycle + + # Check if they appear in the correct order + d_index = cycle.index("D") + e_index = cycle.index("E") + f_index = cycle.index("F") + + # Check the cycle order is correct (allowing for different starting points) + cycle_len = len(cycle) + assert (e_index == (d_index + 1) % cycle_len and f_index == (e_index + 1) % cycle_len) or \ + (f_index == (d_index + 1) % cycle_len and e_index == (f_index + 1) % cycle_len) or \ + (d_index == (e_index + 1) % cycle_len and f_index == (d_index + 1) % cycle_len) + + +def test_complex_dag(): + """Test with a complex directed acyclic graph.""" + graph = { + "A": ["B", "C", "D"], + "B": ["E", "F"], + "C": ["E", "G"], + "D": ["G", "H"], + "E": ["I", "J"], + "F": ["J", "K"], + "G": ["K", "L"], + "H": ["L", "M"], + "I": ["N"], + "J": ["N", "O"], + "K": ["O", "P"], + "L": ["P", "Q"], + "M": ["Q"], + "N": ["R"], + "O": ["R", "S"], + "P": ["S"], + "Q": ["S"], + "R": [], + "S": [] + } + assert detect_cycles(graph) == [] + + +def test_missing_node_in_connections(): + """Test behavior when a node referenced in edges doesn't have its own key.""" + graph = { + "A": ["B", "C"], + "B": ["D"] + # C and D don't have their own entries + } + assert detect_cycles(graph) == [] + + +def test_non_string_keys(): + """Test with non-string keys to ensure the algorithm is generic.""" + graph = { + 1: [2, 3], + 2: [4], + 3: [4], + 4: [] + } + assert detect_cycles(graph) == [] + + graph_with_cycle = { + 1: [2], + 2: [3], + 3: [1] + } + cycles = detect_cycles(graph_with_cycle) + assert len(cycles) == 1 + assert cycles[0] == [1, 2, 3, 1] or cycles[0] == [2, 3, 1, 2] or cycles[0] == [3, 1, 2, 3] + + +def test_complex_network_with_many_nodes(): + """Test with a large network to check performance and correctness.""" + graph = {} + # Create a large DAG + for i in range(100): + # Connect each node to the next few nodes + graph[i] = [j for j in range(i + 1, min(i + 5, 100))] + + # No cycles in this arrangement + assert detect_cycles(graph) == [] + + # Add a single back edge to create a cycle + graph[99] = [0] # This creates a cycle + cycles = detect_cycles(graph) + assert len(cycles) >= 1 + # The cycle might include many nodes, but must contain both 0 and 99 + any_cycle_has_both = any(0 in cycle and 99 in cycle for cycle in cycles) + assert any_cycle_has_both + + +if __name__ == "__main__": + pytest.main(["-v"]) diff --git a/tests/test_effect.py b/tests/test_effect.py index 5cbc04ac6..93f417f22 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -5,10 +5,10 @@ import flixopt as fx -from .conftest import assert_conequal, assert_var_equal, create_linopy_model +from .conftest import assert_conequal, assert_var_equal, create_calculation_and_solve, create_linopy_model -class TestBusModel: +class TestEffectModel: """Test the FlowModel class.""" def test_minimal(self, basic_flow_system_linopy): @@ -140,3 +140,85 @@ def test_shares(self, basic_flow_system_linopy): ) +class TestEffectResults: + def test_shares(self, basic_flow_system_linopy): + flow_system = basic_flow_system_linopy + flow_system.effects['Costs'].specific_share_to_other_effects_operation['Effect1'] = 0.5 + flow_system.add_elements( + fx.Effect('Effect1', '€', 'Testing Effect', + specific_share_to_other_effects_operation={ + 'Effect2': 1.1, + 'Effect3': 1.2 + }, + specific_share_to_other_effects_invest={ + 'Effect2': 2.1, + 'Effect3': 2.2 + } + ), + fx.Effect('Effect2', '€', 'Testing Effect', specific_share_to_other_effects_operation={'Effect3': 5}), + fx.Effect('Effect3', '€', 'Testing Effect'), + fx.linear_converters.Boiler( + 'Boiler', eta=0.5, Q_th=fx.Flow('Q_th', bus='Fernwärme', ),Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + ) + + results = create_calculation_and_solve(flow_system, fx.solvers.HighsSolver(0.01, 60), 'Sim1').results + + effect_share_factors = { + 'operation': { + ('Costs', 'Effect1'): 0.5, + ('Costs', 'Effect2'): 0.5 * 1.1, + ('Costs', 'Effect3'): 0.5 * 1.1 * 5 + 0.5 * 1.2, #This is where the issue lies + ('Effect1', 'Effect2'): 1.1, + ('Effect1', 'Effect3'): 1.2 + 1.1 * 5, + ('Effect2', 'Effect3'): 5, + }, + 'invest': { + ('Effect1', 'Effect2'): 2.1, + ('Effect1', 'Effect3'): 2.2, + } + } + for key, value in effect_share_factors['operation'].items(): + np.testing.assert_allclose(results.effect_share_factors['operation'][key].values, value) + + for key, value in effect_share_factors['invest'].items(): + np.testing.assert_allclose(results.effect_share_factors['invest'][key].values, value) + + xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Costs'], + results.solution['Costs(operation)|total_per_timestep'].fillna(0)) + + xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect1'], + results.solution['Effect1(operation)|total_per_timestep'].fillna(0)) + + xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect2'], + results.solution['Effect2(operation)|total_per_timestep'].fillna(0)) + + xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect3'], + results.solution['Effect3(operation)|total_per_timestep'].fillna(0)) + + # Invest mode checks + xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Costs'], + results.solution['Costs(invest)|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect1'], + results.solution['Effect1(invest)|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect2'], + results.solution['Effect2(invest)|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect3'], + results.solution['Effect3(invest)|total']) + + # Total mode checks + xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Costs'], + results.solution['Costs|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect1'], + results.solution['Effect1|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect2'], + results.solution['Effect2|total']) + + xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect3'], + results.solution['Effect3|total']) + diff --git a/tests/test_effects_shares_summation.py b/tests/test_effects_shares_summation.py new file mode 100644 index 000000000..e2dada7e9 --- /dev/null +++ b/tests/test_effects_shares_summation.py @@ -0,0 +1,236 @@ +from typing import Dict, Tuple + +import numpy as np +import pytest +import xarray as xr + +from flixopt.effects import calculate_all_conversion_paths + + +def test_direct_conversions(): + """Test direct conversions with simple scalar values.""" + conversion_dict = { + 'A': {'B': xr.DataArray(2.0)}, + 'B': {'C': xr.DataArray(3.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Check direct conversions + assert ('A', 'B') in result + assert ('B', 'C') in result + assert result[('A', 'B')].item() == 2.0 + assert result[('B', 'C')].item() == 3.0 + + # Check indirect conversion + assert ('A', 'C') in result + assert result[('A', 'C')].item() == 6.0 # 2.0 * 3.0 + + +def test_multiple_paths(): + """Test multiple paths between nodes that should be summed.""" + conversion_dict = { + 'A': {'B': xr.DataArray(2.0), 'C': xr.DataArray(3.0)}, + 'B': {'D': xr.DataArray(4.0)}, + 'C': {'D': xr.DataArray(5.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # A to D should sum two paths: A->B->D (2*4=8) and A->C->D (3*5=15) + assert ('A', 'D') in result + assert result[('A', 'D')].item() == 8.0 + 15.0 + + +def test_xarray_conversions(): + """Test with xarray DataArrays that have dimensions.""" + # Create DataArrays with a time dimension + time_points = [1, 2, 3] + a_to_b = xr.DataArray([2.0, 2.1, 2.2], dims=['time'], coords={'time': time_points}) + b_to_c = xr.DataArray([3.0, 3.1, 3.2], dims=['time'], coords={'time': time_points}) + + conversion_dict = { + 'A': {'B': a_to_b}, + 'B': {'C': b_to_c} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Check indirect conversion preserves dimensions + assert ('A', 'C') in result + assert result[('A', 'C')].dims == ('time',) + + # Check values at each time point + for i, t in enumerate(time_points): + expected = a_to_b.values[i] * b_to_c.values[i] + assert pytest.approx(result[('A', 'C')].sel(time=t).item()) == expected + + +def test_long_paths(): + """Test with longer paths (more than one intermediate node).""" + conversion_dict = { + 'A': {'B': xr.DataArray(2.0)}, + 'B': {'C': xr.DataArray(3.0)}, + 'C': {'D': xr.DataArray(4.0)}, + 'D': {'E': xr.DataArray(5.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Check the full path A->B->C->D->E + assert ('A', 'E') in result + expected = 2.0 * 3.0 * 4.0 * 5.0 # 120.0 + assert result[('A', 'E')].item() == expected + + +def test_diamond_paths(): + """Test with a diamond shape graph with multiple paths to the same destination.""" + conversion_dict = { + 'A': {'B': xr.DataArray(2.0), 'C': xr.DataArray(3.0)}, + 'B': {'D': xr.DataArray(4.0)}, + 'C': {'D': xr.DataArray(5.0)}, + 'D': {'E': xr.DataArray(6.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # A to E should go through both paths: + # A->B->D->E (2*4*6=48) and A->C->D->E (3*5*6=90) + assert ('A', 'E') in result + expected = 48.0 + 90.0 # 138.0 + assert result[('A', 'E')].item() == expected + + +def test_effect_shares_example(): + """Test the specific example from the effects share factors test.""" + # Create the conversion dictionary based on test example + conversion_dict = { + 'Costs': {'Effect1': xr.DataArray(0.5)}, + 'Effect1': {'Effect2': xr.DataArray(1.1), 'Effect3': xr.DataArray(1.2)}, + 'Effect2': {'Effect3': xr.DataArray(5.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Test direct paths + assert result[('Costs', 'Effect1')].item() == 0.5 + assert result[('Effect1', 'Effect2')].item() == 1.1 + assert result[('Effect2', 'Effect3')].item() == 5.0 + + # Test indirect paths + # Costs -> Effect2 = Costs -> Effect1 -> Effect2 = 0.5 * 1.1 + assert result[('Costs', 'Effect2')].item() == 0.5 * 1.1 + + # Costs -> Effect3 has two paths: + # 1. Costs -> Effect1 -> Effect3 = 0.5 * 1.2 = 0.6 + # 2. Costs -> Effect1 -> Effect2 -> Effect3 = 0.5 * 1.1 * 5 = 2.75 + # Total = 0.6 + 2.75 = 3.35 + assert result[('Costs', 'Effect3')].item() == 0.5 * 1.2 + 0.5 * 1.1 * 5 + + # Effect1 -> Effect3 has two paths: + # 1. Effect1 -> Effect2 -> Effect3 = 1.1 * 5.0 = 5.5 + # 2. Effect1 -> Effect3 = 1.2 + # Total = 0.6 + 2.75 = 3.35 + assert result[('Effect1', 'Effect3')].item() == 1.2 + 1.1 * 5.0 + + +def test_empty_conversion_dict(): + """Test with an empty conversion dictionary.""" + result = calculate_all_conversion_paths({}) + assert len(result) == 0 + + +def test_no_indirect_paths(): + """Test with a dictionary that has no indirect paths.""" + conversion_dict = { + 'A': {'B': xr.DataArray(2.0)}, + 'C': {'D': xr.DataArray(3.0)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Only direct paths should exist + assert len(result) == 2 + assert ('A', 'B') in result + assert ('C', 'D') in result + assert result[('A', 'B')].item() == 2.0 + assert result[('C', 'D')].item() == 3.0 + + +def test_complex_network(): + """Test with a complex network of many nodes and multiple paths, without circular references.""" + # Create a directed acyclic graph with many nodes + # Structure resembles a layered network with multiple paths + conversion_dict = { + 'A': {'B': xr.DataArray(1.5), 'C': xr.DataArray(2.0), 'D': xr.DataArray(0.5)}, + 'B': {'E': xr.DataArray(3.0), 'F': xr.DataArray(1.2)}, + 'C': {'E': xr.DataArray(0.8), 'G': xr.DataArray(2.5)}, + 'D': {'G': xr.DataArray(1.8), 'H': xr.DataArray(3.2)}, + 'E': {'I': xr.DataArray(0.7), 'J': xr.DataArray(1.4)}, + 'F': {'J': xr.DataArray(2.2), 'K': xr.DataArray(0.9)}, + 'G': {'K': xr.DataArray(1.6), 'L': xr.DataArray(2.8)}, + 'H': {'L': xr.DataArray(0.4), 'M': xr.DataArray(1.1)}, + 'I': {'N': xr.DataArray(2.3)}, + 'J': {'N': xr.DataArray(1.9), 'O': xr.DataArray(0.6)}, + 'K': {'O': xr.DataArray(3.5), 'P': xr.DataArray(1.3)}, + 'L': {'P': xr.DataArray(2.7), 'Q': xr.DataArray(0.8)}, + 'M': {'Q': xr.DataArray(2.1)}, + 'N': {'R': xr.DataArray(1.7)}, + 'O': {'R': xr.DataArray(2.9), 'S': xr.DataArray(1.0)}, + 'P': {'S': xr.DataArray(2.4)}, + 'Q': {'S': xr.DataArray(1.5)} + } + + result = calculate_all_conversion_paths(conversion_dict) + + # Check some direct paths + assert result[('A', 'B')].item() == 1.5 + assert result[('D', 'H')].item() == 3.2 + assert result[('G', 'L')].item() == 2.8 + + # Check some two-step paths + assert result[('A', 'E')].item() == 1.5 * 3.0 + 2.0 * 0.8 # A->B->E + A->C->E + assert result[('B', 'J')].item() == 3.0 * 1.4 + 1.2 * 2.2 # B->E->J + B->F->J + + # Check some three-step paths + # A->B->E->I + # A->C->E->I + expected_a_to_i = 1.5 * 3.0 * 0.7 + 2.0 * 0.8 * 0.7 + assert pytest.approx(result[('A', 'I')].item()) == expected_a_to_i + + # Check some four-step paths + # A->B->E->I->N + # A->C->E->I->N + expected_a_to_n = 1.5 * 3.0 * 0.7 * 2.3 + 2.0 * 0.8 * 0.7 * 2.3 + expected_a_to_n += 1.5 * 3.0 * 1.4 * 1.9 + 2.0 * 0.8 * 1.4 * 1.9 # A->B->E->J->N + A->C->E->J->N + expected_a_to_n += 1.5 * 1.2 * 2.2 * 1.9 # A->B->F->J->N + assert pytest.approx(result[('A', 'N')].item()) == expected_a_to_n + + # Check a very long path from A to S + # This should include: + # A->B->E->J->O->S + # A->B->F->K->O->S + # A->C->E->J->O->S + # A->C->G->K->O->S + # A->D->G->K->O->S + # A->D->H->L->P->S + # A->D->H->M->Q->S + # And many more + assert ('A', 'S') in result + + # There are many paths to R from A - check their existence + assert ('A', 'R') in result + + # Check that there's no direct path from A to R + # But there should be indirect paths + assert ('A', 'R') in result + assert 'A' not in conversion_dict.get('R', {}) + + # Count the number of paths calculated to verify algorithm explored all connections + # In a DAG with 19 nodes (A through S), the maximum number of pairs is 19*18 = 342 + # But we won't have all possible connections due to the structure + # Just verify we have a reasonable number + assert len(result) > 50 + +if __name__ == '__main__': + pytest.main() diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 89b5feced..4abdafa5f 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -282,7 +282,7 @@ def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): calc.results.to_file() res = fx.results.CalculationResults.from_file('results', 'test_full_scenario') - fx.FlowSystem.from_dataset(res.flow_system) + fx.FlowSystem.from_dataset(res.flow_system_data) calc = create_calculation_and_solve( flow_system_piecewise_conversion_scenarios, solver=fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60), @@ -301,7 +301,7 @@ def test_io_persistance(flow_system_piecewise_conversion_scenarios): calc.results.to_file() res = fx.results.CalculationResults.from_file('results', 'test_full_scenario') - flow_system_2 = fx.FlowSystem.from_dataset(res.flow_system) + flow_system_2 = fx.FlowSystem.from_dataset(res.flow_system_data) calc_2 = create_calculation_and_solve( flow_system_2, solver=fx.solvers.HighsSolver(mip_gap=0.001, time_limit_seconds=60), @@ -323,7 +323,7 @@ def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): calc.solve(fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60)) calc.results.to_file() - flow_system_2 = fx.FlowSystem.from_dataset(calc.results.flow_system) + flow_system_2 = fx.FlowSystem.from_dataset(calc.results.flow_system_data) assert calc.results.solution.indexes['scenario'].equals(flow_system.time_series_collection.scenarios[0:2]) From 9f2f38b88a95bb0ffbd572c8a10eda0fba180c83 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:26:34 +0200 Subject: [PATCH 038/183] Scenarios/datasets results (#257) * Use dataarray instead of dataset * Change effects dataset to dataarray and use nan when no share was found * Add method for flow_rates dataset * Add methods to get flow_rates and flow_hours as datasets * Rename the dataarrays to the flow * Preserve index order * Improve filter_edges_dataset() * Simplify _create_flow_rates_dataarray() * Add dataset for sizes of Flows * Extend results structure to contain flows AND start/end infos * Add FlowResults Object * BUGFIX:Typo in _ElementResults.constraints * Add flows to results of Nodes * Simplify dataarray creation and improve FlowResults * Add nice docstrings * Improve filtering of flow results * Improve filtering of flow results. Add attribute of component * Add big dataarray with all variables but indexed * Revert "Add big dataarray with all variables but indexed" This reverts commit 08cd8a14fcf28248bf4a4c0a0fe1bae718269731. * Improve filtering method for coords filter and add error handling for restoring the flow system * Remove unnecessary methods in results .from_json() * Ensure consistent coord ordering in Effects dataarray * Rename get_effects_per_component() * Make effects_per_component() a dataset instead of a dataarray * Improve backwards compatability --- flixopt/elements.py | 12 +- flixopt/results.py | 356 ++++++++++++++++++++++++++++++++++++------- flixopt/structure.py | 7 +- tests/test_effect.py | 24 +-- 4 files changed, 332 insertions(+), 67 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index 7dda3e9cf..11246e6d9 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -396,6 +396,14 @@ def do_modeling(self): # Shares self._create_shares() + def results_structure(self): + return { + **super().results_structure(), + 'start': self.element.bus if self.element.is_input_in_component else self.element.component, + 'end': self.element.component if self.element.is_input_in_component else self.element.bus, + 'component': self.element.component, + } + def _create_shares(self): # Arbeitskosten: if self.element.effects_per_flow_hour != {}: @@ -535,7 +543,8 @@ def results_structure(self): inputs.append(self.excess_input.name) if self.excess_output is not None: outputs.append(self.excess_output.name) - return {**super().results_structure(), 'inputs': inputs, 'outputs': outputs} + return {**super().results_structure(), 'inputs': inputs, 'outputs': outputs, + 'flows': [flow.label_full for flow in self.element.inputs + self.element.outputs]} class ComponentModel(ElementModel): @@ -588,4 +597,5 @@ def results_structure(self): **super().results_structure(), 'inputs': [flow.model.flow_rate.name for flow in self.element.inputs], 'outputs': [flow.model.flow_rate.name for flow in self.element.outputs], + 'flows': [flow.label_full for flow in self.element.inputs + self.element.outputs], } diff --git a/flixopt/results.py b/flixopt/results.py index 0f300bbcb..b1e72f4be 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -2,7 +2,8 @@ import json import logging import pathlib -from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union +import warnings +from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union, Any import linopy import matplotlib.pyplot as plt @@ -25,6 +26,11 @@ logger = logging.getLogger('flixopt') +class _FlowSystemRestorationError(Exception): + """Exception raised when a FlowSystem cannot be restored from dataset.""" + pass + + class CalculationResults: """Results container for Calculation results. @@ -37,7 +43,7 @@ class CalculationResults: Attributes: solution (xr.Dataset): Dataset containing optimization results. - flow_system (xr.Dataset): Dataset containing the flow system. + flow_system_data (xr.Dataset): Dataset containing the flow system. summary (Dict): Information about the calculation. name (str): Name identifier for the calculation. model (linopy.Model): The optimization model (if available). @@ -133,6 +139,7 @@ def __init__( summary: Dict, folder: Optional[pathlib.Path] = None, model: Optional[linopy.Model] = None, + **kwargs, # To accept old "flow_system" parameter ): """ Args: @@ -145,6 +152,16 @@ def __init__( Deprecated: flow_system: Use flow_system_data instead. """ + # Handle potential old "flow_system" parameter for backward compatibility + if 'flow_system' in kwargs and flow_system_data is None: + flow_system_data = kwargs.pop('flow_system') + warnings.warn( + "The 'flow_system' parameter is deprecated. Use 'flow_system_data' instead." + "Acess is now by '.flow_system_data', while '.flow_system' returns the restored FlowSystem.", + DeprecationWarning, + stacklevel=2, + ) + self.solution = solution self.flow_system_data = flow_system_data self.summary = summary @@ -152,30 +169,47 @@ def __init__( self.model = model self.folder = pathlib.Path(folder) if folder is not None else pathlib.Path.cwd() / 'results' self.components = { - label: ComponentResults.from_json(self, infos) for label, infos in self.solution.attrs['Components'].items() + label: ComponentResults(self, **infos) for label, infos in self.solution.attrs['Components'].items() } - self.buses = {label: BusResults.from_json(self, infos) for label, infos in self.solution.attrs['Buses'].items()} + self.buses = {label: BusResults(self, **infos) for label, infos in self.solution.attrs['Buses'].items()} self.effects = { - label: EffectResults.from_json(self, infos) for label, infos in self.solution.attrs['Effects'].items() + label: EffectResults(self, **infos) for label, infos in self.solution.attrs['Effects'].items() } + if 'Flows' not in self.solution.attrs: + warnings.warn( + 'No Data about flows found in the results. This data is only included since v2.2.0. Some functionality ' + 'is not availlable. We recommend to evaluate your results with a version <2.2.0.') + self.flows = {} + else: + self.flows = { + label: FlowResults(self, **infos) for label, infos in self.solution.attrs.get('Flows', {}).items() + } + self.timesteps_extra = self.solution.indexes['time'] self.hours_per_timestep = TimeSeriesCollection.calculate_hours_per_timestep(self.timesteps_extra) self.scenarios = self.solution.indexes['scenario'] if 'scenario' in self.solution.indexes else None self._effect_share_factors = None self._flow_system = None + + self._flow_rates = None + self._flow_hours = None + self._sizes = None self._effects_per_component = {'operation': None, 'invest': None, 'total': None} + self._flow_network_info_ = None - def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults']: + def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults', 'FlowResults']: if key in self.components: return self.components[key] if key in self.buses: return self.buses[key] if key in self.effects: return self.effects[key] + if key in self.flows: + return self.flows[key] raise KeyError(f'No element with label {key} found.') @property @@ -211,15 +245,20 @@ def effect_share_factors(self): return self._effect_share_factors @property - def flow_system(self): + def flow_system(self) -> 'FlowSystem': """ The restored flow_system that was used to create the calculation. Contains all input parameters.""" if self._flow_system is None: - from . import FlowSystem - current_logger_level = logger.getEffectiveLevel() - logger.setLevel(logging.CRITICAL) - self._flow_system = FlowSystem.from_dataset(self.flow_system_data) - logger.setLevel(current_logger_level) + try: + from . import FlowSystem + current_logger_level = logger.getEffectiveLevel() + logger.setLevel(logging.CRITICAL) + self._flow_system = FlowSystem.from_dataset(self.flow_system_data) + self._flow_system._connect_network() + logger.setLevel(current_logger_level) + except Exception as e: + logger.critical(f'Not able to restore FlowSystem from dataset. Some functionality is not availlable. {e}') + raise _FlowSystemRestorationError(f'Not able to restore FlowSystem from dataset. {e}') from e return self._flow_system def filter_solution( @@ -265,7 +304,7 @@ def filter_solution( startswith=startswith, ) - def get_effects_per_component(self, mode: Literal['operation', 'invest', 'total'] = 'total') -> xr.Dataset: + def effects_per_component(self, mode: Literal['operation', 'invest', 'total'] = 'total') -> xr.Dataset: """Returns a dataset containing effect totals for each components (including their flows). Args: @@ -280,6 +319,120 @@ def get_effects_per_component(self, mode: Literal['operation', 'invest', 'total' self._effects_per_component[mode] = self._create_effects_dataset(mode) return self._effects_per_component[mode] + def flow_rates( + self, + start: Optional[Union[str, List[str]]] = None, + end: Optional[Union[str, List[str]]] = None, + component: Optional[Union[str, List[str]]] = None, + ) -> xr.DataArray: + """Returns a DataArray containing the flow rates of each Flow. + + Args: + start: Optional source node(s) to filter by. Can be a single node name or a list of names. + end: Optional destination node(s) to filter by. Can be a single node name or a list of names. + component: Optional component(s) to filter by. Can be a single component name or a list of names. + + Further usage: + Convert the dataarray to a dataframe: + >>>results.flow_rates().to_pandas() + Get the max or min over time: + >>>results.flow_rates().max('time') + Sum up the flow rates of flows with the same start and end: + >>>results.flow_rates(end='Fernwärme').groupby('start').sum(dim='flow') + To recombine filtered dataarrays, use `xr.concat` with dim 'flow': + >>>xr.concat([results.flow_rates(start='Fernwärme'), results.flow_rates(end='Fernwärme')], dim='flow') + """ + if self._flow_rates is None: + self._flow_rates = self._assign_flow_coords( + xr.concat([flow.flow_rate.rename(flow.label) for flow in self.flows.values()], + dim=pd.Index(self.flows.keys(), name='flow')) + ).rename('flow_rates') + filters = {k: v for k, v in {'start': start, 'end': end, 'component': component}.items() if v is not None} + return filter_dataarray_by_coord(self._flow_rates, **filters) + + def flow_hours( + self, + start: Optional[Union[str, List[str]]] = None, + end: Optional[Union[str, List[str]]] = None, + component: Optional[Union[str, List[str]]] = None, + ) -> xr.DataArray: + """Returns a DataArray containing the flow hours of each Flow. + + Flow hours represent the total energy/material transferred over time, + calculated by multiplying flow rates by the duration of each timestep. + + Args: + start: Optional source node(s) to filter by. Can be a single node name or a list of names. + end: Optional destination node(s) to filter by. Can be a single node name or a list of names. + component: Optional component(s) to filter by. Can be a single component name or a list of names. + + Further usage: + Convert the dataarray to a dataframe: + >>>results.flow_hours().to_pandas() + Sum up the flow hours over time: + >>>results.flow_hours().sum('time') + Sum up the flow hours of flows with the same start and end: + >>>results.flow_hours(end='Fernwärme').groupby('start').sum(dim='flow') + To recombine filtered dataarrays, use `xr.concat` with dim 'flow': + >>>xr.concat([results.flow_hours(start='Fernwärme'), results.flow_hours(end='Fernwärme')], dim='flow') + + """ + if self._flow_hours is None: + self._flow_hours = (self.flow_rates() * self.hours_per_timestep).rename('flow_hours') + filters = {k: v for k, v in {'start': start, 'end': end, 'component': component}.items() if v is not None} + return filter_dataarray_by_coord(self._flow_hours, **filters) + + def sizes( + self, + start: Optional[Union[str, List[str]]] = None, + end: Optional[Union[str, List[str]]] = None, + component: Optional[Union[str, List[str]]] = None + ) -> xr.DataArray: + """Returns a dataset with the sizes of the Flows. + Args: + start: Optional source node(s) to filter by. Can be a single node name or a list of names. + end: Optional destination node(s) to filter by. Can be a single node name or a list of names. + component: Optional component(s) to filter by. Can be a single component name or a list of names. + + Further usage: + Convert the dataarray to a dataframe: + >>>results.sizes().to_pandas() + To recombine filtered dataarrays, use `xr.concat` with dim 'flow': + >>>xr.concat([results.sizes(start='Fernwärme'), results.sizes(end='Fernwärme')], dim='flow') + + """ + if self._sizes is None: + self._sizes = self._assign_flow_coords( + xr.concat([flow.size.rename(flow.label) for flow in self.flows.values()], + dim=pd.Index(self.flows.keys(), name='flow')) + ).rename('flow_sizes') + filters = {k: v for k, v in {'start': start, 'end': end, 'component': component}.items() if v is not None} + return filter_dataarray_by_coord(self._sizes, **filters) + + def _assign_flow_coords(self, da: xr.DataArray): + # Add start and end coordinates + da = da.assign_coords({ + 'start': ('flow', [flow.start for flow in self.flows.values()]), + 'end': ('flow', [flow.end for flow in self.flows.values()]), + 'component': ('flow', [flow.component for flow in self.flows.values()]), + }) + + # Ensure flow is the last dimension if needed + existing_dims = [d for d in da.dims if d != 'flow'] + da = da.transpose(*(existing_dims + ['flow'])) + return da + + def _get_flow_network_info(self) -> Dict[str, Dict[str, str]]: + flow_network_info = {} + + for flow in self.flows.values(): + flow_network_info[flow.label] = { + 'label': flow.label, + 'start': flow.start, + 'end': flow.end, + } + return flow_network_info + def get_effect_shares( self, element: str, @@ -336,7 +489,7 @@ def _compute_effect_total( element: str, effect: str, mode: Literal['operation', 'invest', 'total'] = 'total', - include_flows: bool = False + include_flows: bool = False, ) -> xr.DataArray: """Calculates the total effect for a specific element and effect. @@ -351,6 +504,7 @@ def _compute_effect_total( 'invest': Returns investment-specific effects. 'total': Returns the sum of operation effects (across all timesteps) and investment effects. Defaults to 'total'. + include_flows: Whether to include effects from flows connected to this element. Returns: An xarray DataArray containing the total effects, named with pattern @@ -365,12 +519,20 @@ def _compute_effect_total( if mode == 'total': operation = self._compute_effect_total(element=element, effect=effect, mode='operation', include_flows=include_flows) - if len(operation.indexes) > 0: + invest = self._compute_effect_total(element=element, effect=effect, mode='invest', include_flows=include_flows) + if invest.isnull().all() and operation.isnull().all(): + return xr.DataArray(np.nan) + if operation.isnull().all(): + return invest.rename(f'{element}->{effect}') + operation = operation.sum('time') + if invest.isnull().all(): + return operation.rename(f'{element}->{effect}') + if 'time' in operation.indexes: operation = operation.sum('time') - return (operation + self._compute_effect_total(element=element, effect=effect, mode='invest', include_flows=include_flows) - ).rename(f'{element}->{effect}') + return invest + operation total = xr.DataArray(0) + share_exists = False relevant_conversion_factors = { key[0]: value for key, value in self.effect_share_factors[mode].items() if key[1] == effect @@ -380,8 +542,9 @@ def _compute_effect_total( for target_effect, conversion_factor in relevant_conversion_factors.items(): label = f'{element}->{target_effect}({mode})' if label in self.solution: + share_exists = True da = self.solution[label] - total = total + da * conversion_factor + total = da * conversion_factor + total if include_flows: if element not in self.components: @@ -391,9 +554,11 @@ def _compute_effect_total( for flow in flows: label = f'{flow}->{target_effect}({mode})' if label in self.solution: + share_exists = True da = self.solution[label] - total = total + da * conversion_factor - + total = da * conversion_factor + total + if not share_exists: + total = xr.DataArray(np.nan) return total.rename(f'{element}->{effect}({mode})') def _create_effects_dataset(self, mode: Literal['operation', 'invest', 'total'] = 'total') -> xr.Dataset: @@ -404,36 +569,41 @@ def _create_effects_dataset(self, mode: Literal['operation', 'invest', 'total'] mode: The calculation mode ('operation', 'invest', or 'total'). Returns: - An xarray Dataset with components as a dimension and effects as variables. + An xarray Dataset with components as dimension and effects as variables. """ - data_vars = {} + # Create an empty dataset + ds = xr.Dataset() + # Add each effect as a variable to the dataset for effect in self.effects: # Create a list of DataArrays, one for each component - effect_arrays = [ - self._compute_effect_total(element=component, effect=effect, mode=mode, include_flows=True) - .expand_dims(component=[component]) # Add component dimension to each array + component_arrays = [ + self._compute_effect_total(element=component, effect=effect, mode=mode, include_flows=True).expand_dims( + component=[component] + ) # Add component dimension to each array for component in list(self.components) ] # Combine all components into one DataArray for this effect - if effect_arrays: - data_vars[effect] = xr.concat(effect_arrays, dim="component", coords='minimal') - - ds = xr.Dataset(data_vars) + if component_arrays: + effect_array = xr.concat(component_arrays, dim='component', coords='minimal') + # Add this effect as a variable to the dataset + ds[effect] = effect_array # For now include a test to ensure correctness - suffix = {'operation': '(operation)|total_per_timestep', - 'invest': '(invest)|total', - 'total': '|total', - } + suffix = { + 'operation': '(operation)|total_per_timestep', + 'invest': '(invest)|total', + 'total': '|total', + } for effect in self.effects: label = f'{effect}{suffix[mode]}' - computed = ds.sum('component')[effect] + computed = ds[effect].sum('component') found = self.solution[label] if not np.allclose(computed.values, found.fillna(0).values): - logger.critical(f'Results for {effect}({mode}) in effects_dataset doesnt match {label}\n' - f'{computed=}\n, {found=}') + logger.critical( + f'Results for {effect}({mode}) in effects_dataset doesnt match {label}\n{computed=}\n, {found=}' + ) return ds @@ -549,10 +719,6 @@ def to_file( class _ElementResults: - @classmethod - def from_json(cls, calculation_results, json_data: Dict) -> '_ElementResults': - return cls(calculation_results, json_data['label'], json_data['variables'], json_data['constraints']) - def __init__( self, calculation_results: CalculationResults, label: str, variables: List[str], constraints: List[str] ): @@ -585,7 +751,7 @@ def constraints(self) -> linopy.Constraints: """ if self._calculation_results.model is None: raise ValueError('The linopy model is not available.') - return self._calculation_results.model.constraints[self._variable_names] + return self._calculation_results.model.constraints[self._constraint_names] def filter_solution( self, @@ -630,17 +796,6 @@ def filter_solution( class _NodeResults(_ElementResults): - @classmethod - def from_json(cls, calculation_results, json_data: Dict) -> '_NodeResults': - return cls( - calculation_results, - json_data['label'], - json_data['variables'], - json_data['constraints'], - json_data['inputs'], - json_data['outputs'], - ) - def __init__( self, calculation_results: CalculationResults, @@ -649,10 +804,12 @@ def __init__( constraints: List[str], inputs: List[str], outputs: List[str], + flows: List[str], ): super().__init__(calculation_results, label, variables, constraints) self.inputs = inputs self.outputs = outputs + self.flows = flows def plot_node_balance( self, @@ -979,6 +1136,42 @@ def get_shares_from(self, element: str): return self.solution[[name for name in self._variable_names if name.startswith(f'{element}->')]] +class FlowResults(_ElementResults): + def __init__( + self, + calculation_results: CalculationResults, + label: str, + variables: List[str], + constraints: List[str], + start: str, + end: str, + component: str, + ): + super().__init__(calculation_results, label, variables, constraints) + self.start = start + self.end = end + self.component = component + + @property + def flow_rate(self) -> xr.DataArray: + return self.solution[f'{self.label}|flow_rate'] + + @property + def flow_hours(self) -> xr.DataArray: + return (self.flow_rate * self._calculation_results.hours_per_timestep).rename(f'{self.label}|flow_hours') + + @property + def size(self) -> xr.DataArray: + name = f'{self.label}|size' + if name in self.solution: + return self.solution[name] + try: + return xr.DataArray(self._calculation_results.flow_system.flows[self.label].size).rename(name) + except _FlowSystemRestorationError: + logger.critical(f'Size of flow {self.label}.size not availlable. Returning NaN') + return xr.DataArray(np.nan).rename(name) + + class SegmentedCalculationResults: """ Class to store the results of a SegmentedCalculation. @@ -1336,3 +1529,62 @@ def filter_dataset( ) from e return filtered_ds + + +def filter_dataarray_by_coord( + da: xr.DataArray, + **kwargs: Optional[Union[str, List[str]]] +) -> xr.DataArray: + """Filter flows by node and component attributes. + + Filters are applied in the order they are specified. All filters must match for an edge to be included. + + To recombine filtered dataarrays, use `xr.concat`. + + xr.concat([res.sizes(start='Fernwärme'), res.sizes(end='Fernwärme')], dim='flow') + + Args: + da: Flow DataArray with network metadata coordinates. + **kwargs: Coord filters as name=value pairs. + + Returns: + Filtered DataArray with matching edges. + + Raises: + AttributeError: If required coordinates are missing. + ValueError: If specified nodes don't exist or no matches found. + """ + # Helper function to process filters + def apply_filter(array, coord_name: str, coord_values: Union[Any, List[Any]]): + # Verify coord exists + if coord_name not in array.coords: + raise AttributeError(f"Missing required coordinate '{coord_name}'") + + # Convert single value to list + val_list = [coord_values] if isinstance(coord_values, str) else coord_values + + # Verify coord_values exist + available = set(array[coord_name].values) + missing = [v for v in val_list if v not in available] + if missing: + raise ValueError(f"{coord_name.title()} value(s) not found: {missing}") + + # Apply filter + return array.where( + array[coord_name].isin(val_list) if isinstance(coord_values, list) else array[coord_name] == coord_values, + drop=True + ) + + # Apply filters from kwargs + filters = {k: v for k, v in kwargs.items() if v is not None} + try: + for coord, values in filters.items(): + da = apply_filter(da, coord, values) + except ValueError as e: + raise ValueError(f"No edges match criteria: {filters}") + + # Verify results exist + if da.size == 0: + raise ValueError(f"No edges match criteria: {filters}") + + return da diff --git a/flixopt/structure.py b/flixopt/structure.py index 1285dc885..7c7772dad 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -106,6 +106,10 @@ def solution(self): effect.label_full: effect.model.results_structure() for effect in sorted(self.flow_system.effects, key=lambda effect: effect.label_full.upper()) }, + 'Flows': { + flow.label_full: flow.model.results_structure() + for flow in sorted(self.flow_system.flows.values(), key=lambda flow: flow.label_full.upper()) + }, } return solution.reindex(time=self.time_series_collection.timesteps_extra) @@ -495,8 +499,7 @@ def __init__(self, model: SystemModel, element: Element): def results_structure(self): return { - 'label': self.label, - 'label_full': self.label_full, + 'label': self.label_full, 'variables': list(self.variables), 'constraints': list(self.constraints), } diff --git a/tests/test_effect.py b/tests/test_effect.py index 93f417f22..ff85d0556 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -184,41 +184,41 @@ def test_shares(self, basic_flow_system_linopy): for key, value in effect_share_factors['invest'].items(): np.testing.assert_allclose(results.effect_share_factors['invest'][key].values, value) - xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Costs'], + xr.testing.assert_allclose(results.effects_per_component('operation').sum('component')['Costs'], results.solution['Costs(operation)|total_per_timestep'].fillna(0)) - xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect1'], + xr.testing.assert_allclose(results.effects_per_component('operation').sum('component')['Effect1'], results.solution['Effect1(operation)|total_per_timestep'].fillna(0)) - xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect2'], + xr.testing.assert_allclose(results.effects_per_component('operation').sum('component')['Effect2'], results.solution['Effect2(operation)|total_per_timestep'].fillna(0)) - xr.testing.assert_allclose(results.get_effects_per_component('operation').sum('component')['Effect3'], + xr.testing.assert_allclose(results.effects_per_component('operation').sum('component')['Effect3'], results.solution['Effect3(operation)|total_per_timestep'].fillna(0)) # Invest mode checks - xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Costs'], + xr.testing.assert_allclose(results.effects_per_component('invest').sum('component')['Costs'], results.solution['Costs(invest)|total']) - xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect1'], + xr.testing.assert_allclose(results.effects_per_component('invest').sum('component')['Effect1'], results.solution['Effect1(invest)|total']) - xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect2'], + xr.testing.assert_allclose(results.effects_per_component('invest').sum('component')['Effect2'], results.solution['Effect2(invest)|total']) - xr.testing.assert_allclose(results.get_effects_per_component('invest').sum('component')['Effect3'], + xr.testing.assert_allclose(results.effects_per_component('invest').sum('component')['Effect3'], results.solution['Effect3(invest)|total']) # Total mode checks - xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Costs'], + xr.testing.assert_allclose(results.effects_per_component('total').sum('component')['Costs'], results.solution['Costs|total']) - xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect1'], + xr.testing.assert_allclose(results.effects_per_component('total').sum('component')['Effect1'], results.solution['Effect1|total']) - xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect2'], + xr.testing.assert_allclose(results.effects_per_component('total').sum('component')['Effect2'], results.solution['Effect2|total']) - xr.testing.assert_allclose(results.get_effects_per_component('total').sum('component')['Effect3'], + xr.testing.assert_allclose(results.effects_per_component('total').sum('component')['Effect3'], results.solution['Effect3|total']) From dbfb1b53ccc6c7f8c3d8ad169bee50b7d2d14937 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:27:56 +0200 Subject: [PATCH 039/183] ruff check --- flixopt/results.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index b1e72f4be..e08e14636 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -3,7 +3,7 @@ import logging import pathlib import warnings -from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union, Any +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union import linopy import matplotlib.pyplot as plt @@ -1580,7 +1580,7 @@ def apply_filter(array, coord_name: str, coord_values: Union[Any, List[Any]]): try: for coord, values in filters.items(): da = apply_filter(da, coord, values) - except ValueError as e: + except ValueError: raise ValueError(f"No edges match criteria: {filters}") # Verify results exist From 6738d34c396e608d49f0206a01528a9efb4e3968 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 28 Apr 2025 20:31:46 +0200 Subject: [PATCH 040/183] ruff check --- flixopt/results.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index e08e14636..41c98be15 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -16,6 +16,7 @@ from . import io as fx_io from . import plotting from .core import TimeSeriesCollection +from .flow_system import FlowSystem if TYPE_CHECKING: import pyvis @@ -181,7 +182,9 @@ def __init__( if 'Flows' not in self.solution.attrs: warnings.warn( 'No Data about flows found in the results. This data is only included since v2.2.0. Some functionality ' - 'is not availlable. We recommend to evaluate your results with a version <2.2.0.') + 'is not availlable. We recommend to evaluate your results with a version <2.2.0.', + stacklevel=2, + ) self.flows = {} else: self.flows = { @@ -393,7 +396,7 @@ def sizes( start: Optional source node(s) to filter by. Can be a single node name or a list of names. end: Optional destination node(s) to filter by. Can be a single node name or a list of names. component: Optional component(s) to filter by. Can be a single component name or a list of names. - + Further usage: Convert the dataarray to a dataframe: >>>results.sizes().to_pandas() @@ -1536,11 +1539,11 @@ def filter_dataarray_by_coord( **kwargs: Optional[Union[str, List[str]]] ) -> xr.DataArray: """Filter flows by node and component attributes. - + Filters are applied in the order they are specified. All filters must match for an edge to be included. - + To recombine filtered dataarrays, use `xr.concat`. - + xr.concat([res.sizes(start='Fernwärme'), res.sizes(end='Fernwärme')], dim='flow') Args: @@ -1580,8 +1583,8 @@ def apply_filter(array, coord_name: str, coord_values: Union[Any, List[Any]]): try: for coord, values in filters.items(): da = apply_filter(da, coord, values) - except ValueError: - raise ValueError(f"No edges match criteria: {filters}") + except ValueError as e: + raise ValueError(f"No edges match criteria: {filters}") from e # Verify results exist if da.size == 0: From c64d12eef6bf7f3d92aa326e4e0d46924f744d6c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:04:00 +0200 Subject: [PATCH 041/183] Scenarios/deprecation (#258) * Deprecate .active_timesteps * Improve logger warning * Starting release notes --- docs/release-notes/v2.2.0.md | 55 ++++++++++++++++++++++++++++++++++++ flixopt/calculation.py | 49 ++++++++++++++++++++++---------- flixopt/components.py | 4 +-- flixopt/core.py | 6 ++-- tests/test_timeseries.py | 2 +- 5 files changed, 95 insertions(+), 21 deletions(-) create mode 100644 docs/release-notes/v2.2.0.md diff --git a/docs/release-notes/v2.2.0.md b/docs/release-notes/v2.2.0.md new file mode 100644 index 000000000..3cf7eef8d --- /dev/null +++ b/docs/release-notes/v2.2.0.md @@ -0,0 +1,55 @@ +# Release v2.2.0 + +**Release Date:** YYYY-MM-DD + +## What's New + +### Scenarios +Scenarios are a new feature of flixopt. They can be used to model uncertainties in the flow system, such as: +* Different demand profiles +* Different price forecasts +* Different weather conditions +* Different climate conditions +The might also be used to model an evolving system with multiple investment periods. Each **scenario** might be a new year, a new month, or a new day, with a different set of investment decisions to take. + +The weighted sum of the total objective effect of each scenario is used as the objective of the optimization. + +#### Investments and scenarios +Scenarios allow for more flexibility in investment decisions. +You can decide to allow different investment decisions for each scenario, or to allow a single investment decision for a subset of all scnarios, while not allowing for an invest in others. +This enables the following use cases: +* Find the best investment decision for each scenario individually +* Find the best overall investment decision for possible scenarios (robust decision-making) +* Find the best overall investment decision for a subset of all scenarios + +The last one might be useful if you want to model a system with multiple investment periods, where one investment decision is made for more than one scenario. +This might occur when scenarios represent years or months, while an investment decision influences the system for multiple years or months. + + +## Other new features +* Balanced storage - Storage charging and discharging sizes can now be forced to be equal in when optimizing their size. +* Feature 2 - Description + +## Improvements + +* Improvement 1 - Description +* Improvement 2 - Description + +## Bug Fixes + +* Fixed issue with X +* Resolved problem with Y + +## Breaking Changes + +* Change 1 - Migration instructions +* Change 2 - Migration instructions + +## Deprecations + +* Feature X will be removed in v{next_version} + +## Dependencies + +* Added dependency X v1.2.3 +* Updated dependency Y to v2.0.0 \ No newline at end of file diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 2024739ea..942b63a81 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -12,6 +12,7 @@ import math import pathlib import timeit +import warnings from typing import Any, Dict, List, Optional, Union import numpy as np @@ -43,22 +44,31 @@ def __init__( self, name: str, flow_system: FlowSystem, - active_timesteps: Optional[pd.DatetimeIndex] = None, + selected_timesteps: Optional[pd.DatetimeIndex] = None, selected_scenarios: Optional[pd.Index] = None, folder: Optional[pathlib.Path] = None, + active_timesteps: Optional[pd.DatetimeIndex] = None, ): """ Args: name: name of calculation flow_system: flow_system which should be calculated - active_timesteps: timesteps which should be used for calculation. If None, then all timesteps are used. + selected_timesteps: timesteps which should be used for calculation. If None, then all timesteps are used. selected_scenarios: scenarios which should be used for calculation. If None, then all scenarios are used. folder: folder where results should be saved. If None, then the current working directory is used. + active_timesteps: Deprecated. Use selected_timesteps instead. """ + if active_timesteps is not None: + warnings.warn( + 'active_timesteps is deprecated. Use selected_timesteps instead.', + DeprecationWarning, + stacklevel=2, + ) + selected_timesteps = active_timesteps self.name = name self.flow_system = flow_system self.model: Optional[SystemModel] = None - self.active_timesteps = active_timesteps + self.selected_timesteps = selected_timesteps self.selected_scenarios = selected_scenarios self.durations = {'modeling': 0.0, 'solving': 0.0, 'saving': 0.0} @@ -133,6 +143,15 @@ def summary(self): 'Config': CONFIG.to_dict(), } + @property + def active_timesteps(self) -> pd.DatetimeIndex: + warnings.warn( + 'active_timesteps is deprecated. Use selected_timesteps instead.', + DeprecationWarning, + stacklevel=2, + ) + return self.selected_timesteps + class FullCalculation(Calculation): """ @@ -189,7 +208,7 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma def _activate_time_series(self): self.flow_system.transform_data() self.flow_system.time_series_collection.set_selection( - timesteps=self.active_timesteps, scenarios=self.selected_scenarios + timesteps=self.selected_timesteps, scenarios=self.selected_scenarios ) @@ -204,7 +223,7 @@ def __init__( flow_system: FlowSystem, aggregation_parameters: AggregationParameters, components_to_clusterize: Optional[List[Component]] = None, - active_timesteps: Optional[pd.DatetimeIndex] = None, + selected_timesteps: Optional[pd.DatetimeIndex] = None, folder: Optional[pathlib.Path] = None, ): """ @@ -218,13 +237,13 @@ def __init__( components_to_clusterize: List of Components to perform aggregation on. If None, then all components are aggregated. This means, teh variables in the components are equalized to each other, according to the typical periods computed in the DataAggregation - active_timesteps: pd.DatetimeIndex or None + selected_timesteps: pd.DatetimeIndex or None list with indices, which should be used for calculation. If None, then all timesteps are used. folder: folder where results should be saved. If None, then the current working directory is used. """ if flow_system.time_series_collection.scenarios is not None: raise ValueError('Aggregation is not supported for scenarios yet. Please use FullCalculation instead.') - super().__init__(name, flow_system, active_timesteps, folder=folder) + super().__init__(name, flow_system, selected_timesteps, folder=folder) self.aggregation_parameters = aggregation_parameters self.components_to_clusterize = components_to_clusterize self.aggregation = None @@ -342,7 +361,7 @@ def __init__( self.segment_names = [ f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment)) ] - self.active_timesteps_per_segment = self._calculate_timesteps_of_segment() + self.selected_timesteps_per_segment = self._calculate_timesteps_of_segment() assert timesteps_per_segment > 2, 'The Segment length must be greater 2, due to unwanted internal side effects' assert self.timesteps_per_segment_with_overlap <= len(self.all_timesteps), ( @@ -368,7 +387,7 @@ def do_modeling_and_solve( logger.info(f'{" Segmented Solving ":#^80}') for i, (segment_name, timesteps_of_segment) in enumerate( - zip(self.segment_names, self.active_timesteps_per_segment, strict=False) + zip(self.segment_names, self.selected_timesteps_per_segment, strict=False) ): if self.sub_calculations: self._transfer_start_values(i) @@ -379,7 +398,7 @@ def do_modeling_and_solve( ) calculation = FullCalculation( - f'{self.name}-{segment_name}', self.flow_system, active_timesteps=timesteps_of_segment + f'{self.name}-{segment_name}', self.flow_system, selected_timesteps=timesteps_of_segment ) self.sub_calculations.append(calculation) calculation.do_modeling() @@ -413,9 +432,9 @@ def _transfer_start_values(self, segment_index: int): This function gets the last values of the previous solved segment and inserts them as start values for the next segment """ - timesteps_of_prior_segment = self.active_timesteps_per_segment[segment_index - 1] + timesteps_of_prior_segment = self.selected_timesteps_per_segment[segment_index - 1] - start = self.active_timesteps_per_segment[segment_index][0] + start = self.selected_timesteps_per_segment[segment_index][0] start_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - self.nr_of_previous_values] end_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - 1] @@ -444,12 +463,12 @@ def _reset_start_values(self): comp.initial_charge_state = self._original_start_values[comp.label_full] def _calculate_timesteps_of_segment(self) -> List[pd.DatetimeIndex]: - active_timesteps_per_segment = [] + selected_timesteps_per_segment = [] for i, _ in enumerate(self.segment_names): start = self.timesteps_per_segment * i end = min(start + self.timesteps_per_segment_with_overlap, len(self.all_timesteps)) - active_timesteps_per_segment.append(self.all_timesteps[start:end]) - return active_timesteps_per_segment + selected_timesteps_per_segment.append(self.all_timesteps[start:end]) + return selected_timesteps_per_segment @property def timesteps_per_segment_with_overlap(self): diff --git a/flixopt/components.py b/flixopt/components.py index 234418694..e02f5c03a 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -87,8 +87,8 @@ def _plausibility_checks(self) -> None: for flow in self.flows.values(): if isinstance(flow.size, InvestParameters) and flow.size.fixed_size is None: logger.warning( - f'Piecewise_conversion (in {self.label_full}) and variable size ' - f'(in flow {flow.label_full}) do not make sense together!' + f'Using a FLow with a fixed size ({flow.label_full}) AND a piecewise_conversion ' + f'(in {self.label_full}) and variable size is uncommon. Please check if this is intended!' ) def transform_data(self, flow_system: 'FlowSystem'): diff --git a/flixopt/core.py b/flixopt/core.py index 5d24e46e4..758deb1aa 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -688,7 +688,7 @@ def to_json(self, path: Optional[pathlib.Path] = None) -> Dict[str, Any]: # Save to file if path is provided if path is not None: - indent = 4 if len(self.active_timesteps) <= 480 else None + indent = 4 if len(self.selected_timesteps) <= 480 else None with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=indent, ensure_ascii=False) @@ -718,7 +718,7 @@ def selected_data(self) -> xr.DataArray: return self._stored_data.sel(**self._valid_selector) @property - def active_timesteps(self) -> Optional[pd.DatetimeIndex]: + def selected_timesteps(self) -> Optional[pd.DatetimeIndex]: """Get the current active timesteps, or None if no time dimension.""" if not self.has_time_dim: return None @@ -749,7 +749,7 @@ def update_stored_data(self, value: xr.DataArray) -> None: """ new_data = DataConverter.as_dataarray( value, - timesteps=self.active_timesteps if self.has_time_dim else None, + timesteps=self.selected_timesteps if self.has_time_dim else None, scenarios=self.active_scenarios if self.has_scenario_dim else None, ) diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index 237935e59..bb35231a6 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -675,7 +675,7 @@ def test_selection_propagation_with_scenarios( # Clear selections sample_scenario_allocator.set_selection() assert ts1._selected_timesteps is None - assert ts1.active_timesteps.equals(sample_scenario_allocator.timesteps) + assert ts1.selected_timesteps.equals(sample_scenario_allocator.timesteps) assert ts1._selected_scenarios is None assert ts1.active_scenarios.equals(sample_scenario_allocator.scenarios) assert ts1.selected_data.shape == (5, 3) # Back to full shape From 0499497d3c7dc026b8e7348cd4af9177eea83917 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 28 Apr 2025 22:13:48 +0200 Subject: [PATCH 042/183] Bugfix in plausibility_check: Index 0 --- flixopt/components.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index e02f5c03a..e4c220e05 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -248,9 +248,9 @@ def _plausibility_checks(self) -> None: minimum_capacity = self.capacity_in_flow_hours # initial capacity >= allowed min for maximum_size: - minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) + minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) # initial capacity <= allowed max for minimum_size: - maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) + maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0) # TODO: index=1 ??? I think index 0 if (self.initial_charge_state > maximum_inital_capacity).any(): From ee005777eb8a363691cde276666c95832005b056 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 29 Apr 2025 10:32:58 +0200 Subject: [PATCH 043/183] Set bargap to 0 in stacked bars --- flixopt/plotting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/plotting.py b/flixopt/plotting.py index 6a2170674..8537d3815 100644 --- a/flixopt/plotting.py +++ b/flixopt/plotting.py @@ -258,7 +258,7 @@ def with_plotly( fig.update_layout( barmode='relative', - bargap=0.2, # No space between bars + bargap=0, # No space between bars bargroupgap=0, # No space between grouped bars ) if style == 'grouped_bar': From a11ed92a20bacf46605d504a938f506aa12922b1 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 29 Apr 2025 11:05:03 +0200 Subject: [PATCH 044/183] Ensure the size is always properly indexed in results. --- flixopt/results.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index 41c98be15..26dd9a770 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -15,7 +15,7 @@ from . import io as fx_io from . import plotting -from .core import TimeSeriesCollection +from .core import TimeSeriesCollection, DataConverter from .flow_system import FlowSystem if TYPE_CHECKING: @@ -1169,7 +1169,10 @@ def size(self) -> xr.DataArray: if name in self.solution: return self.solution[name] try: - return xr.DataArray(self._calculation_results.flow_system.flows[self.label].size).rename(name) + return DataConverter.as_dataarray( + self._calculation_results.flow_system.flows[self.label].size, + scenarios=self._calculation_results.scenarios + ).rename(name) except _FlowSystemRestorationError: logger.critical(f'Size of flow {self.label}.size not availlable. Returning NaN') return xr.DataArray(np.nan).rename(name) From 240024405e1d9500aca527727f866d5db3fe981d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 29 Apr 2025 13:48:52 +0200 Subject: [PATCH 045/183] ruff check --- flixopt/results.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/results.py b/flixopt/results.py index 26dd9a770..f0f0b2b0e 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -15,7 +15,7 @@ from . import io as fx_io from . import plotting -from .core import TimeSeriesCollection, DataConverter +from .core import DataConverter, TimeSeriesCollection from .flow_system import FlowSystem if TYPE_CHECKING: From 0f9b30ab8fc201d681721889bb92064e46618d7b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 5 May 2025 14:28:29 +0200 Subject: [PATCH 046/183] BUGFIX in extract data, that causes coords in linopy to be incorrect (scalar xarray.DataArrays) --- flixopt/core.py | 2 +- flixopt/effects.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/flixopt/core.py b/flixopt/core.py index 758deb1aa..bec472aaa 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -1481,5 +1481,5 @@ def extract_data( if isinstance(data, xr.DataArray): return data if isinstance(data, (int, float, np.integer, np.floating)): - return xr.DataArray(data) + return data raise TypeError(f'Unsupported data type: {type(data).__name__}') diff --git a/flixopt/effects.py b/flixopt/effects.py index 914100362..44687fffe 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -509,6 +509,10 @@ def calculate_all_conversion_paths( # Add new path to queue for further exploration queue.append((target, indirect_factor, new_path)) + # Convert all values to DataArrays + result = {key: value if isinstance(value, xr.DataArray) else xr.DataArray(value) + for key, value in result.items()} + return result From 26e89a95083ae76c88059d9c42e30a621d8749b9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 5 May 2025 14:35:23 +0200 Subject: [PATCH 047/183] Improve yaml formatting for model documentation (#259) --- flixopt/io.py | 79 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 25 deletions(-) diff --git a/flixopt/io.py b/flixopt/io.py index 1ef9578e5..05ae6741d 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -79,15 +79,17 @@ def _save_to_yaml(data, output_file='formatted_output.yaml'): output_file (str): Path to output YAML file """ # Process strings to normalize all newlines and handle special patterns - processed_data = _process_complex_strings(data) + processed_data = _normalize_complex_data(data) # Define a custom representer for strings def represent_str(dumper, data): - # Use literal block style (|) for any string with newlines + # Use literal block style (|) for multi-line strings if '\n' in data: + # Clean up formatting for literal block style + data = data.strip() # Remove leading/trailing whitespace return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|') - # Use quoted style for strings with special characters to ensure proper parsing + # Use quoted style for strings with special characters elif any(char in data for char in ':`{}[]#,&*!|>%@'): return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='"') @@ -97,53 +99,80 @@ def represent_str(dumper, data): # Add the string representer to SafeDumper yaml.add_representer(str, represent_str, Dumper=yaml.SafeDumper) + # Configure dumper options for better formatting + class CustomDumper(yaml.SafeDumper): + def increase_indent(self, flow=False, indentless=False): + return super(CustomDumper, self).increase_indent(flow, False) + # Write to file with settings that ensure proper formatting with open(output_file, 'w', encoding='utf-8') as file: yaml.dump( processed_data, file, - Dumper=yaml.SafeDumper, + Dumper=CustomDumper, sort_keys=False, # Preserve dictionary order default_flow_style=False, # Use block style for mappings - width=float('inf'), # Don't wrap long lines + width=1000, # Set a reasonable line width allow_unicode=True, # Support Unicode characters + indent=2, # Set consistent indentation ) -def _process_complex_strings(data): +def _normalize_complex_data(data): """ - Process dictionary data recursively with comprehensive string normalization. - Handles various types of strings and special formatting. + Recursively normalize strings in complex data structures. + + Handles dictionaries, lists, and strings, applying various text normalization + rules while preserving important formatting elements. Args: - data: The data to process (dict, list, str, or other) + data: Any data type (dict, list, str, or primitive) Returns: - Processed data with normalized strings + Data with all strings normalized according to defined rules """ if isinstance(data, dict): - return {k: _process_complex_strings(v) for k, v in data.items()} + return {key: _normalize_complex_data(value) for key, value in data.items()} + elif isinstance(data, list): - return [_process_complex_strings(item) for item in data] + return [_normalize_complex_data(item) for item in data] + elif isinstance(data, str): - # Step 1: Normalize line endings to \n - normalized = data.replace('\r\n', '\n').replace('\r', '\n') + return _normalize_string_content(data) - # Step 2: Handle escaped newlines with robust regex - normalized = re.sub(r'(? Dict[str, str]: From c0cbaaeb88cb3100866085a7ff6d79a4321634fd Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 5 May 2025 15:04:57 +0200 Subject: [PATCH 048/183] Make the size/capacity a TimeSeries (#260) --- flixopt/components.py | 6 +++++- flixopt/elements.py | 16 ++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index e4c220e05..5a986ede0 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -128,7 +128,7 @@ def __init__( label: str, charging: Flow, discharging: Flow, - capacity_in_flow_hours: Union[Scalar, InvestParameters], + capacity_in_flow_hours: Union[ScenarioData, InvestParameters], relative_minimum_charge_state: TimestepData = 0, relative_maximum_charge_state: TimestepData = 1, initial_charge_state: Union[ScenarioData, Literal['lastValueOfSim']] = 0, @@ -226,6 +226,10 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: ) if isinstance(self.capacity_in_flow_hours, InvestParameters): self.capacity_in_flow_hours.transform_data(flow_system, f'{self.label_full}|InvestParameters') + else: + self.capacity_in_flow_hours = flow_system.create_time_series( + f'{self.label_full}|capacity_in_flow_hours', self.capacity_in_flow_hours, has_time_dim=False + ) def _plausibility_checks(self) -> None: """ diff --git a/flixopt/elements.py b/flixopt/elements.py index 11246e6d9..d216c2ca7 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -154,7 +154,7 @@ def __init__( self, label: str, bus: str, - size: Union[Scalar, InvestParameters] = None, + size: Union[ScenarioData, InvestParameters] = None, fixed_relative_profile: Optional[TimestepData] = None, relative_minimum: TimestepData = 0, relative_maximum: TimestepData = 1, @@ -265,6 +265,8 @@ def transform_data(self, flow_system: 'FlowSystem'): self.on_off_parameters.transform_data(flow_system, self.label_full) if isinstance(self.size, InvestParameters): self.size.transform_data(flow_system, self.label_full) + else: + self.size = flow_system.create_time_series(f'{self.label_full}|size', self.size, has_time_dim=False) def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: infos = super().infos(use_numpy, use_element_label) @@ -282,8 +284,8 @@ def _plausibility_checks(self) -> None: if np.any(self.relative_minimum > self.relative_maximum): raise PlausibilityError(self.label_full + ': Take care, that relative_minimum <= relative_maximum!') - if ( - self.size == CONFIG.modeling.BIG and self.fixed_relative_profile is not None + if not isinstance(self.size, InvestParameters) and ( + np.any(self.size == CONFIG.modeling.BIG) and self.fixed_relative_profile is not None ): # Default Size --> Most likely by accident logger.warning( f'Flow "{self.label}" has no size assigned, but a "fixed_relative_profile". ' @@ -453,10 +455,12 @@ def flow_rate_bounds_on(self) -> Tuple[TimestepData, TimestepData]: relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative size = self.element.size if not isinstance(size, InvestParameters): - return relative_minimum * size, relative_maximum * size + return relative_minimum * extract_data(size), relative_maximum * extract_data(size) if size.fixed_size is not None: - return size.fixed_size * relative_minimum, size.fixed_size * relative_maximum - return size.minimum_size * relative_minimum, size.maximum_size * relative_maximum + return (relative_minimum * extract_data(size.fixed_size), + relative_maximum * extract_data(size.fixed_size)) + return (relative_minimum * extract_data(size.minimum_size), + relative_maximum * extract_data(size.maximum_size)) @property def flow_rate_lower_bound_relative(self) -> TimestepData: From 67ebfbbe75870a3f9226f9435c00748f30cc554d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 13 May 2025 11:55:29 +0200 Subject: [PATCH 049/183] Scenarios/plot network (#262) * Catch bug in plot_network with 2D arrays * Add plot_network() to test_io.py --- flixopt/structure.py | 2 ++ tests/test_io.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/flixopt/structure.py b/flixopt/structure.py index 7c7772dad..8d4779457 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -636,6 +636,8 @@ def describe_numpy_arrays(arr: np.ndarray) -> Union[str, List]: def normalized_center_of_mass(array: Any) -> float: # position in array (0 bis 1 normiert) + if array.ndim >= 2: # No good way to calculate center of mass for 2D arrays + return np.nan positions = np.linspace(0, 1, len(array)) # weights w_i # mass center if np.sum(array) == 0: diff --git a/tests/test_io.py b/tests/test_io.py index 2b3a03399..541e5b2a4 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -28,6 +28,7 @@ def test_flow_system_file_io(flow_system, highs_solver): calculation_0 = fx.FullCalculation('IO', flow_system=flow_system) calculation_0.do_modeling() calculation_0.solve(highs_solver) + calculation_0.flow_system.plot_network() calculation_0.results.to_file() paths = CalculationResultsPaths(calculation_0.folder, calculation_0.name) @@ -36,6 +37,7 @@ def test_flow_system_file_io(flow_system, highs_solver): calculation_1 = fx.FullCalculation('Loaded_IO', flow_system=flow_system_1) calculation_1.do_modeling() calculation_1.solve(highs_solver) + calculation_1.flow_system.plot_network() assert_almost_equal_numeric( calculation_0.results.model.objective.value, From 9edd1fa13da2aeb527007e1e6e80f31a75ec9b0b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 13 May 2025 12:58:11 +0200 Subject: [PATCH 050/183] Update deploy-docs.yaml: Run on Release publishing instead of creation and only run for stable releases (vx.y.z) --- .github/workflows/deploy-docs.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index 73a3f0b1f..d582bb3eb 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -1,9 +1,11 @@ -name: Documentation +name: Deploy Stable Documentation on: release: - types: [created] # Automatically deploy docs on release - workflow_dispatch: # Allow manual triggering + types: [published] + tags: + # Only match stable version patterns (no pre-release identifiers) + - 'v[0-9]+.[0-9]+.[0-9]+' jobs: deploy-docs: From d3c0c48b77817495971c44da3cc10713ac0912bf Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 13 May 2025 13:42:57 +0200 Subject: [PATCH 051/183] Bugfix DataConverter and add tests (#263) --- flixopt/core.py | 8 ++++---- tests/test_dataconverter.py | 39 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index bec472aaa..ea447e652 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -255,7 +255,7 @@ def _broadcast_time_to_scenarios( return data.copy(deep=True) # Broadcast values - values = np.tile(data.values, (len(coords['scenario']), 1)).T # Tile seems to be faster than repeat() + values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) return xr.DataArray(values.copy(), coords=coords, dims=dims) @staticmethod @@ -278,7 +278,7 @@ def _broadcast_scenario_to_time( raise ConversionError("Source scenario coordinates don't match target scenario coordinates") # Broadcast values - values = np.repeat(data.values[:, np.newaxis], len(coords['time']), axis=1) + values = np.repeat(data.values[:, np.newaxis], len(coords['time']), axis=1).T return xr.DataArray(values.copy(), coords=coords, dims=dims) @staticmethod @@ -361,7 +361,7 @@ def _convert_ndarray_two_dims(data: np.ndarray, coords: Dict[str, pd.Index], dim return xr.DataArray(values, coords=coords, dims=dims) elif data.shape[0] == scenario_length: # Broadcast across time - values = np.tile(data, (time_length, 1)) + values = np.repeat(data[np.newaxis, :], time_length, axis=0) return xr.DataArray(values, coords=coords, dims=dims) else: raise ConversionError(f"1D array length {data.shape[0]} doesn't match either dimension") @@ -414,7 +414,7 @@ def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[st # Case 1: Series is indexed by time if data.index.equals(coords['time']): # Broadcast across scenarios - values = np.tile(data.values[:, np.newaxis], (1, len(coords['scenario']))) + values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) return xr.DataArray(values.copy(), coords=coords, dims=dims) # Case 2: Series is indexed by scenario diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index a50754301..0484d4aac 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -451,6 +451,45 @@ def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_i with pytest.raises(ConversionError): DataConverter.as_dataarray(wrong_length, sample_time_index, sample_scenario_index) +class TestDataArrayBroadcasting: + """Tests for broadcasting DataArrays.""" + def test_broadcast_1d_array_to_2d(self, sample_time_index, sample_scenario_index): + """Test broadcasting a 1D array to all scenarios.""" + arr_1d = np.array([1, 2, 3, 4, 5]) + + xr.testing.assert_equal( + DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index), + xr.DataArray( + np.array([arr_1d] * 3).T, + coords=(sample_time_index, sample_scenario_index) + ) + ) + + arr_1d = np.array([1, 2, 3]) + xr.testing.assert_equal( + DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index), + xr.DataArray( + np.array([arr_1d] * 5), + coords=(sample_time_index, sample_scenario_index) + ) + ) + + def test_broadcast_1d_array_to_1d(self, sample_time_index,): + """Test broadcasting a 1D array to all scenarios.""" + arr_1d = np.array([1, 2, 3, 4, 5]) + + xr.testing.assert_equal( + DataConverter.as_dataarray(arr_1d, sample_time_index), + xr.DataArray( + arr_1d, + coords=(sample_time_index,) + ) + ) + + arr_1d = np.array([1, 2, 3]) + with pytest.raises(ConversionError): + DataConverter.as_dataarray(arr_1d, sample_time_index) + class TestEdgeCases: """Tests for edge cases and special scenarios.""" From e6e680cb66423379c089e675e536f5d028838d9a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 13 May 2025 14:28:13 +0200 Subject: [PATCH 052/183] Fix doc deployment to not publish on non stable releases --- .github/workflows/deploy-docs.yaml | 50 +++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy-docs.yaml b/.github/workflows/deploy-docs.yaml index d582bb3eb..dfb3f3f87 100644 --- a/.github/workflows/deploy-docs.yaml +++ b/.github/workflows/deploy-docs.yaml @@ -3,15 +3,38 @@ name: Deploy Stable Documentation on: release: types: [published] - tags: - # Only match stable version patterns (no pre-release identifiers) - - 'v[0-9]+.[0-9]+.[0-9]+' jobs: + check-release: + runs-on: ubuntu-latest + outputs: + is_stable: ${{ steps.check_version.outputs.is_stable }} + version: ${{ steps.check_version.outputs.version }} + steps: + - name: Check if stable release + id: check_version + run: | + # Extract version from the tag + VERSION="${GITHUB_REF#refs/tags/v}" + echo "Raw version: $VERSION" + + # Check if version contains any pre-release identifiers using regex + if [[ $VERSION =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "is_stable=true" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Stable version detected: $VERSION" + else + echo "is_stable=false" >> $GITHUB_OUTPUT + echo "Pre-release version detected: $VERSION" + fi + deploy-docs: + needs: check-release + if: needs.check-release.outputs.is_stable == 'true' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - name: Checkout repository + uses: actions/checkout@v4 with: fetch-depth: 0 # Fetch all history for proper versioning @@ -20,7 +43,8 @@ jobs: git config user.name github-actions[bot] git config user.email 41898282+github-actions[bot]@users.noreply.github.com - - uses: actions/setup-python@v5 + - name: Set up Python + uses: actions/setup-python@v5 with: python-version: 3.11 @@ -31,5 +55,17 @@ jobs: - name: Deploy docs run: | - VERSION=${GITHUB_REF#refs/tags/v} - mike deploy --push --update-aliases $VERSION latest \ No newline at end of file + VERSION="${{ needs.check-release.outputs.version }}" + echo "Deploying documentation for version $VERSION" + mike deploy --push --update-aliases $VERSION latest + + - name: Verify deployment + run: | + # Simple verification that the deployment succeeded + git checkout gh-pages + if [ -d "${{ needs.check-release.outputs.version }}" ]; then + echo "Documentation successfully deployed" + else + echo "Documentation deployment failed!" + exit 1 + fi \ No newline at end of file From 3d89b74f0dcab9f9bfffac7fab2d556861528baa Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 15 May 2025 15:16:23 +0200 Subject: [PATCH 053/183] Remove unused code --- flixopt/results.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index f0f0b2b0e..bd0abaa5e 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -202,7 +202,6 @@ def __init__( self._flow_hours = None self._sizes = None self._effects_per_component = {'operation': None, 'invest': None, 'total': None} - self._flow_network_info_ = None def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults', 'FlowResults']: if key in self.components: @@ -425,17 +424,6 @@ def _assign_flow_coords(self, da: xr.DataArray): da = da.transpose(*(existing_dims + ['flow'])) return da - def _get_flow_network_info(self) -> Dict[str, Dict[str, str]]: - flow_network_info = {} - - for flow in self.flows.values(): - flow_network_info[flow.label] = { - 'label': flow.label, - 'start': flow.start, - 'end': flow.end, - } - return flow_network_info - def get_effect_shares( self, element: str, From cc772a46528f1d7361efa38f0fae90ab2eca3842 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 16 May 2025 12:17:29 +0200 Subject: [PATCH 054/183] Remove legend placing for better auto placing in plotly --- flixopt/plotting.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/flixopt/plotting.py b/flixopt/plotting.py index 8537d3815..91fc5e7e7 100644 --- a/flixopt/plotting.py +++ b/flixopt/plotting.py @@ -348,14 +348,6 @@ def with_plotly( plot_bgcolor='rgba(0,0,0,0)', # Transparent background paper_bgcolor='rgba(0,0,0,0)', # Transparent paper background font=dict(size=14), # Increase font size for better readability - legend=dict( - orientation='h', # Horizontal legend - yanchor='bottom', - y=-0.3, # Adjusts how far below the plot it appears - xanchor='center', - x=0.5, - title_text=None, # Removes legend title for a cleaner look - ), ) return fig @@ -397,7 +389,6 @@ def with_matplotlib( - If `style` is 'stacked_bar', bars are stacked for both positive and negative values. Negative values are stacked separately without extra labels in the legend. - If `style` is 'line', stepped lines are drawn for each data series. - - The legend is placed below the plot to accommodate multiple data series. """ assert style in ['stacked_bar', 'line'], f"'style' must be one of {['stacked_bar', 'line']} for matplotlib" @@ -1137,7 +1128,6 @@ def create_pie_trace(data_series, side): paper_bgcolor='rgba(0,0,0,0)', # Transparent paper background font=dict(size=14), margin=dict(t=80, b=50, l=30, r=30), - legend=dict(orientation='h', yanchor='bottom', y=-0.2, xanchor='center', x=0.5, font=dict(size=12)), ) return fig From d92349ed19be9d0a774bdb5446fbc5d20d42ac4a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 16 May 2025 13:12:57 +0200 Subject: [PATCH 055/183] Fix plotly dependency --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f3b48273f..0078d11a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,10 +38,10 @@ dependencies = [ "linopy >= 0.5.1", "netcdf4 >= 1.6.1", "rich >= 13.0.1", - "highspy >= 1.5.3", # Default solver - "pandas >= 2, < 3", # Used in post-processing + "highspy >= 1.5.3", # Default solver + "pandas >= 2, < 3", # Used in post-processing "matplotlib >= 3.5.2", # Used in post-processing - "plotly >= 5.15", # Used in post-processing + "plotly >= 5.15, < 6", # Used in post-processing "tomli >= 2.0.1" # TOML parser (only needed until python 3.11) ] From 5c2900a4290479af584bb9ca3d78474be6138752 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 19 May 2025 21:45:08 +0200 Subject: [PATCH 056/183] Improve validation when adding new effects --- flixopt/effects.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flixopt/effects.py b/flixopt/effects.py index 44687fffe..8affc933b 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -313,7 +313,10 @@ def __contains__(self, item: Union[str, 'Effect']) -> bool: if isinstance(item, str): return item in self.effects # Check if the label exists elif isinstance(item, Effect): - return item in self.effects.values() # Check if the object exists + if item.label_full in self.effects: + return True + if item in self.effects.values(): # Check if the object exists + return True return False @property From 8d3bbe920296928086f9a098cb52698cbcbde886 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 14 Jun 2025 17:35:02 +0200 Subject: [PATCH 057/183] Moved release notes to CHANGELOG.md --- CHANGELOG.md | 27 ++++++++++++++++++ docs/release-notes/v2.2.0.md | 55 ------------------------------------ 2 files changed, 27 insertions(+), 55 deletions(-) delete mode 100644 docs/release-notes/v2.2.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d692d5e5..ff01fa498 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## What's New + +### Scenarios +Scenarios are a new feature of flixopt. They can be used to model uncertainties in the flow system, such as: +* Different demand profiles +* Different price forecasts +* Different weather conditions +They might also be used to model an evolving system with multiple investment periods. Each **scenario** might be a new year, a new month, or a new day, with a different set of investment decisions to take. + +The weighted sum of the total objective effect of each scenario is used as the objective of the optimization. + +#### Investments and scenarios +Scenarios allow for more flexibility in investment decisions. +You can decide to allow different investment decisions for each scenario, or to allow a single investment decision for a subset of all scenarios, while not allowing for an invest in others. +This enables the following use cases: +* Find the best investment decision for each scenario individually +* Find the best overall investment decision for possible scenarios (robust decision-making) +* Find the best overall investment decision for a subset of all scenarios + +The last one might be useful if you want to model a system with multiple investment periods, where one investment decision is made for more than one scenario. +This might occur when scenarios represent years or months, while an investment decision influences the system for multiple years or months. + + +### Other new features +* Balanced storage - Storage charging and discharging sizes can now be forced to be equal in when optimizing their size. +* Feature 2 - Description + ## [2.1.2] - 2025-06-14 ### Fixed diff --git a/docs/release-notes/v2.2.0.md b/docs/release-notes/v2.2.0.md deleted file mode 100644 index 3cf7eef8d..000000000 --- a/docs/release-notes/v2.2.0.md +++ /dev/null @@ -1,55 +0,0 @@ -# Release v2.2.0 - -**Release Date:** YYYY-MM-DD - -## What's New - -### Scenarios -Scenarios are a new feature of flixopt. They can be used to model uncertainties in the flow system, such as: -* Different demand profiles -* Different price forecasts -* Different weather conditions -* Different climate conditions -The might also be used to model an evolving system with multiple investment periods. Each **scenario** might be a new year, a new month, or a new day, with a different set of investment decisions to take. - -The weighted sum of the total objective effect of each scenario is used as the objective of the optimization. - -#### Investments and scenarios -Scenarios allow for more flexibility in investment decisions. -You can decide to allow different investment decisions for each scenario, or to allow a single investment decision for a subset of all scnarios, while not allowing for an invest in others. -This enables the following use cases: -* Find the best investment decision for each scenario individually -* Find the best overall investment decision for possible scenarios (robust decision-making) -* Find the best overall investment decision for a subset of all scenarios - -The last one might be useful if you want to model a system with multiple investment periods, where one investment decision is made for more than one scenario. -This might occur when scenarios represent years or months, while an investment decision influences the system for multiple years or months. - - -## Other new features -* Balanced storage - Storage charging and discharging sizes can now be forced to be equal in when optimizing their size. -* Feature 2 - Description - -## Improvements - -* Improvement 1 - Description -* Improvement 2 - Description - -## Bug Fixes - -* Fixed issue with X -* Resolved problem with Y - -## Breaking Changes - -* Change 1 - Migration instructions -* Change 2 - Migration instructions - -## Deprecations - -* Feature X will be removed in v{next_version} - -## Dependencies - -* Added dependency X v1.2.3 -* Updated dependency Y to v2.0.0 \ No newline at end of file From be6572de5552c31c94b0ed9b9d7777eea27cc4aa Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 14:35:49 +0200 Subject: [PATCH 058/183] Try to add to_dataset to Elements --- flixopt/flow_system.py | 521 +++++++++++++++++++++++++++-------------- flixopt/structure.py | 381 +++++++++++++++++++++++------- 2 files changed, 647 insertions(+), 255 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 93720de60..8887a6eae 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -30,6 +30,7 @@ class FlowSystem: """ A FlowSystem organizes the high level Elements (Components & Effects). + Uses xr.Dataset directly from its Interface elements instead of TimeSeriesCollection. """ def __init__( @@ -47,13 +48,15 @@ def __init__( This is needed to calculate previous durations (for example consecutive_on_hours). If you use an array, take care that its long enough to cover all previous values! """ - self.time_series_collection = TimeSeriesCollection( - timesteps=timesteps, - hours_of_last_timestep=hours_of_last_timestep, - hours_of_previous_timesteps=hours_of_previous_timesteps, + # Store timing information directly + self.timesteps = self._validate_timesteps(timesteps) + self.timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) + self.hours_per_timestep = self._calculate_hours_per_timestep(self.timesteps_extra) + self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( + timesteps, hours_of_previous_timesteps ) - # defaults: + # Element collections self.components: Dict[str, Component] = {} self.buses: Dict[str, Bus] = {} self.effects: EffectCollection = EffectCollection() @@ -61,60 +64,373 @@ def __init__( self._connected = False + @staticmethod + def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: + """Validate timesteps format and rename if needed.""" + if not isinstance(timesteps, pd.DatetimeIndex): + raise TypeError('timesteps must be a pandas DatetimeIndex') + if len(timesteps) < 2: + raise ValueError('timesteps must contain at least 2 timestamps') + if timesteps.name != 'time': + timesteps.name = 'time' + if not timesteps.is_monotonic_increasing: + raise ValueError('timesteps must be sorted') + return timesteps + + @staticmethod + def _create_timesteps_with_extra( + timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] + ) -> pd.DatetimeIndex: + """Create timesteps with an extra step at the end.""" + if hours_of_last_timestep is None: + hours_of_last_timestep = (timesteps[-1] - timesteps[-2]) / pd.Timedelta(hours=1) + + last_date = pd.DatetimeIndex([timesteps[-1] + pd.Timedelta(hours=hours_of_last_timestep)], name='time') + return pd.DatetimeIndex(timesteps.append(last_date), name='time') + + @staticmethod + def _calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: + """Calculate duration of each timestep.""" + hours_per_step = np.diff(timesteps_extra) / pd.Timedelta(hours=1) + return xr.DataArray( + hours_per_step, coords={'time': timesteps_extra[:-1]}, dims=['time'], name='hours_per_timestep' + ) + + @staticmethod + def _calculate_hours_of_previous_timesteps( + timesteps: pd.DatetimeIndex, hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] + ) -> Union[float, np.ndarray]: + """Calculate duration of regular timesteps.""" + if hours_of_previous_timesteps is not None: + return hours_of_previous_timesteps + # Calculate from the first interval + first_interval = timesteps[1] - timesteps[0] + return first_interval.total_seconds() / 3600 # Convert to hours + + def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: + """ + Create reference structure for FlowSystem following the Interface pattern. + Extracts all DataArrays from components, buses, and effects. + + Returns: + Tuple of (reference_structure, extracted_arrays_dict) + """ + reference_structure = { + '__class__': self.__class__.__name__, + 'timesteps_extra': [date.isoformat() for date in self.timesteps_extra], + 'hours_of_previous_timesteps': self.hours_of_previous_timesteps, + } + + all_extracted_arrays = {} + + # Add timing arrays directly + all_extracted_arrays['hours_per_timestep'] = self.hours_per_timestep + + # Extract from components + components_structure = {} + for comp_label, component in self.components.items(): + comp_structure, comp_arrays = self._extract_from_interface(component) + all_extracted_arrays.update(comp_arrays) + components_structure[comp_label] = comp_structure + reference_structure['components'] = components_structure + + # Extract from buses + buses_structure = {} + for bus_label, bus in self.buses.items(): + bus_structure, bus_arrays = self._extract_from_interface(bus) + all_extracted_arrays.update(bus_arrays) + buses_structure[bus_label] = bus_structure + reference_structure['buses'] = buses_structure + + # Extract from effects + effects_structure = {} + for effect in self.effects: + effect_structure, effect_arrays = self._extract_from_interface(effect) + all_extracted_arrays.update(effect_arrays) + effects_structure[effect.label] = effect_structure + reference_structure['effects'] = effects_structure + + return reference_structure, all_extracted_arrays + + def _extract_from_interface(self, interface_obj) -> Tuple[Dict, Dict[str, xr.DataArray]]: + """Extract arrays from an Interface object using its reference system.""" + if hasattr(interface_obj, '_create_reference_structure'): + return interface_obj._create_reference_structure() + else: + # Fallback for objects that don't have the new Interface methods + logger.warning(f"Object {interface_obj} doesn't have _create_reference_structure method") + return interface_obj.to_dict(), {} + + @classmethod + def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataArray]): + """ + Resolve reference structure back to actual objects. + Reuses the Interface pattern for consistency. + """ + if isinstance(structure, str) and structure.startswith(':::'): + # This is a reference to a DataArray + array_name = structure[3:] # Remove ":::" prefix + if array_name in arrays_dict: + return arrays_dict[array_name] + else: + logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") + return None + + elif isinstance(structure, list): + resolved_list = [] + for item in structure: + resolved_item = cls._resolve_reference_structure(item, arrays_dict) + if resolved_item is not None: + resolved_list.append(resolved_item) + return resolved_list + + elif isinstance(structure, dict): + # Check if this is a serialized Interface object + if structure.get('__class__') and structure['__class__'] in CLASS_REGISTRY: + # This is a nested Interface object - restore it recursively + nested_class = CLASS_REGISTRY[structure['__class__']] + # Remove the __class__ key and process the rest + nested_data = {k: v for k, v in structure.items() if k != '__class__'} + # Resolve references in the nested data + resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) + # Create the nested Interface object + return nested_class(**resolved_nested_data) + else: + # Regular dictionary - resolve references in values + resolved_dict = {} + for key, value in structure.items(): + resolved_value = cls._resolve_reference_structure(value, arrays_dict) + if resolved_value is not None or value is None: + resolved_dict[key] = resolved_value + return resolved_dict + + else: + return structure + + def to_dataset(self, constants_in_dataset: bool = True) -> xr.Dataset: + """ + Convert the FlowSystem to an xarray Dataset using the Interface pattern. + All DataArrays become dataset variables, structure goes to attrs. + + Args: + constants_in_dataset: If True, constants are included as Dataset variables. + + Returns: + xr.Dataset: Dataset containing all DataArrays with structure in attributes + """ + reference_structure, extracted_arrays = self._create_reference_structure() + + # Create the dataset with extracted arrays as variables and structure as attrs + ds = xr.Dataset(extracted_arrays, attrs=reference_structure) + return ds + + def as_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: + """ + Convert the object to a dictionary representation. + Now builds on the reference structure for consistency. + """ + reference_structure, _ = self._create_reference_structure() + + if data_mode == 'data': + return reference_structure + elif data_mode == 'stats': + # For stats mode, we might want to process the structure further + return fx_io.remove_none_and_empty(reference_structure) + else: # name mode + return reference_structure + @classmethod - def from_dataset(cls, ds: xr.Dataset): - timesteps_extra = pd.DatetimeIndex(ds.attrs['timesteps_extra'], name='time') - hours_of_last_timestep = TimeSeriesCollection.calculate_hours_per_timestep(timesteps_extra).isel(time=-1).item() + def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': + """ + Create a FlowSystem from an xarray Dataset using the Interface pattern. - flow_system = FlowSystem( + Args: + ds: Dataset containing the FlowSystem data + + Returns: + FlowSystem instance + """ + # Get the reference structure from attrs + reference_structure = dict(ds.attrs) + + # Extract FlowSystem constructor parameters + timesteps_extra = pd.DatetimeIndex(reference_structure['timesteps_extra'], name='time') + hours_of_previous_timesteps = reference_structure['hours_of_previous_timesteps'] + + # Calculate hours_of_last_timestep from the timesteps + hours_of_last_timestep = float((timesteps_extra[-1] - timesteps_extra[-2]) / pd.Timedelta(hours=1)) + + # Create FlowSystem instance + flow_system = cls( timesteps=timesteps_extra[:-1], hours_of_last_timestep=hours_of_last_timestep, - hours_of_previous_timesteps=ds.attrs['hours_of_previous_timesteps'], + hours_of_previous_timesteps=hours_of_previous_timesteps, ) - structure = fx_io.insert_dataarray({key: ds.attrs[key] for key in ['components', 'buses', 'effects']}, ds) - flow_system.add_elements( - *[Bus.from_dict(bus) for bus in structure['buses'].values()] - + [Effect.from_dict(effect) for effect in structure['effects'].values()] - + [CLASS_REGISTRY[comp['__class__']].from_dict(comp) for comp in structure['components'].values()] - ) + # Create arrays dictionary from dataset variables + arrays_dict = {name: array for name, array in ds.data_vars.items()} + + # Restore components + components_structure = reference_structure.get('components', {}) + for comp_label, comp_data in components_structure.items(): + component = cls._resolve_reference_structure(comp_data, arrays_dict) + if not isinstance(component, Component): + logger.critical(f'Restoring component {comp_label} failed.') + flow_system._add_components(component) + + # Restore buses + buses_structure = reference_structure.get('buses', {}) + for bus_label, bus_data in buses_structure.items(): + bus = cls._resolve_reference_structure(bus_data, arrays_dict) + if not isinstance(bus, Bus): + logger.critical(f'Restoring component {bus_label} failed.') + flow_system._add_buses(bus) + + # Restore effects + effects_structure = reference_structure.get('effects', {}) + for effect_label, effect_data in effects_structure.items(): + effect = cls._resolve_reference_structure(effect_data, arrays_dict) + + if not isinstance(effect, Effect): + logger.critical(f'Restoring component {effect_label} failed.') + flow_system._add_effects(effect) + return flow_system @classmethod def from_dict(cls, data: Dict) -> 'FlowSystem': """ - Load a FlowSystem from a dictionary. + Load a FlowSystem from a dictionary using the Interface pattern. Args: data: Dictionary containing the FlowSystem data. """ - timesteps_extra = pd.DatetimeIndex(data['timesteps_extra'], name='time') - hours_of_last_timestep = TimeSeriesCollection.calculate_hours_per_timestep(timesteps_extra).isel(time=-1).item() + # For dict format, resolve with empty arrays (references may not be used) + resolved_data = cls._resolve_reference_structure(data, {}) - flow_system = FlowSystem( + # Extract constructor parameters + timesteps_extra = pd.DatetimeIndex(resolved_data['timesteps_extra'], name='time') + hours_of_last_timestep = float((timesteps_extra[-1] - timesteps_extra[-2]) / pd.Timedelta(hours=1)) + + flow_system = cls( timesteps=timesteps_extra[:-1], hours_of_last_timestep=hours_of_last_timestep, - hours_of_previous_timesteps=data['hours_of_previous_timesteps'], + hours_of_previous_timesteps=resolved_data['hours_of_previous_timesteps'], ) - flow_system.add_elements(*[Bus.from_dict(bus) for bus in data['buses'].values()]) + # Add elements using resolved data + for bus_data in resolved_data.get('buses', {}).values(): + bus = Bus.from_dict(bus_data) + flow_system.add_elements(bus) - flow_system.add_elements(*[Effect.from_dict(effect) for effect in data['effects'].values()]) + for effect_data in resolved_data.get('effects', {}).values(): + effect = Effect.from_dict(effect_data) + flow_system.add_elements(effect) - flow_system.add_elements( - *[CLASS_REGISTRY[comp['__class__']].from_dict(comp) for comp in data['components'].values()] - ) + for comp_data in resolved_data.get('components', {}).values(): + component = CLASS_REGISTRY[comp_data['__class__']].from_dict(comp_data) + flow_system.add_elements(component) flow_system.transform_data() - return flow_system @classmethod - def from_netcdf(cls, path: Union[str, pathlib.Path]): + def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'FlowSystem': + """ + Load a FlowSystem from a netcdf file using the Interface pattern. + """ + ds = fx_io.load_dataset_from_netcdf(path) + return cls.from_dataset(ds) + + def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0, constants_in_dataset: bool = True): + """ + Save the FlowSystem to a NetCDF file using the Interface pattern. + + Args: + path: The path to the netCDF file. + compression: The compression level to use when saving the file. + constants_in_dataset: If True, constants are included as Dataset variables. + """ + ds = self.to_dataset(constants_in_dataset=constants_in_dataset) + fx_io.save_dataset_to_netcdf(ds, path, compression=compression) + logger.info(f'Saved FlowSystem to {path}') + + def to_json(self, path: Union[str, pathlib.Path]): + """ + Save the flow system to a JSON file using the Interface pattern. + This is meant for documentation and comparison, not for reloading. + + Args: + path: The path to the JSON file. + """ + # Use the stats mode for JSON export (cleaner output) + data = get_compact_representation(self.as_dict('stats')) + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + + def create_time_series( + self, + name: str, + data: Optional[Union[NumericData, TimeSeriesData, TimeSeries]], + needs_extra_timestep: bool = False, + ) -> Optional[TimeSeries]: + """ + Create a TimeSeries-like object (now just an xr.DataArray with proper coordinates). + This method is kept for API compatibility but simplified. + + Args: + name: Name of the time series + data: Data to convert + needs_extra_timestep: Whether to use timesteps_extra + + Returns: + xr.DataArray with proper time coordinates + """ + if data is None: + return None + + # Choose appropriate timesteps + target_timesteps = self.timesteps_extra if needs_extra_timestep else self.timesteps + + if isinstance(data, TimeSeries): + # Extract the data and rename + return data.selected_data.rename(name) + elif isinstance(data, TimeSeriesData): + # Convert TimeSeriesData to DataArray + from .core import DataConverter # Assuming this exists + + return DataConverter.as_dataarray(data.data, timesteps=target_timesteps).rename(name) + else: + # Convert other data types to DataArray + from .core import DataConverter # Assuming this exists + + return DataConverter.as_dataarray(data, timesteps=target_timesteps).rename(name) + + def create_effect_time_series( + self, + label_prefix: Optional[str], + effect_values: EffectValuesUser, + label_suffix: Optional[str] = None, + ) -> Optional[Dict[str, xr.DataArray]]: """ - Load a FlowSystem from a netcdf file + Transform EffectValues to effect DataArrays. + Simplified version that returns DataArrays directly. """ - return cls.from_dataset(fx_io.load_dataset_from_netcdf(path)) + effect_values_dict: Optional[EffectValuesDict] = self.effects.create_effect_values_dict(effect_values) + if effect_values_dict is None: + return None + + return { + effect: self.create_time_series('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) + for effect, value in effect_values_dict.items() + } + + def transform_data(self): + """Transform data for all elements using the new simplified approach.""" + if not self._connected: + self._connect_network() + for element in self.all_elements.values(): + element.transform_data(self) def add_elements(self, *elements: Element) -> None: """ @@ -142,63 +458,11 @@ def add_elements(self, *elements: Element) -> None: f'Tried to add incompatible object to FlowSystem: {type(new_element)=}: {new_element=} ' ) - def to_json(self, path: Union[str, pathlib.Path]): - """ - Saves the flow system to a json file. - This not meant to be reloaded and recreate the object, - but rather used to document or compare the flow_system to others. - - Args: - path: The path to the json file. - """ - with open(path, 'w', encoding='utf-8') as f: - json.dump(self.as_dict('stats'), f, indent=4, ensure_ascii=False) - - def as_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: - """Convert the object to a dictionary representation.""" - data = { - 'components': { - comp.label: comp.to_dict() - for comp in sorted(self.components.values(), key=lambda component: component.label.upper()) - }, - 'buses': { - bus.label: bus.to_dict() for bus in sorted(self.buses.values(), key=lambda bus: bus.label.upper()) - }, - 'effects': { - effect.label: effect.to_dict() - for effect in sorted(self.effects, key=lambda effect: effect.label.upper()) - }, - 'timesteps_extra': [date.isoformat() for date in self.time_series_collection.timesteps_extra], - 'hours_of_previous_timesteps': self.time_series_collection.hours_of_previous_timesteps, - } - if data_mode == 'data': - return fx_io.replace_timeseries(data, 'data') - elif data_mode == 'stats': - return fx_io.remove_none_and_empty(fx_io.replace_timeseries(data, data_mode)) - return fx_io.replace_timeseries(data, data_mode) - - def as_dataset(self, constants_in_dataset: bool = False) -> xr.Dataset: - """ - Convert the FlowSystem to a xarray Dataset. - - Args: - constants_in_dataset: If True, constants are included as Dataset variables. - """ - ds = self.time_series_collection.to_dataset(include_constants=constants_in_dataset) - ds.attrs = self.as_dict(data_mode='name') - return ds - - def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0, constants_in_dataset: bool = True): - """ - Saves the FlowSystem to a netCDF file. - Args: - path: The path to the netCDF file. - compression: The compression level to use when saving the file. - constants_in_dataset: If True, constants are included as Dataset variables. - """ - ds = self.as_dataset(constants_in_dataset=constants_in_dataset) - fx_io.save_dataset_to_netcdf(ds, path, compression=compression) - logger.info(f'Saved FlowSystem to {path}') + def create_model(self) -> SystemModel: + if not self._connected: + raise RuntimeError('FlowSystem is not connected. Call FlowSystem.connect() first.') + self.model = SystemModel(self) + return self.model def plot_network( self, @@ -213,28 +477,6 @@ def plot_network( ) -> Optional['pyvis.network.Network']: """ Visualizes the network structure of a FlowSystem using PyVis, saving it as an interactive HTML file. - - Args: - path: Path to save the HTML visualization. - - `False`: Visualization is created but not saved. - - `str` or `Path`: Specifies file path (default: 'flow_system.html'). - controls: UI controls to add to the visualization. - - `True`: Enables all available controls. - - `List`: Specify controls, e.g., ['nodes', 'layout']. - - Options: 'nodes', 'edges', 'layout', 'interaction', 'manipulation', 'physics', 'selection', 'renderer'. - show: Whether to open the visualization in the web browser. - - Returns: - - Optional[pyvis.network.Network]: The `Network` instance representing the visualization, or `None` if `pyvis` is not installed. - - Examples: - >>> flow_system.plot_network() - >>> flow_system.plot_network(show=False) - >>> flow_system.plot_network(path='output/custom_network.html', controls=['nodes', 'layout']) - - Notes: - - This function requires `pyvis`. If not installed, the function prints a warning and returns `None`. - - Nodes are styled based on type (e.g., circles for buses, boxes for components) and annotated with node information. """ from . import plotting @@ -265,67 +507,6 @@ def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, return nodes, edges - def transform_data(self): - if not self._connected: - self._connect_network() - for element in self.all_elements.values(): - element.transform_data(self) - - def create_time_series( - self, - name: str, - data: Optional[Union[NumericData, TimeSeriesData, TimeSeries]], - needs_extra_timestep: bool = False, - ) -> Optional[TimeSeries]: - """ - Tries to create a TimeSeries from NumericData Data and adds it to the time_series_collection - If the data already is a TimeSeries, nothing happens and the TimeSeries gets reset and returned - If the data is a TimeSeriesData, it is converted to a TimeSeries, and the aggregation weights are applied. - If the data is None, nothing happens. - """ - - if data is None: - return None - elif isinstance(data, TimeSeries): - data.restore_data() - if data in self.time_series_collection: - return data - return self.time_series_collection.create_time_series( - data=data.active_data, name=name, needs_extra_timestep=needs_extra_timestep - ) - return self.time_series_collection.create_time_series( - data=data, name=name, needs_extra_timestep=needs_extra_timestep - ) - - def create_effect_time_series( - self, - label_prefix: Optional[str], - effect_values: EffectValuesUser, - label_suffix: Optional[str] = None, - ) -> Optional[EffectTimeSeries]: - """ - Transform EffectValues to EffectTimeSeries. - Creates a TimeSeries for each key in the nested_values dictionary, using the value as the data. - - The resulting label of the TimeSeries is the label of the parent_element, - followed by the label of the Effect in the nested_values and the label_suffix. - If the key in the EffectValues is None, the alias 'Standard_Effect' is used - """ - effect_values: Optional[EffectValuesDict] = self.effects.create_effect_values_dict(effect_values) - if effect_values is None: - return None - - return { - effect: self.create_time_series('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) - for effect, value in effect_values.items() - } - - def create_model(self) -> SystemModel: - if not self._connected: - raise RuntimeError('FlowSystem is not connected. Call FlowSystem.connect() first.') - self.model = SystemModel(self) - return self.model - def _check_if_element_is_unique(self, element: Element) -> None: """ checks if element or label of element already exists in list diff --git a/flixopt/structure.py b/flixopt/structure.py index 1d0f2324f..b9dbd889c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -116,130 +116,341 @@ def transform_data(self, flow_system: 'FlowSystem'): """Transforms the data of the interface to match the FlowSystem's dimensions""" raise NotImplementedError('Every Interface needs a transform_data() method') + def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: + """ + Convert all DataArrays/TimeSeries to references and extract them. + This is the core method that both to_dict() and to_dataset() build upon. + + Returns: + Tuple of (reference_structure, extracted_arrays_dict) + """ + # Get constructor parameters + init_params = inspect.signature(self.__init__).parameters + + # Process all constructor parameters + reference_structure = {'__class__': self.__class__.__name__} + all_extracted_arrays = {} + + for name in init_params: + if name == 'self': + continue + + value = getattr(self, name, None) + if value is None: + continue + + # Extract arrays and get reference structure + processed_value, extracted_arrays = self._extract_dataarrays_recursive(value) + + # Add extracted arrays to the collection + all_extracted_arrays.update(extracted_arrays) + + # Only store in structure if it's not None/empty after processing + if processed_value is not None and not (isinstance(processed_value, (dict, list)) and not processed_value): + reference_structure[name] = processed_value + + return reference_structure, all_extracted_arrays + + def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArray]]: + """ + Recursively extract DataArrays/TimeSeries from nested structures. + + Args: + obj: Object to process + + Returns: + Tuple of (processed_object_with_references, extracted_arrays_dict) + """ + extracted_arrays = {} + + # Handle TimeSeries objects - extract their data using their unique name + if isinstance(obj, TimeSeries): + data_array = obj.active_data.rename(obj.name) + extracted_arrays[obj.name] = data_array + return f':::{obj.name}', extracted_arrays + + # Handle DataArrays directly - use their unique name + elif isinstance(obj, xr.DataArray): + if not obj.name: + raise ValueError('DataArray must have a unique name for serialization') + extracted_arrays[obj.name] = obj + return f':::{obj.name}', extracted_arrays + + # Handle Interface objects - extract their DataArrays too + elif isinstance(obj, Interface): + # Get the Interface's reference structure and arrays + interface_structure, interface_arrays = obj._create_reference_structure() + + # Add all extracted arrays from the nested Interface + extracted_arrays.update(interface_arrays) + + return interface_structure, extracted_arrays + + # Handle lists + elif isinstance(obj, list): + processed_list = [] + for item in obj: + processed_item, nested_arrays = self._extract_dataarrays_recursive(item) + extracted_arrays.update(nested_arrays) + processed_list.append(processed_item) + return processed_list, extracted_arrays + + # Handle dictionaries + elif isinstance(obj, dict): + processed_dict = {} + for key, value in obj.items(): + processed_value, nested_arrays = self._extract_dataarrays_recursive(value) + extracted_arrays.update(nested_arrays) + processed_dict[key] = processed_value + return processed_dict, extracted_arrays + + # Handle tuples (convert to list for JSON compatibility) + elif isinstance(obj, tuple): + processed_list = [] + for item in obj: + processed_item, nested_arrays = self._extract_dataarrays_recursive(item) + extracted_arrays.update(nested_arrays) + processed_list.append(processed_item) + return processed_list, extracted_arrays + + # For all other types, serialize to basic types + else: + return self._serialize_to_basic_types(obj), extracted_arrays + + @classmethod + def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataArray]): + """ + Convert reference structure back to actual objects using provided arrays. + + Args: + structure: Structure containing references (:::name) + arrays_dict: Dictionary of available DataArrays + + Returns: + Structure with references resolved to actual DataArrays + """ + if isinstance(structure, str) and structure.startswith(':::'): + # This is a reference to a DataArray + array_name = structure[3:] # Remove ":::" prefix + if array_name in arrays_dict: + return arrays_dict[array_name] + else: + logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") + return None + + elif isinstance(structure, list): + resolved_list = [] + for item in structure: + resolved_item = cls._resolve_reference_structure(item, arrays_dict) + if resolved_item is not None: # Filter out None values from missing references + resolved_list.append(resolved_item) + return resolved_list + + elif isinstance(structure, dict): + # Check if this is a serialized Interface object + if structure.get('__class__') and structure['__class__'] in CLASS_REGISTRY: + # This is a nested Interface object - restore it recursively + nested_class = CLASS_REGISTRY[structure['__class__']] + # Remove the __class__ key and process the rest + nested_data = {k: v for k, v in structure.items() if k != '__class__'} + # Resolve references in the nested data + resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) + # Create the nested Interface object + return nested_class(**resolved_nested_data) + else: + # Regular dictionary - resolve references in values + resolved_dict = {} + for key, value in structure.items(): + resolved_value = cls._resolve_reference_structure(value, arrays_dict) + if resolved_value is not None or value is None: # Keep None values if they were originally None + resolved_dict[key] = resolved_value + return resolved_dict + + else: + return structure + + def _serialize_to_basic_types(self, obj): + """Convert object to basic Python types only (no DataArrays, no custom objects).""" + if obj is None or isinstance(obj, (str, int, float, bool)): + return obj + elif isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, (np.ndarray, pd.Series, pd.DataFrame)): + return obj.tolist() if hasattr(obj, 'tolist') else list(obj) + elif isinstance(obj, dict): + return {k: self._serialize_to_basic_types(v) for k, v in obj.items()} + elif isinstance(obj, (list, tuple)): + return [self._serialize_to_basic_types(item) for item in obj] + elif hasattr(obj, 'isoformat'): # datetime objects + return obj.isoformat() + else: + # For any other object, try to convert to string as fallback + logger.warning(f'Converting unknown type {type(obj)} to string: {obj}') + return str(obj) + + def to_dataset(self) -> xr.Dataset: + """ + Convert the object to an xarray Dataset representation. + All DataArrays and TimeSeries become dataset variables, everything else goes to attrs. + + Returns: + xr.Dataset: Dataset containing all DataArrays with basic objects only in attributes + """ + reference_structure, extracted_arrays = self._create_reference_structure() + + # Create the dataset with extracted arrays as variables and structure as attrs + ds = xr.Dataset(extracted_arrays, attrs=reference_structure) + return ds + + def to_dict(self) -> Dict: + """ + Convert the object to a dictionary representation. + DataArrays/TimeSeries are converted to references, but structure is preserved. + + Returns: + Dict: Dictionary with references to DataArrays/TimeSeries + """ + reference_structure, _ = self._create_reference_structure() + return reference_structure + def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: """ Generate a dictionary representation of the object's constructor arguments. - Excludes default values and empty dictionaries and lists. - Converts data to be compatible with JSON. + Built on top of dataset creation for better consistency and analytics capabilities. Args: use_numpy: Whether to convert NumPy arrays to lists. Defaults to True. - If True, numeric numpy arrays (`np.ndarray`) are preserved as-is. + If True, numeric numpy arrays are preserved as-is. If False, they are converted to lists. - use_element_label: Whether to use the element label instead of the infos of the element. Defaults to False. - Note that Elements used as keys in dictionaries are always converted to their labels. + use_element_label: Whether to use element labels instead of full infos for nested objects. Returns: - A dictionary representation of the object's constructor arguments. - + A dictionary representation optimized for documentation and analysis. """ - # Get the constructor arguments and their default values - init_params = sorted( - inspect.signature(self.__init__).parameters.items(), - key=lambda x: (x[0].lower() != 'label', x[0].lower()), # Prioritize 'label' - ) - # Build a dict of attribute=value pairs, excluding defaults - details = {'class': ':'.join([cls.__name__ for cls in self.__class__.__mro__])} - for name, param in init_params: - if name == 'self': - continue - value, default = getattr(self, name, None), param.default - # Ignore default values and empty dicts and list - if np.all(value == default) or (isinstance(value, (dict, list)) and not value): - continue - details[name] = copy_and_convert_datatypes(value, use_numpy, use_element_label) - return details + # Get the core dataset representation + ds = self.to_dataset() + + # Start with the reference structure from attrs + info_dict = dict(ds.attrs) + + # Process DataArrays in the dataset based on preferences + for var_name, data_array in ds.data_vars.items(): + if use_numpy: + # Keep as DataArray/numpy for analysis + info_dict[f'_data_{var_name}'] = data_array + else: + # Convert to lists for JSON compatibility + info_dict[f'_data_{var_name}'] = data_array.values.tolist() + + # Apply element label preference to nested structures + if use_element_label: + info_dict = self._apply_element_label_preference(info_dict) + + return info_dict + + def _apply_element_label_preference(self, obj): + """Apply element label preference to nested structures.""" + if isinstance(obj, dict): + if obj.get('__class__') and 'label' in obj: + # This looks like an Interface with a label - return just the label + return obj.get('label', obj.get('__class__')) + else: + return {k: self._apply_element_label_preference(v) for k, v in obj.items()} + elif isinstance(obj, list): + return [self._apply_element_label_preference(item) for item in obj] + else: + return obj def to_json(self, path: Union[str, pathlib.Path]): """ - Saves the element to a json file. - This not meant to be reloaded and recreate the object, but rather used to document or compare the object. + Save the element to a JSON file for documentation purposes. + Uses the infos() method for consistent representation. Args: - path: The path to the json file. + path: The path to the JSON file. """ - data = get_compact_representation(self.infos(use_numpy=True, use_element_label=True)) + data = get_compact_representation(self.infos(use_numpy=False, use_element_label=True)) with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) - def to_dict(self) -> Dict: - """Convert the object to a dictionary representation.""" - data = {'__class__': self.__class__.__name__} + def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): + """ + Save the object to a NetCDF file. - # Get the constructor parameters - init_params = inspect.signature(self.__init__).parameters + Args: + path: Path to save the NetCDF file + compression: Compression level (0-9) + """ + from . import io as fx_io # Assuming fx_io is available - for name in init_params: - if name == 'self': - continue + ds = self.to_dataset() + fx_io.save_dataset_to_netcdf(ds, path, compression=compression) - value = getattr(self, name, None) - data[name] = self._serialize_value(value) + @classmethod + def from_dataset(cls, ds: xr.Dataset) -> 'Interface': + """ + Create an instance from an xarray Dataset. - return data + Args: + ds: Dataset containing the object data - def _serialize_value(self, value: Any): - """Helper method to serialize a value based on its type.""" - if value is None: - return None - elif isinstance(value, Interface): - return value.to_dict() - elif isinstance(value, (list, tuple)): - return self._serialize_list(value) - elif isinstance(value, dict): - return self._serialize_dict(value) - else: - return value + Returns: + Interface instance + """ + # Get class name and verify it matches + class_name = ds.attrs.get('__class__') + if class_name != cls.__name__: + logger.warning(f"Dataset class '{class_name}' doesn't match target class '{cls.__name__}'") - def _serialize_list(self, items): - """Serialize a list of items.""" - return [self._serialize_value(item) for item in items] + # Get the reference structure from attrs + reference_structure = dict(ds.attrs) - def _serialize_dict(self, d): - """Serialize a dictionary of items.""" - return {k: self._serialize_value(v) for k, v in d.items()} + # Remove the class name since it's not a constructor parameter + reference_structure.pop('__class__', None) - @classmethod - def _deserialize_dict(cls, data: Dict) -> Union[Dict, 'Interface']: - if '__class__' in data: - class_name = data.pop('__class__') - try: - class_type = CLASS_REGISTRY[class_name] - if issubclass(class_type, Interface): - # Use _deserialize_dict to process the arguments - processed_data = {k: cls._deserialize_value(v) for k, v in data.items()} - return class_type(**processed_data) - else: - raise ValueError(f'Class "{class_name}" is not an Interface.') - except (AttributeError, KeyError) as e: - raise ValueError(f'Class "{class_name}" could not get reconstructed.') from e - else: - return {k: cls._deserialize_value(v) for k, v in data.items()} + # Create arrays dictionary from dataset variables + arrays_dict = {name: array for name, array in ds.data_vars.items()} - @classmethod - def _deserialize_list(cls, data: List) -> List: - return [cls._deserialize_value(value) for value in data] + # Resolve all references using the centralized method + resolved_params = cls._resolve_reference_structure(reference_structure, arrays_dict) + + return cls(**resolved_params) @classmethod - def _deserialize_value(cls, value: Any): - """Helper method to deserialize a value based on its type.""" - if value is None: - return None - elif isinstance(value, dict): - return cls._deserialize_dict(value) - elif isinstance(value, list): - return cls._deserialize_list(value) - return value + def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': + """ + Load an instance from a NetCDF file. + + Args: + path: Path to the NetCDF file + + Returns: + Interface instance + """ + from . import io as fx_io # Assuming fx_io is available + + ds = fx_io.load_dataset_from_netcdf(path) + return cls.from_dataset(ds) @classmethod def from_dict(cls, data: Dict) -> 'Interface': """ Create an instance from a dictionary representation. + This is now a thin wrapper around the reference resolution system. Args: data: Dictionary containing the data for the object. """ - return cls._deserialize_dict(data) + class_name = data.pop('__class__', None) + if class_name and class_name != cls.__name__: + logger.warning(f"Dict class '{class_name}' doesn't match target class '{cls.__name__}'") + + # Since dict format doesn't separate arrays, resolve with empty arrays dict + # References in dict format would need to be handled differently if they exist + resolved_params = cls._resolve_reference_structure(data, {}) + return cls(**resolved_params) def __repr__(self): # Get the constructor arguments and their current values From f63db8b54004cc2a2618c20cb561dff299dc2ce3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:04:06 +0200 Subject: [PATCH 059/183] Remove TimeSeries --- examples/01_Simple/simple_example.py | 5 + flixopt/calculation.py | 3 - flixopt/components.py | 20 +- flixopt/core.py | 815 +-------------------------- flixopt/effects.py | 8 +- flixopt/elements.py | 14 +- flixopt/features.py | 5 - flixopt/flow_system.py | 2 +- flixopt/io.py | 2 +- flixopt/structure.py | 17 +- 10 files changed, 40 insertions(+), 851 deletions(-) diff --git a/examples/01_Simple/simple_example.py b/examples/01_Simple/simple_example.py index 45550c9cc..da10aed62 100644 --- a/examples/01_Simple/simple_example.py +++ b/examples/01_Simple/simple_example.py @@ -103,9 +103,14 @@ calculation = fx.FullCalculation(name='Sim1', flow_system=flow_system) calculation.do_modeling() # Translate the model to a solvable form, creating equations and Variables + calculation2 = fx.FullCalculation(name='Sim2', flow_system=flow_system) + calculation2.do_modeling() # Translate the model to a solvable form, creating equations and Variables + # --- Solve the Calculation and Save Results --- calculation.solve(fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=30)) + calculation2.solve(fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=30)) + # --- Analyze Results --- calculation.results['Fernwärme'].plot_node_balance_pie() calculation.results['Fernwärme'].plot_node_balance() diff --git a/flixopt/calculation.py b/flixopt/calculation.py index c7367cad2..2f08dd457 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -183,9 +183,6 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma def _activate_time_series(self): self.flow_system.transform_data() - self.flow_system.time_series_collection.activate_timesteps( - active_timesteps=self.active_timesteps, - ) class AggregatedCalculation(FullCalculation): diff --git a/flixopt/components.py b/flixopt/components.py index 1f5fe5ece..81baaeea5 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -348,7 +348,7 @@ def __init__(self, model: SystemModel, element: Transmission): def do_modeling(self): """Initiates all FlowModels""" # Force On Variable if absolute losses are present - if (self.element.absolute_losses is not None) and np.any(self.element.absolute_losses.active_data != 0): + if (self.element.absolute_losses is not None) and np.any(self.element.absolute_losses != 0): for flow in self.element.inputs + self.element.outputs: if flow.on_off_parameters is None: flow.on_off_parameters = OnOffParameters() @@ -385,14 +385,14 @@ def create_transmission_equation(self, name: str, in_flow: Flow, out_flow: Flow) # eq: out(t) + on(t)*loss_abs(t) = in(t)*(1 - loss_rel(t)) con_transmission = self.add( self._model.add_constraints( - out_flow.model.flow_rate == -in_flow.model.flow_rate * (self.element.relative_losses.active_data - 1), + out_flow.model.flow_rate == -in_flow.model.flow_rate * (self.element.relative_losses - 1), name=f'{self.label_full}|{name}', ), name, ) if self.element.absolute_losses is not None: - con_transmission.lhs += in_flow.model.on_off.on * self.element.absolute_losses.active_data + con_transmission.lhs += in_flow.model.on_off.on * self.element.absolute_losses return con_transmission @@ -420,8 +420,8 @@ def do_modeling(self): self.add( self._model.add_constraints( - sum([flow.model.flow_rate * conv_factors[flow.label].active_data for flow in used_inputs]) - == sum([flow.model.flow_rate * conv_factors[flow.label].active_data for flow in used_outputs]), + sum([flow.model.flow_rate * conv_factors[flow.label] for flow in used_inputs]) + == sum([flow.model.flow_rate * conv_factors[flow.label] for flow in used_outputs]), name=f'{self.label_full}|conversion_{i}', ) ) @@ -481,12 +481,12 @@ def do_modeling(self): ) charge_state = self.charge_state - rel_loss = self.element.relative_loss_per_hour.active_data + rel_loss = self.element.relative_loss_per_hour hours_per_step = self._model.hours_per_step charge_rate = self.element.charging.model.flow_rate discharge_rate = self.element.discharging.model.flow_rate - eff_charge = self.element.eta_charge.active_data - eff_discharge = self.element.eta_discharge.active_data + eff_charge = self.element.eta_charge + eff_discharge = self.element.eta_discharge self.add( self._model.add_constraints( @@ -572,8 +572,8 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: @property def relative_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: return ( - self.element.relative_minimum_charge_state.active_data, - self.element.relative_maximum_charge_state.active_data, + self.element.relative_minimum_charge_state, + self.element.relative_maximum_charge_state, ) diff --git a/flixopt/core.py b/flixopt/core.py index 08be18f1d..022bf8e6f 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -136,392 +136,8 @@ def __str__(self): class TimeSeries: - """ - A class representing time series data with active and stored states. - - TimeSeries provides a way to store time-indexed data and work with temporal subsets. - It supports arithmetic operations, aggregation, and JSON serialization. - - Attributes: - name (str): The name of the time series - aggregation_weight (Optional[float]): Weight used for aggregation - aggregation_group (Optional[str]): Group name for shared aggregation weighting - needs_extra_timestep (bool): Whether this series needs an extra timestep - """ - - @classmethod - def from_datasource( - cls, - data: NumericData, - name: str, - timesteps: pd.DatetimeIndex, - aggregation_weight: Optional[float] = None, - aggregation_group: Optional[str] = None, - needs_extra_timestep: bool = False, - ) -> 'TimeSeries': - """ - Initialize the TimeSeries from multiple data sources. - - Args: - data: The time series data - name: The name of the TimeSeries - timesteps: The timesteps of the TimeSeries - aggregation_weight: The weight in aggregation calculations - aggregation_group: Group this TimeSeries belongs to for aggregation weight sharing - needs_extra_timestep: Whether this series requires an extra timestep - - Returns: - A new TimeSeries instance - """ - return cls( - DataConverter.as_dataarray(data, timesteps), - name, - aggregation_weight, - aggregation_group, - needs_extra_timestep, - ) - - @classmethod - def from_json(cls, data: Optional[Dict[str, Any]] = None, path: Optional[str] = None) -> 'TimeSeries': - """ - Load a TimeSeries from a dictionary or json file. - - Args: - data: Dictionary containing TimeSeries data - path: Path to a JSON file containing TimeSeries data - - Returns: - A new TimeSeries instance - - Raises: - ValueError: If both path and data are provided or neither is provided - """ - if (path is None and data is None) or (path is not None and data is not None): - raise ValueError("Exactly one of 'path' or 'data' must be provided") - - if path is not None: - with open(path, 'r') as f: - data = json.load(f) - - # Convert ISO date strings to datetime objects - data['data']['coords']['time']['data'] = pd.to_datetime(data['data']['coords']['time']['data']) - - # Create the TimeSeries instance - return cls( - data=xr.DataArray.from_dict(data['data']), - name=data['name'], - aggregation_weight=data['aggregation_weight'], - aggregation_group=data['aggregation_group'], - needs_extra_timestep=data['needs_extra_timestep'], - ) - - def __init__( - self, - data: xr.DataArray, - name: str, - aggregation_weight: Optional[float] = None, - aggregation_group: Optional[str] = None, - needs_extra_timestep: bool = False, - ): - """ - Initialize a TimeSeries with a DataArray. - - Args: - data: The DataArray containing time series data - name: The name of the TimeSeries - aggregation_weight: The weight in aggregation calculations - aggregation_group: Group this TimeSeries belongs to for weight sharing - needs_extra_timestep: Whether this series requires an extra timestep - - Raises: - ValueError: If data doesn't have a 'time' index or has more than 1 dimension - """ - if 'time' not in data.indexes: - raise ValueError(f'DataArray must have a "time" index. Got {data.indexes}') - if data.ndim > 1: - raise ValueError(f'Number of dimensions of DataArray must be 1. Got {data.ndim}') - - self.name = name - self.aggregation_weight = aggregation_weight - self.aggregation_group = aggregation_group - self.needs_extra_timestep = needs_extra_timestep - - # Data management - self._stored_data = data.copy(deep=True) - self._backup = self._stored_data.copy(deep=True) - self._active_timesteps = self._stored_data.indexes['time'] - self._active_data = None - self._update_active_data() - - def reset(self): - """ - Reset active timesteps to the full set of stored timesteps. - """ - self.active_timesteps = None - - def restore_data(self): - """ - Restore stored_data from the backup and reset active timesteps. - """ - self._stored_data = self._backup.copy(deep=True) - self.reset() - - def to_json(self, path: Optional[pathlib.Path] = None) -> Dict[str, Any]: - """ - Save the TimeSeries to a dictionary or JSON file. - - Args: - path: Optional path to save JSON file - - Returns: - Dictionary representation of the TimeSeries - """ - data = { - 'name': self.name, - 'aggregation_weight': self.aggregation_weight, - 'aggregation_group': self.aggregation_group, - 'needs_extra_timestep': self.needs_extra_timestep, - 'data': self.active_data.to_dict(), - } - - # Convert datetime objects to ISO strings - data['data']['coords']['time']['data'] = [date.isoformat() for date in data['data']['coords']['time']['data']] - - # Save to file if path is provided - if path is not None: - indent = 4 if len(self.active_timesteps) <= 480 else None - with open(path, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=indent, ensure_ascii=False) - - return data - - @property - def stats(self) -> str: - """ - Return a statistical summary of the active data. - - Returns: - String representation of data statistics - """ - return get_numeric_stats(self.active_data, padd=0) - - def _update_active_data(self): - """ - Update the active data based on active_timesteps. - """ - self._active_data = self._stored_data.sel(time=self.active_timesteps) - - @property - def all_equal(self) -> bool: - """Check if all values in the series are equal.""" - return np.unique(self.active_data.values).size == 1 - - @property - def active_timesteps(self) -> pd.DatetimeIndex: - """Get the current active timesteps.""" - return self._active_timesteps - - @active_timesteps.setter - def active_timesteps(self, timesteps: Optional[pd.DatetimeIndex]): - """ - Set active_timesteps and refresh active_data. - - Args: - timesteps: New timesteps to activate, or None to use all stored timesteps - - Raises: - TypeError: If timesteps is not a pandas DatetimeIndex or None - """ - if timesteps is None: - self._active_timesteps = self.stored_data.indexes['time'] - elif isinstance(timesteps, pd.DatetimeIndex): - self._active_timesteps = timesteps - else: - raise TypeError('active_timesteps must be a pandas DatetimeIndex or None') - - self._update_active_data() - - @property - def active_data(self) -> xr.DataArray: - """Get a view of stored_data based on active_timesteps.""" - return self._active_data - - @property - def stored_data(self) -> xr.DataArray: - """Get a copy of the full stored data.""" - return self._stored_data.copy() - - @stored_data.setter - def stored_data(self, value: NumericData): - """ - Update stored_data and refresh active_data. - - Args: - value: New data to store - """ - new_data = DataConverter.as_dataarray(value, timesteps=self.active_timesteps) - - # Skip if data is unchanged to avoid overwriting backup - if new_data.equals(self._stored_data): - return - - self._stored_data = new_data - self.active_timesteps = None # Reset to full timeline - - @property - def sel(self): - return self.active_data.sel - - @property - def isel(self): - return self.active_data.isel - - def _apply_operation(self, other, op): - """Apply an operation between this TimeSeries and another object.""" - if isinstance(other, TimeSeries): - other = other.active_data - return op(self.active_data, other) - - def __add__(self, other): - return self._apply_operation(other, lambda x, y: x + y) - - def __sub__(self, other): - return self._apply_operation(other, lambda x, y: x - y) - - def __mul__(self, other): - return self._apply_operation(other, lambda x, y: x * y) - - def __truediv__(self, other): - return self._apply_operation(other, lambda x, y: x / y) - - def __radd__(self, other): - return other + self.active_data - - def __rsub__(self, other): - return other - self.active_data - - def __rmul__(self, other): - return other * self.active_data - - def __rtruediv__(self, other): - return other / self.active_data - - def __neg__(self) -> xr.DataArray: - return -self.active_data - - def __pos__(self) -> xr.DataArray: - return +self.active_data - - def __abs__(self) -> xr.DataArray: - return abs(self.active_data) - - def __gt__(self, other): - """ - Compare if this TimeSeries is greater than another. - - Args: - other: Another TimeSeries to compare with - - Returns: - True if all values in this TimeSeries are greater than other - """ - if isinstance(other, TimeSeries): - return self.active_data > other.active_data - return self.active_data > other - - def __ge__(self, other): - """ - Compare if this TimeSeries is greater than or equal to another. - - Args: - other: Another TimeSeries to compare with - - Returns: - True if all values in this TimeSeries are greater than or equal to other - """ - if isinstance(other, TimeSeries): - return self.active_data >= other.active_data - return self.active_data >= other - - def __lt__(self, other): - """ - Compare if this TimeSeries is less than another. - - Args: - other: Another TimeSeries to compare with - - Returns: - True if all values in this TimeSeries are less than other - """ - if isinstance(other, TimeSeries): - return self.active_data < other.active_data - return self.active_data < other - - def __le__(self, other): - """ - Compare if this TimeSeries is less than or equal to another. - - Args: - other: Another TimeSeries to compare with - - Returns: - True if all values in this TimeSeries are less than or equal to other - """ - if isinstance(other, TimeSeries): - return self.active_data <= other.active_data - return self.active_data <= other - - def __eq__(self, other): - """ - Compare if this TimeSeries is equal to another. - - Args: - other: Another TimeSeries to compare with - - Returns: - True if all values in this TimeSeries are equal to other - """ - if isinstance(other, TimeSeries): - return self.active_data == other.active_data - return self.active_data == other - - def __array_ufunc__(self, ufunc, method, *inputs, **kwargs): - """ - Handle NumPy universal functions. - - This allows NumPy functions to work with TimeSeries objects. - """ - # Convert any TimeSeries inputs to their active_data - inputs = [x.active_data if isinstance(x, TimeSeries) else x for x in inputs] - return getattr(ufunc, method)(*inputs, **kwargs) - - def __repr__(self): - """ - Get a string representation of the TimeSeries. - - Returns: - String showing TimeSeries details - """ - attrs = { - 'name': self.name, - 'aggregation_weight': self.aggregation_weight, - 'aggregation_group': self.aggregation_group, - 'needs_extra_timestep': self.needs_extra_timestep, - 'shape': self.active_data.shape, - 'time_range': f'{self.active_timesteps[0]} to {self.active_timesteps[-1]}', - } - attr_str = ', '.join(f'{k}={repr(v)}' for k, v in attrs.items()) - return f'TimeSeries({attr_str})' - - def __str__(self): - """ - Get a human-readable string representation. - - Returns: - Descriptive string with statistics - """ - return f"TimeSeries '{self.name}': {self.stats}" - + def __init__(self): + raise NotImplementedError('TimeSeries was removed') class TimeSeriesCollection: """ @@ -531,431 +147,8 @@ class TimeSeriesCollection: timesteps, provides operations on collections, and manages extra timesteps. """ - def __init__( - self, - timesteps: pd.DatetimeIndex, - hours_of_last_timestep: Optional[float] = None, - hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] = None, - ): - """ - Args: - timesteps: The timesteps of the Collection. - hours_of_last_timestep: The duration of the last time step. Uses the last time interval if not specified - hours_of_previous_timesteps: The duration of previous timesteps. - If None, the first time increment of time_series is used. - This is needed to calculate previous durations (for example consecutive_on_hours). - If you use an array, take care that its long enough to cover all previous values! - """ - # Prepare and validate timesteps - self._validate_timesteps(timesteps) - self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( - timesteps, hours_of_previous_timesteps - ) - - # Set up timesteps and hours - self.all_timesteps = timesteps - self.all_timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - self.all_hours_per_timestep = self.calculate_hours_per_timestep(self.all_timesteps_extra) - - # Active timestep tracking - self._active_timesteps = None - self._active_timesteps_extra = None - self._active_hours_per_timestep = None - - # Dictionary of time series by name - self.time_series_data: Dict[str, TimeSeries] = {} - - # Aggregation - self.group_weights: Dict[str, float] = {} - self.weights: Dict[str, float] = {} - - @classmethod - def with_uniform_timesteps( - cls, start_time: pd.Timestamp, periods: int, freq: str, hours_per_step: Optional[float] = None - ) -> 'TimeSeriesCollection': - """Create a collection with uniform timesteps.""" - timesteps = pd.date_range(start_time, periods=periods, freq=freq, name='time') - return cls(timesteps, hours_of_previous_timesteps=hours_per_step) - - def create_time_series( - self, data: Union[NumericData, TimeSeriesData], name: str, needs_extra_timestep: bool = False - ) -> TimeSeries: - """ - Creates a TimeSeries from the given data and adds it to the collection. - - Args: - data: The data to create the TimeSeries from. - name: The name of the TimeSeries. - needs_extra_timestep: Whether to create an additional timestep at the end of the timesteps. - The data to create the TimeSeries from. - - Returns: - The created TimeSeries. - - """ - # Check for duplicate name - if name in self.time_series_data: - raise ValueError(f"TimeSeries '{name}' already exists in this collection") - - # Determine which timesteps to use - timesteps_to_use = self.timesteps_extra if needs_extra_timestep else self.timesteps - - # Create the time series - if isinstance(data, TimeSeriesData): - time_series = TimeSeries.from_datasource( - name=name, - data=data.data, - timesteps=timesteps_to_use, - aggregation_weight=data.agg_weight, - aggregation_group=data.agg_group, - needs_extra_timestep=needs_extra_timestep, - ) - # Connect the user time series to the created TimeSeries - data.label = name - else: - time_series = TimeSeries.from_datasource( - name=name, data=data, timesteps=timesteps_to_use, needs_extra_timestep=needs_extra_timestep - ) - - # Add to the collection - self.add_time_series(time_series) - - return time_series - - def calculate_aggregation_weights(self) -> Dict[str, float]: - """Calculate and return aggregation weights for all time series.""" - self.group_weights = self._calculate_group_weights() - self.weights = self._calculate_weights() - - if np.all(np.isclose(list(self.weights.values()), 1, atol=1e-6)): - logger.info('All Aggregation weights were set to 1') - - return self.weights - - def activate_timesteps(self, active_timesteps: Optional[pd.DatetimeIndex] = None): - """ - Update active timesteps for the collection and all time series. - If no arguments are provided, the active timesteps are reset. - - Args: - active_timesteps: The active timesteps of the model. - If None, the all timesteps of the TimeSeriesCollection are taken. - """ - if active_timesteps is None: - return self.reset() - - if not np.all(np.isin(active_timesteps, self.all_timesteps)): - raise ValueError('active_timesteps must be a subset of the timesteps of the TimeSeriesCollection') - - # Calculate derived timesteps - self._active_timesteps = active_timesteps - first_ts_index = np.where(self.all_timesteps == active_timesteps[0])[0][0] - last_ts_idx = np.where(self.all_timesteps == active_timesteps[-1])[0][0] - self._active_timesteps_extra = self.all_timesteps_extra[first_ts_index : last_ts_idx + 2] - self._active_hours_per_timestep = self.all_hours_per_timestep.isel(time=slice(first_ts_index, last_ts_idx + 1)) - - # Update all time series - self._update_time_series_timesteps() - - def reset(self): - """Reset active timesteps to defaults for all time series.""" - self._active_timesteps = None - self._active_timesteps_extra = None - self._active_hours_per_timestep = None - - for time_series in self.time_series_data.values(): - time_series.reset() - - def restore_data(self): - """Restore original data for all time series.""" - for time_series in self.time_series_data.values(): - time_series.restore_data() - - def add_time_series(self, time_series: TimeSeries): - """Add an existing TimeSeries to the collection.""" - if time_series.name in self.time_series_data: - raise ValueError(f"TimeSeries '{time_series.name}' already exists in this collection") - - self.time_series_data[time_series.name] = time_series - - def insert_new_data(self, data: pd.DataFrame, include_extra_timestep: bool = False): - """ - Update time series with new data from a DataFrame. - - Args: - data: DataFrame containing new data with timestamps as index - include_extra_timestep: Whether the provided data already includes the extra timestep, by default False - """ - if not isinstance(data, pd.DataFrame): - raise TypeError(f'data must be a pandas DataFrame, got {type(data).__name__}') - - # Check if the DataFrame index matches the expected timesteps - expected_timesteps = self.timesteps_extra if include_extra_timestep else self.timesteps - if not data.index.equals(expected_timesteps): - raise ValueError( - f'DataFrame index must match {"collection timesteps with extra timestep" if include_extra_timestep else "collection timesteps"}' - ) - - for name, ts in self.time_series_data.items(): - if name in data.columns: - if not ts.needs_extra_timestep: - # For time series without extra timestep - if include_extra_timestep: - # If data includes extra timestep but series doesn't need it, exclude the last point - ts.stored_data = data[name].iloc[:-1] - else: - # Use data as is - ts.stored_data = data[name] - else: - # For time series with extra timestep - if include_extra_timestep: - # Data already includes extra timestep - ts.stored_data = data[name] - else: - # Need to add extra timestep - extrapolate from the last value - extra_step_value = data[name].iloc[-1] - extra_step_index = pd.DatetimeIndex([self.timesteps_extra[-1]], name='time') - extra_step_series = pd.Series([extra_step_value], index=extra_step_index) - - # Combine the regular data with the extra timestep - ts.stored_data = pd.concat([data[name], extra_step_series]) - - logger.debug(f'Updated data for {name}') - - def to_dataframe( - self, filtered: Literal['all', 'constant', 'non_constant'] = 'non_constant', include_extra_timestep: bool = True - ) -> pd.DataFrame: - """ - Convert collection to DataFrame with optional filtering and timestep control. - - Args: - filtered: Filter time series by variability, by default 'non_constant' - include_extra_timestep: Whether to include the extra timestep in the result, by default True - - Returns: - DataFrame representation of the collection - """ - include_constants = filtered != 'non_constant' - ds = self.to_dataset(include_constants=include_constants) - - if not include_extra_timestep: - ds = ds.isel(time=slice(None, -1)) - - df = ds.to_dataframe() - - # Apply filtering - if filtered == 'all': - return df - elif filtered == 'constant': - return df.loc[:, df.nunique() == 1] - elif filtered == 'non_constant': - return df.loc[:, df.nunique() > 1] - else: - raise ValueError("filtered must be one of: 'all', 'constant', 'non_constant'") - - def to_dataset(self, include_constants: bool = True) -> xr.Dataset: - """ - Combine all time series into a single Dataset with all timesteps. - - Args: - include_constants: Whether to include time series with constant values, by default True - - Returns: - Dataset containing all selected time series with all timesteps - """ - # Determine which series to include - if include_constants: - series_to_include = self.time_series_data.values() - else: - series_to_include = self.non_constants - - # Create individual datasets and merge them - ds = xr.merge([ts.active_data.to_dataset(name=ts.name) for ts in series_to_include]) - - # Ensure the correct time coordinates - ds = ds.reindex(time=self.timesteps_extra) - - ds.attrs.update( - { - 'timesteps_extra': f'{self.timesteps_extra[0]} ... {self.timesteps_extra[-1]} | len={len(self.timesteps_extra)}', - 'hours_per_timestep': self._format_stats(self.hours_per_timestep), - } - ) - - return ds - - def _update_time_series_timesteps(self): - """Update active timesteps for all time series.""" - for ts in self.time_series_data.values(): - if ts.needs_extra_timestep: - ts.active_timesteps = self.timesteps_extra - else: - ts.active_timesteps = self.timesteps - - @staticmethod - def _validate_timesteps(timesteps: pd.DatetimeIndex): - """Validate timesteps format and rename if needed.""" - if not isinstance(timesteps, pd.DatetimeIndex): - raise TypeError('timesteps must be a pandas DatetimeIndex') - - if len(timesteps) < 2: - raise ValueError('timesteps must contain at least 2 timestamps') - - # Ensure timesteps has the required name - if timesteps.name != 'time': - logger.warning('Renamed timesteps to "time" (was "%s")', timesteps.name) - timesteps.name = 'time' - - @staticmethod - def _create_timesteps_with_extra( - timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] - ) -> pd.DatetimeIndex: - """Create timesteps with an extra step at the end.""" - if hours_of_last_timestep is not None: - # Create the extra timestep using the specified duration - last_date = pd.DatetimeIndex([timesteps[-1] + pd.Timedelta(hours=hours_of_last_timestep)], name='time') - else: - # Use the last interval as the extra timestep duration - last_date = pd.DatetimeIndex([timesteps[-1] + (timesteps[-1] - timesteps[-2])], name='time') - - # Combine with original timesteps - return pd.DatetimeIndex(timesteps.append(last_date), name='time') - - @staticmethod - def _calculate_hours_of_previous_timesteps( - timesteps: pd.DatetimeIndex, hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] - ) -> Union[float, np.ndarray]: - """Calculate duration of regular timesteps.""" - if hours_of_previous_timesteps is not None: - return hours_of_previous_timesteps - - # Calculate from the first interval - first_interval = timesteps[1] - timesteps[0] - return first_interval.total_seconds() / 3600 # Convert to hours - - @staticmethod - def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: - """Calculate duration of each timestep.""" - # Calculate differences between consecutive timestamps - hours_per_step = np.diff(timesteps_extra) / pd.Timedelta(hours=1) - - return xr.DataArray( - data=hours_per_step, coords={'time': timesteps_extra[:-1]}, dims=('time',), name='hours_per_step' - ) - - def _calculate_group_weights(self) -> Dict[str, float]: - """Calculate weights for aggregation groups.""" - # Count series in each group - groups = [ts.aggregation_group for ts in self.time_series_data.values() if ts.aggregation_group is not None] - group_counts = Counter(groups) - - # Calculate weight for each group (1/count) - return {group: 1 / count for group, count in group_counts.items()} - - def _calculate_weights(self) -> Dict[str, float]: - """Calculate weights for all time series.""" - # Calculate weight for each time series - weights = {} - for name, ts in self.time_series_data.items(): - if ts.aggregation_group is not None: - # Use group weight - weights[name] = self.group_weights.get(ts.aggregation_group, 1) - else: - # Use individual weight or default to 1 - weights[name] = ts.aggregation_weight or 1 - - return weights - - def _format_stats(self, data) -> str: - """Format statistics for a data array.""" - if hasattr(data, 'values'): - values = data.values - else: - values = np.asarray(data) - - mean_val = np.mean(values) - min_val = np.min(values) - max_val = np.max(values) - - return f'mean: {mean_val:.2f}, min: {min_val:.2f}, max: {max_val:.2f}' - - def __getitem__(self, name: str) -> TimeSeries: - """Get a TimeSeries by name.""" - try: - return self.time_series_data[name] - except KeyError as e: - raise KeyError(f'TimeSeries "{name}" not found in the TimeSeriesCollection') from e - - def __iter__(self) -> Iterator[TimeSeries]: - """Iterate through all TimeSeries in the collection.""" - return iter(self.time_series_data.values()) - - def __len__(self) -> int: - """Get the number of TimeSeries in the collection.""" - return len(self.time_series_data) - - def __contains__(self, item: Union[str, TimeSeries]) -> bool: - """Check if a TimeSeries exists in the collection.""" - if isinstance(item, str): - return item in self.time_series_data - elif isinstance(item, TimeSeries): - return any([item is ts for ts in self.time_series_data.values()]) - return False - - @property - def non_constants(self) -> List[TimeSeries]: - """Get time series with varying values.""" - return [ts for ts in self.time_series_data.values() if not ts.all_equal] - - @property - def constants(self) -> List[TimeSeries]: - """Get time series with constant values.""" - return [ts for ts in self.time_series_data.values() if ts.all_equal] - - @property - def timesteps(self) -> pd.DatetimeIndex: - """Get the active timesteps.""" - return self.all_timesteps if self._active_timesteps is None else self._active_timesteps - - @property - def timesteps_extra(self) -> pd.DatetimeIndex: - """Get the active timesteps with extra step.""" - return self.all_timesteps_extra if self._active_timesteps_extra is None else self._active_timesteps_extra - - @property - def hours_per_timestep(self) -> xr.DataArray: - """Get the duration of each active timestep.""" - return ( - self.all_hours_per_timestep if self._active_hours_per_timestep is None else self._active_hours_per_timestep - ) - - @property - def hours_of_last_timestep(self) -> float: - """Get the duration of the last timestep.""" - return float(self.hours_per_timestep[-1].item()) - - def __repr__(self): - return f'TimeSeriesCollection:\n{self.to_dataset()}' - - def __str__(self): - longest_name = max([time_series.name for time_series in self.time_series_data], key=len) - - stats_summary = '\n'.join( - [ - f' - {time_series.name:<{len(longest_name)}}: {get_numeric_stats(time_series.active_data)}' - for time_series in self.time_series_data - ] - ) - - return ( - f'TimeSeriesCollection with {len(self.time_series_data)} series\n' - f' Time Range: {self.timesteps[0]} → {self.timesteps[-1]}\n' - f' No. of timesteps: {len(self.timesteps)} + 1 extra\n' - f' Hours per timestep: {get_numeric_stats(self.hours_per_timestep)}\n' - f' Time Series Data:\n' - f'{stats_summary}' - ) - + def __init__(self): + raise NotImplementedError('TimeSeriesCollection was removed') def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10) -> str: """Calculates the mean, median, min, max, and standard deviation of a numeric DataArray.""" diff --git a/flixopt/effects.py b/flixopt/effects.py index 82aa63a43..b043f4492 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -13,7 +13,7 @@ import numpy as np import pandas as pd -from .core import NumericData, NumericDataTS, Scalar, TimeSeries, TimeSeriesCollection +from .core import NumericData, NumericDataTS, Scalar, TimeSeriesCollection, TimeSeries from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -137,10 +137,10 @@ def __init__(self, model: SystemModel, element: Effect): label_full=f'{self.label_full}(operation)', total_max=self.element.maximum_operation, total_min=self.element.minimum_operation, - min_per_hour=self.element.minimum_operation_per_hour.active_data + min_per_hour=self.element.minimum_operation_per_hour if self.element.minimum_operation_per_hour is not None else None, - max_per_hour=self.element.maximum_operation_per_hour.active_data + max_per_hour=self.element.maximum_operation_per_hour if self.element.maximum_operation_per_hour is not None else None, ) @@ -376,7 +376,7 @@ def _add_share_between_effects(self): for target_effect, time_series in origin_effect.specific_share_to_other_effects_operation.items(): self.effects[target_effect].model.operation.add_share( origin_effect.model.operation.label_full, - origin_effect.model.operation.total_per_timestep * time_series.active_data, + origin_effect.model.operation.total_per_timestep * time_series, ) # 2. invest: -> hier ist es Scalar (share) for target_effect, factor in origin_effect.specific_share_to_other_effects_invest.items(): diff --git a/flixopt/elements.py b/flixopt/elements.py index a0bd8c91f..3ea29a09f 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -287,7 +287,7 @@ def _plausibility_checks(self) -> None: if (self.relative_minimum > 0).any() and self.on_off_parameters is None: logger.warning( - f'Flow {self.label} has a relative_minimum of {self.relative_minimum.active_data} and no on_off_parameters. ' + f'Flow {self.label} has a relative_minimum of {self.relative_minimum} and no on_off_parameters. ' f'This prevents the flow_rate from switching off (flow_rate = 0). ' f'Consider using on_off_parameters to allow the flow to be switched on and off.' ) @@ -390,7 +390,7 @@ def _create_shares(self): self._model.effects.add_share_to_effects( name=self.label_full, # Use the full label of the element expressions={ - effect: self.flow_rate * self._model.hours_per_step * factor.active_data + effect: self.flow_rate * self._model.hours_per_step * factor for effect, factor in self.element.effects_per_flow_hour.items() }, target='operation', @@ -443,16 +443,16 @@ def flow_rate_lower_bound_relative(self) -> NumericData: """Returns the lower bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: - return self.element.relative_minimum.active_data - return fixed_profile.active_data + return self.element.relative_minimum + return fixed_profile @property def flow_rate_upper_bound_relative(self) -> NumericData: """ Returns the upper bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: - return self.element.relative_maximum.active_data - return fixed_profile.active_data + return self.element.relative_maximum + return fixed_profile @property def flow_rate_lower_bound(self) -> NumericData: @@ -497,7 +497,7 @@ def do_modeling(self) -> None: # Fehlerplus/-minus: if self.element.with_excess: excess_penalty = np.multiply( - self._model.hours_per_step, self.element.excess_penalty_per_flow_hour.active_data + self._model.hours_per_step, self.element.excess_penalty_per_flow_hour ) self.excess_input = self.add( self._model.add_variables(lower=0, coords=self._model.coords, name=f'{self.label_full}|excess_input'), diff --git a/flixopt/features.py b/flixopt/features.py index c2a62adb1..dc719a2a6 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -474,11 +474,6 @@ def __init__( self._minimum_duration = minimum_duration self._maximum_duration = maximum_duration - if isinstance(self._minimum_duration, TimeSeries): - self._minimum_duration = self._minimum_duration.active_data - if isinstance(self._maximum_duration, TimeSeries): - self._maximum_duration = self._maximum_duration.active_data - self.duration = None def do_modeling(self): diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 8887a6eae..ae9df6407 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,7 +16,7 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import NumericData, NumericDataTS, TimeSeries, TimeSeriesCollection, TimeSeriesData +from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeriesData, TimeSeries from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser from .elements import Bus, Component, Flow from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation diff --git a/flixopt/io.py b/flixopt/io.py index 35d927136..1376cafae 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -23,7 +23,7 @@ def replace_timeseries(obj, mode: Literal['name', 'stats', 'data'] = 'name'): return [replace_timeseries(v, mode) for v in obj] elif isinstance(obj, TimeSeries): # Adjust this based on the actual class if obj.all_equal: - return obj.active_data.values[0].item() + return obj.values[0].item() elif mode == 'name': return f'::::{obj.name}' elif mode == 'stats': diff --git a/flixopt/structure.py b/flixopt/structure.py index b9dbd889c..71efe31df 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from rich.pretty import Pretty from .config import CONFIG -from .core import NumericData, Scalar, TimeSeries, TimeSeriesCollection, TimeSeriesData +from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeriesData, TimeSeries if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -56,7 +56,6 @@ def __init__(self, flow_system: 'FlowSystem'): """ super().__init__(force_dim_names=True) self.flow_system = flow_system - self.time_series_collection = flow_system.time_series_collection self.effects: Optional[EffectCollectionModel] = None def do_modeling(self): @@ -88,23 +87,23 @@ def solution(self): for effect in sorted(self.flow_system.effects, key=lambda effect: effect.label_full.upper()) }, } - return solution.reindex(time=self.time_series_collection.timesteps_extra) + return solution.reindex(time=self.flow_system.timesteps_extra) @property def hours_per_step(self): - return self.time_series_collection.hours_per_timestep + return self.flow_system.hours_per_timestep @property def hours_of_previous_timesteps(self): - return self.time_series_collection.hours_of_previous_timesteps + return self.flow_system.hours_of_previous_timesteps @property def coords(self) -> Tuple[pd.DatetimeIndex]: - return (self.time_series_collection.timesteps,) + return (self.flow_system.timesteps,) @property def coords_extra(self) -> Tuple[pd.DatetimeIndex]: - return (self.time_series_collection.timesteps_extra,) + return (self.flow_system.timesteps_extra,) class Interface: @@ -165,7 +164,7 @@ def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArra # Handle TimeSeries objects - extract their data using their unique name if isinstance(obj, TimeSeries): - data_array = obj.active_data.rename(obj.name) + data_array = obj.rename(obj.name) extracted_arrays[obj.name] = data_array return f':::{obj.name}', extracted_arrays @@ -745,7 +744,7 @@ def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_la return copy_and_convert_datatypes(data.tolist(), use_numpy, use_element_label) elif isinstance(data, TimeSeries): - return copy_and_convert_datatypes(data.active_data, use_numpy, use_element_label) + return copy_and_convert_datatypes(data, use_numpy, use_element_label) elif isinstance(data, TimeSeriesData): return copy_and_convert_datatypes(data.data, use_numpy, use_element_label) From 167fb2ca59dc6f9ae157e64e990f8a31fba6bdc8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:05:21 +0200 Subject: [PATCH 060/183] Remove TimeSeries --- flixopt/calculation.py | 20 ++++++------ tests/conftest.py | 4 +-- tests/test_bus.py | 2 +- tests/test_component.py | 4 +-- tests/test_effect.py | 4 +-- tests/test_flow.py | 36 +++++++++++----------- tests/test_linear_converter.py | 8 ++--- tests/test_storage.py | 8 ++--- tests/test_timeseries.py | 56 +++++++++++++++++----------------- 9 files changed, 71 insertions(+), 71 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 2f08dd457..8439142c1 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -119,7 +119,7 @@ def main_results(self) -> Dict[str, Union[Scalar, Dict]]: def summary(self): return { 'Name': self.name, - 'Number of timesteps': len(self.flow_system.time_series_collection.timesteps), + 'Number of timesteps': len(self.flow_system.timesteps), 'Calculation Type': self.__class__.__name__, 'Constraints': self.model.constraints.ncons, 'Variables': self.model.variables.nvars, @@ -242,8 +242,8 @@ def _perform_aggregation(self): # Validation dt_min, dt_max = ( - np.min(self.flow_system.time_series_collection.hours_per_timestep), - np.max(self.flow_system.time_series_collection.hours_per_timestep), + np.min(self.flow_system.hours_per_timestep), + np.max(self.flow_system.hours_per_timestep), ) if not dt_min == dt_max: raise ValueError( @@ -252,11 +252,11 @@ def _perform_aggregation(self): ) steps_per_period = ( self.aggregation_parameters.hours_per_period - / self.flow_system.time_series_collection.hours_per_timestep.max() + / self.flow_system.hours_per_timestep.max() ) is_integer = ( self.aggregation_parameters.hours_per_period - % self.flow_system.time_series_collection.hours_per_timestep.max() + % self.flow_system.hours_per_timestep.max() ).item() == 0 if not (steps_per_period.size == 1 and is_integer): raise ValueError( @@ -269,13 +269,13 @@ def _perform_aggregation(self): # Aggregation - creation of aggregated timeseries: self.aggregation = Aggregation( - original_data=self.flow_system.time_series_collection.to_dataframe( + original_data=self.flow_system.to_dataframe( include_extra_timestep=False ), # Exclude last row (NaN) hours_per_time_step=float(dt_min), hours_per_period=self.aggregation_parameters.hours_per_period, nr_of_periods=self.aggregation_parameters.nr_of_periods, - weights=self.flow_system.time_series_collection.calculate_aggregation_weights(), + weights=self.flow_system.calculate_aggregation_weights(), time_series_for_high_peaks=self.aggregation_parameters.labels_for_high_peaks, time_series_for_low_peaks=self.aggregation_parameters.labels_for_low_peaks, ) @@ -283,7 +283,7 @@ def _perform_aggregation(self): self.aggregation.cluster() self.aggregation.plot(show=True, save=self.folder / 'aggregation.html') if self.aggregation_parameters.aggregate_data_and_fix_non_binary_vars: - self.flow_system.time_series_collection.insert_new_data( + self.flow_system.insert_new_data( self.aggregation.aggregated_data, include_extra_timestep=False ) self.durations['aggregation'] = round(timeit.default_timer() - t_start_agg, 2) @@ -324,8 +324,8 @@ def __init__( self.nr_of_previous_values = nr_of_previous_values self.sub_calculations: List[FullCalculation] = [] - self.all_timesteps = self.flow_system.time_series_collection.all_timesteps - self.all_timesteps_extra = self.flow_system.time_series_collection.all_timesteps_extra + self.all_timesteps = self.flow_system.all_timesteps + self.all_timesteps_extra = self.flow_system.all_timesteps_extra self.segment_names = [ f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment)) diff --git a/tests/conftest.py b/tests/conftest.py index 5399be72a..43f9f8bae 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -293,8 +293,8 @@ def flow_system_segments_of_flows_2(flow_system_complex) -> fx.FlowSystem: { 'P_el': fx.Piecewise( [ - fx.Piece(np.linspace(5, 6, len(flow_system.time_series_collection.timesteps)), 30), - fx.Piece(40, np.linspace(60, 70, len(flow_system.time_series_collection.timesteps))), + fx.Piece(np.linspace(5, 6, len(flow_system.timesteps)), 30), + fx.Piece(40, np.linspace(60, 70, len(flow_system.timesteps))), ] ), 'Q_th': fx.Piecewise([fx.Piece(6, 35), fx.Piece(45, 100)]), diff --git a/tests/test_bus.py b/tests/test_bus.py index 4a41a9f9e..136f9d2cc 100644 --- a/tests/test_bus.py +++ b/tests/test_bus.py @@ -31,7 +31,7 @@ def test_bus(self, basic_flow_system_linopy): def test_bus_penalty(self, basic_flow_system_linopy): """Test that flow model constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps bus = fx.Bus('TestBus') flow_system.add_elements(bus, fx.Sink('WärmelastTest', sink=fx.Flow('Q_th_Last', 'TestBus')), diff --git a/tests/test_component.py b/tests/test_component.py index d87a28c29..18ceb717a 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -57,7 +57,7 @@ def test_component(self, basic_flow_system_linopy): def test_on_with_multiple_flows(self, basic_flow_system_linopy): """Test that flow model constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps ub_out2 = np.linspace(1, 1.5, 10).round(2) inputs = [ fx.Flow('In1', 'Fernwärme', relative_minimum=np.ones(10) * 0.1, size=100), @@ -128,7 +128,7 @@ def test_on_with_multiple_flows(self, basic_flow_system_linopy): def test_on_with_single_flow(self, basic_flow_system_linopy): """Test that flow model constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps inputs = [ fx.Flow('In1', 'Fernwärme', relative_minimum=np.ones(10) * 0.1, size=100), ] diff --git a/tests/test_effect.py b/tests/test_effect.py index 5cbc04ac6..9b4e1012a 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -13,7 +13,7 @@ class TestBusModel: def test_minimal(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps effect = fx.Effect('Effect1', '€', 'Testing Effect') flow_system.add_elements(effect) @@ -43,7 +43,7 @@ def test_minimal(self, basic_flow_system_linopy): def test_bounds(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps effect = fx.Effect('Effect1', '€', 'Testing Effect', minimum_operation=1.0, maximum_operation=1.1, diff --git a/tests/test_flow.py b/tests/test_flow.py index 2308dbd31..cce10b21a 100644 --- a/tests/test_flow.py +++ b/tests/test_flow.py @@ -14,7 +14,7 @@ class TestFlowModel: def test_flow_minimal(self, basic_flow_system_linopy): """Test that flow model constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow('Wärme', bus='Fernwärme', size=100) flow_system.add_elements(fx.Sink('Sink', sink=flow)) @@ -34,7 +34,7 @@ def test_flow_minimal(self, basic_flow_system_linopy): def test_flow(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', bus='Fernwärme', @@ -86,7 +86,7 @@ def test_flow(self, basic_flow_system_linopy): def test_effects_per_flow_hour(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps costs_per_flow_hour = xr.DataArray(np.linspace(1,2,timesteps.size), coords=(timesteps,)) co2_per_flow_hour = xr.DataArray(np.linspace(4, 5, timesteps.size), coords=(timesteps,)) @@ -120,7 +120,7 @@ class TestFlowInvestModel: def test_flow_invest(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -175,7 +175,7 @@ def test_flow_invest(self, basic_flow_system_linopy): def test_flow_invest_optional(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -239,7 +239,7 @@ def test_flow_invest_optional(self, basic_flow_system_linopy): def test_flow_invest_optional_wo_min_size(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -303,7 +303,7 @@ def test_flow_invest_optional_wo_min_size(self, basic_flow_system_linopy): def test_flow_invest_wo_min_size_non_optional(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -354,7 +354,7 @@ def test_flow_invest_wo_min_size_non_optional(self, basic_flow_system_linopy): def test_flow_invest_fixed_size(self, basic_flow_system_linopy): """Test flow with fixed size investment.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -446,7 +446,7 @@ class TestFlowOnModel: def test_flow_on(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', bus='Fernwärme', @@ -506,7 +506,7 @@ def test_flow_on(self, basic_flow_system_linopy): def test_effects_per_running_hour(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps costs_per_running_hour = xr.DataArray(np.linspace(1, 2, timesteps.size), coords=(timesteps,)) co2_per_running_hour = xr.DataArray(np.linspace(4, 5, timesteps.size), coords=(timesteps,)) @@ -553,7 +553,7 @@ def test_effects_per_running_hour(self, basic_flow_system_linopy): def test_consecutive_on_hours(self, basic_flow_system_linopy): """Test flow with minimum and maximum consecutive on hours.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -619,7 +619,7 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy): def test_consecutive_on_hours_previous(self, basic_flow_system_linopy): """Test flow with minimum and maximum consecutive on hours.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -686,7 +686,7 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy): def test_consecutive_off_hours(self, basic_flow_system_linopy): """Test flow with minimum and maximum consecutive off hours.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -753,7 +753,7 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy): def test_consecutive_off_hours_previous(self, basic_flow_system_linopy): """Test flow with minimum and maximum consecutive off hours.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', @@ -906,7 +906,7 @@ class TestFlowOnInvestModel: def test_flow_on_invest_optional(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', bus='Fernwärme', @@ -991,7 +991,7 @@ def test_flow_on_invest_optional(self, basic_flow_system_linopy): def test_flow_on_invest_non_optional(self, basic_flow_system_linopy): flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps flow = fx.Flow( 'Wärme', bus='Fernwärme', @@ -1078,7 +1078,7 @@ class TestFlowWithFixedProfile: def test_fixed_relative_profile(self, basic_flow_system_linopy): """Test flow with a fixed relative profile.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create a time-varying profile (e.g., for a load or renewable generation) profile = np.sin(np.linspace(0, 2 * np.pi, len(timesteps))) * 0.5 + 0.5 # Values between 0 and 1 @@ -1100,7 +1100,7 @@ def test_fixed_relative_profile(self, basic_flow_system_linopy): def test_fixed_profile_with_investment(self, basic_flow_system_linopy): """Test flow with fixed profile and investment.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create a fixed profile profile = np.sin(np.linspace(0, 2 * np.pi, len(timesteps))) * 0.5 + 0.5 diff --git a/tests/test_linear_converter.py b/tests/test_linear_converter.py index aaab60dcc..a01c17ef2 100644 --- a/tests/test_linear_converter.py +++ b/tests/test_linear_converter.py @@ -52,7 +52,7 @@ def test_basic_linear_converter(self, basic_flow_system_linopy): def test_linear_converter_time_varying(self, basic_flow_system_linopy): """Test a LinearConverter with time-varying conversion factors.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create time-varying efficiency (e.g., temperature-dependent) varying_efficiency = np.linspace(0.7, 0.9, len(timesteps)) @@ -268,7 +268,7 @@ def test_linear_converter_multidimensional(self, basic_flow_system_linopy): def test_edge_case_time_varying_conversion(self, basic_flow_system_linopy): """Test edge case with extreme time-varying conversion factors.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create fluctuating conversion efficiency (e.g., for a heat pump) # Values range from very low (0.1) to very high (5.0) @@ -317,7 +317,7 @@ def test_edge_case_time_varying_conversion(self, basic_flow_system_linopy): def test_piecewise_conversion(self, basic_flow_system_linopy): """Test a LinearConverter with PiecewiseConversion.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create input and output flows input_flow = fx.Flow('input', bus='input_bus', size=100) @@ -423,7 +423,7 @@ def test_piecewise_conversion(self, basic_flow_system_linopy): def test_piecewise_conversion_with_onoff(self, basic_flow_system_linopy): """Test a LinearConverter with PiecewiseConversion and OnOffParameters.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps + timesteps = flow_system.timesteps # Create input and output flows input_flow = fx.Flow('input', bus='input_bus', size=100) diff --git a/tests/test_storage.py b/tests/test_storage.py index a3b453c2b..472ba4add 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -14,8 +14,8 @@ class TestStorageModel: def test_basic_storage(self, basic_flow_system_linopy): """Test that basic storage model variables and constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps - timesteps_extra = flow_system.time_series_collection.timesteps_extra + timesteps = flow_system.timesteps + timesteps_extra = flow_system.timesteps_extra # Create a simple storage storage = fx.Storage( @@ -91,8 +91,8 @@ def test_basic_storage(self, basic_flow_system_linopy): def test_lossy_storage(self, basic_flow_system_linopy): """Test that basic storage model variables and constraints are correctly generated.""" flow_system = basic_flow_system_linopy - timesteps = flow_system.time_series_collection.timesteps - timesteps_extra = flow_system.time_series_collection.timesteps_extra + timesteps = flow_system.timesteps + timesteps_extra = flow_system.timesteps_extra # Create a simple storage storage = fx.Storage( diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py index a8bc5fa85..8702a57fe 100644 --- a/tests/test_timeseries.py +++ b/tests/test_timeseries.py @@ -8,7 +8,7 @@ import pytest import xarray as xr -from flixopt.core import ConversionError, DataConverter, TimeSeries, TimeSeriesCollection, TimeSeriesData +from flixopt.core import ConversionError, DataConverter, TimeSeriesCollection, TimeSeriesData @pytest.fixture @@ -44,7 +44,7 @@ def test_initialization(self, simple_dataarray): # Check data initialization assert isinstance(ts.stored_data, xr.DataArray) assert ts.stored_data.equals(simple_dataarray) - assert ts.active_data.equals(simple_dataarray) + assert ts.equals(simple_dataarray) # Check backup was created assert ts._backup.equals(simple_dataarray) @@ -87,7 +87,7 @@ def test_active_timesteps_getter_setter(self, sample_timeseries, sample_timestep assert sample_timeseries.active_timesteps.equals(subset_index) # Active data should reflect the subset - assert sample_timeseries.active_data.equals(sample_timeseries.stored_data.sel(time=subset_index)) + assert sample_timeseries.equals(sample_timeseries.stored_data.sel(time=subset_index)) # Reset to full index sample_timeseries.active_timesteps = None @@ -108,7 +108,7 @@ def test_reset(self, sample_timeseries, sample_timesteps): # Should be back to full index assert sample_timeseries.active_timesteps.equals(sample_timesteps) - assert sample_timeseries.active_data.equals(sample_timeseries.stored_data) + assert sample_timeseries.equals(sample_timeseries.stored_data) def test_restore_data(self, sample_timeseries, simple_dataarray): """Test restore_data method.""" @@ -127,7 +127,7 @@ def test_restore_data(self, sample_timeseries, simple_dataarray): # Should be back to original data assert sample_timeseries.stored_data.equals(original_data) - assert sample_timeseries.active_data.equals(original_data) + assert sample_timeseries.equals(original_data) def test_stored_data_setter(self, sample_timeseries, sample_timesteps): """Test stored_data setter with different data types.""" @@ -234,30 +234,30 @@ def test_arithmetic_operations(self, sample_timeseries): # Test operations between two TimeSeries objects assert np.array_equal( - (sample_timeseries + ts2).values, sample_timeseries.active_data.values + ts2.active_data.values + (sample_timeseries + ts2).values, sample_timeseries.values + ts2.values ) assert np.array_equal( - (sample_timeseries - ts2).values, sample_timeseries.active_data.values - ts2.active_data.values + (sample_timeseries - ts2).values, sample_timeseries.values - ts2.values ) assert np.array_equal( - (sample_timeseries * ts2).values, sample_timeseries.active_data.values * ts2.active_data.values + (sample_timeseries * ts2).values, sample_timeseries.values * ts2.values ) assert np.array_equal( - (sample_timeseries / ts2).values, sample_timeseries.active_data.values / ts2.active_data.values + (sample_timeseries / ts2).values, sample_timeseries.values / ts2.values ) # Test operations with DataArrays - assert np.array_equal((sample_timeseries + data2).values, sample_timeseries.active_data.values + data2.values) - assert np.array_equal((data2 + sample_timeseries).values, data2.values + sample_timeseries.active_data.values) + assert np.array_equal((sample_timeseries + data2).values, sample_timeseries.values + data2.values) + assert np.array_equal((data2 + sample_timeseries).values, data2.values + sample_timeseries.values) # Test operations with scalars - assert np.array_equal((sample_timeseries + 5).values, sample_timeseries.active_data.values + 5) - assert np.array_equal((5 + sample_timeseries).values, 5 + sample_timeseries.active_data.values) + assert np.array_equal((sample_timeseries + 5).values, sample_timeseries.values + 5) + assert np.array_equal((5 + sample_timeseries).values, 5 + sample_timeseries.values) # Test unary operations - assert np.array_equal((-sample_timeseries).values, -sample_timeseries.active_data.values) - assert np.array_equal((+sample_timeseries).values, +sample_timeseries.active_data.values) - assert np.array_equal((abs(sample_timeseries)).values, abs(sample_timeseries.active_data.values)) + assert np.array_equal((-sample_timeseries).values, -sample_timeseries.values) + assert np.array_equal((+sample_timeseries).values, +sample_timeseries.values) + assert np.array_equal((abs(sample_timeseries)).values, abs(sample_timeseries.values)) def test_comparison_operations(self, sample_timesteps): """Test comparison operations.""" @@ -279,10 +279,10 @@ def test_comparison_operations(self, sample_timesteps): def test_numpy_ufunc(self, sample_timeseries): """Test numpy ufunc compatibility.""" # Test basic numpy functions - assert np.array_equal(np.add(sample_timeseries, 5).values, np.add(sample_timeseries.active_data, 5).values) + assert np.array_equal(np.add(sample_timeseries, 5).values, np.add(sample_timeseries, 5).values) assert np.array_equal( - np.multiply(sample_timeseries, 2).values, np.multiply(sample_timeseries.active_data, 2).values + np.multiply(sample_timeseries, 2).values, np.multiply(sample_timeseries, 2).values ) # Test with two TimeSeries objects @@ -290,18 +290,18 @@ def test_numpy_ufunc(self, sample_timeseries): ts2 = TimeSeries(data2, 'Second Series') assert np.array_equal( - np.add(sample_timeseries, ts2).values, np.add(sample_timeseries.active_data, ts2.active_data).values + np.add(sample_timeseries, ts2).values, np.add(sample_timeseries, ts2).values ) def test_sel_and_isel_properties(self, sample_timeseries): """Test sel and isel properties.""" # Test that sel property works selected = sample_timeseries.sel(time=sample_timeseries.active_timesteps[0]) - assert selected.item() == sample_timeseries.active_data.values[0] + assert selected.item() == sample_timeseries.values[0] # Test that isel property works indexed = sample_timeseries.isel(time=0) - assert indexed.item() == sample_timeseries.active_data.values[0] + assert indexed.item() == sample_timeseries.values[0] @pytest.fixture @@ -372,12 +372,12 @@ def test_create_time_series(self, sample_collection): # Test scalar ts1 = sample_collection.create_time_series(42, 'scalar_series') assert ts1.name == 'scalar_series' - assert np.all(ts1.active_data.values == 42) + assert np.all(ts1.values == 42) # Test numpy array data = np.array([1, 2, 3, 4, 5]) ts2 = sample_collection.create_time_series(data, 'array_series') - assert np.array_equal(ts2.active_data.values, data) + assert np.array_equal(ts2.values, data) # Test with TimeSeriesData ts3 = sample_collection.create_time_series(TimeSeriesData(10, agg_weight=0.7), 'weighted_series') @@ -386,7 +386,7 @@ def test_create_time_series(self, sample_collection): # Test with extra timestep ts4 = sample_collection.create_time_series(5, 'extra_series', needs_extra_timestep=True) assert ts4.needs_extra_timestep - assert len(ts4.active_data) == len(sample_collection.timesteps_extra) + assert len(ts4) == len(sample_collection.timesteps_extra) # Test duplicate name with pytest.raises(ValueError, match='already exists'): @@ -509,12 +509,12 @@ def test_insert_new_data(self, populated_collection, sample_timesteps): populated_collection.insert_new_data(new_data) # Verify updates - assert np.all(populated_collection['constant_series'].active_data.values == 100) - assert np.array_equal(populated_collection['varying_series'].active_data.values, np.array([5, 10, 15, 20, 25])) + assert np.all(populated_collection['constant_series'].values == 100) + assert np.array_equal(populated_collection['varying_series'].values, np.array([5, 10, 15, 20, 25])) # Series not in the DataFrame should be unchanged assert np.array_equal( - populated_collection['extra_timestep_series'].active_data.values[:-1], np.array([1, 2, 3, 4, 5]) + populated_collection['extra_timestep_series'].values[:-1], np.array([1, 2, 3, 4, 5]) ) # Test with mismatched index @@ -542,7 +542,7 @@ def test_restore_data(self, populated_collection): populated_collection.insert_new_data(new_data) # Verify data was changed - assert np.all(populated_collection['constant_series'].active_data.values == 999) + assert np.all(populated_collection['constant_series'].values == 999) # Restore data populated_collection.restore_data() From fc76adf7e2a9aa9010cb9a04dc57fd65ce3829f2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:22:00 +0200 Subject: [PATCH 061/183] Rename conversion method to pattern: to_... --- flixopt/core.py | 2 +- flixopt/flow_system.py | 10 +++++----- flixopt/results.py | 2 +- tests/test_dataconverter.py | 26 +++++++++++++------------- tests/test_io.py | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 022bf8e6f..73ad098ba 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -46,7 +46,7 @@ class DataConverter: """ @staticmethod - def as_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray: + def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray: """Convert data to xarray.DataArray with specified timesteps index.""" if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index ae9df6407..de94c14e5 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -224,7 +224,7 @@ def to_dataset(self, constants_in_dataset: bool = True) -> xr.Dataset: ds = xr.Dataset(extracted_arrays, attrs=reference_structure) return ds - def as_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: + def to_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: """ Convert the object to a dictionary representation. Now builds on the reference structure for consistency. @@ -364,7 +364,7 @@ def to_json(self, path: Union[str, pathlib.Path]): path: The path to the JSON file. """ # Use the stats mode for JSON export (cleaner output) - data = get_compact_representation(self.as_dict('stats')) + data = get_compact_representation(self.to_dict('stats')) with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) @@ -399,12 +399,12 @@ def create_time_series( # Convert TimeSeriesData to DataArray from .core import DataConverter # Assuming this exists - return DataConverter.as_dataarray(data.data, timesteps=target_timesteps).rename(name) + return DataConverter.to_dataarray(data.data, timesteps=target_timesteps).rename(name) else: # Convert other data types to DataArray from .core import DataConverter # Assuming this exists - return DataConverter.as_dataarray(data, timesteps=target_timesteps).rename(name) + return DataConverter.to_dataarray(data, timesteps=target_timesteps).rename(name) def create_effect_time_series( self, @@ -576,7 +576,7 @@ def __repr__(self): def __str__(self): with StringIO() as output_buffer: console = Console(file=output_buffer, width=1000) # Adjust width as needed - console.print(Pretty(self.as_dict('stats'), expand_all=True, indent_guides=True)) + console.print(Pretty(self.to_dict('stats'), expand_all=True, indent_guides=True)) value = output_buffer.getvalue() return value diff --git a/flixopt/results.py b/flixopt/results.py index 223e3708e..9c0f7245b 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -118,7 +118,7 @@ def from_calculation(cls, calculation: 'Calculation'): """ return cls( solution=calculation.model.solution, - flow_system=calculation.flow_system.as_dataset(constants_in_dataset=True), + flow_system=calculation.flow_system.to_dataset(constants_in_dataset=True), summary=calculation.summary, model=calculation.model, name=calculation.name, diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 49f1438e7..329da7f92 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -14,7 +14,7 @@ def sample_time_index(request): def test_scalar_conversion(sample_time_index): # Test scalar conversion - result = DataConverter.as_dataarray(42, sample_time_index) + result = DataConverter.to_dataarray(42, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (len(sample_time_index),) assert result.dims == ('time',) @@ -25,7 +25,7 @@ def test_series_conversion(sample_time_index): series = pd.Series([1, 2, 3, 4, 5], index=sample_time_index) # Test Series conversion - result = DataConverter.as_dataarray(series, sample_time_index) + result = DataConverter.to_dataarray(series, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) @@ -37,7 +37,7 @@ def test_dataframe_conversion(sample_time_index): df = pd.DataFrame({'A': [1, 2, 3, 4, 5]}, index=sample_time_index) # Test DataFrame conversion - result = DataConverter.as_dataarray(df, sample_time_index) + result = DataConverter.to_dataarray(df, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) @@ -47,7 +47,7 @@ def test_dataframe_conversion(sample_time_index): def test_ndarray_conversion(sample_time_index): # Test 1D array conversion arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index) assert result.shape == (5,) assert result.dims == ('time',) assert np.array_equal(result.values, arr_1d) @@ -58,7 +58,7 @@ def test_dataarray_conversion(sample_time_index): original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) # Test DataArray conversion - result = DataConverter.as_dataarray(original, sample_time_index) + result = DataConverter.to_dataarray(original, sample_time_index) assert result.shape == (5,) assert result.dims == ('time',) assert np.array_equal(result.values, original.values) @@ -71,42 +71,42 @@ def test_dataarray_conversion(sample_time_index): def test_invalid_inputs(sample_time_index): # Test invalid input type with pytest.raises(ConversionError): - DataConverter.as_dataarray('invalid_string', sample_time_index) + DataConverter.to_dataarray('invalid_string', sample_time_index) # Test mismatched Series index mismatched_series = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D')) with pytest.raises(ConversionError): - DataConverter.as_dataarray(mismatched_series, sample_time_index) + DataConverter.to_dataarray(mismatched_series, sample_time_index) # Test DataFrame with multiple columns df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) with pytest.raises(ConversionError): - DataConverter.as_dataarray(df_multi_col, sample_time_index) + DataConverter.to_dataarray(df_multi_col, sample_time_index) # Test mismatched array shape with pytest.raises(ConversionError): - DataConverter.as_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length + DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length # Test multi-dimensional array with pytest.raises(ConversionError): - DataConverter.as_dataarray(np.array([[1, 2], [3, 4]]), sample_time_index) # 2D array not allowed + DataConverter.to_dataarray(np.array([[1, 2], [3, 4]]), sample_time_index) # 2D array not allowed def test_time_index_validation(): # Test with unnamed index unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, unnamed_index) + DataConverter.to_dataarray(42, unnamed_index) # Test with empty index empty_index = pd.DatetimeIndex([], name='time') with pytest.raises(ValueError): - DataConverter.as_dataarray(42, empty_index) + DataConverter.to_dataarray(42, empty_index) # Test with non-DatetimeIndex wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') with pytest.raises(ValueError): - DataConverter.as_dataarray(42, wrong_type_index) + DataConverter.to_dataarray(42, wrong_type_index) if __name__ == '__main__': diff --git a/tests/test_io.py b/tests/test_io.py index 2e6c61ccf..8bcdb050e 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -50,10 +50,10 @@ def test_flow_system_file_io(flow_system, highs_solver): def test_flow_system_io(flow_system): - di = flow_system.as_dict() + di = flow_system.to_dict() _ = fx.FlowSystem.from_dict(di) - ds = flow_system.as_dataset() + ds = flow_system.to_dataset() _ = fx.FlowSystem.from_dataset(ds) print(flow_system) From cc7b15555e321cf3779edba21cb8cd7b6eeb860f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:23:49 +0200 Subject: [PATCH 062/183] Move methods to FlowSystem --- flixopt/flow_system.py | 4 ++-- flixopt/results.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index de94c14e5..6b65d8d00 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -51,7 +51,7 @@ def __init__( # Store timing information directly self.timesteps = self._validate_timesteps(timesteps) self.timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - self.hours_per_timestep = self._calculate_hours_per_timestep(self.timesteps_extra) + self.hours_per_timestep = self.calculate_hours_per_timestep(self.timesteps_extra) self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps ) @@ -89,7 +89,7 @@ def _create_timesteps_with_extra( return pd.DatetimeIndex(timesteps.append(last_date), name='time') @staticmethod - def _calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: + def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: """Calculate duration of each timestep.""" hours_per_step = np.diff(timesteps_extra) / pd.Timedelta(hours=1) return xr.DataArray( diff --git a/flixopt/results.py b/flixopt/results.py index 9c0f7245b..232aaf5af 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -14,7 +14,7 @@ from . import io as fx_io from . import plotting -from .core import TimeSeriesCollection +from .flow_system import FlowSystem if TYPE_CHECKING: import pyvis @@ -160,7 +160,7 @@ def __init__( } self.timesteps_extra = self.solution.indexes['time'] - self.hours_per_timestep = TimeSeriesCollection.calculate_hours_per_timestep(self.timesteps_extra) + self.hours_per_timestep = FlowSystem.calculate_hours_per_timestep(self.timesteps_extra) def __getitem__(self, key: str) -> Union['ComponentResults', 'BusResults', 'EffectResults']: if key in self.components: @@ -684,7 +684,7 @@ def __init__( self.overlap_timesteps = overlap_timesteps self.name = name self.folder = pathlib.Path(folder) if folder is not None else pathlib.Path.cwd() / 'results' - self.hours_per_timestep = TimeSeriesCollection.calculate_hours_per_timestep(self.all_timesteps) + self.hours_per_timestep = FlowSystem.calculate_hours_per_timestep(self.all_timesteps) @property def meta_data(self) -> Dict[str, Union[int, List[str]]]: From ec6e792bf059a641e29fce72b34ee8d5761174de Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 23 Jun 2025 17:41:59 +0200 Subject: [PATCH 063/183] Drop nan values across time dimension if present --- flixopt/flow_system.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 6b65d8d00..039cd2bfa 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -171,7 +171,12 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA # This is a reference to a DataArray array_name = structure[3:] # Remove ":::" prefix if array_name in arrays_dict: - return arrays_dict[array_name] + #TODO: Improve this! + da = arrays_dict[array_name] + if da.isnull().any(): + logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") + return da.dropna(dim='time', how='all') + return da else: logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") return None From b42aad2b1dbecd3cfad88ebe201e846acee57de6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:12:17 +0200 Subject: [PATCH 064/183] Allow lists of values to create DataArray --- flixopt/core.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flixopt/core.py b/flixopt/core.py index 73ad098ba..d629787bb 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -84,6 +84,9 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray f"DataArray length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" ) return data.copy(deep=True) + elif isinstance(data, list): + logger.warning(f'Converting list to DataArray. This is not reccomended.') + return xr.DataArray(data, coords=coords, dims=dims) else: raise ConversionError(f'Unsupported type: {type(data).__name__}') except Exception as e: From b55af45a2e6d3538e098dda4586c519237239da9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:12:32 +0200 Subject: [PATCH 065/183] Update resolving of FlowSystem --- flixopt/flow_system.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 039cd2bfa..9a28e1ad0 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -324,16 +324,13 @@ def from_dict(cls, data: Dict) -> 'FlowSystem': ) # Add elements using resolved data - for bus_data in resolved_data.get('buses', {}).values(): - bus = Bus.from_dict(bus_data) + for bus in resolved_data.get('buses', {}).values(): flow_system.add_elements(bus) - for effect_data in resolved_data.get('effects', {}).values(): - effect = Effect.from_dict(effect_data) + for effect in resolved_data.get('effects', {}).values(): flow_system.add_elements(effect) - for comp_data in resolved_data.get('components', {}).values(): - component = CLASS_REGISTRY[comp_data['__class__']].from_dict(comp_data) + for component in resolved_data.get('components', {}).values(): flow_system.add_elements(component) flow_system.transform_data() From d5ace96959015aabe4f869f4e9a12fb1f0e8419f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:12:45 +0200 Subject: [PATCH 066/183] Simplify TimeSeriesData --- flixopt/core.py | 81 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 30 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index d629787bb..3aad560b2 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -96,43 +96,64 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray class TimeSeriesData: - # TODO: Move to Interface.py - def __init__(self, data: NumericData, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): + """Minimal wrapper around xr.DataArray with aggregation metadata.""" + + def __init__( + self, + data: Union[NumericData, xr.DataArray], + agg_group: Optional[str] = None, + agg_weight: Optional[float] = None, + ): """ - timeseries class for transmit timeseries AND special characteristics of timeseries, - i.g. to define weights needed in calculation_type 'aggregated' - EXAMPLE solar: - you have several solar timeseries. These should not be overweighted - compared to the remaining timeseries (i.g. heat load, price)! - fixed_relative_profile_solar1 = TimeSeriesData(sol_array_1, type = 'solar') - fixed_relative_profile_solar2 = TimeSeriesData(sol_array_2, type = 'solar') - fixed_relative_profile_solar3 = TimeSeriesData(sol_array_3, type = 'solar') - --> this 3 series of same type share one weight, i.e. internally assigned each weight = 1/3 - (instead of standard weight = 1) - Args: - data: The timeseries data, which can be a scalar, array, or numpy array. - agg_group: The group this TimeSeriesData is a part of. agg_weight is split between members of a group. Default is None. - agg_weight: The weight for calculation_type 'aggregated', should be between 0 and 1. Default is None. - - Raises: - Exception: If both agg_group and agg_weight are set, an exception is raised. + data: Numeric data or DataArray + agg_group: Aggregation group name + agg_weight: Aggregation weight (0-1) """ - self.data = data + if (agg_group is not None) and (agg_weight is not None): + raise ValueError('Use either agg_group or agg_weight, not both') + self.agg_group = agg_group self.agg_weight = agg_weight - if (agg_group is not None) and (agg_weight is not None): - raise ValueError('Either or explicit can be used. Not both!') - self.label: Optional[str] = None - def __repr__(self): - # Get the constructor arguments and their current values - init_signature = inspect.signature(self.__init__) - init_args = init_signature.parameters + # Store as DataArray + if isinstance(data, xr.DataArray): + self.data = data + else: + # Simple conversion - let caller handle timesteps/coords + self.data = xr.DataArray(np.asarray(data)) + + @property + def label(self) -> Optional[str]: + return self.data.name + + @label.setter + def label(self, value: Optional[str]): + self.data.name = value + + def to_dataarray(self) -> xr.DataArray: + """Return the DataArray with metadata in attrs.""" + attrs = {} + if self.agg_group is not None: + attrs['agg_group'] = self.agg_group + if self.agg_weight is not None: + attrs['agg_weight'] = self.agg_weight + + da = self.data.copy() + da.attrs.update(attrs) + return da + + @classmethod + def from_dataarray(cls, da: xr.DataArray) -> 'TimeSeriesData': + """Create from DataArray, extracting metadata from attrs.""" + return cls(data=da, agg_group=da.attrs.get('agg_group'), agg_weight=da.attrs.get('agg_weight')) + + def __getattr__(self, name): + """Delegate to underlying DataArray.""" + return getattr(self.data, name) - # Create a dictionary with argument names and their values - args_str = ', '.join(f'{name}={repr(getattr(self, name, None))}' for name in init_args if name != 'self') - return f'{self.__class__.__name__}({args_str})' + def __repr__(self): + return f'TimeSeriesData(agg_group={self.agg_group!r}, agg_weight={self.agg_weight!r})' def __str__(self): return str(self.data) From 4187f305f4d6d73a71aea686604772908525197f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:54:02 +0200 Subject: [PATCH 067/183] Move TImeSeriesData to Structure and simplyfy to inherrit from xarray.DataArray --- flixopt/aggregation.py | 3 +- flixopt/commons.py | 2 +- flixopt/core.py | 66 +----------------------------- flixopt/flow_system.py | 4 +- flixopt/linear_converters.py | 4 +- flixopt/structure.py | 79 +++++++++++++++++++++++++++++++----- 6 files changed, 77 insertions(+), 81 deletions(-) diff --git a/flixopt/aggregation.py b/flixopt/aggregation.py index f149d5f20..e558dc19b 100644 --- a/flixopt/aggregation.py +++ b/flixopt/aggregation.py @@ -22,13 +22,14 @@ TSAM_AVAILABLE = False from .components import Storage -from .core import Scalar, TimeSeriesData +from .core import Scalar from .elements import Component from .flow_system import FlowSystem from .structure import ( Element, Model, SystemModel, + TimeSeriesData, ) if TYPE_CHECKING: diff --git a/flixopt/commons.py b/flixopt/commons.py index 68412d6fe..7d03909c0 100644 --- a/flixopt/commons.py +++ b/flixopt/commons.py @@ -14,11 +14,11 @@ Transmission, ) from .config import CONFIG, change_logging_level -from .core import TimeSeriesData from .effects import Effect from .elements import Bus, Flow from .flow_system import FlowSystem from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects +from .structure import TimeSeriesData __all__ = [ 'TimeSeriesData', diff --git a/flixopt/core.py b/flixopt/core.py index 3aad560b2..43056cedb 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -95,74 +95,11 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray raise ConversionError(f'Converting data {type(data)} to xarray.Dataset raised an error: {str(e)}') from e -class TimeSeriesData: - """Minimal wrapper around xr.DataArray with aggregation metadata.""" - - def __init__( - self, - data: Union[NumericData, xr.DataArray], - agg_group: Optional[str] = None, - agg_weight: Optional[float] = None, - ): - """ - Args: - data: Numeric data or DataArray - agg_group: Aggregation group name - agg_weight: Aggregation weight (0-1) - """ - if (agg_group is not None) and (agg_weight is not None): - raise ValueError('Use either agg_group or agg_weight, not both') - - self.agg_group = agg_group - self.agg_weight = agg_weight - - # Store as DataArray - if isinstance(data, xr.DataArray): - self.data = data - else: - # Simple conversion - let caller handle timesteps/coords - self.data = xr.DataArray(np.asarray(data)) - - @property - def label(self) -> Optional[str]: - return self.data.name - - @label.setter - def label(self, value: Optional[str]): - self.data.name = value - - def to_dataarray(self) -> xr.DataArray: - """Return the DataArray with metadata in attrs.""" - attrs = {} - if self.agg_group is not None: - attrs['agg_group'] = self.agg_group - if self.agg_weight is not None: - attrs['agg_weight'] = self.agg_weight - - da = self.data.copy() - da.attrs.update(attrs) - return da - - @classmethod - def from_dataarray(cls, da: xr.DataArray) -> 'TimeSeriesData': - """Create from DataArray, extracting metadata from attrs.""" - return cls(data=da, agg_group=da.attrs.get('agg_group'), agg_weight=da.attrs.get('agg_weight')) - - def __getattr__(self, name): - """Delegate to underlying DataArray.""" - return getattr(self.data, name) - - def __repr__(self): - return f'TimeSeriesData(agg_group={self.agg_group!r}, agg_weight={self.agg_weight!r})' - - def __str__(self): - return str(self.data) - - class TimeSeries: def __init__(self): raise NotImplementedError('TimeSeries was removed') + class TimeSeriesCollection: """ Collection of TimeSeries objects with shared timestep management. @@ -174,6 +111,7 @@ class TimeSeriesCollection: def __init__(self): raise NotImplementedError('TimeSeriesCollection was removed') + def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10) -> str: """Calculates the mean, median, min, max, and standard deviation of a numeric DataArray.""" format_spec = f'>{padd}.{decimals}f' if padd else f'.{decimals}f' diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 9a28e1ad0..097b3af83 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,10 +16,10 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeriesData, TimeSeries +from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeries from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser from .elements import Bus, Component, Flow -from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation +from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation, TimeSeriesData if TYPE_CHECKING: import pyvis diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 3fd032632..83527fef0 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -8,10 +8,10 @@ import numpy as np from .components import LinearConverter -from .core import NumericDataTS, TimeSeriesData +from .core import NumericDataTS from .elements import Flow from .interface import OnOffParameters -from .structure import register_class_for_io +from .structure import register_class_for_io, TimeSeriesData logger = logging.getLogger('flixopt') diff --git a/flixopt/structure.py b/flixopt/structure.py index 71efe31df..fadc1a06f 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from rich.pretty import Pretty from .config import CONFIG -from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeriesData, TimeSeries +from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeries if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -162,14 +162,8 @@ def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArra """ extracted_arrays = {} - # Handle TimeSeries objects - extract their data using their unique name - if isinstance(obj, TimeSeries): - data_array = obj.rename(obj.name) - extracted_arrays[obj.name] = data_array - return f':::{obj.name}', extracted_arrays - # Handle DataArrays directly - use their unique name - elif isinstance(obj, xr.DataArray): + if isinstance(obj, xr.DataArray): if not obj.name: raise ValueError('DataArray must have a unique name for serialization') extracted_arrays[obj.name] = obj @@ -222,12 +216,13 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA Convert reference structure back to actual objects using provided arrays. Args: - structure: Structure containing references (:::name) + structure: Structure containing references (:::name) or special type markers arrays_dict: Dictionary of available DataArrays Returns: - Structure with references resolved to actual DataArrays + Structure with references resolved to actual DataArrays or TimeSeriesData objects """ + # Handle regular DataArray references if isinstance(structure, str) and structure.startswith(':::'): # This is a reference to a DataArray array_name = structure[3:] # Remove ":::" prefix @@ -246,7 +241,6 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA return resolved_list elif isinstance(structure, dict): - # Check if this is a serialized Interface object if structure.get('__class__') and structure['__class__'] in CLASS_REGISTRY: # This is a nested Interface object - restore it recursively nested_class = CLASS_REGISTRY[structure['__class__']] @@ -256,6 +250,7 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) # Create the nested Interface object return nested_class(**resolved_nested_data) + else: # Regular dictionary - resolve references in values resolved_dict = {} @@ -355,6 +350,9 @@ def _apply_element_label_preference(self, obj): if obj.get('__class__') and 'label' in obj: # This looks like an Interface with a label - return just the label return obj.get('label', obj.get('__class__')) + elif obj.get('__class__') == 'TimeSeriesData': + # For TimeSeriesData, show a compact representation + return f'TimeSeriesData(agg_group={obj.get("agg_group")}, agg_weight={obj.get("agg_weight")})' else: return {k: self._apply_element_label_preference(v) for k, v in obj.items()} elif isinstance(obj, list): @@ -666,6 +664,65 @@ def results_structure(self): } +class TimeSeriesData(xr.DataArray): + """Minimal TimeSeriesData that inherits from xr.DataArray with aggregation metadata.""" + + def __init__(self, *args, agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): + """ + Args: + *args: Arguments passed to DataArray + agg_group: Aggregation group name + agg_weight: Aggregation weight (0-1) + **kwargs: Additional arguments passed to DataArray + """ + if (agg_group is not None) and (agg_weight is not None): + raise ValueError('Use either agg_group or agg_weight, not both') + + # Let xarray handle all the initialization complexity + super().__init__(*args, **kwargs) + + # Add our metadata to attrs after initialization + if agg_group is not None: + self.attrs['agg_group'] = agg_group + if agg_weight is not None: + self.attrs['agg_weight'] = agg_weight + + # Always mark as TimeSeriesData + self.attrs['__timeseries_data__'] = True + + @property + def agg_group(self) -> Optional[str]: + return self.attrs.get('agg_group') + + @property + def agg_weight(self) -> Optional[float]: + return self.attrs.get('agg_weight') + + @classmethod + def from_dataarray(cls, da: xr.DataArray, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): + """Create TimeSeriesData from DataArray, extracting metadata from attrs.""" + # Get aggregation metadata from attrs or parameters + final_agg_group = agg_group if agg_group is not None else da.attrs.get('agg_group') + final_agg_weight = agg_weight if agg_weight is not None else da.attrs.get('agg_weight') + + return cls(da, agg_group=final_agg_group, agg_weight=final_agg_weight) + + @classmethod + def is_timeseries_data(cls, obj) -> bool: + """Check if an object is TimeSeriesData.""" + return isinstance(obj, xr.DataArray) and obj.attrs.get('__timeseries_data__', False) + + def __repr__(self): + agg_info = [] + if self.agg_group: + agg_info.append(f"agg_group='{self.agg_group}'") + if self.agg_weight is not None: + agg_info.append(f'agg_weight={self.agg_weight}') + + info_str = f'TimeSeriesData({", ".join(agg_info)})' if agg_info else 'TimeSeriesData' + return f'{info_str}\n{super().__repr__()}' + + def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_label: bool = False) -> Any: """ Converts values in a nested data structure into JSON-compatible types while preserving or transforming numpy arrays From 617600fe833fc4ee4448bd1936a0bbb484e44212 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 09:58:05 +0200 Subject: [PATCH 068/183] Adjust IO --- flixopt/structure.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index fadc1a06f..166d2182c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -176,7 +176,6 @@ def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArra # Add all extracted arrays from the nested Interface extracted_arrays.update(interface_arrays) - return interface_structure, extracted_arrays # Handle lists @@ -222,12 +221,17 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA Returns: Structure with references resolved to actual DataArrays or TimeSeriesData objects """ - # Handle regular DataArray references + # Handle DataArray references (including TimeSeriesData) if isinstance(structure, str) and structure.startswith(':::'): - # This is a reference to a DataArray array_name = structure[3:] # Remove ":::" prefix if array_name in arrays_dict: - return arrays_dict[array_name] + array = arrays_dict[array_name] + + # Check if this should be restored as TimeSeriesData + if TimeSeriesData.is_timeseries_data(array): + return TimeSeriesData.from_dataarray(array) + else: + return array else: logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") return None @@ -250,7 +254,6 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) # Create the nested Interface object return nested_class(**resolved_nested_data) - else: # Regular dictionary - resolve references in values resolved_dict = {} @@ -350,9 +353,6 @@ def _apply_element_label_preference(self, obj): if obj.get('__class__') and 'label' in obj: # This looks like an Interface with a label - return just the label return obj.get('label', obj.get('__class__')) - elif obj.get('__class__') == 'TimeSeriesData': - # For TimeSeriesData, show a compact representation - return f'TimeSeriesData(agg_group={obj.get("agg_group")}, agg_weight={obj.get("agg_weight")})' else: return {k: self._apply_element_label_preference(v) for k, v in obj.items()} elif isinstance(obj, list): From e80bba0dadc9ec4246a95b40de6e53882cb35286 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:22:30 +0200 Subject: [PATCH 069/183] Move TimeSeriesData back to core.py and fix Conversion --- flixopt/aggregation.py | 3 +- flixopt/commons.py | 2 +- flixopt/core.py | 149 +++++++++++++++++++++++++++++++++++++++-- flixopt/flow_system.py | 24 +++---- flixopt/structure.py | 63 +---------------- 5 files changed, 160 insertions(+), 81 deletions(-) diff --git a/flixopt/aggregation.py b/flixopt/aggregation.py index e558dc19b..f149d5f20 100644 --- a/flixopt/aggregation.py +++ b/flixopt/aggregation.py @@ -22,14 +22,13 @@ TSAM_AVAILABLE = False from .components import Storage -from .core import Scalar +from .core import Scalar, TimeSeriesData from .elements import Component from .flow_system import FlowSystem from .structure import ( Element, Model, SystemModel, - TimeSeriesData, ) if TYPE_CHECKING: diff --git a/flixopt/commons.py b/flixopt/commons.py index 7d03909c0..222c07324 100644 --- a/flixopt/commons.py +++ b/flixopt/commons.py @@ -18,7 +18,7 @@ from .elements import Bus, Flow from .flow_system import FlowSystem from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects -from .structure import TimeSeriesData +from .core import TimeSeriesData __all__ = [ 'TimeSeriesData', diff --git a/flixopt/core.py b/flixopt/core.py index 43056cedb..31738f6c7 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -37,14 +37,134 @@ class ConversionError(Exception): pass +class TimeSeriesData(xr.DataArray): + """Minimal TimeSeriesData that inherits from xr.DataArray with aggregation metadata.""" + + __slots__ = () # No additional instance attributes - everything goes in attrs + + def __init__(self, *args, agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): + """ + Args: + *args: Arguments passed to DataArray + agg_group: Aggregation group name + agg_weight: Aggregation weight (0-1) + **kwargs: Additional arguments passed to DataArray + """ + if (agg_group is not None) and (agg_weight is not None): + raise ValueError('Use either agg_group or agg_weight, not both') + + # Let xarray handle all the initialization complexity + super().__init__(*args, **kwargs) + + # Add our metadata to attrs after initialization + if agg_group is not None: + self.attrs['agg_group'] = agg_group + if agg_weight is not None: + self.attrs['agg_weight'] = agg_weight + + # Always mark as TimeSeriesData + self.attrs['__timeseries_data__'] = True + + @property + def agg_group(self) -> Optional[str]: + return self.attrs.get('agg_group') + + @property + def agg_weight(self) -> Optional[float]: + return self.attrs.get('agg_weight') + + @classmethod + def from_dataarray(cls, da: xr.DataArray, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): + """Create TimeSeriesData from DataArray, extracting metadata from attrs.""" + # Get aggregation metadata from attrs or parameters + final_agg_group = agg_group if agg_group is not None else da.attrs.get('agg_group') + final_agg_weight = agg_weight if agg_weight is not None else da.attrs.get('agg_weight') + + return cls(da, agg_group=final_agg_group, agg_weight=final_agg_weight) + + @classmethod + def is_timeseries_data(cls, obj) -> bool: + """Check if an object is TimeSeriesData.""" + return isinstance(obj, xr.DataArray) and obj.attrs.get('__timeseries_data__', False) + + def __repr__(self): + agg_info = [] + if self.agg_group: + agg_info.append(f"agg_group='{self.agg_group}'") + if self.agg_weight is not None: + agg_info.append(f'agg_weight={self.agg_weight}') + + info_str = f'TimeSeriesData({", ".join(agg_info)})' if agg_info else 'TimeSeriesData' + return f'{info_str}\n{super().__repr__()}' + class DataConverter: """ Converts various data types into xarray.DataArray with a timesteps index. - Supports: scalars, arrays, Series, DataFrames, and DataArrays. + Supports: scalars, arrays, Series, DataFrames, DataArrays, and TimeSeriesData. """ + @staticmethod + def _fix_timeseries_data_indexing( + data: TimeSeriesData, timesteps: pd.DatetimeIndex, dims: list, coords: list + ) -> TimeSeriesData: + """ + Fix TimeSeriesData indexing issues and return properly indexed TimeSeriesData. + + Args: + data: TimeSeriesData that might have indexing issues + timesteps: Target timesteps + dims: Expected dimensions + coords: Expected coordinates + + Returns: + TimeSeriesData with correct indexing + + Raises: + ConversionError: If data cannot be fixed to match expected indexing + """ + expected_shape = (len(timesteps),) + + # Check if dimensions match + if data.dims != tuple(dims): + logger.warning(f'TimeSeriesData has dimensions {data.dims}, expected {dims}. Reshaping to match timesteps.') + # Try to reshape the data to match expected dimensions + if data.size != len(timesteps): + raise ConversionError( + f'TimeSeriesData has {data.size} elements, cannot reshape to match {len(timesteps)} timesteps' + ) + # Create new DataArray with correct coordinates, preserving metadata + reshaped_data = xr.DataArray( + data.values.reshape(expected_shape), coords=coords, dims=dims, name=data.name, attrs=data.attrs.copy() + ) + return TimeSeriesData(reshaped_data) + + # Check if time coordinate length matches + elif data.sizes[dims[0]] != len(coords[0]): + logger.warning( + f'TimeSeriesData has {data.sizes[dims[0]]} time points, ' + f"expected {len(coords[0])}. Cannot reindex - lengths don't match." + ) + raise ConversionError( + f"TimeSeriesData length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" + ) + + # Check if time coordinates are identical + elif not data.coords['time'].equals(timesteps): + logger.warning( + f'TimeSeriesData has different time coordinates than expected. Replacing with provided timesteps.' + ) + # Replace time coordinates while preserving data and metadata + recoordinated_data = xr.DataArray( + data.values, coords=coords, dims=dims, name=data.name, attrs=data.attrs.copy() + ) + return TimeSeriesData(recoordinated_data) + + else: + # Everything matches - return copy to avoid modifying original + return data.copy(deep=True) + @staticmethod def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray: """Convert data to xarray.DataArray with specified timesteps index.""" @@ -58,24 +178,38 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray expected_shape = (len(timesteps),) try: - if isinstance(data, (int, float, np.integer, np.floating)): - return xr.DataArray(data, coords=coords, dims=dims) + # Handle TimeSeriesData first (before generic DataArray check) + if isinstance(data, TimeSeriesData): + return DataConverter._fix_timeseries_data_indexing(data, timesteps, dims, coords) + + elif isinstance(data, TimeSeries): + # Handle TimeSeries objects (your existing logic) + pass # Add your TimeSeries handling here + + elif isinstance(data, (int, float, np.integer, np.floating)): + # Scalar: broadcast to all timesteps + scalar_data = np.full(expected_shape, data) + return xr.DataArray(scalar_data, coords=coords, dims=dims) + elif isinstance(data, pd.DataFrame): if not data.index.equals(timesteps): raise ConversionError("DataFrame index doesn't match timesteps index") if not len(data.columns) == 1: raise ConversionError('DataFrame must have exactly one column') return xr.DataArray(data.values.flatten(), coords=coords, dims=dims) + elif isinstance(data, pd.Series): if not data.index.equals(timesteps): raise ConversionError("Series index doesn't match timesteps index") return xr.DataArray(data.values, coords=coords, dims=dims) + elif isinstance(data, np.ndarray): if data.ndim != 1: raise ConversionError(f'Array must be 1-dimensional, got {data.ndim}') elif data.shape[0] != expected_shape[0]: raise ConversionError(f"Array shape {data.shape} doesn't match expected {expected_shape}") return xr.DataArray(data, coords=coords, dims=dims) + elif isinstance(data, xr.DataArray): if data.dims != tuple(dims): raise ConversionError(f"DataArray dimensions {data.dims} don't match expected {dims}") @@ -84,15 +218,20 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray f"DataArray length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" ) return data.copy(deep=True) + elif isinstance(data, list): - logger.warning(f'Converting list to DataArray. This is not reccomended.') + logger.warning(f'Converting list to DataArray. This is not recommended.') + if len(data) != expected_shape[0]: + raise ConversionError(f"List length {len(data)} doesn't match expected {expected_shape[0]}") return xr.DataArray(data, coords=coords, dims=dims) + else: raise ConversionError(f'Unsupported type: {type(data).__name__}') + except Exception as e: if isinstance(e, ConversionError): raise - raise ConversionError(f'Converting data {type(data)} to xarray.Dataset raised an error: {str(e)}') from e + raise ConversionError(f'Converting data {type(data)} to xarray.DataArray raised an error: {str(e)}') from e class TimeSeries: diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 097b3af83..48b9d5296 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,10 +16,10 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeries +from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeries, DataConverter, ConversionError, TimeSeriesData from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser from .elements import Bus, Component, Flow -from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation, TimeSeriesData +from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation if TYPE_CHECKING: import pyvis @@ -394,18 +394,16 @@ def create_time_series( # Choose appropriate timesteps target_timesteps = self.timesteps_extra if needs_extra_timestep else self.timesteps - if isinstance(data, TimeSeries): - # Extract the data and rename - return data.selected_data.rename(name) - elif isinstance(data, TimeSeriesData): - # Convert TimeSeriesData to DataArray - from .core import DataConverter # Assuming this exists - - return DataConverter.to_dataarray(data.data, timesteps=target_timesteps).rename(name) + if isinstance(data, TimeSeriesData): + try: + return TimeSeriesData( + DataConverter.to_dataarray(data, timesteps=target_timesteps), + agg_group=data.agg_group, agg_weight=data.agg_weight + ).rename(name) + except ConversionError as e: + logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' + f'Take care to use the correct (time) index.') else: - # Convert other data types to DataArray - from .core import DataConverter # Assuming this exists - return DataConverter.to_dataarray(data, timesteps=target_timesteps).rename(name) def create_effect_time_series( diff --git a/flixopt/structure.py b/flixopt/structure.py index 166d2182c..e39a7d0ac 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -165,7 +165,9 @@ def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArra # Handle DataArrays directly - use their unique name if isinstance(obj, xr.DataArray): if not obj.name: - raise ValueError('DataArray must have a unique name for serialization') + raise ValueError(f'DataArrays must have a unique name for serialization. Unnamed DataArrays are not supported. {obj}') + if obj.name in extracted_arrays: + raise ValueError(f' must have a unique name for serialization. "{obj.name}" is a duplicate. {obj}') extracted_arrays[obj.name] = obj return f':::{obj.name}', extracted_arrays @@ -664,65 +666,6 @@ def results_structure(self): } -class TimeSeriesData(xr.DataArray): - """Minimal TimeSeriesData that inherits from xr.DataArray with aggregation metadata.""" - - def __init__(self, *args, agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): - """ - Args: - *args: Arguments passed to DataArray - agg_group: Aggregation group name - agg_weight: Aggregation weight (0-1) - **kwargs: Additional arguments passed to DataArray - """ - if (agg_group is not None) and (agg_weight is not None): - raise ValueError('Use either agg_group or agg_weight, not both') - - # Let xarray handle all the initialization complexity - super().__init__(*args, **kwargs) - - # Add our metadata to attrs after initialization - if agg_group is not None: - self.attrs['agg_group'] = agg_group - if agg_weight is not None: - self.attrs['agg_weight'] = agg_weight - - # Always mark as TimeSeriesData - self.attrs['__timeseries_data__'] = True - - @property - def agg_group(self) -> Optional[str]: - return self.attrs.get('agg_group') - - @property - def agg_weight(self) -> Optional[float]: - return self.attrs.get('agg_weight') - - @classmethod - def from_dataarray(cls, da: xr.DataArray, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): - """Create TimeSeriesData from DataArray, extracting metadata from attrs.""" - # Get aggregation metadata from attrs or parameters - final_agg_group = agg_group if agg_group is not None else da.attrs.get('agg_group') - final_agg_weight = agg_weight if agg_weight is not None else da.attrs.get('agg_weight') - - return cls(da, agg_group=final_agg_group, agg_weight=final_agg_weight) - - @classmethod - def is_timeseries_data(cls, obj) -> bool: - """Check if an object is TimeSeriesData.""" - return isinstance(obj, xr.DataArray) and obj.attrs.get('__timeseries_data__', False) - - def __repr__(self): - agg_info = [] - if self.agg_group: - agg_info.append(f"agg_group='{self.agg_group}'") - if self.agg_weight is not None: - agg_info.append(f'agg_weight={self.agg_weight}') - - info_str = f'TimeSeriesData({", ".join(agg_info)})' if agg_info else 'TimeSeriesData' - return f'{info_str}\n{super().__repr__()}' - - def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_label: bool = False) -> Any: """ Converts values in a nested data structure into JSON-compatible types while preserving or transforming numpy arrays From 387cac64cd0e874788ad16edca1ed77a774b7e26 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:31:04 +0200 Subject: [PATCH 070/183] Adjust IO to account for attrs of DataArrays in a Dataset --- flixopt/io.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/flixopt/io.py b/flixopt/io.py index 1376cafae..23b06cacd 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -206,7 +206,7 @@ def save_dataset_to_netcdf( compression: int = 0, ) -> None: """ - Save a dataset to a netcdf file. Store the attrs as a json string in the 'attrs' attribute. + Save a dataset to a netcdf file. Store all attrs as JSON strings in 'attrs' attributes. Args: ds: Dataset to save. @@ -216,6 +216,7 @@ def save_dataset_to_netcdf( Raises: ValueError: If the path has an invalid file extension. """ + path = pathlib.Path(path) if path.suffix not in ['.nc', '.nc4']: raise ValueError(f'Invalid file extension for path {path}. Only .nc and .nc4 are supported') @@ -228,8 +229,20 @@ def save_dataset_to_netcdf( 'Dataset was exported without compression due to missing dependency "netcdf4".' 'Install netcdf4 via `pip install netcdf4`.' ) + ds = ds.copy(deep=True) ds.attrs = {'attrs': json.dumps(ds.attrs)} + + # Convert all DataArray attrs to JSON strings + for var_name, data_var in ds.data_vars.items(): + if data_var.attrs: # Only if there are attrs + ds[var_name].attrs = {'attrs': json.dumps(data_var.attrs)} + + # Also handle coordinate attrs if they exist + for coord_name, coord_var in ds.coords.items(): + if hasattr(coord_var, 'attrs') and coord_var.attrs: + ds[coord_name].attrs = {'attrs': json.dumps(coord_var.attrs)} + ds.to_netcdf( path, encoding=None @@ -240,16 +253,30 @@ def save_dataset_to_netcdf( def load_dataset_from_netcdf(path: Union[str, pathlib.Path]) -> xr.Dataset: """ - Load a dataset from a netcdf file. Load the attrs from the 'attrs' attribute. + Load a dataset from a netcdf file. Load all attrs from 'attrs' attributes. Args: path: Path to load the dataset from. Returns: - Dataset: Loaded dataset. + Dataset: Loaded dataset with restored attrs. """ ds = xr.load_dataset(path) - ds.attrs = json.loads(ds.attrs['attrs']) + + # Restore Dataset attrs + if 'attrs' in ds.attrs: + ds.attrs = json.loads(ds.attrs['attrs']) + + # Restore DataArray attrs + for var_name, data_var in ds.data_vars.items(): + if 'attrs' in data_var.attrs: + ds[var_name].attrs = json.loads(data_var.attrs['attrs']) + + # Restore coordinate attrs + for coord_name, coord_var in ds.coords.items(): + if hasattr(coord_var, 'attrs') and 'attrs' in coord_var.attrs: + ds[coord_name].attrs = json.loads(coord_var.attrs['attrs']) + return ds From 27734cf67a3ac69a3d4977dfdc477898d956bf98 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 10:42:24 +0200 Subject: [PATCH 071/183] Rename transforming and connection methods in FlowSystem --- flixopt/calculation.py | 7 ++----- flixopt/flow_system.py | 40 +++++++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 8439142c1..e477f6c11 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -136,7 +136,7 @@ class for defined way of solving a flow_system optimization def do_modeling(self) -> SystemModel: t_start = timeit.default_timer() - self._activate_time_series() + self.flow_system.connect_and_transform() self.model = self.flow_system.create_model() self.model.do_modeling() @@ -181,9 +181,6 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma self.results = CalculationResults.from_calculation(self) - def _activate_time_series(self): - self.flow_system.transform_data() - class AggregatedCalculation(FullCalculation): """ @@ -221,7 +218,7 @@ def __init__( def do_modeling(self) -> SystemModel: t_start = timeit.default_timer() - self._activate_time_series() + self.flow_system.connect_and_transform() self._perform_aggregation() # Model the System diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 48b9d5296..ed374319d 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -62,7 +62,7 @@ def __init__( self.effects: EffectCollection = EffectCollection() self.model: Optional[SystemModel] = None - self._connected = False + self._connected_and_transformed = False @staticmethod def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: @@ -223,6 +223,10 @@ def to_dataset(self, constants_in_dataset: bool = True) -> xr.Dataset: Returns: xr.Dataset: Dataset containing all DataArrays with structure in attributes """ + if not self._connected_and_transformed: + logger.warning('FlowSystem is not connected_and_transformed..') + self.connect_and_transform() + reference_structure, extracted_arrays = self._create_reference_structure() # Create the dataset with extracted arrays as variables and structure as attrs @@ -234,6 +238,10 @@ def to_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: Convert the object to a dictionary representation. Now builds on the reference structure for consistency. """ + if not self._connected_and_transformed: + logger.warning('FlowSystem is not connected. Calling connect() now.') + self.connect_and_transform() + reference_structure, _ = self._create_reference_structure() if data_mode == 'data': @@ -333,7 +341,7 @@ def from_dict(cls, data: Dict) -> 'FlowSystem': for component in resolved_data.get('components', {}).values(): flow_system.add_elements(component) - flow_system.transform_data() + flow_system.connect_and_transform() return flow_system @classmethod @@ -353,6 +361,10 @@ def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0, consta compression: The compression level to use when saving the file. constants_in_dataset: If True, constants are included as Dataset variables. """ + if not self._connected_and_transformed: + logger.warning('FlowSystem is not connected. Calling connect() now.') + self.connect_and_transform() + ds = self.to_dataset(constants_in_dataset=constants_in_dataset) fx_io.save_dataset_to_netcdf(ds, path, compression=compression) logger.info(f'Saved FlowSystem to {path}') @@ -365,6 +377,9 @@ def to_json(self, path: Union[str, pathlib.Path]): Args: path: The path to the JSON file. """ + if not self._connected_and_transformed: + logger.warning('FlowSystem needs to be connected and transformed before saving to JSON. Calling connect_and_transform() now.') + self.connect_and_transform() # Use the stats mode for JSON export (cleaner output) data = get_compact_representation(self.to_dict('stats')) with open(path, 'w', encoding='utf-8') as f: @@ -425,12 +440,12 @@ def create_effect_time_series( for effect, value in effect_values_dict.items() } - def transform_data(self): + def connect_and_transform(self): """Transform data for all elements using the new simplified approach.""" - if not self._connected: + if not self._connected_and_transformed: self._connect_network() - for element in self.all_elements.values(): - element.transform_data(self) + for element in self.all_elements.values(): + element.transform_data(self) def add_elements(self, *elements: Element) -> None: """ @@ -440,12 +455,12 @@ def add_elements(self, *elements: Element) -> None: *elements: childs of Element like Boiler, HeatPump, Bus,... modeling Elements """ - if self._connected: + if self._connected_and_transformed: warnings.warn( 'You are adding elements to an already connected FlowSystem. This is not recommended (But it works).', stacklevel=2, ) - self._connected = False + self._connected_and_transformed = False for new_element in list(elements): if isinstance(new_element, Component): self._add_components(new_element) @@ -459,8 +474,8 @@ def add_elements(self, *elements: Element) -> None: ) def create_model(self) -> SystemModel: - if not self._connected: - raise RuntimeError('FlowSystem is not connected. Call FlowSystem.connect() first.') + if not self._connected_and_transformed: + raise RuntimeError('FlowSystem is not connected_and_transformed. Call FlowSystem.connect_and_transform() first.') self.model = SystemModel(self) return self.model @@ -484,8 +499,8 @@ def plot_network( return plotting.plot_network(node_infos, edge_infos, path, controls, show) def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, str]]]: - if not self._connected: - self._connect_network() + if not self._connected_and_transformed: + self.connect_and_transform() nodes = { node.label_full: { 'label': node.label, @@ -568,7 +583,6 @@ def _connect_network(self): f'Connected {len(self.buses)} Buses and {len(self.components)} ' f'via {len(self.flows)} Flows inside the FlowSystem.' ) - self._connected = True def __repr__(self): return f'<{self.__class__.__name__} with {len(self.components)} components and {len(self.effects)} effects>' From 4915b81f876e30578f977ea201ac2de030510d70 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:27:00 +0200 Subject: [PATCH 072/183] Compacted IO methods --- flixopt/flow_system.py | 114 ++++++++++++++--------------------- flixopt/linear_converters.py | 4 +- flixopt/results.py | 2 +- flixopt/structure.py | 23 ------- 4 files changed, 47 insertions(+), 96 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index ed374319d..3737c6e58 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -129,7 +129,7 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: # Extract from components components_structure = {} for comp_label, component in self.components.items(): - comp_structure, comp_arrays = self._extract_from_interface(component) + comp_structure, comp_arrays = component._create_reference_structure() all_extracted_arrays.update(comp_arrays) components_structure[comp_label] = comp_structure reference_structure['components'] = components_structure @@ -137,7 +137,7 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: # Extract from buses buses_structure = {} for bus_label, bus in self.buses.items(): - bus_structure, bus_arrays = self._extract_from_interface(bus) + bus_structure, bus_arrays = bus._create_reference_structure() all_extracted_arrays.update(bus_arrays) buses_structure[bus_label] = bus_structure reference_structure['buses'] = buses_structure @@ -145,22 +145,13 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: # Extract from effects effects_structure = {} for effect in self.effects: - effect_structure, effect_arrays = self._extract_from_interface(effect) + effect_structure, effect_arrays = effect._create_reference_structure() all_extracted_arrays.update(effect_arrays) effects_structure[effect.label] = effect_structure reference_structure['effects'] = effects_structure return reference_structure, all_extracted_arrays - def _extract_from_interface(self, interface_obj) -> Tuple[Dict, Dict[str, xr.DataArray]]: - """Extract arrays from an Interface object using its reference system.""" - if hasattr(interface_obj, '_create_reference_structure'): - return interface_obj._create_reference_structure() - else: - # Fallback for objects that don't have the new Interface methods - logger.warning(f"Object {interface_obj} doesn't have _create_reference_structure method") - return interface_obj.to_dict(), {} - @classmethod def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataArray]): """ @@ -212,7 +203,7 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA else: return structure - def to_dataset(self, constants_in_dataset: bool = True) -> xr.Dataset: + def to_dataset(self) -> xr.Dataset: """ Convert the FlowSystem to an xarray Dataset using the Interface pattern. All DataArrays become dataset variables, structure goes to attrs. @@ -233,25 +224,6 @@ def to_dataset(self, constants_in_dataset: bool = True) -> xr.Dataset: ds = xr.Dataset(extracted_arrays, attrs=reference_structure) return ds - def to_dict(self, data_mode: Literal['data', 'name', 'stats'] = 'data') -> Dict: - """ - Convert the object to a dictionary representation. - Now builds on the reference structure for consistency. - """ - if not self._connected_and_transformed: - logger.warning('FlowSystem is not connected. Calling connect() now.') - self.connect_and_transform() - - reference_structure, _ = self._create_reference_structure() - - if data_mode == 'data': - return reference_structure - elif data_mode == 'stats': - # For stats mode, we might want to process the structure further - return fx_io.remove_none_and_empty(reference_structure) - else: # name mode - return reference_structure - @classmethod def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': """ @@ -310,39 +282,22 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': return flow_system - @classmethod - def from_dict(cls, data: Dict) -> 'FlowSystem': + def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ - Load a FlowSystem from a dictionary using the Interface pattern. + Save the FlowSystem to a NetCDF file using the Interface pattern. Args: - data: Dictionary containing the FlowSystem data. + path: The path to the netCDF file. + compression: The compression level to use when saving the file. + constants_in_dataset: If True, constants are included as Dataset variables. """ - # For dict format, resolve with empty arrays (references may not be used) - resolved_data = cls._resolve_reference_structure(data, {}) - - # Extract constructor parameters - timesteps_extra = pd.DatetimeIndex(resolved_data['timesteps_extra'], name='time') - hours_of_last_timestep = float((timesteps_extra[-1] - timesteps_extra[-2]) / pd.Timedelta(hours=1)) - - flow_system = cls( - timesteps=timesteps_extra[:-1], - hours_of_last_timestep=hours_of_last_timestep, - hours_of_previous_timesteps=resolved_data['hours_of_previous_timesteps'], - ) - - # Add elements using resolved data - for bus in resolved_data.get('buses', {}).values(): - flow_system.add_elements(bus) - - for effect in resolved_data.get('effects', {}).values(): - flow_system.add_elements(effect) - - for component in resolved_data.get('components', {}).values(): - flow_system.add_elements(component) + if not self._connected_and_transformed: + logger.warning('FlowSystem is not connected. Calling connect() now.') + self.connect_and_transform() - flow_system.connect_and_transform() - return flow_system + ds = self.to_dataset() + fx_io.save_dataset_to_netcdf(ds, path, compression=compression) + logger.info(f'Saved FlowSystem to {path}') @classmethod def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'FlowSystem': @@ -352,22 +307,22 @@ def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'FlowSystem': ds = fx_io.load_dataset_from_netcdf(path) return cls.from_dataset(ds) - def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0, constants_in_dataset: bool = True): + def get_structure(self, clean: bool = False) -> Dict: """ - Save the FlowSystem to a NetCDF file using the Interface pattern. + Get FlowSystem structure. Args: - path: The path to the netCDF file. - compression: The compression level to use when saving the file. - constants_in_dataset: If True, constants are included as Dataset variables. + clean: If True, remove None and empty dicts and lists. """ if not self._connected_and_transformed: logger.warning('FlowSystem is not connected. Calling connect() now.') self.connect_and_transform() - ds = self.to_dataset(constants_in_dataset=constants_in_dataset) - fx_io.save_dataset_to_netcdf(ds, path, compression=compression) - logger.info(f'Saved FlowSystem to {path}') + reference_structure, _ = self._create_reference_structure() + if clean: + return fx_io.remove_none_and_empty(reference_structure) + else: + return reference_structure def to_json(self, path: Union[str, pathlib.Path]): """ @@ -381,7 +336,7 @@ def to_json(self, path: Union[str, pathlib.Path]): logger.warning('FlowSystem needs to be connected and transformed before saving to JSON. Calling connect_and_transform() now.') self.connect_and_transform() # Use the stats mode for JSON export (cleaner output) - data = get_compact_representation(self.to_dict('stats')) + data = get_compact_representation(self.get_structure(clean=True)) with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) @@ -446,6 +401,7 @@ def connect_and_transform(self): self._connect_network() for element in self.all_elements.values(): element.transform_data(self) + self._connected_and_transformed = True def add_elements(self, *elements: Element) -> None: """ @@ -590,10 +546,28 @@ def __repr__(self): def __str__(self): with StringIO() as output_buffer: console = Console(file=output_buffer, width=1000) # Adjust width as needed - console.print(Pretty(self.to_dict('stats'), expand_all=True, indent_guides=True)) + console.print(Pretty(self.get_structure(clean=True), expand_all=True, indent_guides=True)) value = output_buffer.getvalue() return value + def __eq__(self, other: 'FlowSystem'): + """Check if two FlowSystems are equal by comparing their dataset representations.""" + if not isinstance(other, FlowSystem): + raise NotImplementedError('Comparison with other types is not implemented for class FlowSystem') + + ds_me = self.to_dataset() + ds_other = other.to_dataset() + + try: + xr.testing.assert_equal(ds_me, ds_other) + except AssertionError: + return False + + if ds_me.attrs != ds_other.attrs: + return False + + return True + @property def flows(self) -> Dict[str, Flow]: set_of_flows = {flow for comp in self.components.values() for flow in comp.inputs + comp.outputs} diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 83527fef0..3fd032632 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -8,10 +8,10 @@ import numpy as np from .components import LinearConverter -from .core import NumericDataTS +from .core import NumericDataTS, TimeSeriesData from .elements import Flow from .interface import OnOffParameters -from .structure import register_class_for_io, TimeSeriesData +from .structure import register_class_for_io logger = logging.getLogger('flixopt') diff --git a/flixopt/results.py b/flixopt/results.py index 232aaf5af..e13cb0785 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -118,7 +118,7 @@ def from_calculation(cls, calculation: 'Calculation'): """ return cls( solution=calculation.model.solution, - flow_system=calculation.flow_system.to_dataset(constants_in_dataset=True), + flow_system=calculation.flow_system.to_dataset(), summary=calculation.summary, model=calculation.model, name=calculation.name, diff --git a/flixopt/structure.py b/flixopt/structure.py index e39a7d0ac..10ab7ad8c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -303,17 +303,6 @@ def to_dataset(self) -> xr.Dataset: ds = xr.Dataset(extracted_arrays, attrs=reference_structure) return ds - def to_dict(self) -> Dict: - """ - Convert the object to a dictionary representation. - DataArrays/TimeSeries are converted to references, but structure is preserved. - - Returns: - Dict: Dictionary with references to DataArrays/TimeSeries - """ - reference_structure, _ = self._create_reference_structure() - return reference_structure - def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: """ Generate a dictionary representation of the object's constructor arguments. @@ -362,18 +351,6 @@ def _apply_element_label_preference(self, obj): else: return obj - def to_json(self, path: Union[str, pathlib.Path]): - """ - Save the element to a JSON file for documentation purposes. - Uses the infos() method for consistent representation. - - Args: - path: The path to the JSON file. - """ - data = get_compact_representation(self.infos(use_numpy=False, use_element_label=True)) - with open(path, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=4, ensure_ascii=False) - def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ Save the object to a NetCDF file. From fc5549a20a6d1e6ebc35d760b72a524ad18457fc Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:33:41 +0200 Subject: [PATCH 073/183] Remove infos() --- flixopt/elements.py | 11 ----------- flixopt/structure.py | 35 ----------------------------------- 2 files changed, 46 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index 3ea29a09f..ba74030cb 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -72,12 +72,6 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: if self.on_off_parameters is not None: self.on_off_parameters.transform_data(flow_system, self.label_full) - def infos(self, use_numpy=True, use_element_label: bool = False) -> Dict: - infos = super().infos(use_numpy, use_element_label) - infos['inputs'] = [flow.infos(use_numpy, use_element_label) for flow in self.inputs] - infos['outputs'] = [flow.infos(use_numpy, use_element_label) for flow in self.outputs] - return infos - def _check_unique_flow_labels(self): all_flow_labels = [flow.label for flow in self.inputs + self.outputs] @@ -253,11 +247,6 @@ def transform_data(self, flow_system: 'FlowSystem'): if isinstance(self.size, InvestParameters): self.size.transform_data(flow_system) - def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: - infos = super().infos(use_numpy, use_element_label) - infos['is_input_in_component'] = self.is_input_in_component - return infos - def to_dict(self) -> Dict: data = super().to_dict() if isinstance(data.get('previous_flow_rate'), np.ndarray): diff --git a/flixopt/structure.py b/flixopt/structure.py index 10ab7ad8c..abcfdf9d2 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -303,41 +303,6 @@ def to_dataset(self) -> xr.Dataset: ds = xr.Dataset(extracted_arrays, attrs=reference_structure) return ds - def infos(self, use_numpy: bool = True, use_element_label: bool = False) -> Dict: - """ - Generate a dictionary representation of the object's constructor arguments. - Built on top of dataset creation for better consistency and analytics capabilities. - - Args: - use_numpy: Whether to convert NumPy arrays to lists. Defaults to True. - If True, numeric numpy arrays are preserved as-is. - If False, they are converted to lists. - use_element_label: Whether to use element labels instead of full infos for nested objects. - - Returns: - A dictionary representation optimized for documentation and analysis. - """ - # Get the core dataset representation - ds = self.to_dataset() - - # Start with the reference structure from attrs - info_dict = dict(ds.attrs) - - # Process DataArrays in the dataset based on preferences - for var_name, data_array in ds.data_vars.items(): - if use_numpy: - # Keep as DataArray/numpy for analysis - info_dict[f'_data_{var_name}'] = data_array - else: - # Convert to lists for JSON compatibility - info_dict[f'_data_{var_name}'] = data_array.values.tolist() - - # Apply element label preference to nested structures - if use_element_label: - info_dict = self._apply_element_label_preference(info_dict) - - return info_dict - def _apply_element_label_preference(self, obj): """Apply element label preference to nested structures.""" if isinstance(obj, dict): From 299ff433e31682088a91f51f4e1669c513236a95 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:35:15 +0200 Subject: [PATCH 074/183] remove from_dict() and to_dict() --- flixopt/elements.py | 6 ------ flixopt/structure.py | 31 ------------------------------- 2 files changed, 37 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index ba74030cb..48e73ef76 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -247,12 +247,6 @@ def transform_data(self, flow_system: 'FlowSystem'): if isinstance(self.size, InvestParameters): self.size.transform_data(flow_system) - def to_dict(self) -> Dict: - data = super().to_dict() - if isinstance(data.get('previous_flow_rate'), np.ndarray): - data['previous_flow_rate'] = data['previous_flow_rate'].tolist() - return data - def _plausibility_checks(self) -> None: # TODO: Incorporate into Variable? (Lower_bound can not be greater than upper bound if np.any(self.relative_minimum > self.relative_maximum): diff --git a/flixopt/structure.py b/flixopt/structure.py index abcfdf9d2..4f94073e7 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -303,19 +303,6 @@ def to_dataset(self) -> xr.Dataset: ds = xr.Dataset(extracted_arrays, attrs=reference_structure) return ds - def _apply_element_label_preference(self, obj): - """Apply element label preference to nested structures.""" - if isinstance(obj, dict): - if obj.get('__class__') and 'label' in obj: - # This looks like an Interface with a label - return just the label - return obj.get('label', obj.get('__class__')) - else: - return {k: self._apply_element_label_preference(v) for k, v in obj.items()} - elif isinstance(obj, list): - return [self._apply_element_label_preference(item) for item in obj] - else: - return obj - def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ Save the object to a NetCDF file. @@ -375,24 +362,6 @@ def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': ds = fx_io.load_dataset_from_netcdf(path) return cls.from_dataset(ds) - @classmethod - def from_dict(cls, data: Dict) -> 'Interface': - """ - Create an instance from a dictionary representation. - This is now a thin wrapper around the reference resolution system. - - Args: - data: Dictionary containing the data for the object. - """ - class_name = data.pop('__class__', None) - if class_name and class_name != cls.__name__: - logger.warning(f"Dict class '{class_name}' doesn't match target class '{cls.__name__}'") - - # Since dict format doesn't separate arrays, resolve with empty arrays dict - # References in dict format would need to be handled differently if they exist - resolved_params = cls._resolve_reference_structure(data, {}) - return cls(**resolved_params) - def __repr__(self): # Get the constructor arguments and their current values init_signature = inspect.signature(self.__init__) From abc22b108b207072a53d8de4ac50b89c803e72ca Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 11:39:22 +0200 Subject: [PATCH 075/183] Update __str__ of Interface --- flixopt/structure.py | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 4f94073e7..d19e371d1 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -20,6 +20,7 @@ from .config import CONFIG from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeries +from . import io as fx_io if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -311,8 +312,6 @@ def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): path: Path to save the NetCDF file compression: Compression level (0-9) """ - from . import io as fx_io # Assuming fx_io is available - ds = self.to_dataset() fx_io.save_dataset_to_netcdf(ds, path, compression=compression) @@ -357,11 +356,35 @@ def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': Returns: Interface instance """ - from . import io as fx_io # Assuming fx_io is available - ds = fx_io.load_dataset_from_netcdf(path) return cls.from_dataset(ds) + def get_structure(self, clean: bool = False) -> Dict: + """ + Get FlowSystem structure. + + Args: + clean: If True, remove None and empty dicts and lists. + """ + + reference_structure, _ = self._create_reference_structure() + if clean: + return fx_io.remove_none_and_empty(reference_structure) + return reference_structure + + def to_json(self, path: Union[str, pathlib.Path]): + """ + Save the Element to a JSON file using the Interface pattern. + This is meant for documentation and comparison, not for reloading. + + Args: + path: The path to the JSON file. + """ + # Use the stats mode for JSON export (cleaner output) + data = get_compact_representation(self.get_structure(clean=True)) + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + def __repr__(self): # Get the constructor arguments and their current values init_signature = inspect.signature(self.__init__) @@ -372,7 +395,7 @@ def __repr__(self): return f'{self.__class__.__name__}({args_str})' def __str__(self): - return get_str_representation(self.infos(use_numpy=True, use_element_label=True)) + return get_str_representation(self.get_structure(clean=True)) class Element(Interface): From 9b4c44c8315abf2cea89c953f96b5535158e0a2c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:01:06 +0200 Subject: [PATCH 076/183] Improve str and repr --- flixopt/flow_system.py | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 3737c6e58..f5077434d 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -540,15 +540,38 @@ def _connect_network(self): f'via {len(self.flows)} Flows inside the FlowSystem.' ) - def __repr__(self): - return f'<{self.__class__.__name__} with {len(self.components)} components and {len(self.effects)} effects>' - - def __str__(self): - with StringIO() as output_buffer: - console = Console(file=output_buffer, width=1000) # Adjust width as needed - console.print(Pretty(self.get_structure(clean=True), expand_all=True, indent_guides=True)) - value = output_buffer.getvalue() - return value + def __repr__(self) -> str: + """Compact representation for debugging.""" + status = '✓' if self._connected_and_transformed else '⚠' + return ( + f'FlowSystem({len(self.timesteps)} timesteps ' + f'[{self.timesteps[0].strftime("%Y-%m-%d")} to {self.timesteps[-1].strftime("%Y-%m-%d")}], ' + f'{len(self.components)} Components / {len(self.buses)} Buses / {len(self.effects)} Effects, {status})' + ) + + def __str__(self) -> str: + """Structured summary for users.""" + + def format_elements(parts: list, label: str): + if not parts: + return f'{label}:{"":>8} {len(parts)}' + name_list = ', '.join(parts[:3]) + if len(parts) > 3: + name_list += f' ... (+{len(parts) - 3} more)' + return f'{label}:{"":>8} {len(parts)} ({name_list})' + + lines = [ + f'FlowSystem Overview:', + f'{"─" * 50}', + f'Time period: {self.timesteps[0].date()} to {self.timesteps[-1].date()}', + f'Timesteps: {len(self.timesteps)} ({self.timesteps.freq or "irregular frequency"})', + format_elements(list(self.components), 'Components'), + format_elements(list(self.buses), 'Buses'), + format_elements(list(self.effects.effects), 'Effects'), + f'Status: {"Connected & Transformed" if self._connected_and_transformed else "Not connected"}', + ] + + return '\n'.join(lines) def __eq__(self, other: 'FlowSystem'): """Check if two FlowSystems are equal by comparing their dataset representations.""" From 0ab7ea6f75d2b3b6c68339a1e770386a2f5b6f62 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:03:44 +0200 Subject: [PATCH 077/183] Improve str and repr --- flixopt/flow_system.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index f5077434d..7d62c35ca 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -546,28 +546,32 @@ def __repr__(self) -> str: return ( f'FlowSystem({len(self.timesteps)} timesteps ' f'[{self.timesteps[0].strftime("%Y-%m-%d")} to {self.timesteps[-1].strftime("%Y-%m-%d")}], ' - f'{len(self.components)} Components / {len(self.buses)} Buses / {len(self.effects)} Effects, {status})' + f'{len(self.components)} Components, {len(self.buses)} Buses, {len(self.effects)} Effects, {status})' ) def __str__(self) -> str: """Structured summary for users.""" - def format_elements(parts: list, label: str): - if not parts: - return f'{label}:{"":>8} {len(parts)}' - name_list = ', '.join(parts[:3]) - if len(parts) > 3: - name_list += f' ... (+{len(parts) - 3} more)' - return f'{label}:{"":>8} {len(parts)} ({name_list})' + def format_elements(element_names: list, label: str, alignment: int = 12): + name_list = ', '.join(element_names[:3]) + if len(element_names) > 3: + name_list += f' ... (+{len(element_names) - 3} more)' + + suffix = f' ({name_list})' if element_names else '' + padding = alignment - len(label) - 1 # -1 for the colon + return f'{label}:{"":<{padding}} {len(element_names)}{suffix}' + + time_period = f'Time period: {self.timesteps[0].date()} to {self.timesteps[-1].date()}' + freq_str = str(self.timesteps.freq).replace('<', '').replace('>', '') if self.timesteps.freq else 'irregular' lines = [ f'FlowSystem Overview:', f'{"─" * 50}', - f'Time period: {self.timesteps[0].date()} to {self.timesteps[-1].date()}', - f'Timesteps: {len(self.timesteps)} ({self.timesteps.freq or "irregular frequency"})', - format_elements(list(self.components), 'Components'), - format_elements(list(self.buses), 'Buses'), - format_elements(list(self.effects.effects), 'Effects'), + time_period, + f'Timesteps: {len(self.timesteps)} ({freq_str})', + format_elements(list(self.components.keys()), 'Components'), + format_elements(list(self.buses.keys()), 'Buses'), + format_elements(list(self.effects.effects.keys()), 'Effects'), f'Status: {"Connected & Transformed" if self._connected_and_transformed else "Not connected"}', ] From 1dcbbb05c25074cc4e0c4d5dd8463431f02d9527 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:20:56 +0200 Subject: [PATCH 078/183] Add docstring --- flixopt/flow_system.py | 14 ++++++++++++-- flixopt/structure.py | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 7d62c35ca..4a227df9c 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -29,8 +29,18 @@ class FlowSystem: """ - A FlowSystem organizes the high level Elements (Components & Effects). - Uses xr.Dataset directly from its Interface elements instead of TimeSeriesCollection. + FlowSystem serves as the main container for energy system modeling, organizing + high-level elements including Components (like boilers, heat pumps, storages), + Buses (connection points), and Effects (system-wide influences). It handles + time series data management, network connectivity, and provides serialization + capabilities for saving and loading complete system configurations. + + The system uses xarray.Dataset for efficient time series data handling. It can be exported and restored to NETCDF. + + See Also: + Component: Base class for system components like boilers, heat pumps. + Bus: Connection points for flows between components. + Effect: System-wide effects, like the optimization objective. """ def __init__( diff --git a/flixopt/structure.py b/flixopt/structure.py index d19e371d1..7dc19318d 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from rich.pretty import Pretty from .config import CONFIG -from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeries +from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeries, TimeSeriesData from . import io as fx_io if TYPE_CHECKING: # for type checking and preventing circular imports From 9aec99081486388b6152ac6ea748ec8fbf0851d6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:28:06 +0200 Subject: [PATCH 079/183] Unify IO stuff in Interface class --- flixopt/flow_system.py | 145 +++++++++++------------------------------ flixopt/structure.py | 6 ++ 2 files changed, 43 insertions(+), 108 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 4a227df9c..ff99725a5 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -19,7 +19,7 @@ from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeries, DataConverter, ConversionError, TimeSeriesData from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser from .elements import Bus, Component, Flow -from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation +from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation, Interface if TYPE_CHECKING: import pyvis @@ -27,7 +27,7 @@ logger = logging.getLogger('flixopt') -class FlowSystem: +class FlowSystem(Interface): """ FlowSystem serves as the main container for energy system modeling, organizing high-level elements including Components (like boilers, heat pumps, storages), @@ -44,10 +44,10 @@ class FlowSystem: """ def __init__( - self, - timesteps: pd.DatetimeIndex, - hours_of_last_timestep: Optional[float] = None, - hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, + self, + timesteps: pd.DatetimeIndex, + hours_of_last_timestep: Optional[float] = None, + hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, ): """ Args: @@ -89,7 +89,7 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: @staticmethod def _create_timesteps_with_extra( - timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] + timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] ) -> pd.DatetimeIndex: """Create timesteps with an extra step at the end.""" if hours_of_last_timestep is None: @@ -108,7 +108,7 @@ def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataAr @staticmethod def _calculate_hours_of_previous_timesteps( - timesteps: pd.DatetimeIndex, hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] + timesteps: pd.DatetimeIndex, hours_of_previous_timesteps: Optional[Union[float, np.ndarray]] ) -> Union[float, np.ndarray]: """Calculate duration of regular timesteps.""" if hours_of_previous_timesteps is not None: @@ -119,21 +119,22 @@ def _calculate_hours_of_previous_timesteps( def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: """ - Create reference structure for FlowSystem following the Interface pattern. - Extracts all DataArrays from components, buses, and effects. + Override Interface method to handle FlowSystem-specific serialization. + Combines custom FlowSystem logic with Interface pattern for nested objects. Returns: Tuple of (reference_structure, extracted_arrays_dict) """ - reference_structure = { - '__class__': self.__class__.__name__, - 'timesteps_extra': [date.isoformat() for date in self.timesteps_extra], - 'hours_of_previous_timesteps': self.hours_of_previous_timesteps, - } + # Start with Interface base functionality for constructor parameters + reference_structure, all_extracted_arrays = super()._create_reference_structure() + + # Override timesteps serialization (we need timesteps_extra instead of timesteps) + reference_structure['timesteps_extra'] = [date.isoformat() for date in self.timesteps_extra] - all_extracted_arrays = {} + # Remove timesteps from structure since we're using timesteps_extra + reference_structure.pop('timesteps', None) - # Add timing arrays directly + # Add timing arrays directly (not handled by Interface introspection) all_extracted_arrays['hours_per_timestep'] = self.hours_per_timestep # Extract from components @@ -162,64 +163,10 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: return reference_structure, all_extracted_arrays - @classmethod - def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataArray]): - """ - Resolve reference structure back to actual objects. - Reuses the Interface pattern for consistency. - """ - if isinstance(structure, str) and structure.startswith(':::'): - # This is a reference to a DataArray - array_name = structure[3:] # Remove ":::" prefix - if array_name in arrays_dict: - #TODO: Improve this! - da = arrays_dict[array_name] - if da.isnull().any(): - logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") - return da.dropna(dim='time', how='all') - return da - else: - logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") - return None - - elif isinstance(structure, list): - resolved_list = [] - for item in structure: - resolved_item = cls._resolve_reference_structure(item, arrays_dict) - if resolved_item is not None: - resolved_list.append(resolved_item) - return resolved_list - - elif isinstance(structure, dict): - # Check if this is a serialized Interface object - if structure.get('__class__') and structure['__class__'] in CLASS_REGISTRY: - # This is a nested Interface object - restore it recursively - nested_class = CLASS_REGISTRY[structure['__class__']] - # Remove the __class__ key and process the rest - nested_data = {k: v for k, v in structure.items() if k != '__class__'} - # Resolve references in the nested data - resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) - # Create the nested Interface object - return nested_class(**resolved_nested_data) - else: - # Regular dictionary - resolve references in values - resolved_dict = {} - for key, value in structure.items(): - resolved_value = cls._resolve_reference_structure(value, arrays_dict) - if resolved_value is not None or value is None: - resolved_dict[key] = resolved_value - return resolved_dict - - else: - return structure - def to_dataset(self) -> xr.Dataset: """ - Convert the FlowSystem to an xarray Dataset using the Interface pattern. - All DataArrays become dataset variables, structure goes to attrs. - - Args: - constants_in_dataset: If True, constants are included as Dataset variables. + Convert the FlowSystem to an xarray Dataset. + Ensures FlowSystem is connected before serialization. Returns: xr.Dataset: Dataset containing all DataArrays with structure in attributes @@ -228,16 +175,13 @@ def to_dataset(self) -> xr.Dataset: logger.warning('FlowSystem is not connected_and_transformed..') self.connect_and_transform() - reference_structure, extracted_arrays = self._create_reference_structure() - - # Create the dataset with extracted arrays as variables and structure as attrs - ds = xr.Dataset(extracted_arrays, attrs=reference_structure) - return ds + return super().to_dataset() @classmethod def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': """ - Create a FlowSystem from an xarray Dataset using the Interface pattern. + Create a FlowSystem from an xarray Dataset. + Handles FlowSystem-specific reconstruction logic. Args: ds: Dataset containing the FlowSystem data @@ -255,7 +199,7 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': # Calculate hours_of_last_timestep from the timesteps hours_of_last_timestep = float((timesteps_extra[-1] - timesteps_extra[-2]) / pd.Timedelta(hours=1)) - # Create FlowSystem instance + # Create FlowSystem instance with constructor parameters flow_system = cls( timesteps=timesteps_extra[:-1], hours_of_last_timestep=hours_of_last_timestep, @@ -278,66 +222,53 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': for bus_label, bus_data in buses_structure.items(): bus = cls._resolve_reference_structure(bus_data, arrays_dict) if not isinstance(bus, Bus): - logger.critical(f'Restoring component {bus_label} failed.') + logger.critical(f'Restoring bus {bus_label} failed.') flow_system._add_buses(bus) # Restore effects effects_structure = reference_structure.get('effects', {}) for effect_label, effect_data in effects_structure.items(): effect = cls._resolve_reference_structure(effect_data, arrays_dict) - if not isinstance(effect, Effect): - logger.critical(f'Restoring component {effect_label} failed.') + logger.critical(f'Restoring effect {effect_label} failed.') flow_system._add_effects(effect) return flow_system def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ - Save the FlowSystem to a NetCDF file using the Interface pattern. + Save the FlowSystem to a NetCDF file. + Ensures FlowSystem is connected before saving. Args: path: The path to the netCDF file. compression: The compression level to use when saving the file. - constants_in_dataset: If True, constants are included as Dataset variables. """ if not self._connected_and_transformed: - logger.warning('FlowSystem is not connected. Calling connect() now.') + logger.warning('FlowSystem is not connected. Calling connect_and_transform() now.') self.connect_and_transform() - ds = self.to_dataset() - fx_io.save_dataset_to_netcdf(ds, path, compression=compression) + super().to_netcdf(path, compression) logger.info(f'Saved FlowSystem to {path}') - @classmethod - def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'FlowSystem': - """ - Load a FlowSystem from a netcdf file using the Interface pattern. - """ - ds = fx_io.load_dataset_from_netcdf(path) - return cls.from_dataset(ds) - def get_structure(self, clean: bool = False) -> Dict: """ Get FlowSystem structure. + Ensures FlowSystem is connected before getting structure. Args: clean: If True, remove None and empty dicts and lists. """ if not self._connected_and_transformed: - logger.warning('FlowSystem is not connected. Calling connect() now.') + logger.warning('FlowSystem is not connected. Calling connect_and_transform() now.') self.connect_and_transform() - reference_structure, _ = self._create_reference_structure() - if clean: - return fx_io.remove_none_and_empty(reference_structure) - else: - return reference_structure + return super().get_structure(clean) def to_json(self, path: Union[str, pathlib.Path]): """ - Save the flow system to a JSON file using the Interface pattern. - This is meant for documentation and comparison, not for reloading. + Save the flow system to a JSON file. + Ensures FlowSystem is connected before saving. Args: path: The path to the JSON file. @@ -345,10 +276,8 @@ def to_json(self, path: Union[str, pathlib.Path]): if not self._connected_and_transformed: logger.warning('FlowSystem needs to be connected and transformed before saving to JSON. Calling connect_and_transform() now.') self.connect_and_transform() - # Use the stats mode for JSON export (cleaner output) - data = get_compact_representation(self.get_structure(clean=True)) - with open(path, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=4, ensure_ascii=False) + + super().to_json(path) def create_time_series( self, diff --git a/flixopt/structure.py b/flixopt/structure.py index 7dc19318d..55a347651 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -230,6 +230,12 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA if array_name in arrays_dict: array = arrays_dict[array_name] + #TODO: Improve this! + if array.isnull().any(): + logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") + return array.dropna(dim='time', how='all') + return array + # Check if this should be restored as TimeSeriesData if TimeSeriesData.is_timeseries_data(array): return TimeSeriesData.from_dataarray(array) From e3703117883822044a4e5497abc012abcfc26ad3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 19:29:16 +0200 Subject: [PATCH 080/183] Improve test tu utilize __eq__ method --- tests/test_io.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_io.py b/tests/test_io.py index 8bcdb050e..497b334c8 100644 --- a/tests/test_io.py +++ b/tests/test_io.py @@ -50,11 +50,12 @@ def test_flow_system_file_io(flow_system, highs_solver): def test_flow_system_io(flow_system): - di = flow_system.to_dict() - _ = fx.FlowSystem.from_dict(di) + flow_system.to_json('fs.json') ds = flow_system.to_dataset() - _ = fx.FlowSystem.from_dataset(ds) + new_fs = fx.FlowSystem.from_dataset(ds) + + assert flow_system == new_fs print(flow_system) flow_system.__repr__() From 793e820de5cba614ca2b10fd8b4b683d60fdc412 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 21:42:27 +0200 Subject: [PATCH 081/183] Make Interface class more robust and improve exceptions --- flixopt/structure.py | 340 +++++++++++++++++++++++++++++++------------ 1 file changed, 243 insertions(+), 97 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 55a347651..36f723ad1 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -109,12 +109,46 @@ def coords_extra(self) -> Tuple[pd.DatetimeIndex]: class Interface: """ - This class is used to collect arguments about a Model. Its the base class for all Elements and Models in flixopt. + Base class for all Elements and Models in flixopt that provides serialization capabilities. + + This class enables automatic serialization/deserialization of objects containing xarray DataArrays + and nested Interface objects to/from xarray Datasets and NetCDF files. It uses introspection + of constructor parameters to automatically handle most serialization scenarios. + + Key Features: + - Automatic extraction and restoration of xarray DataArrays + - Support for nested Interface objects + - NetCDF and JSON export/import + - Recursive handling of complex nested structures + + Subclasses must implement: + transform_data(flow_system): Transform data to match FlowSystem dimensions + + Example: + >>> class MyComponent(Interface): + ... def __init__(self, name: str, power_data: xr.DataArray): + ... self.name = name + ... self.power_data = power_data + ... + ... def transform_data(self, flow_system): + ... # Transform power_data to match flow_system timesteps + ... pass + >>> + >>> component = MyComponent('gen1', power_array) + >>> component.to_netcdf('component.nc') # Save to file + >>> restored = MyComponent.from_netcdf('component.nc') # Load from file """ def transform_data(self, flow_system: 'FlowSystem'): - """Transforms the data of the interface to match the FlowSystem's dimensions""" - raise NotImplementedError('Every Interface needs a transform_data() method') + """Transform the data of the interface to match the FlowSystem's dimensions. + + Args: + flow_system: The FlowSystem containing timing and dimensional information + + Raises: + NotImplementedError: Must be implemented by subclasses + """ + raise NotImplementedError('Every Interface subclass needs a transform_data() method') def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: """ @@ -123,15 +157,19 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: Returns: Tuple of (reference_structure, extracted_arrays_dict) + + Raises: + ValueError: If DataArrays don't have unique names or are duplicated """ - # Get constructor parameters - init_params = inspect.signature(self.__init__).parameters + # Get constructor parameters using caching for performance + if not hasattr(self, '_cached_init_params'): + self._cached_init_params = list(inspect.signature(self.__init__).parameters.keys()) # Process all constructor parameters reference_structure = {'__class__': self.__class__.__name__} all_extracted_arrays = {} - for name in init_params: + for name in self._cached_init_params: if name == 'self': continue @@ -140,73 +178,102 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: continue # Extract arrays and get reference structure - processed_value, extracted_arrays = self._extract_dataarrays_recursive(value) + processed_value, extracted_arrays = self._extract_dataarrays_recursive(value, name) + + # Check for array name conflicts + conflicts = set(all_extracted_arrays.keys()) & set(extracted_arrays.keys()) + if conflicts: + raise ValueError( + f'DataArray name conflicts detected: {conflicts}. ' + f'Each DataArray must have a unique name for serialization.' + ) # Add extracted arrays to the collection all_extracted_arrays.update(extracted_arrays) # Only store in structure if it's not None/empty after processing - if processed_value is not None and not (isinstance(processed_value, (dict, list)) and not processed_value): + if processed_value is not None and not self._is_empty_container(processed_value): reference_structure[name] = processed_value return reference_structure, all_extracted_arrays - def _extract_dataarrays_recursive(self, obj) -> Tuple[Any, Dict[str, xr.DataArray]]: + @staticmethod + def _is_empty_container(obj) -> bool: + """Check if object is an empty container (dict, list, tuple, set).""" + return isinstance(obj, (dict, list, tuple, set)) and len(obj) == 0 + + def _extract_dataarrays_recursive(self, obj, context_name: str = '') -> Tuple[Any, Dict[str, xr.DataArray]]: """ Recursively extract DataArrays/TimeSeries from nested structures. Args: obj: Object to process + context_name: Name context for better error messages Returns: Tuple of (processed_object_with_references, extracted_arrays_dict) + + Raises: + ValueError: If DataArrays don't have unique names """ extracted_arrays = {} # Handle DataArrays directly - use their unique name if isinstance(obj, xr.DataArray): if not obj.name: - raise ValueError(f'DataArrays must have a unique name for serialization. Unnamed DataArrays are not supported. {obj}') - if obj.name in extracted_arrays: - raise ValueError(f' must have a unique name for serialization. "{obj.name}" is a duplicate. {obj}') - extracted_arrays[obj.name] = obj - return f':::{obj.name}', extracted_arrays + raise ValueError( + f'DataArrays must have a unique name for serialization. ' + f'Unnamed DataArray found in {context_name}. Please set array.name = "unique_name"' + ) + + array_name = str(obj.name) # Ensure string type + if array_name in extracted_arrays: + raise ValueError( + f'DataArray name "{array_name}" is duplicated in {context_name}. ' + f'Each DataArray must have a unique name for serialization.' + ) + + extracted_arrays[array_name] = obj + return f':::{array_name}', extracted_arrays # Handle Interface objects - extract their DataArrays too elif isinstance(obj, Interface): - # Get the Interface's reference structure and arrays - interface_structure, interface_arrays = obj._create_reference_structure() - - # Add all extracted arrays from the nested Interface - extracted_arrays.update(interface_arrays) - return interface_structure, extracted_arrays - - # Handle lists - elif isinstance(obj, list): - processed_list = [] - for item in obj: - processed_item, nested_arrays = self._extract_dataarrays_recursive(item) + try: + interface_structure, interface_arrays = obj._create_reference_structure() + extracted_arrays.update(interface_arrays) + return interface_structure, extracted_arrays + except Exception as e: + raise ValueError(f'Failed to process nested Interface object in {context_name}: {e}') from e + + # Handle sequences (lists, tuples) + elif isinstance(obj, (list, tuple)): + processed_items = [] + for i, item in enumerate(obj): + item_context = f'{context_name}[{i}]' if context_name else f'item[{i}]' + processed_item, nested_arrays = self._extract_dataarrays_recursive(item, item_context) extracted_arrays.update(nested_arrays) - processed_list.append(processed_item) - return processed_list, extracted_arrays + processed_items.append(processed_item) + return processed_items, extracted_arrays # Handle dictionaries elif isinstance(obj, dict): processed_dict = {} for key, value in obj.items(): - processed_value, nested_arrays = self._extract_dataarrays_recursive(value) + key_context = f'{context_name}.{key}' if context_name else str(key) + processed_value, nested_arrays = self._extract_dataarrays_recursive(value, key_context) extracted_arrays.update(nested_arrays) processed_dict[key] = processed_value return processed_dict, extracted_arrays - # Handle tuples (convert to list for JSON compatibility) - elif isinstance(obj, tuple): - processed_list = [] - for item in obj: - processed_item, nested_arrays = self._extract_dataarrays_recursive(item) + # Handle sets (convert to list for JSON compatibility) + elif isinstance(obj, set): + processed_items = [] + for i, item in enumerate(obj): + item_context = f'{context_name}.set_item[{i}]' if context_name else f'set_item[{i}]' + processed_item, nested_arrays = self._extract_dataarrays_recursive(item, item_context) extracted_arrays.update(nested_arrays) - processed_list.append(processed_item) - return processed_list, extracted_arrays + processed_items.append(processed_item) + return processed_items, extracted_arrays # For all other types, serialize to basic types else: @@ -222,28 +289,29 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA arrays_dict: Dictionary of available DataArrays Returns: - Structure with references resolved to actual DataArrays or TimeSeriesData objects + Structure with references resolved to actual DataArrays or objects + + Raises: + ValueError: If referenced arrays are not found or class is not registered """ - # Handle DataArray references (including TimeSeriesData) + # Handle DataArray references if isinstance(structure, str) and structure.startswith(':::'): array_name = structure[3:] # Remove ":::" prefix - if array_name in arrays_dict: - array = arrays_dict[array_name] - - #TODO: Improve this! - if array.isnull().any(): - logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") - return array.dropna(dim='time', how='all') - return array - - # Check if this should be restored as TimeSeriesData - if TimeSeriesData.is_timeseries_data(array): - return TimeSeriesData.from_dataarray(array) - else: - return array - else: - logger.critical(f"Referenced DataArray '{array_name}' not found in dataset") - return None + if array_name not in arrays_dict: + raise ValueError(f"Referenced DataArray '{array_name}' not found in dataset") + + array = arrays_dict[array_name] + + # Handle null values with warning + if array.isnull().any(): + logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") + array = array.dropna(dim='time', how='all') + + # Check if this should be restored as TimeSeriesData + if TimeSeriesData.is_timeseries_data(array): + return TimeSeriesData.from_dataarray(array) + + return array elif isinstance(structure, list): resolved_list = [] @@ -254,15 +322,25 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA return resolved_list elif isinstance(structure, dict): - if structure.get('__class__') and structure['__class__'] in CLASS_REGISTRY: + if structure.get('__class__'): + class_name = structure['__class__'] + if class_name not in CLASS_REGISTRY: + raise ValueError( + f"Class '{class_name}' not found in CLASS_REGISTRY. " + f'Available classes: {list(CLASS_REGISTRY.keys())}' + ) + # This is a nested Interface object - restore it recursively - nested_class = CLASS_REGISTRY[structure['__class__']] + nested_class = CLASS_REGISTRY[class_name] # Remove the __class__ key and process the rest nested_data = {k: v for k, v in structure.items() if k != '__class__'} # Resolve references in the nested data resolved_nested_data = cls._resolve_reference_structure(nested_data, arrays_dict) - # Create the nested Interface object - return nested_class(**resolved_nested_data) + + try: + return nested_class(**resolved_nested_data) + except Exception as e: + raise ValueError(f'Failed to create instance of {class_name}: {e}') from e else: # Regular dictionary - resolve references in values resolved_dict = {} @@ -276,21 +354,36 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA return structure def _serialize_to_basic_types(self, obj): - """Convert object to basic Python types only (no DataArrays, no custom objects).""" + """ + Convert object to basic Python types only (no DataArrays, no custom objects). + + Args: + obj: Object to serialize + + Returns: + Object converted to basic Python types (str, int, float, bool, list, dict) + """ if obj is None or isinstance(obj, (str, int, float, bool)): return obj elif isinstance(obj, np.integer): return int(obj) elif isinstance(obj, np.floating): return float(obj) + elif isinstance(obj, np.bool_): + return bool(obj) elif isinstance(obj, (np.ndarray, pd.Series, pd.DataFrame)): return obj.tolist() if hasattr(obj, 'tolist') else list(obj) elif isinstance(obj, dict): return {k: self._serialize_to_basic_types(v) for k, v in obj.items()} elif isinstance(obj, (list, tuple)): return [self._serialize_to_basic_types(item) for item in obj] + elif isinstance(obj, set): + return [self._serialize_to_basic_types(item) for item in obj] elif hasattr(obj, 'isoformat'): # datetime objects return obj.isoformat() + elif hasattr(obj, '__dict__'): # Custom objects with attributes + logger.warning(f'Converting custom object {type(obj)} to dict representation: {obj}') + return {str(k): self._serialize_to_basic_types(v) for k, v in obj.__dict__.items()} else: # For any other object, try to convert to string as fallback logger.warning(f'Converting unknown type {type(obj)} to string: {obj}') @@ -303,12 +396,16 @@ def to_dataset(self) -> xr.Dataset: Returns: xr.Dataset: Dataset containing all DataArrays with basic objects only in attributes - """ - reference_structure, extracted_arrays = self._create_reference_structure() - # Create the dataset with extracted arrays as variables and structure as attrs - ds = xr.Dataset(extracted_arrays, attrs=reference_structure) - return ds + Raises: + ValueError: If serialization fails due to naming conflicts or invalid data + """ + try: + reference_structure, extracted_arrays = self._create_reference_structure() + # Create the dataset with extracted arrays as variables and structure as attrs + return xr.Dataset(extracted_arrays, attrs=reference_structure) + except Exception as e: + raise ValueError(f'Failed to convert {self.__class__.__name__} to dataset: {e}') from e def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ @@ -317,9 +414,16 @@ def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): Args: path: Path to save the NetCDF file compression: Compression level (0-9) + + Raises: + ValueError: If serialization fails + IOError: If file cannot be written """ - ds = self.to_dataset() - fx_io.save_dataset_to_netcdf(ds, path, compression=compression) + try: + ds = self.to_dataset() + fx_io.save_dataset_to_netcdf(ds, path, compression=compression) + except Exception as e: + raise IOError(f'Failed to save {self.__class__.__name__} to NetCDF file {path}: {e}') from e @classmethod def from_dataset(cls, ds: xr.Dataset) -> 'Interface': @@ -331,25 +435,31 @@ def from_dataset(cls, ds: xr.Dataset) -> 'Interface': Returns: Interface instance + + Raises: + ValueError: If dataset format is invalid or class mismatch """ - # Get class name and verify it matches - class_name = ds.attrs.get('__class__') - if class_name != cls.__name__: - logger.warning(f"Dataset class '{class_name}' doesn't match target class '{cls.__name__}'") + try: + # Get class name and verify it matches + class_name = ds.attrs.get('__class__') + if class_name and class_name != cls.__name__: + logger.warning(f"Dataset class '{class_name}' doesn't match target class '{cls.__name__}'") - # Get the reference structure from attrs - reference_structure = dict(ds.attrs) + # Get the reference structure from attrs + reference_structure = dict(ds.attrs) - # Remove the class name since it's not a constructor parameter - reference_structure.pop('__class__', None) + # Remove the class name since it's not a constructor parameter + reference_structure.pop('__class__', None) - # Create arrays dictionary from dataset variables - arrays_dict = {name: array for name, array in ds.data_vars.items()} + # Create arrays dictionary from dataset variables + arrays_dict = {name: array for name, array in ds.data_vars.items()} - # Resolve all references using the centralized method - resolved_params = cls._resolve_reference_structure(reference_structure, arrays_dict) + # Resolve all references using the centralized method + resolved_params = cls._resolve_reference_structure(reference_structure, arrays_dict) - return cls(**resolved_params) + return cls(**resolved_params) + except Exception as e: + raise ValueError(f'Failed to create {cls.__name__} from dataset: {e}') from e @classmethod def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': @@ -361,18 +471,27 @@ def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': Returns: Interface instance + + Raises: + IOError: If file cannot be read + ValueError: If file format is invalid """ - ds = fx_io.load_dataset_from_netcdf(path) - return cls.from_dataset(ds) + try: + ds = fx_io.load_dataset_from_netcdf(path) + return cls.from_dataset(ds) + except Exception as e: + raise IOError(f'Failed to load {cls.__name__} from NetCDF file {path}: {e}') from e def get_structure(self, clean: bool = False) -> Dict: """ - Get FlowSystem structure. + Get object structure as a dictionary. Args: clean: If True, remove None and empty dicts and lists. - """ + Returns: + Dictionary representation of the object structure + """ reference_structure, _ = self._create_reference_structure() if clean: return fx_io.remove_none_and_empty(reference_structure) @@ -380,28 +499,55 @@ def get_structure(self, clean: bool = False) -> Dict: def to_json(self, path: Union[str, pathlib.Path]): """ - Save the Element to a JSON file using the Interface pattern. + Save the object to a JSON file. This is meant for documentation and comparison, not for reloading. Args: path: The path to the JSON file. + + Raises: + IOError: If file cannot be written """ - # Use the stats mode for JSON export (cleaner output) - data = get_compact_representation(self.get_structure(clean=True)) - with open(path, 'w', encoding='utf-8') as f: - json.dump(data, f, indent=4, ensure_ascii=False) + try: + # Use the stats mode for JSON export (cleaner output) + data = get_compact_representation(self.get_structure(clean=True)) + with open(path, 'w', encoding='utf-8') as f: + json.dump(data, f, indent=4, ensure_ascii=False) + except Exception as e: + raise IOError(f'Failed to save {self.__class__.__name__} to JSON file {path}: {e}') from e def __repr__(self): - # Get the constructor arguments and their current values - init_signature = inspect.signature(self.__init__) - init_args = init_signature.parameters - - # Create a dictionary with argument names and their values - args_str = ', '.join(f'{name}={repr(getattr(self, name, None))}' for name in init_args if name != 'self') - return f'{self.__class__.__name__}({args_str})' + """Return a detailed string representation for debugging.""" + try: + # Get the constructor arguments and their current values + init_signature = inspect.signature(self.__init__) + init_args = init_signature.parameters + + # Create a dictionary with argument names and their values, with better formatting + args_parts = [] + for name in init_args: + if name == 'self': + continue + value = getattr(self, name, None) + # Truncate long representations + value_repr = repr(value) + if len(value_repr) > 50: + value_repr = value_repr[:47] + '...' + args_parts.append(f'{name}={value_repr}') + + args_str = ', '.join(args_parts) + return f'{self.__class__.__name__}({args_str})' + except Exception: + # Fallback if introspection fails + return f'{self.__class__.__name__}()' def __str__(self): - return get_str_representation(self.get_structure(clean=True)) + """Return a user-friendly string representation.""" + try: + return get_str_representation(self.get_structure(clean=True)) + except Exception: + # Fallback if structure generation fails + return f'{self.__class__.__name__} instance' class Element(Interface): From b87d979bb797c0e02c7b547e62dff51375c90def Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 24 Jun 2025 21:50:31 +0200 Subject: [PATCH 082/183] Add option to copy Interfaces (And the FlowSystem) --- flixopt/structure.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/flixopt/structure.py b/flixopt/structure.py index 36f723ad1..9cb830ff0 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -549,6 +549,28 @@ def __str__(self): # Fallback if structure generation fails return f'{self.__class__.__name__} instance' + def copy(self) -> 'Interface': + """ + Create a copy of the Interface object. + + Uses the existing serialization infrastructure to ensure proper copying + of all DataArrays and nested objects. + + Returns: + A new instance of the same class with copied data. + """ + # Convert to dataset, copy it, and convert back + dataset = self.to_dataset().copy(deep=True) + return self.__class__.from_dataset(dataset) + + def __copy__(self): + """Support for copy.copy().""" + return self.copy() + + def __deepcopy__(self, memo): + """Support for copy.deepcopy().""" + return self.copy() + class Element(Interface): """This class is the basic Element of flixopt. Every Element has a label""" From 8ec265ec35d9f43de951884accd74c1ddf4de945 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:01:46 +0200 Subject: [PATCH 083/183] Make a copy of a FLowSytsem that gets reused in a second Calculation --- flixopt/calculation.py | 6 ++++++ flixopt/flow_system.py | 13 +++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index e477f6c11..f52c1ca19 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -54,7 +54,13 @@ def __init__( folder: folder where results should be saved. If None, then the current working directory is used. """ self.name = name + if flow_system.used_in_calculation: + logging.warning(f'FlowSystem {flow_system.name} is already used in a calculation. ' + f'Creating a copy for Calculation "{self.name}".') + flow_system = flow_system.copy() + self.flow_system = flow_system + self.flow_system._used_in_calculation = True self.model: Optional[SystemModel] = None self.active_timesteps = active_timesteps diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index ff99725a5..386f54a72 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -44,10 +44,10 @@ class FlowSystem(Interface): """ def __init__( - self, - timesteps: pd.DatetimeIndex, - hours_of_last_timestep: Optional[float] = None, - hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, + self, + timesteps: pd.DatetimeIndex, + hours_of_last_timestep: Optional[float] = None, + hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, ): """ Args: @@ -73,6 +73,7 @@ def __init__( self.model: Optional[SystemModel] = None self._connected_and_transformed = False + self._used_in_calculation = False @staticmethod def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: @@ -542,3 +543,7 @@ def flows(self) -> Dict[str, Flow]: @property def all_elements(self) -> Dict[str, Element]: return {**self.components, **self.effects.effects, **self.flows, **self.buses} + + @property + def used_in_calculation(self) -> bool: + return self._used_in_calculation From a46fe648af7c8c279449327b28ebb3832dc19c40 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:02:35 +0200 Subject: [PATCH 084/183] Remove test_timeseries.py --- tests/test_timeseries.py | 605 --------------------------------------- 1 file changed, 605 deletions(-) delete mode 100644 tests/test_timeseries.py diff --git a/tests/test_timeseries.py b/tests/test_timeseries.py deleted file mode 100644 index 8702a57fe..000000000 --- a/tests/test_timeseries.py +++ /dev/null @@ -1,605 +0,0 @@ -import json -import tempfile -from pathlib import Path -from typing import Dict, List, Tuple - -import numpy as np -import pandas as pd -import pytest -import xarray as xr - -from flixopt.core import ConversionError, DataConverter, TimeSeriesCollection, TimeSeriesData - - -@pytest.fixture -def sample_timesteps(): - """Create a sample time index with the required 'time' name.""" - return pd.date_range('2023-01-01', periods=5, freq='D', name='time') - - -@pytest.fixture -def simple_dataarray(sample_timesteps): - """Create a simple DataArray with time dimension.""" - return xr.DataArray([10, 20, 30, 40, 50], coords={'time': sample_timesteps}, dims=['time']) - - -@pytest.fixture -def sample_timeseries(simple_dataarray): - """Create a sample TimeSeries object.""" - return TimeSeries(simple_dataarray, name='Test Series') - - -class TestTimeSeries: - """Test suite for TimeSeries class.""" - - def test_initialization(self, simple_dataarray): - """Test basic initialization of TimeSeries.""" - ts = TimeSeries(simple_dataarray, name='Test Series') - - # Check basic properties - assert ts.name == 'Test Series' - assert ts.aggregation_weight is None - assert ts.aggregation_group is None - - # Check data initialization - assert isinstance(ts.stored_data, xr.DataArray) - assert ts.stored_data.equals(simple_dataarray) - assert ts.equals(simple_dataarray) - - # Check backup was created - assert ts._backup.equals(simple_dataarray) - - # Check active timesteps - assert ts.active_timesteps.equals(simple_dataarray.indexes['time']) - - def test_initialization_with_aggregation_params(self, simple_dataarray): - """Test initialization with aggregation parameters.""" - ts = TimeSeries( - simple_dataarray, name='Weighted Series', aggregation_weight=0.5, aggregation_group='test_group' - ) - - assert ts.name == 'Weighted Series' - assert ts.aggregation_weight == 0.5 - assert ts.aggregation_group == 'test_group' - - def test_initialization_validation(self, sample_timesteps): - """Test validation during initialization.""" - # Test missing time dimension - invalid_data = xr.DataArray([1, 2, 3], dims=['invalid_dim']) - with pytest.raises(ValueError, match='must have a "time" index'): - TimeSeries(invalid_data, name='Invalid Series') - - # Test multi-dimensional data - multi_dim_data = xr.DataArray( - [[1, 2, 3], [4, 5, 6]], coords={'dim1': [0, 1], 'time': sample_timesteps[:3]}, dims=['dim1', 'time'] - ) - with pytest.raises(ValueError, match='dimensions of DataArray must be 1'): - TimeSeries(multi_dim_data, name='Multi-dim Series') - - def test_active_timesteps_getter_setter(self, sample_timeseries, sample_timesteps): - """Test active_timesteps getter and setter.""" - # Initial state should use all timesteps - assert sample_timeseries.active_timesteps.equals(sample_timesteps) - - # Set to a subset - subset_index = sample_timesteps[1:3] - sample_timeseries.active_timesteps = subset_index - assert sample_timeseries.active_timesteps.equals(subset_index) - - # Active data should reflect the subset - assert sample_timeseries.equals(sample_timeseries.stored_data.sel(time=subset_index)) - - # Reset to full index - sample_timeseries.active_timesteps = None - assert sample_timeseries.active_timesteps.equals(sample_timesteps) - - # Test invalid type - with pytest.raises(TypeError, match='must be a pandas DatetimeIndex'): - sample_timeseries.active_timesteps = 'invalid' - - def test_reset(self, sample_timeseries, sample_timesteps): - """Test reset method.""" - # Set to subset first - subset_index = sample_timesteps[1:3] - sample_timeseries.active_timesteps = subset_index - - # Reset - sample_timeseries.reset() - - # Should be back to full index - assert sample_timeseries.active_timesteps.equals(sample_timesteps) - assert sample_timeseries.equals(sample_timeseries.stored_data) - - def test_restore_data(self, sample_timeseries, simple_dataarray): - """Test restore_data method.""" - # Modify the stored data - new_data = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) - - # Store original data for comparison - original_data = sample_timeseries.stored_data - - # Set new data - sample_timeseries.stored_data = new_data - assert sample_timeseries.stored_data.equals(new_data) - - # Restore from backup - sample_timeseries.restore_data() - - # Should be back to original data - assert sample_timeseries.stored_data.equals(original_data) - assert sample_timeseries.equals(original_data) - - def test_stored_data_setter(self, sample_timeseries, sample_timesteps): - """Test stored_data setter with different data types.""" - # Test with a Series - series_data = pd.Series([5, 6, 7, 8, 9], index=sample_timesteps) - sample_timeseries.stored_data = series_data - assert np.array_equal(sample_timeseries.stored_data.values, series_data.values) - - # Test with a single-column DataFrame - df_data = pd.DataFrame({'col1': [15, 16, 17, 18, 19]}, index=sample_timesteps) - sample_timeseries.stored_data = df_data - assert np.array_equal(sample_timeseries.stored_data.values, df_data['col1'].values) - - # Test with a NumPy array - array_data = np.array([25, 26, 27, 28, 29]) - sample_timeseries.stored_data = array_data - assert np.array_equal(sample_timeseries.stored_data.values, array_data) - - # Test with a scalar - sample_timeseries.stored_data = 42 - assert np.all(sample_timeseries.stored_data.values == 42) - - # Test with another DataArray - another_dataarray = xr.DataArray([30, 31, 32, 33, 34], coords={'time': sample_timesteps}, dims=['time']) - sample_timeseries.stored_data = another_dataarray - assert sample_timeseries.stored_data.equals(another_dataarray) - - def test_stored_data_setter_no_change(self, sample_timeseries): - """Test stored_data setter when data doesn't change.""" - # Get current data - current_data = sample_timeseries.stored_data - current_backup = sample_timeseries._backup - - # Set the same data - sample_timeseries.stored_data = current_data - - # Backup shouldn't change - assert sample_timeseries._backup is current_backup # Should be the same object - - def test_from_datasource(self, sample_timesteps): - """Test from_datasource class method.""" - # Test with scalar - ts_scalar = TimeSeries.from_datasource(42, 'Scalar Series', sample_timesteps) - assert np.all(ts_scalar.stored_data.values == 42) - - # Test with Series - series_data = pd.Series([1, 2, 3, 4, 5], index=sample_timesteps) - ts_series = TimeSeries.from_datasource(series_data, 'Series Data', sample_timesteps) - assert np.array_equal(ts_series.stored_data.values, series_data.values) - - # Test with aggregation parameters - ts_with_agg = TimeSeries.from_datasource( - series_data, 'Aggregated Series', sample_timesteps, aggregation_weight=0.7, aggregation_group='group1' - ) - assert ts_with_agg.aggregation_weight == 0.7 - assert ts_with_agg.aggregation_group == 'group1' - - def test_to_json_from_json(self, sample_timeseries): - """Test to_json and from_json methods.""" - # Test to_json (dictionary only) - json_dict = sample_timeseries.to_json() - assert json_dict['name'] == sample_timeseries.name - assert 'data' in json_dict - assert 'coords' in json_dict['data'] - assert 'time' in json_dict['data']['coords'] - - # Test to_json with file saving - with tempfile.TemporaryDirectory() as tmpdirname: - filepath = Path(tmpdirname) / 'timeseries.json' - sample_timeseries.to_json(filepath) - assert filepath.exists() - - # Test from_json with file loading - loaded_ts = TimeSeries.from_json(path=filepath) - assert loaded_ts.name == sample_timeseries.name - assert np.array_equal(loaded_ts.stored_data.values, sample_timeseries.stored_data.values) - - # Test from_json with dictionary - loaded_ts_dict = TimeSeries.from_json(data=json_dict) - assert loaded_ts_dict.name == sample_timeseries.name - assert np.array_equal(loaded_ts_dict.stored_data.values, sample_timeseries.stored_data.values) - - # Test validation in from_json - with pytest.raises(ValueError, match="one of 'path' or 'data'"): - TimeSeries.from_json(data=json_dict, path='dummy.json') - - def test_all_equal(self, sample_timesteps): - """Test all_equal property.""" - # All equal values - equal_data = xr.DataArray([5, 5, 5, 5, 5], coords={'time': sample_timesteps}, dims=['time']) - ts_equal = TimeSeries(equal_data, 'Equal Series') - assert ts_equal.all_equal is True - - # Not all equal - unequal_data = xr.DataArray([5, 5, 6, 5, 5], coords={'time': sample_timesteps}, dims=['time']) - ts_unequal = TimeSeries(unequal_data, 'Unequal Series') - assert ts_unequal.all_equal is False - - def test_arithmetic_operations(self, sample_timeseries): - """Test arithmetic operations.""" - # Create a second TimeSeries for testing - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) - ts2 = TimeSeries(data2, 'Second Series') - - # Test operations between two TimeSeries objects - assert np.array_equal( - (sample_timeseries + ts2).values, sample_timeseries.values + ts2.values - ) - assert np.array_equal( - (sample_timeseries - ts2).values, sample_timeseries.values - ts2.values - ) - assert np.array_equal( - (sample_timeseries * ts2).values, sample_timeseries.values * ts2.values - ) - assert np.array_equal( - (sample_timeseries / ts2).values, sample_timeseries.values / ts2.values - ) - - # Test operations with DataArrays - assert np.array_equal((sample_timeseries + data2).values, sample_timeseries.values + data2.values) - assert np.array_equal((data2 + sample_timeseries).values, data2.values + sample_timeseries.values) - - # Test operations with scalars - assert np.array_equal((sample_timeseries + 5).values, sample_timeseries.values + 5) - assert np.array_equal((5 + sample_timeseries).values, 5 + sample_timeseries.values) - - # Test unary operations - assert np.array_equal((-sample_timeseries).values, -sample_timeseries.values) - assert np.array_equal((+sample_timeseries).values, +sample_timeseries.values) - assert np.array_equal((abs(sample_timeseries)).values, abs(sample_timeseries.values)) - - def test_comparison_operations(self, sample_timesteps): - """Test comparison operations.""" - data1 = xr.DataArray([10, 20, 30, 40, 50], coords={'time': sample_timesteps}, dims=['time']) - data2 = xr.DataArray([5, 10, 15, 20, 25], coords={'time': sample_timesteps}, dims=['time']) - - ts1 = TimeSeries(data1, 'Series 1') - ts2 = TimeSeries(data2, 'Series 2') - - # Test __gt__ method - assert (ts1 > ts2).all().item() - - # Test with mixed values - data3 = xr.DataArray([5, 25, 15, 45, 25], coords={'time': sample_timesteps}, dims=['time']) - ts3 = TimeSeries(data3, 'Series 3') - - assert not (ts1 > ts3).all().item() # Not all values in ts1 are greater than ts3 - - def test_numpy_ufunc(self, sample_timeseries): - """Test numpy ufunc compatibility.""" - # Test basic numpy functions - assert np.array_equal(np.add(sample_timeseries, 5).values, np.add(sample_timeseries, 5).values) - - assert np.array_equal( - np.multiply(sample_timeseries, 2).values, np.multiply(sample_timeseries, 2).values - ) - - # Test with two TimeSeries objects - data2 = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_timeseries.active_timesteps}, dims=['time']) - ts2 = TimeSeries(data2, 'Second Series') - - assert np.array_equal( - np.add(sample_timeseries, ts2).values, np.add(sample_timeseries, ts2).values - ) - - def test_sel_and_isel_properties(self, sample_timeseries): - """Test sel and isel properties.""" - # Test that sel property works - selected = sample_timeseries.sel(time=sample_timeseries.active_timesteps[0]) - assert selected.item() == sample_timeseries.values[0] - - # Test that isel property works - indexed = sample_timeseries.isel(time=0) - assert indexed.item() == sample_timeseries.values[0] - - -@pytest.fixture -def sample_collection(sample_timesteps): - """Create a sample TimeSeriesCollection.""" - return TimeSeriesCollection(sample_timesteps) - - -@pytest.fixture -def populated_collection(sample_collection): - """Create a TimeSeriesCollection with test data.""" - # Add a constant time series - sample_collection.create_time_series(42, 'constant_series') - - # Add a varying time series - varying_data = np.array([10, 20, 30, 40, 50]) - sample_collection.create_time_series(varying_data, 'varying_series') - - # Add a time series with extra timestep - sample_collection.create_time_series( - np.array([1, 2, 3, 4, 5, 6]), 'extra_timestep_series', needs_extra_timestep=True - ) - - # Add series with aggregation settings - sample_collection.create_time_series( - TimeSeriesData(np.array([5, 5, 5, 5, 5]), agg_group='group1'), 'group1_series1' - ) - sample_collection.create_time_series( - TimeSeriesData(np.array([6, 6, 6, 6, 6]), agg_group='group1'), 'group1_series2' - ) - sample_collection.create_time_series( - TimeSeriesData(np.array([10, 10, 10, 10, 10]), agg_weight=0.5), 'weighted_series' - ) - - return sample_collection - - -class TestTimeSeriesCollection: - """Test suite for TimeSeriesCollection.""" - - def test_initialization(self, sample_timesteps): - """Test basic initialization.""" - collection = TimeSeriesCollection(sample_timesteps) - - assert collection.all_timesteps.equals(sample_timesteps) - assert len(collection.all_timesteps_extra) == len(sample_timesteps) + 1 - assert isinstance(collection.all_hours_per_timestep, xr.DataArray) - assert len(collection) == 0 - - def test_initialization_with_custom_hours(self, sample_timesteps): - """Test initialization with custom hour settings.""" - # Test with last timestep duration - last_timestep_hours = 12 - collection = TimeSeriesCollection(sample_timesteps, hours_of_last_timestep=last_timestep_hours) - - # Verify the last timestep duration - extra_step_delta = collection.all_timesteps_extra[-1] - collection.all_timesteps_extra[-2] - assert extra_step_delta == pd.Timedelta(hours=last_timestep_hours) - - # Test with previous timestep duration - hours_per_step = 8 - collection2 = TimeSeriesCollection(sample_timesteps, hours_of_previous_timesteps=hours_per_step) - - assert collection2.hours_of_previous_timesteps == hours_per_step - - def test_create_time_series(self, sample_collection): - """Test creating time series.""" - # Test scalar - ts1 = sample_collection.create_time_series(42, 'scalar_series') - assert ts1.name == 'scalar_series' - assert np.all(ts1.values == 42) - - # Test numpy array - data = np.array([1, 2, 3, 4, 5]) - ts2 = sample_collection.create_time_series(data, 'array_series') - assert np.array_equal(ts2.values, data) - - # Test with TimeSeriesData - ts3 = sample_collection.create_time_series(TimeSeriesData(10, agg_weight=0.7), 'weighted_series') - assert ts3.aggregation_weight == 0.7 - - # Test with extra timestep - ts4 = sample_collection.create_time_series(5, 'extra_series', needs_extra_timestep=True) - assert ts4.needs_extra_timestep - assert len(ts4) == len(sample_collection.timesteps_extra) - - # Test duplicate name - with pytest.raises(ValueError, match='already exists'): - sample_collection.create_time_series(1, 'scalar_series') - - def test_access_time_series(self, populated_collection): - """Test accessing time series.""" - # Test __getitem__ - ts = populated_collection['varying_series'] - assert ts.name == 'varying_series' - - # Test __contains__ with string - assert 'constant_series' in populated_collection - assert 'nonexistent_series' not in populated_collection - - # Test __contains__ with TimeSeries object - assert populated_collection['varying_series'] in populated_collection - - # Test __iter__ - names = [ts.name for ts in populated_collection] - assert len(names) == 6 - assert 'varying_series' in names - - # Test access to non-existent series - with pytest.raises(KeyError): - populated_collection['nonexistent_series'] - - def test_constants_and_non_constants(self, populated_collection): - """Test constants and non_constants properties.""" - # Test constants - constants = populated_collection.constants - assert len(constants) == 4 # constant_series, group1_series1, group1_series2, weighted_series - assert all(ts.all_equal for ts in constants) - - # Test non_constants - non_constants = populated_collection.non_constants - assert len(non_constants) == 2 # varying_series, extra_timestep_series - assert all(not ts.all_equal for ts in non_constants) - - # Test modifying a series changes the results - populated_collection['constant_series'].stored_data = np.array([1, 2, 3, 4, 5]) - updated_constants = populated_collection.constants - assert len(updated_constants) == 3 # One less constant - assert 'constant_series' not in [ts.name for ts in updated_constants] - - def test_timesteps_properties(self, populated_collection, sample_timesteps): - """Test timestep-related properties.""" - # Test default (all) timesteps - assert populated_collection.timesteps.equals(sample_timesteps) - assert len(populated_collection.timesteps_extra) == len(sample_timesteps) + 1 - - # Test activating a subset - subset = sample_timesteps[1:3] - populated_collection.activate_timesteps(subset) - - assert populated_collection.timesteps.equals(subset) - assert len(populated_collection.timesteps_extra) == len(subset) + 1 - - # Check that time series were updated - assert populated_collection['varying_series'].active_timesteps.equals(subset) - assert populated_collection['extra_timestep_series'].active_timesteps.equals( - populated_collection.timesteps_extra - ) - - # Test reset - populated_collection.reset() - assert populated_collection.timesteps.equals(sample_timesteps) - - def test_to_dataframe_and_dataset(self, populated_collection): - """Test conversion to DataFrame and Dataset.""" - # Test to_dataset - ds = populated_collection.to_dataset() - assert isinstance(ds, xr.Dataset) - assert len(ds.data_vars) == 6 - - # Test to_dataframe with different filters - df_all = populated_collection.to_dataframe(filtered='all') - assert len(df_all.columns) == 6 - - df_constant = populated_collection.to_dataframe(filtered='constant') - assert len(df_constant.columns) == 4 - - df_non_constant = populated_collection.to_dataframe(filtered='non_constant') - assert len(df_non_constant.columns) == 2 - - # Test invalid filter - with pytest.raises(ValueError): - populated_collection.to_dataframe(filtered='invalid') - - def test_calculate_aggregation_weights(self, populated_collection): - """Test aggregation weight calculation.""" - weights = populated_collection.calculate_aggregation_weights() - - # Group weights should be 0.5 each (1/2) - assert populated_collection.group_weights['group1'] == 0.5 - - # Series in group1 should have weight 0.5 - assert weights['group1_series1'] == 0.5 - assert weights['group1_series2'] == 0.5 - - # Series with explicit weight should have that weight - assert weights['weighted_series'] == 0.5 - - # Series without group or weight should have weight 1 - assert weights['constant_series'] == 1 - - def test_insert_new_data(self, populated_collection, sample_timesteps): - """Test inserting new data.""" - # Create new data - new_data = pd.DataFrame( - { - 'constant_series': [100, 100, 100, 100, 100], - 'varying_series': [5, 10, 15, 20, 25], - # extra_timestep_series is omitted to test partial updates - }, - index=sample_timesteps, - ) - - # Insert data - populated_collection.insert_new_data(new_data) - - # Verify updates - assert np.all(populated_collection['constant_series'].values == 100) - assert np.array_equal(populated_collection['varying_series'].values, np.array([5, 10, 15, 20, 25])) - - # Series not in the DataFrame should be unchanged - assert np.array_equal( - populated_collection['extra_timestep_series'].values[:-1], np.array([1, 2, 3, 4, 5]) - ) - - # Test with mismatched index - bad_index = pd.date_range('2023-02-01', periods=5, freq='D', name='time') - bad_data = pd.DataFrame({'constant_series': [1, 1, 1, 1, 1]}, index=bad_index) - - with pytest.raises(ValueError, match='must match collection timesteps'): - populated_collection.insert_new_data(bad_data) - - def test_restore_data(self, populated_collection): - """Test restoring original data.""" - # Capture original data - original_values = {name: ts.stored_data.copy() for name, ts in populated_collection.time_series_data.items()} - - # Modify data - new_data = pd.DataFrame( - { - name: np.ones(len(populated_collection.timesteps)) * 999 - for name in populated_collection.time_series_data - if not populated_collection[name].needs_extra_timestep - }, - index=populated_collection.timesteps, - ) - - populated_collection.insert_new_data(new_data) - - # Verify data was changed - assert np.all(populated_collection['constant_series'].values == 999) - - # Restore data - populated_collection.restore_data() - - # Verify data was restored - for name, original in original_values.items(): - restored = populated_collection[name].stored_data - assert np.array_equal(restored.values, original.values) - - def test_class_method_with_uniform_timesteps(self): - """Test the with_uniform_timesteps class method.""" - collection = TimeSeriesCollection.with_uniform_timesteps( - start_time=pd.Timestamp('2023-01-01'), periods=24, freq='h', hours_per_step=1 - ) - - assert len(collection.timesteps) == 24 - assert collection.hours_of_previous_timesteps == 1 - assert (collection.timesteps[1] - collection.timesteps[0]) == pd.Timedelta(hours=1) - - def test_hours_per_timestep(self, populated_collection): - """Test hours_per_timestep calculation.""" - # Standard case - uniform timesteps - hours = populated_collection.hours_per_timestep.values - assert np.allclose(hours, 24) # Default is daily timesteps - - # Create non-uniform timesteps - non_uniform_times = pd.DatetimeIndex( - [ - pd.Timestamp('2023-01-01'), - pd.Timestamp('2023-01-02'), - pd.Timestamp('2023-01-03 12:00:00'), # 1.5 days from previous - pd.Timestamp('2023-01-04'), # 0.5 days from previous - pd.Timestamp('2023-01-06'), # 2 days from previous - ], - name='time', - ) - - collection = TimeSeriesCollection(non_uniform_times) - hours = collection.hours_per_timestep.values - - # Expected hours between timestamps - expected = np.array([24, 36, 12, 48, 48]) - assert np.allclose(hours, expected) - - def test_validation_and_errors(self, sample_timesteps): - """Test validation and error handling.""" - # Test non-DatetimeIndex - with pytest.raises(TypeError, match='must be a pandas DatetimeIndex'): - TimeSeriesCollection(pd.Index([1, 2, 3, 4, 5])) - - # Test too few timesteps - with pytest.raises(ValueError, match='must contain at least 2 timestamps'): - TimeSeriesCollection(pd.DatetimeIndex([pd.Timestamp('2023-01-01')], name='time')) - - # Test invalid active_timesteps - collection = TimeSeriesCollection(sample_timesteps) - invalid_timesteps = pd.date_range('2024-01-01', periods=3, freq='D', name='time') - - with pytest.raises(ValueError, match='must be a subset'): - collection.activate_timesteps(invalid_timesteps) From 201d0667356e174f5f7f87effec54013bf14a767 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:15:40 +0200 Subject: [PATCH 085/183] Reorganizing Datatypes --- flixopt/commons.py | 2 +- flixopt/components.py | 48 ++++++++++++++++++------------------ flixopt/core.py | 38 +++++++--------------------- flixopt/effects.py | 29 +++++++++++----------- flixopt/elements.py | 32 ++++++++++++------------ flixopt/features.py | 32 ++++++++++++------------ flixopt/flow_system.py | 48 ++++++++++++++++++++---------------- flixopt/interface.py | 36 +++++++++++++-------------- flixopt/linear_converters.py | 22 ++++++++--------- flixopt/structure.py | 6 ++--- 10 files changed, 139 insertions(+), 154 deletions(-) diff --git a/flixopt/commons.py b/flixopt/commons.py index 222c07324..68412d6fe 100644 --- a/flixopt/commons.py +++ b/flixopt/commons.py @@ -14,11 +14,11 @@ Transmission, ) from .config import CONFIG, change_logging_level +from .core import TimeSeriesData from .effects import Effect from .elements import Bus, Flow from .flow_system import FlowSystem from .interface import InvestParameters, OnOffParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects -from .core import TimeSeriesData __all__ = [ 'TimeSeriesData', diff --git a/flixopt/components.py b/flixopt/components.py index 81baaeea5..8e172d573 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -9,7 +9,7 @@ import numpy as np from . import utils -from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeries +from .core import NumericDataUser, PlausibilityError, Scalar, TimeSeries from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion @@ -34,7 +34,7 @@ def __init__( inputs: List[Flow], outputs: List[Flow], on_off_parameters: OnOffParameters = None, - conversion_factors: List[Dict[str, NumericDataTS]] = None, + conversion_factors: List[Dict[str, NumericDataUser]] = None, piecewise_conversion: Optional[PiecewiseConversion] = None, meta_data: Optional[Dict] = None, ): @@ -105,7 +105,7 @@ def _transform_conversion_factors(self, flow_system: 'FlowSystem') -> List[Dict[ transformed_dict = {} for flow, values in conversion_factor.items(): # TODO: Might be better to use the label of the component instead of the flow - transformed_dict[flow] = flow_system.create_time_series( + transformed_dict[flow] = flow_system.fit_to_model_coords( f'{self.flows[flow].label_full}|conversion_factor{idx}', values ) list_of_conversion_factors.append(transformed_dict) @@ -128,14 +128,14 @@ def __init__( charging: Flow, discharging: Flow, capacity_in_flow_hours: Union[Scalar, InvestParameters], - relative_minimum_charge_state: NumericData = 0, - relative_maximum_charge_state: NumericData = 1, + relative_minimum_charge_state: NumericDataUser = 0, + relative_maximum_charge_state: NumericDataUser = 1, initial_charge_state: Union[Scalar, Literal['lastValueOfSim']] = 0, minimal_final_charge_state: Optional[Scalar] = None, maximal_final_charge_state: Optional[Scalar] = None, - eta_charge: NumericData = 1, - eta_discharge: NumericData = 1, - relative_loss_per_hour: NumericData = 0, + eta_charge: NumericDataUser = 1, + eta_discharge: NumericDataUser = 1, + relative_loss_per_hour: NumericDataUser = 0, prevent_simultaneous_charge_and_discharge: bool = True, meta_data: Optional[Dict] = None, ): @@ -176,16 +176,16 @@ def __init__( self.charging = charging self.discharging = discharging self.capacity_in_flow_hours = capacity_in_flow_hours - self.relative_minimum_charge_state: NumericDataTS = relative_minimum_charge_state - self.relative_maximum_charge_state: NumericDataTS = relative_maximum_charge_state + self.relative_minimum_charge_state: NumericDataUser = relative_minimum_charge_state + self.relative_maximum_charge_state: NumericDataUser = relative_maximum_charge_state self.initial_charge_state = initial_charge_state self.minimal_final_charge_state = minimal_final_charge_state self.maximal_final_charge_state = maximal_final_charge_state - self.eta_charge: NumericDataTS = eta_charge - self.eta_discharge: NumericDataTS = eta_discharge - self.relative_loss_per_hour: NumericDataTS = relative_loss_per_hour + self.eta_charge: NumericDataUser = eta_charge + self.eta_discharge: NumericDataUser = eta_discharge + self.relative_loss_per_hour: NumericDataUser = relative_loss_per_hour self.prevent_simultaneous_charge_and_discharge = prevent_simultaneous_charge_and_discharge def create_model(self, model: SystemModel) -> 'StorageModel': @@ -195,19 +195,19 @@ def create_model(self, model: SystemModel) -> 'StorageModel': def transform_data(self, flow_system: 'FlowSystem') -> None: super().transform_data(flow_system) - self.relative_minimum_charge_state = flow_system.create_time_series( + self.relative_minimum_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_minimum_charge_state', self.relative_minimum_charge_state, needs_extra_timestep=True, ) - self.relative_maximum_charge_state = flow_system.create_time_series( + self.relative_maximum_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_maximum_charge_state', self.relative_maximum_charge_state, needs_extra_timestep=True, ) - self.eta_charge = flow_system.create_time_series(f'{self.label_full}|eta_charge', self.eta_charge) - self.eta_discharge = flow_system.create_time_series(f'{self.label_full}|eta_discharge', self.eta_discharge) - self.relative_loss_per_hour = flow_system.create_time_series( + self.eta_charge = flow_system.fit_to_model_coords(f'{self.label_full}|eta_charge', self.eta_charge) + self.eta_discharge = flow_system.fit_to_model_coords(f'{self.label_full}|eta_discharge', self.eta_discharge) + self.relative_loss_per_hour = flow_system.fit_to_model_coords( f'{self.label_full}|relative_loss_per_hour', self.relative_loss_per_hour ) if isinstance(self.capacity_in_flow_hours, InvestParameters): @@ -264,8 +264,8 @@ def __init__( out1: Flow, in2: Optional[Flow] = None, out2: Optional[Flow] = None, - relative_losses: Optional[NumericDataTS] = None, - absolute_losses: Optional[NumericDataTS] = None, + relative_losses: Optional[NumericDataUser] = None, + absolute_losses: Optional[NumericDataUser] = None, on_off_parameters: OnOffParameters = None, prevent_simultaneous_flows_in_both_directions: bool = True, meta_data: Optional[Dict] = None, @@ -331,10 +331,10 @@ def create_model(self, model) -> 'TransmissionModel': def transform_data(self, flow_system: 'FlowSystem') -> None: super().transform_data(flow_system) - self.relative_losses = flow_system.create_time_series( + self.relative_losses = flow_system.fit_to_model_coords( f'{self.label_full}|relative_losses', self.relative_losses ) - self.absolute_losses = flow_system.create_time_series( + self.absolute_losses = flow_system.fit_to_model_coords( f'{self.label_full}|absolute_losses', self.absolute_losses ) @@ -556,7 +556,7 @@ def _initial_and_final_charge_state(self): ) @property - def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: + def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser]: relative_lower_bound, relative_upper_bound = self.relative_charge_state_bounds if not isinstance(self.element.capacity_in_flow_hours, InvestParameters): return ( @@ -570,7 +570,7 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: ) @property - def relative_charge_state_bounds(self) -> Tuple[NumericData, NumericData]: + def relative_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser]: return ( self.element.relative_minimum_charge_state, self.element.relative_maximum_charge_state, diff --git a/flixopt/core.py b/flixopt/core.py index 31738f6c7..4ab97b219 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -17,13 +17,13 @@ logger = logging.getLogger('flixopt') Scalar = Union[int, float] -"""A type representing a single number, either integer or float.""" +"""A single number, either integer or float.""" -NumericData = Union[int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray] -"""Represents any form of numeric data, from simple scalars to complex data structures.""" +NumericDataUser = Union[int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, 'TimeSeriesData'] +"""Numeric data accepted in varios types. Will be converted to an xr.DataArray or Scalar internally.""" -NumericDataTS = Union[NumericData, 'TimeSeriesData'] -"""Represents either standard numeric data or TimeSeriesData.""" +NumericDataInternal = Union[int, float, xr.DataArray, 'TimeSeriesData'] +"""Internally used datatypes for numeric data.""" class PlausibilityError(Exception): @@ -37,6 +37,7 @@ class ConversionError(Exception): pass + class TimeSeriesData(xr.DataArray): """Minimal TimeSeriesData that inherits from xr.DataArray with aggregation metadata.""" @@ -153,7 +154,7 @@ def _fix_timeseries_data_indexing( # Check if time coordinates are identical elif not data.coords['time'].equals(timesteps): logger.warning( - f'TimeSeriesData has different time coordinates than expected. Replacing with provided timesteps.' + 'TimeSeriesData has different time coordinates than expected. Replacing with provided timesteps.' ) # Replace time coordinates while preserving data and metadata recoordinated_data = xr.DataArray( @@ -166,7 +167,7 @@ def _fix_timeseries_data_indexing( return data.copy(deep=True) @staticmethod - def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray: + def to_dataarray(data: NumericDataUser, timesteps: pd.DatetimeIndex) -> xr.DataArray: """Convert data to xarray.DataArray with specified timesteps index.""" if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') @@ -182,10 +183,6 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray if isinstance(data, TimeSeriesData): return DataConverter._fix_timeseries_data_indexing(data, timesteps, dims, coords) - elif isinstance(data, TimeSeries): - # Handle TimeSeries objects (your existing logic) - pass # Add your TimeSeries handling here - elif isinstance(data, (int, float, np.integer, np.floating)): # Scalar: broadcast to all timesteps scalar_data = np.full(expected_shape, data) @@ -220,7 +217,7 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray return data.copy(deep=True) elif isinstance(data, list): - logger.warning(f'Converting list to DataArray. This is not recommended.') + logger.warning('Converting list to DataArray. This is not recommended.') if len(data) != expected_shape[0]: raise ConversionError(f"List length {len(data)} doesn't match expected {expected_shape[0]}") return xr.DataArray(data, coords=coords, dims=dims) @@ -234,23 +231,6 @@ def to_dataarray(data: NumericData, timesteps: pd.DatetimeIndex) -> xr.DataArray raise ConversionError(f'Converting data {type(data)} to xarray.DataArray raised an error: {str(e)}') from e -class TimeSeries: - def __init__(self): - raise NotImplementedError('TimeSeries was removed') - - -class TimeSeriesCollection: - """ - Collection of TimeSeries objects with shared timestep management. - - TimeSeriesCollection handles multiple TimeSeries objects with synchronized - timesteps, provides operations on collections, and manages extra timesteps. - """ - - def __init__(self): - raise NotImplementedError('TimeSeriesCollection was removed') - - def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10) -> str: """Calculates the mean, median, min, max, and standard deviation of a numeric DataArray.""" format_spec = f'>{padd}.{decimals}f' if padd else f'.{decimals}f' diff --git a/flixopt/effects.py b/flixopt/effects.py index b043f4492..7fa136f5b 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -13,7 +13,7 @@ import numpy as np import pandas as pd -from .core import NumericData, NumericDataTS, Scalar, TimeSeriesCollection, TimeSeries +from .core import NumericDataInternal, NumericDataUser, Scalar from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -44,8 +44,8 @@ def __init__( maximum_operation: Optional[Scalar] = None, minimum_invest: Optional[Scalar] = None, maximum_invest: Optional[Scalar] = None, - minimum_operation_per_hour: Optional[NumericDataTS] = None, - maximum_operation_per_hour: Optional[NumericDataTS] = None, + minimum_operation_per_hour: Optional[NumericDataUser] = None, + maximum_operation_per_hour: Optional[NumericDataUser] = None, minimum_total: Optional[Scalar] = None, maximum_total: Optional[Scalar] = None, ): @@ -82,22 +82,22 @@ def __init__( self.specific_share_to_other_effects_invest: EffectValuesUser = specific_share_to_other_effects_invest or {} self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation - self.minimum_operation_per_hour: NumericDataTS = minimum_operation_per_hour - self.maximum_operation_per_hour: NumericDataTS = maximum_operation_per_hour + self.minimum_operation_per_hour: NumericDataUser = minimum_operation_per_hour + self.maximum_operation_per_hour: NumericDataUser = maximum_operation_per_hour self.minimum_invest = minimum_invest self.maximum_invest = maximum_invest self.minimum_total = minimum_total self.maximum_total = maximum_total def transform_data(self, flow_system: 'FlowSystem'): - self.minimum_operation_per_hour = flow_system.create_time_series( + self.minimum_operation_per_hour = flow_system.fit_to_model_coords( f'{self.label_full}|minimum_operation_per_hour', self.minimum_operation_per_hour ) - self.maximum_operation_per_hour = flow_system.create_time_series( + self.maximum_operation_per_hour = flow_system.fit_to_model_coords( f'{self.label_full}|maximum_operation_per_hour', self.maximum_operation_per_hour, flow_system ) - self.specific_share_to_other_effects_operation = flow_system.create_effect_time_series( + self.specific_share_to_other_effects_operation = flow_system.fit_effects_to_model_coords( f'{self.label_full}|operation->', self.specific_share_to_other_effects_operation, 'operation' ) @@ -168,10 +168,9 @@ def do_modeling(self): ) -EffectValuesExpr = Dict[str, linopy.LinearExpression] # Used to create Shares -EffectTimeSeries = Dict[str, TimeSeries] # Used internally to index values -EffectValuesDict = Dict[str, NumericDataTS] # How effect values are stored -EffectValuesUser = Union[NumericDataTS, Dict[str, NumericDataTS]] # User-specified Shares to Effects +EffectExpr = Dict[str, linopy.LinearExpression] # Used to create Shares +EffectValuesInternal = Dict[str, NumericDataInternal] # Used internally to index values +EffectValuesUser = Union[NumericDataUser, Dict[str, NumericDataUser]] # User-specified Shares to Effects """ This datatype is used to define the share to an effect by a certain attribute. """ EffectValuesUserScalar = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects @@ -207,7 +206,7 @@ def add_effects(self, *effects: Effect) -> None: self._effects[effect.label] = effect logger.info(f'Registered new Effect: {effect.label}') - def create_effect_values_dict(self, effect_values_user: EffectValuesUser) -> Optional[EffectValuesDict]: + def create_effect_values_dict(self, effect_values_user: EffectValuesUser) -> Optional[Dict[str, NumericDataUser]]: """ Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type. @@ -233,6 +232,8 @@ def get_effect_label(eff: Union[Effect, str]) -> str: stacklevel=2, ) return eff.label_full + elif eff is None: + return self.standard_effect.label_full else: return eff @@ -341,7 +342,7 @@ def __init__(self, model: SystemModel, effects: EffectCollection): def add_share_to_effects( self, name: str, - expressions: EffectValuesExpr, + expressions: EffectExpr, target: Literal['operation', 'invest'], ) -> None: for effect, expression in expressions.items(): diff --git a/flixopt/elements.py b/flixopt/elements.py index 48e73ef76..a2ba8f7c1 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import NumericData, NumericDataTS, PlausibilityError, Scalar, TimeSeriesCollection +from .core import NumericDataUser, PlausibilityError, Scalar, TimeSeriesCollection from .effects import EffectValuesUser from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters @@ -90,7 +90,7 @@ class Bus(Element): """ def __init__( - self, label: str, excess_penalty_per_flow_hour: Optional[NumericDataTS] = 1e5, meta_data: Optional[Dict] = None + self, label: str, excess_penalty_per_flow_hour: Optional[NumericDataUser] = 1e5, meta_data: Optional[Dict] = None ): """ Args: @@ -111,7 +111,7 @@ def create_model(self, model: SystemModel) -> 'BusModel': return self.model def transform_data(self, flow_system: 'FlowSystem'): - self.excess_penalty_per_flow_hour = flow_system.create_time_series( + self.excess_penalty_per_flow_hour = flow_system.fit_to_model_coords( f'{self.label_full}|excess_penalty_per_flow_hour', self.excess_penalty_per_flow_hour ) @@ -149,16 +149,16 @@ def __init__( label: str, bus: str, size: Union[Scalar, InvestParameters] = None, - fixed_relative_profile: Optional[NumericDataTS] = None, - relative_minimum: NumericDataTS = 0, - relative_maximum: NumericDataTS = 1, + fixed_relative_profile: Optional[NumericDataUser] = None, + relative_minimum: NumericDataUser = 0, + relative_maximum: NumericDataUser = 1, effects_per_flow_hour: Optional[EffectValuesUser] = None, on_off_parameters: Optional[OnOffParameters] = None, flow_hours_total_max: Optional[Scalar] = None, flow_hours_total_min: Optional[Scalar] = None, load_factor_min: Optional[Scalar] = None, load_factor_max: Optional[Scalar] = None, - previous_flow_rate: Optional[NumericData] = None, + previous_flow_rate: Optional[NumericDataUser] = None, meta_data: Optional[Dict] = None, ): r""" @@ -230,16 +230,16 @@ def create_model(self, model: SystemModel) -> 'FlowModel': return self.model def transform_data(self, flow_system: 'FlowSystem'): - self.relative_minimum = flow_system.create_time_series( + self.relative_minimum = flow_system.fit_to_model_coords( f'{self.label_full}|relative_minimum', self.relative_minimum ) - self.relative_maximum = flow_system.create_time_series( + self.relative_maximum = flow_system.fit_to_model_coords( f'{self.label_full}|relative_maximum', self.relative_maximum ) - self.fixed_relative_profile = flow_system.create_time_series( + self.fixed_relative_profile = flow_system.fit_to_model_coords( f'{self.label_full}|fixed_relative_profile', self.fixed_relative_profile ) - self.effects_per_flow_hour = flow_system.create_effect_time_series( + self.effects_per_flow_hour = flow_system.fit_effects_to_model_coords( self.label_full, self.effects_per_flow_hour, 'per_flow_hour' ) if self.on_off_parameters is not None: @@ -411,7 +411,7 @@ def _create_bounds_for_load_factor(self): ) @property - def flow_rate_bounds_on(self) -> Tuple[NumericData, NumericData]: + def flow_rate_bounds_on(self) -> Tuple[NumericDataUser, NumericDataUser]: """Returns absolute flow rate bounds. Important for OnOffModel""" relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative size = self.element.size @@ -422,7 +422,7 @@ def flow_rate_bounds_on(self) -> Tuple[NumericData, NumericData]: return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size @property - def flow_rate_lower_bound_relative(self) -> NumericData: + def flow_rate_lower_bound_relative(self) -> NumericDataUser: """Returns the lower bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: @@ -430,7 +430,7 @@ def flow_rate_lower_bound_relative(self) -> NumericData: return fixed_profile @property - def flow_rate_upper_bound_relative(self) -> NumericData: + def flow_rate_upper_bound_relative(self) -> NumericDataUser: """ Returns the upper bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: @@ -438,7 +438,7 @@ def flow_rate_upper_bound_relative(self) -> NumericData: return fixed_profile @property - def flow_rate_lower_bound(self) -> NumericData: + def flow_rate_lower_bound(self) -> NumericDataUser: """ Returns the minimum bound the flow_rate can reach. Further constraining might be done in OnOffModel and InvestmentModel @@ -452,7 +452,7 @@ def flow_rate_lower_bound(self) -> NumericData: return self.flow_rate_lower_bound_relative * self.element.size @property - def flow_rate_upper_bound(self) -> NumericData: + def flow_rate_upper_bound(self) -> NumericDataUser: """ Returns the maximum bound the flow_rate can reach. Further constraining might be done in OnOffModel and InvestmentModel diff --git a/flixopt/features.py b/flixopt/features.py index dc719a2a6..20412ed46 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -11,7 +11,7 @@ from . import utils from .config import CONFIG -from .core import NumericData, Scalar, TimeSeries +from .core import NumericDataUser, Scalar, TimeSeries from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel @@ -27,7 +27,7 @@ def __init__( label_of_element: str, parameters: InvestParameters, defining_variable: [linopy.Variable], - relative_bounds_of_defining_variable: Tuple[NumericData, NumericData], + relative_bounds_of_defining_variable: Tuple[NumericDataUser, NumericDataUser], label: Optional[str] = None, on_variable: Optional[linopy.Variable] = None, ): @@ -203,12 +203,12 @@ def __init__( model: SystemModel, label_of_element: str, defining_variables: List[linopy.Variable], - defining_bounds: List[Tuple[NumericData, NumericData]], - previous_values: List[Optional[NumericData]] = None, + defining_bounds: List[Tuple[NumericDataUser, NumericDataUser]], + previous_values: List[Optional[NumericDataUser]] = None, use_off: bool = True, - on_hours_total_min: Optional[NumericData] = 0, - on_hours_total_max: Optional[NumericData] = None, - effects_per_running_hour: Dict[str, NumericData] = None, + on_hours_total_min: Optional[NumericDataUser] = 0, + on_hours_total_max: Optional[NumericDataUser] = None, + effects_per_running_hour: Dict[str, NumericDataUser] = None, label: Optional[str] = None, ): """ @@ -344,7 +344,7 @@ def previous_off_states(self): return 1 - self.previous_states @staticmethod - def compute_previous_states(previous_values: List[NumericData], epsilon: float = 1e-5) -> np.ndarray: + def compute_previous_states(previous_values: List[NumericDataUser], epsilon: float = 1e-5) -> np.ndarray: """Computes the previous states {0, 1} of defining variables as a binary array from their previous values.""" if not previous_values or all([val is None for val in previous_values]): return np.array([0]) @@ -451,9 +451,9 @@ def __init__( model: SystemModel, label_of_element: str, state_variable: linopy.Variable, - minimum_duration: Optional[NumericData] = None, - maximum_duration: Optional[NumericData] = None, - previous_states: Optional[NumericData] = None, + minimum_duration: Optional[NumericDataUser] = None, + maximum_duration: Optional[NumericDataUser] = None, + previous_states: Optional[NumericDataUser] = None, label: Optional[str] = None, ): """ @@ -570,7 +570,7 @@ def previous_duration(self) -> Scalar: @staticmethod def compute_consecutive_hours_in_state( - binary_values: NumericData, hours_per_timestep: Union[int, float, np.ndarray] + binary_values: NumericDataUser, hours_per_timestep: Union[int, float, np.ndarray] ) -> Scalar: """ Computes the final consecutive duration in state 'on' (=1) in hours, from a binary array. @@ -629,8 +629,8 @@ def __init__( on_off_parameters: OnOffParameters, label_of_element: str, defining_variables: List[linopy.Variable], - defining_bounds: List[Tuple[NumericData, NumericData]], - previous_values: List[Optional[NumericData]], + defining_bounds: List[Tuple[NumericDataUser, NumericDataUser]], + previous_values: List[Optional[NumericDataUser]], label: Optional[str] = None, ): """ @@ -918,8 +918,8 @@ def __init__( label_full: Optional[str] = None, total_max: Optional[Scalar] = None, total_min: Optional[Scalar] = None, - max_per_hour: Optional[NumericData] = None, - min_per_hour: Optional[NumericData] = None, + max_per_hour: Optional[NumericDataUser] = None, + min_per_hour: Optional[NumericDataUser] = None, ): super().__init__(model, label_of_element=label_of_element, label=label, label_full=label_full) if not shares_are_time_series: # If the condition is True diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 386f54a72..024d8b3c5 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,10 +16,17 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import NumericData, NumericDataTS, TimeSeriesCollection, TimeSeries, DataConverter, ConversionError, TimeSeriesData -from .effects import Effect, EffectCollection, EffectTimeSeries, EffectValuesDict, EffectValuesUser +from .core import ConversionError, DataConverter, NumericDataInternal, NumericDataUser, TimeSeriesData +from .effects import Effect, EffectCollection, EffectValuesInternal, EffectValuesUser from .elements import Bus, Component, Flow -from .structure import CLASS_REGISTRY, Element, SystemModel, get_compact_representation, get_str_representation, Interface +from .structure import ( + CLASS_REGISTRY, + Element, + Interface, + SystemModel, + get_compact_representation, + get_str_representation, +) if TYPE_CHECKING: import pyvis @@ -280,23 +287,22 @@ def to_json(self, path: Union[str, pathlib.Path]): super().to_json(path) - def create_time_series( + def fit_to_model_coords( self, name: str, - data: Optional[Union[NumericData, TimeSeriesData, TimeSeries]], + data: Optional[NumericDataUser], needs_extra_timestep: bool = False, - ) -> Optional[TimeSeries]: + ) -> Optional[NumericDataInternal]: """ - Create a TimeSeries-like object (now just an xr.DataArray with proper coordinates). - This method is kept for API compatibility but simplified. + Fit data to model coordinate system (currently time, but extensible). Args: - name: Name of the time series - data: Data to convert - needs_extra_timestep: Whether to use timesteps_extra + name: Name of the data + data: Data to fit to model coordinates + needs_extra_timestep: Whether to use extended time coordinates Returns: - xr.DataArray with proper time coordinates + xr.DataArray aligned to model coordinate system """ if data is None: return None @@ -316,22 +322,22 @@ def create_time_series( else: return DataConverter.to_dataarray(data, timesteps=target_timesteps).rename(name) - def create_effect_time_series( + def fit_effects_to_model_coords( self, label_prefix: Optional[str], - effect_values: EffectValuesUser, + effect_values: Optional[EffectValuesUser], label_suffix: Optional[str] = None, - ) -> Optional[Dict[str, xr.DataArray]]: + ) -> Optional[EffectValuesInternal]: """ - Transform EffectValues to effect DataArrays. - Simplified version that returns DataArrays directly. + Transform EffectValues from the user to Internal Datatypes aligned with model coordinates. """ - effect_values_dict: Optional[EffectValuesDict] = self.effects.create_effect_values_dict(effect_values) - if effect_values_dict is None: + if effect_values is None: return None + effect_values_dict = self.effects.create_effect_values_dict(effect_values) + return { - effect: self.create_time_series('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) + effect: self.fit_to_model_coords('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) for effect, value in effect_values_dict.items() } @@ -505,7 +511,7 @@ def format_elements(element_names: list, label: str, alignment: int = 12): freq_str = str(self.timesteps.freq).replace('<', '').replace('>', '') if self.timesteps.freq else 'irregular' lines = [ - f'FlowSystem Overview:', + 'FlowSystem Overview:', f'{"─" * 50}', time_period, f'Timesteps: {len(self.timesteps)} ({freq_str})', diff --git a/flixopt/interface.py b/flixopt/interface.py index c38d6c619..e5ee962ed 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from .config import CONFIG -from .core import NumericData, NumericDataTS, Scalar +from .core import NumericDataUser, Scalar from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports @@ -20,7 +20,7 @@ @register_class_for_io class Piece(Interface): - def __init__(self, start: NumericData, end: NumericData): + def __init__(self, start: NumericDataUser, end: NumericDataUser): """ Define a Piece, which is part of a Piecewise object. @@ -32,8 +32,8 @@ def __init__(self, start: NumericData, end: NumericData): self.end = end def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - self.start = flow_system.create_time_series(f'{name_prefix}|start', self.start) - self.end = flow_system.create_time_series(f'{name_prefix}|end', self.end) + self.start = flow_system.fit_to_model_coords(f'{name_prefix}|start', self.start) + self.end = flow_system.fit_to_model_coords(f'{name_prefix}|end', self.end) @register_class_for_io @@ -175,10 +175,10 @@ def __init__( effects_per_running_hour: Optional['EffectValuesUser'] = None, on_hours_total_min: Optional[int] = None, on_hours_total_max: Optional[int] = None, - consecutive_on_hours_min: Optional[NumericData] = None, - consecutive_on_hours_max: Optional[NumericData] = None, - consecutive_off_hours_min: Optional[NumericData] = None, - consecutive_off_hours_max: Optional[NumericData] = None, + consecutive_on_hours_min: Optional[NumericDataUser] = None, + consecutive_on_hours_max: Optional[NumericDataUser] = None, + consecutive_off_hours_min: Optional[NumericDataUser] = None, + consecutive_off_hours_max: Optional[NumericDataUser] = None, switch_on_total_max: Optional[int] = None, force_switch_on: bool = False, ): @@ -206,30 +206,30 @@ def __init__( self.effects_per_running_hour: EffectValuesUser = effects_per_running_hour or {} self.on_hours_total_min: Scalar = on_hours_total_min self.on_hours_total_max: Scalar = on_hours_total_max - self.consecutive_on_hours_min: NumericDataTS = consecutive_on_hours_min - self.consecutive_on_hours_max: NumericDataTS = consecutive_on_hours_max - self.consecutive_off_hours_min: NumericDataTS = consecutive_off_hours_min - self.consecutive_off_hours_max: NumericDataTS = consecutive_off_hours_max + self.consecutive_on_hours_min: NumericDataUser = consecutive_on_hours_min + self.consecutive_on_hours_max: NumericDataUser = consecutive_on_hours_max + self.consecutive_off_hours_min: NumericDataUser = consecutive_off_hours_min + self.consecutive_off_hours_max: NumericDataUser = consecutive_off_hours_max self.switch_on_total_max: Scalar = switch_on_total_max self.force_switch_on: bool = force_switch_on def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - self.effects_per_switch_on = flow_system.create_effect_time_series( + self.effects_per_switch_on = flow_system.fit_effects_to_model_coords( name_prefix, self.effects_per_switch_on, 'per_switch_on' ) - self.effects_per_running_hour = flow_system.create_effect_time_series( + self.effects_per_running_hour = flow_system.fit_effects_to_model_coords( name_prefix, self.effects_per_running_hour, 'per_running_hour' ) - self.consecutive_on_hours_min = flow_system.create_time_series( + self.consecutive_on_hours_min = flow_system.fit_to_model_coords( f'{name_prefix}|consecutive_on_hours_min', self.consecutive_on_hours_min ) - self.consecutive_on_hours_max = flow_system.create_time_series( + self.consecutive_on_hours_max = flow_system.fit_to_model_coords( f'{name_prefix}|consecutive_on_hours_max', self.consecutive_on_hours_max ) - self.consecutive_off_hours_min = flow_system.create_time_series( + self.consecutive_off_hours_min = flow_system.fit_to_model_coords( f'{name_prefix}|consecutive_off_hours_min', self.consecutive_off_hours_min ) - self.consecutive_off_hours_max = flow_system.create_time_series( + self.consecutive_off_hours_max = flow_system.fit_to_model_coords( f'{name_prefix}|consecutive_off_hours_max', self.consecutive_off_hours_max ) diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 3fd032632..94463c492 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -8,7 +8,7 @@ import numpy as np from .components import LinearConverter -from .core import NumericDataTS, TimeSeriesData +from .core import NumericDataUser, TimeSeriesData from .elements import Flow from .interface import OnOffParameters from .structure import register_class_for_io @@ -21,7 +21,7 @@ class Boiler(LinearConverter): def __init__( self, label: str, - eta: NumericDataTS, + eta: NumericDataUser, Q_fu: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -62,7 +62,7 @@ class Power2Heat(LinearConverter): def __init__( self, label: str, - eta: NumericDataTS, + eta: NumericDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -104,7 +104,7 @@ class HeatPump(LinearConverter): def __init__( self, label: str, - COP: NumericDataTS, + COP: NumericDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -146,7 +146,7 @@ class CoolingTower(LinearConverter): def __init__( self, label: str, - specific_electricity_demand: NumericDataTS, + specific_electricity_demand: NumericDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -190,8 +190,8 @@ class CHP(LinearConverter): def __init__( self, label: str, - eta_th: NumericDataTS, - eta_el: NumericDataTS, + eta_th: NumericDataUser, + eta_el: NumericDataUser, Q_fu: Flow, P_el: Flow, Q_th: Flow, @@ -251,7 +251,7 @@ class HeatPumpWithSource(LinearConverter): def __init__( self, label: str, - COP: NumericDataTS, + COP: NumericDataUser, P_el: Flow, Q_ab: Flow, Q_th: Flow, @@ -297,11 +297,11 @@ def COP(self, value): # noqa: N802 def check_bounds( - value: NumericDataTS, + value: NumericDataUser, parameter_label: str, element_label: str, - lower_bound: NumericDataTS, - upper_bound: NumericDataTS, + lower_bound: NumericDataUser, + upper_bound: NumericDataUser, ) -> None: """ Check if the value is within the bounds. The bounds are exclusive. diff --git a/flixopt/structure.py b/flixopt/structure.py index 9cb830ff0..1e3d2849e 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -18,9 +18,9 @@ from rich.console import Console from rich.pretty import Pretty -from .config import CONFIG -from .core import NumericData, Scalar, TimeSeriesCollection, TimeSeries, TimeSeriesData from . import io as fx_io +from .config import CONFIG +from .core import NumericDataUser, Scalar, TimeSeriesData if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -851,8 +851,6 @@ def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_la ) return copy_and_convert_datatypes(data.tolist(), use_numpy, use_element_label) - elif isinstance(data, TimeSeries): - return copy_and_convert_datatypes(data, use_numpy, use_element_label) elif isinstance(data, TimeSeriesData): return copy_and_convert_datatypes(data.data, use_numpy, use_element_label) From 10d2925cec8639df06505829889f33b83cc99d4e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:50:23 +0200 Subject: [PATCH 086/183] Remove TImeSeries and TimeSeriesCollection entirely --- flixopt/components.py | 7 ++++--- flixopt/elements.py | 2 +- flixopt/features.py | 2 +- flixopt/io.py | 37 ------------------------------------- flixopt/structure.py | 6 +++--- 5 files changed, 9 insertions(+), 45 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 8e172d573..3f41783a8 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -7,9 +7,10 @@ import linopy import numpy as np +import xarray as xr from . import utils -from .core import NumericDataUser, PlausibilityError, Scalar, TimeSeries +from .core import NumericDataUser, PlausibilityError, Scalar from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion @@ -98,8 +99,8 @@ def transform_data(self, flow_system: 'FlowSystem'): if self.piecewise_conversion: self.piecewise_conversion.transform_data(flow_system, f'{self.label_full}|PiecewiseConversion') - def _transform_conversion_factors(self, flow_system: 'FlowSystem') -> List[Dict[str, TimeSeries]]: - """macht alle Faktoren, die nicht TimeSeries sind, zu TimeSeries""" + def _transform_conversion_factors(self, flow_system: 'FlowSystem') -> List[Dict[str, xr.DataArray]]: + """Converts all conversion factors to internal datatypes""" list_of_conversion_factors = [] for idx, conversion_factor in enumerate(self.conversion_factors): transformed_dict = {} diff --git a/flixopt/elements.py b/flixopt/elements.py index a2ba8f7c1..061a00b65 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import NumericDataUser, PlausibilityError, Scalar, TimeSeriesCollection +from .core import NumericDataUser, PlausibilityError, Scalar from .effects import EffectValuesUser from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters diff --git a/flixopt/features.py b/flixopt/features.py index 20412ed46..5bc8f7922 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -11,7 +11,7 @@ from . import utils from .config import CONFIG -from .core import NumericDataUser, Scalar, TimeSeries +from .core import NumericDataUser, Scalar from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel diff --git a/flixopt/io.py b/flixopt/io.py index 23b06cacd..b01844f3a 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -10,47 +10,10 @@ import xarray as xr import yaml -from .core import TimeSeries logger = logging.getLogger('flixopt') -def replace_timeseries(obj, mode: Literal['name', 'stats', 'data'] = 'name'): - """Recursively replaces TimeSeries objects with their names prefixed by '::::'.""" - if isinstance(obj, dict): - return {k: replace_timeseries(v, mode) for k, v in obj.items()} - elif isinstance(obj, list): - return [replace_timeseries(v, mode) for v in obj] - elif isinstance(obj, TimeSeries): # Adjust this based on the actual class - if obj.all_equal: - return obj.values[0].item() - elif mode == 'name': - return f'::::{obj.name}' - elif mode == 'stats': - return obj.stats - elif mode == 'data': - return obj - else: - raise ValueError(f'Invalid mode {mode}') - else: - return obj - - -def insert_dataarray(obj, ds: xr.Dataset): - """Recursively inserts TimeSeries objects into a dataset.""" - if isinstance(obj, dict): - return {k: insert_dataarray(v, ds) for k, v in obj.items()} - elif isinstance(obj, list): - return [insert_dataarray(v, ds) for v in obj] - elif isinstance(obj, str) and obj.startswith('::::'): - da = ds[obj[4:]] - if da.isel(time=-1).isnull(): - return da.isel(time=slice(0, -1)) - return da - else: - return obj - - def remove_none_and_empty(obj): """Recursively removes None and empty dicts and lists values from a dictionary or list.""" diff --git a/flixopt/structure.py b/flixopt/structure.py index 1e3d2849e..cc7b166eb 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -152,7 +152,7 @@ def transform_data(self, flow_system: 'FlowSystem'): def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: """ - Convert all DataArrays/TimeSeries to references and extract them. + Convert all DataArrays to references and extract them. This is the core method that both to_dict() and to_dataset() build upon. Returns: @@ -204,7 +204,7 @@ def _is_empty_container(obj) -> bool: def _extract_dataarrays_recursive(self, obj, context_name: str = '') -> Tuple[Any, Dict[str, xr.DataArray]]: """ - Recursively extract DataArrays/TimeSeries from nested structures. + Recursively extract DataArrays from nested structures. Args: obj: Object to process @@ -392,7 +392,7 @@ def _serialize_to_basic_types(self, obj): def to_dataset(self) -> xr.Dataset: """ Convert the object to an xarray Dataset representation. - All DataArrays and TimeSeries become dataset variables, everything else goes to attrs. + All DataArrays become dataset variables, everything else goes to attrs. Returns: xr.Dataset: Dataset containing all DataArrays with basic objects only in attributes From cf9d17f4d34098985cda4be4ae24bcc7fc093594 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:52:07 +0200 Subject: [PATCH 087/183] Remove old method --- flixopt/core.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 4ab97b219..1b91cc1cc 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -229,16 +229,3 @@ def to_dataarray(data: NumericDataUser, timesteps: pd.DatetimeIndex) -> xr.DataA if isinstance(e, ConversionError): raise raise ConversionError(f'Converting data {type(data)} to xarray.DataArray raised an error: {str(e)}') from e - - -def get_numeric_stats(data: xr.DataArray, decimals: int = 2, padd: int = 10) -> str: - """Calculates the mean, median, min, max, and standard deviation of a numeric DataArray.""" - format_spec = f'>{padd}.{decimals}f' if padd else f'.{decimals}f' - if np.unique(data).size == 1: - return f'{data.max().item():{format_spec}} (constant)' - mean = data.mean().item() - median = data.median().item() - min_val = data.min().item() - max_val = data.max().item() - std = data.std().item() - return f'{mean:{format_spec}} (mean), {median:{format_spec}} (median), {min_val:{format_spec}} (min), {max_val:{format_spec}} (max), {std:{format_spec}} (std)' From bd52e059a6bf7228f5865c2bbf5f75dcaf554103 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:00:33 +0200 Subject: [PATCH 088/183] Add option to get structure with stats of dataarrays --- flixopt/core.py | 28 ++++++++++++++++++++++++++++ flixopt/flow_system.py | 5 +++-- flixopt/structure.py | 28 +++++++++++++++++++++++++--- 3 files changed, 56 insertions(+), 5 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 1b91cc1cc..61e951019 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -229,3 +229,31 @@ def to_dataarray(data: NumericDataUser, timesteps: pd.DatetimeIndex) -> xr.DataA if isinstance(e, ConversionError): raise raise ConversionError(f'Converting data {type(data)} to xarray.DataArray raised an error: {str(e)}') from e + + +def get_dataarray_stats(arr: xr.DataArray) -> Dict: + """Generate statistical summary of a DataArray.""" + stats = {} + + if arr.dtype.kind in 'biufc': # bool, int, uint, float, complex + try: + stats.update( + { + 'min': float(arr.min().values), + 'max': float(arr.max().values), + 'mean': float(arr.mean().values), + 'median': float(arr.median().values), + 'std': float(arr.std().values), + 'count': int(arr.count().values), # non-null count + } + ) + + # Add null count only if there are nulls + null_count = int(arr.isnull().sum().values) + if null_count > 0: + stats['nulls'] = null_count + + except Exception: + pass + + return stats diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 024d8b3c5..64f9b39bd 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -259,19 +259,20 @@ def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): super().to_netcdf(path, compression) logger.info(f'Saved FlowSystem to {path}') - def get_structure(self, clean: bool = False) -> Dict: + def get_structure(self, clean: bool = False, stats: bool = False) -> Dict: """ Get FlowSystem structure. Ensures FlowSystem is connected before getting structure. Args: clean: If True, remove None and empty dicts and lists. + stats: If True, replace DataArray references with statistics """ if not self._connected_and_transformed: logger.warning('FlowSystem is not connected. Calling connect_and_transform() now.') self.connect_and_transform() - return super().get_structure(clean) + return super().get_structure(clean, stats) def to_json(self, path: Union[str, pathlib.Path]): """ diff --git a/flixopt/structure.py b/flixopt/structure.py index cc7b166eb..651aa765a 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -20,7 +20,7 @@ from . import io as fx_io from .config import CONFIG -from .core import NumericDataUser, Scalar, TimeSeriesData +from .core import NumericDataUser, Scalar, TimeSeriesData, get_dataarray_stats if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -482,21 +482,43 @@ def from_netcdf(cls, path: Union[str, pathlib.Path]) -> 'Interface': except Exception as e: raise IOError(f'Failed to load {cls.__name__} from NetCDF file {path}: {e}') from e - def get_structure(self, clean: bool = False) -> Dict: + def get_structure(self, clean: bool = False, stats: bool = False) -> Dict: """ Get object structure as a dictionary. Args: clean: If True, remove None and empty dicts and lists. + stats: If True, replace DataArray references with statistics Returns: Dictionary representation of the object structure """ - reference_structure, _ = self._create_reference_structure() + reference_structure, extracted_arrays = self._create_reference_structure() + + if stats: + # Replace references with statistics + reference_structure = self._replace_references_with_stats(reference_structure, extracted_arrays) + if clean: return fx_io.remove_none_and_empty(reference_structure) return reference_structure + def _replace_references_with_stats(self, structure, arrays_dict: Dict[str, xr.DataArray]): + """Replace DataArray references with statistical summaries.""" + if isinstance(structure, str) and structure.startswith(':::'): + array_name = structure[3:] + if array_name in arrays_dict: + return get_dataarray_stats(arrays_dict[array_name]) + return structure + + elif isinstance(structure, dict): + return {k: self._replace_references_with_stats(v, arrays_dict) for k, v in structure.items()} + + elif isinstance(structure, list): + return [self._replace_references_with_stats(item, arrays_dict) for item in structure] + + return structure + def to_json(self, path: Union[str, pathlib.Path]): """ Save the object to a JSON file. From aa366892ae3ebbdf844932f9d442c5378edeba03 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:22:49 +0200 Subject: [PATCH 089/183] Change __str__ method --- flixopt/structure.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 651aa765a..33817ec4f 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -532,7 +532,7 @@ def to_json(self, path: Union[str, pathlib.Path]): """ try: # Use the stats mode for JSON export (cleaner output) - data = get_compact_representation(self.get_structure(clean=True)) + data = self.get_structure(clean=True, stats=True) with open(path, 'w', encoding='utf-8') as f: json.dump(data, f, indent=4, ensure_ascii=False) except Exception as e: @@ -566,7 +566,11 @@ def __repr__(self): def __str__(self): """Return a user-friendly string representation.""" try: - return get_str_representation(self.get_structure(clean=True)) + data = self.get_structure(clean=True, stats=True) + with StringIO() as output_buffer: + console = Console(file=output_buffer, width=1000) # Adjust width as needed + console.print(Pretty(data, expand_all=True, indent_guides=True)) + return output_buffer.getvalue() except Exception: # Fallback if structure generation fails return f'{self.__class__.__name__} instance' From 63b1c926ea42b6cc9e374967237c4c6ee1ebc363 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:23:14 +0200 Subject: [PATCH 090/183] Remove old methods --- flixopt/io.py | 1 - flixopt/structure.py | 186 ------------------------------------------- 2 files changed, 187 deletions(-) diff --git a/flixopt/io.py b/flixopt/io.py index b01844f3a..9527eb66a 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -10,7 +10,6 @@ import xarray as xr import yaml - logger = logging.getLogger('flixopt') diff --git a/flixopt/structure.py b/flixopt/structure.py index 33817ec4f..b4fcf7d38 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -123,20 +123,6 @@ class Interface: Subclasses must implement: transform_data(flow_system): Transform data to match FlowSystem dimensions - - Example: - >>> class MyComponent(Interface): - ... def __init__(self, name: str, power_data: xr.DataArray): - ... self.name = name - ... self.power_data = power_data - ... - ... def transform_data(self, flow_system): - ... # Transform power_data to match flow_system timesteps - ... pass - >>> - >>> component = MyComponent('gen1', power_array) - >>> component.to_netcdf('component.nc') # Save to file - >>> restored = MyComponent.from_netcdf('component.nc') # Load from file """ def transform_data(self, flow_system: 'FlowSystem'): @@ -798,175 +784,3 @@ def results_structure(self): 'variables': list(self.variables), 'constraints': list(self.constraints), } - - -def copy_and_convert_datatypes(data: Any, use_numpy: bool = True, use_element_label: bool = False) -> Any: - """ - Converts values in a nested data structure into JSON-compatible types while preserving or transforming numpy arrays - and custom `Element` objects based on the specified options. - - The function handles various data types and transforms them into a consistent, readable format: - - Primitive types (`int`, `float`, `str`, `bool`, `None`) are returned as-is. - - Numpy scalars are converted to their corresponding Python scalar types. - - Collections (`list`, `tuple`, `set`, `dict`) are recursively processed to ensure all elements are compatible. - - Numpy arrays are preserved or converted to lists, depending on `use_numpy`. - - Custom `Element` objects can be represented either by their `label` or their initialization parameters as a dictionary. - - Timestamps (`datetime`) are converted to ISO 8601 strings. - - Args: - data: The input data to process, which may be deeply nested and contain a mix of types. - use_numpy: If `True`, numeric numpy arrays (`np.ndarray`) are preserved as-is. If `False`, they are converted to lists. - Default is `True`. - use_element_label: If `True`, `Element` objects are represented by their `label`. If `False`, they are converted into a dictionary - based on their initialization parameters. Default is `False`. - - Returns: - A transformed version of the input data, containing only JSON-compatible types: - - `int`, `float`, `str`, `bool`, `None` - - `list`, `dict` - - `np.ndarray` (if `use_numpy=True`. This is NOT JSON-compatible) - - Raises: - TypeError: If the data cannot be converted to the specified types. - - Examples: - >>> copy_and_convert_datatypes({'a': np.array([1, 2, 3]), 'b': Element(label='example')}) - {'a': array([1, 2, 3]), 'b': {'class': 'Element', 'label': 'example'}} - - >>> copy_and_convert_datatypes({'a': np.array([1, 2, 3]), 'b': Element(label='example')}, use_numpy=False) - {'a': [1, 2, 3], 'b': {'class': 'Element', 'label': 'example'}} - - Notes: - - The function gracefully handles unexpected types by issuing a warning and returning a deep copy of the data. - - Empty collections (lists, dictionaries) and default parameter values in `Element` objects are omitted from the output. - - Numpy arrays with non-numeric data types are automatically converted to lists. - """ - if isinstance(data, np.integer): # This must be checked before checking for regular int and float! - return int(data) - elif isinstance(data, np.floating): - return float(data) - - elif isinstance(data, (int, float, str, bool, type(None))): - return data - elif isinstance(data, datetime): - return data.isoformat() - - elif isinstance(data, (tuple, set)): - return copy_and_convert_datatypes([item for item in data], use_numpy, use_element_label) - elif isinstance(data, dict): - return { - copy_and_convert_datatypes(key, use_numpy, use_element_label=True): copy_and_convert_datatypes( - value, use_numpy, use_element_label - ) - for key, value in data.items() - } - elif isinstance(data, list): # Shorten arrays/lists to be readable - if use_numpy and all([isinstance(value, (int, float)) for value in data]): - return np.array([item for item in data]) - else: - return [copy_and_convert_datatypes(item, use_numpy, use_element_label) for item in data] - - elif isinstance(data, np.ndarray): - if not use_numpy: - return copy_and_convert_datatypes(data.tolist(), use_numpy, use_element_label) - elif use_numpy and np.issubdtype(data.dtype, np.number): - return data - else: - logger.critical( - f'An np.array with non-numeric content was found: {data=}.It will be converted to a list instead' - ) - return copy_and_convert_datatypes(data.tolist(), use_numpy, use_element_label) - - elif isinstance(data, TimeSeriesData): - return copy_and_convert_datatypes(data.data, use_numpy, use_element_label) - - elif isinstance(data, Interface): - if use_element_label and isinstance(data, Element): - return data.label - return data.infos(use_numpy, use_element_label) - elif isinstance(data, xr.DataArray): - # TODO: This is a temporary basic work around - return copy_and_convert_datatypes(data.values, use_numpy, use_element_label) - else: - raise TypeError(f'copy_and_convert_datatypes() did get unexpected data of type "{type(data)}": {data=}') - - -def get_compact_representation(data: Any, array_threshold: int = 50, decimals: int = 2) -> Dict: - """ - Generate a compact json serializable representation of deeply nested data. - Numpy arrays are statistically described if they exceed a threshold and converted to lists. - - Args: - data (Any): The data to format and represent. - array_threshold (int): Maximum length of NumPy arrays to display. Longer arrays are statistically described. - decimals (int): Number of decimal places in which to describe the arrays. - - Returns: - Dict: A dictionary representation of the data - """ - - def format_np_array_if_found(value: Any) -> Any: - """Recursively processes the data, formatting NumPy arrays.""" - if isinstance(value, (int, float, str, bool, type(None))): - return value - elif isinstance(value, np.ndarray): - return describe_numpy_arrays(value) - elif isinstance(value, dict): - return {format_np_array_if_found(k): format_np_array_if_found(v) for k, v in value.items()} - elif isinstance(value, (list, tuple, set)): - return [format_np_array_if_found(v) for v in value] - else: - logger.warning( - f'Unexpected value found when trying to format numpy array numpy array: {type(value)=}; {value=}' - ) - return value - - def describe_numpy_arrays(arr: np.ndarray) -> Union[str, List]: - """Shortens NumPy arrays if they exceed the specified length.""" - - def normalized_center_of_mass(array: Any) -> float: - # position in array (0 bis 1 normiert) - positions = np.linspace(0, 1, len(array)) # weights w_i - # mass center - if np.sum(array) == 0: - return np.nan - else: - return np.sum(positions * array) / np.sum(array) - - if arr.size > array_threshold: # Calculate basic statistics - fmt = f'.{decimals}f' - return ( - f'Array (min={np.min(arr):{fmt}}, max={np.max(arr):{fmt}}, mean={np.mean(arr):{fmt}}, ' - f'median={np.median(arr):{fmt}}, std={np.std(arr):{fmt}}, len={len(arr)}, ' - f'center={normalized_center_of_mass(arr):{fmt}})' - ) - else: - return np.around(arr, decimals=decimals).tolist() - - # Process the data to handle NumPy arrays - formatted_data = format_np_array_if_found(copy_and_convert_datatypes(data, use_numpy=True)) - - return formatted_data - - -def get_str_representation(data: Any, array_threshold: int = 50, decimals: int = 2) -> str: - """ - Generate a string representation of deeply nested data using `rich.print`. - NumPy arrays are shortened to the specified length and converted to strings. - - Args: - data (Any): The data to format and represent. - array_threshold (int): Maximum length of NumPy arrays to display. Longer arrays are statistically described. - decimals (int): Number of decimal places in which to describe the arrays. - - Returns: - str: The formatted string representation of the data. - """ - - formatted_data = get_compact_representation(data, array_threshold, decimals) - - # Use Rich to format and print the data - with StringIO() as output_buffer: - console = Console(file=output_buffer, width=1000) # Adjust width as needed - console.print(Pretty(formatted_data, expand_all=True, indent_guides=True)) - return output_buffer.getvalue() From 29062fac6df49614955b33244e95ad55bee05225 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:24:57 +0200 Subject: [PATCH 091/183] remove old imports --- flixopt/calculation.py | 2 +- flixopt/flow_system.py | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index f52c1ca19..251a50075 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -29,7 +29,7 @@ from .flow_system import FlowSystem from .results import CalculationResults, SegmentedCalculationResults from .solvers import _Solver -from .structure import SystemModel, copy_and_convert_datatypes, get_compact_representation +from .structure import SystemModel logger = logging.getLogger('flixopt') diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 64f9b39bd..7724a9e61 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -19,14 +19,7 @@ from .core import ConversionError, DataConverter, NumericDataInternal, NumericDataUser, TimeSeriesData from .effects import Effect, EffectCollection, EffectValuesInternal, EffectValuesUser from .elements import Bus, Component, Flow -from .structure import ( - CLASS_REGISTRY, - Element, - Interface, - SystemModel, - get_compact_representation, - get_str_representation, -) +from .structure import Element, Interface, SystemModel if TYPE_CHECKING: import pyvis From 18c43e49d5e5adc2286354c35823031202ce555d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 08:59:32 +0200 Subject: [PATCH 092/183] Add isel, sel and resample methods to FlowSystem --- flixopt/core.py | 2 +- flixopt/flow_system.py | 140 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 2 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 61e951019..831b90b37 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -212,7 +212,7 @@ def to_dataarray(data: NumericDataUser, timesteps: pd.DatetimeIndex) -> xr.DataA raise ConversionError(f"DataArray dimensions {data.dims} don't match expected {dims}") if data.sizes[dims[0]] != len(coords[0]): raise ConversionError( - f"DataArray length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" + f"DataArray length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}: {data}" ) return data.copy(deep=True) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 7724a9e61..2bdfd0bbc 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -194,7 +194,7 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': reference_structure = dict(ds.attrs) # Extract FlowSystem constructor parameters - timesteps_extra = pd.DatetimeIndex(reference_structure['timesteps_extra'], name='time') + timesteps_extra = ds.indexes['time'] hours_of_previous_timesteps = reference_structure['hours_of_previous_timesteps'] # Calculate hours_of_last_timestep from the timesteps @@ -547,3 +547,141 @@ def all_elements(self) -> Dict[str, Element]: @property def used_in_calculation(self) -> bool: return self._used_in_calculation + + def sel(self, **indexers) -> 'FlowSystem': + """Select a subset of the flowsystem like dataset.sel(time=slice('2023-01', '2023-06'))""" + if not self._connected_and_transformed: + self.connect_and_transform() + + # Convert to dataset, select, then convert back + dataset = self.to_dataset() + + # Extend time selection and handle NaN preservation + if 'time' in indexers: + indexers = self._extend_time_selection(indexers, dataset) + selected_dataset = dataset.sel(**indexers) + selected_dataset = self._preserve_nan_pattern(selected_dataset, dataset) + else: + selected_dataset = dataset.sel(**indexers) + + return self.__class__.from_dataset(selected_dataset) + + def isel(self, **indexers) -> 'FlowSystem': + """Select by integer index like dataset.isel(time=slice(0, 100))""" + if not self._connected_and_transformed: + self.connect_and_transform() + + # Convert to dataset, select, then convert back + dataset = self.to_dataset() + + # Extend time selection and handle NaN preservation + if 'time' in indexers: + indexers = self._extend_time_iselection(indexers, dataset) + selected_dataset = dataset.isel(**indexers) + selected_dataset = self._preserve_nan_pattern(selected_dataset, dataset) + else: + selected_dataset = dataset.isel(**indexers) + + return self.__class__.from_dataset(selected_dataset) + + def _preserve_nan_pattern(self, processed_dataset: xr.Dataset, original_dataset: xr.Dataset) -> xr.Dataset: + """ + Preserve NaN pattern at the last timestep for arrays that originally had NaN at the end. + Works for both selection and resampling operations. + """ + for var_name, processed_array in processed_dataset.data_vars.items(): + if var_name in original_dataset.data_vars: + original_array = original_dataset.data_vars[var_name] + + # Check if original array had NaN at the last timestep + if len(original_array.time) > 0 and len(processed_array.time) > 0: + last_original = original_array.isel(time=-1) + + if last_original.isnull().all(): # All values at last timestep are NaN + # Set all values at last timestep to NaN + processed_array = processed_array.copy() + processed_array.values[..., -1] = np.nan + processed_dataset[var_name] = processed_array + elif last_original.isnull().any(): # Some values at last timestep are NaN + # Preserve the specific NaN pattern (if dimensions allow) + processed_array = processed_array.copy() + try: + nan_mask = last_original.isnull().values + processed_array.values[..., -1][nan_mask] = np.nan + except (IndexError, ValueError): + # Fallback: set entire last timestep to NaN if dimensions don't match + processed_array.values[..., -1] = np.nan + processed_dataset[var_name] = processed_array + + return processed_dataset + + def _extend_time_selection(self, indexers: dict, dataset: xr.Dataset) -> dict: + """Extend time selection to include the next timestep for proper boundaries.""" + new_indexers = indexers.copy() + time_sel = indexers['time'] + + if isinstance(time_sel, slice): + # For slice, extend the stop point + if time_sel.stop is not None: + time_coord = dataset.coords['time'] + try: + # Find the index of the stop time and add 1 + stop_idx = time_coord.get_index('time').get_indexer([time_sel.stop], method='nearest')[0] + if stop_idx < len(time_coord) - 1: # Don't go beyond bounds + next_time = time_coord.isel(time=stop_idx + 1).values + new_indexers['time'] = slice(time_sel.start, next_time, time_sel.step) + except Exception: + pass # Keep original if extension fails + + return new_indexers + + def _extend_time_iselection(self, indexers: dict, dataset: xr.Dataset) -> dict: + """Extend integer time selection to include the next timestep.""" + new_indexers = indexers.copy() + time_sel = indexers['time'] + + if isinstance(time_sel, slice): + # For slice, extend the stop point by 1 + stop = time_sel.stop + if stop is not None and stop < len(dataset.coords['time']) - 1: + new_indexers['time'] = slice(time_sel.start, stop + 1, time_sel.step) + elif isinstance(time_sel, int): + # For single index, convert to slice including next + if time_sel < len(dataset.coords['time']) - 1: + new_indexers['time'] = slice(time_sel, time_sel + 2) + elif isinstance(time_sel, (list, np.ndarray)): + # For list/array of indices, add next indices + extended_indices = list(time_sel) + max_idx = len(dataset.coords['time']) - 1 + for idx in time_sel: + if isinstance(idx, int) and idx < max_idx and (idx + 1) not in extended_indices: + extended_indices.append(idx + 1) + new_indexers['time'] = sorted(extended_indices) + + return new_indexers + + def resample(self, time, method: str = 'mean', **kwargs) -> 'FlowSystem': + """ + Resample time dimension like dataset.resample(). + + Args: + time: Resampling frequency (e.g., '1H', '1D') + method: Resampling method ('mean', 'sum', 'max', 'min', 'first', 'last') + **kwargs: Additional arguments passed to xarray.resample() + """ + if not self._connected_and_transformed: + self.connect_and_transform() + + dataset = self.to_dataset() + resampler = dataset.resample(time=time, **kwargs) + + # Apply the specified method + if hasattr(resampler, method): + resampled_dataset = getattr(resampler, method)() + else: + raise ValueError(f'Unsupported resampling method: {method}') + + # Preserve NaN pattern at the last timestep + resampled_dataset = self._preserve_nan_pattern(resampled_dataset, dataset) + + return self.__class__.from_dataset(resampled_dataset) From 1f9ef072abb56a73efe04d57f27dd44287238412 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 09:28:06 +0200 Subject: [PATCH 093/183] Remove need for timeseries with extra timestep --- flixopt/components.py | 25 ++++++++++++++++++++----- flixopt/effects.py | 2 +- flixopt/flow_system.py | 9 ++------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 3f41783a8..ae8cdfbf0 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -134,6 +134,8 @@ def __init__( initial_charge_state: Union[Scalar, Literal['lastValueOfSim']] = 0, minimal_final_charge_state: Optional[Scalar] = None, maximal_final_charge_state: Optional[Scalar] = None, + relative_minimum_final_charge_state: Optional[Scalar] = None, + relative_maximum_final_charge_state: Optional[Scalar] = None, eta_charge: NumericDataUser = 1, eta_discharge: NumericDataUser = 1, relative_loss_per_hour: NumericDataUser = 0, @@ -158,6 +160,8 @@ def __init__( initial_charge_state: storage charge_state at the beginning. The default is 0. minimal_final_charge_state: minimal value of chargeState at the end of timeseries. maximal_final_charge_state: maximal value of chargeState at the end of timeseries. + minimal_final_charge_state: relative minimal value of chargeState at the end of timeseries. + maximal_final_charge_state: relative maximal value of chargeState at the end of timeseries. eta_charge: efficiency factor of charging/loading. The default is 1. eta_discharge: efficiency factor of uncharging/unloading. The default is 1. relative_loss_per_hour: loss per chargeState-Unit per hour. The default is 0. @@ -180,6 +184,9 @@ def __init__( self.relative_minimum_charge_state: NumericDataUser = relative_minimum_charge_state self.relative_maximum_charge_state: NumericDataUser = relative_maximum_charge_state + self.relative_minimum_final_charge_state: NumericDataUser = relative_minimum_final_charge_state + self.relative_maximum_final_charge_state: NumericDataUser = relative_maximum_final_charge_state + self.initial_charge_state = initial_charge_state self.minimal_final_charge_state = minimal_final_charge_state self.maximal_final_charge_state = maximal_final_charge_state @@ -199,12 +206,10 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: self.relative_minimum_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_minimum_charge_state', self.relative_minimum_charge_state, - needs_extra_timestep=True, ) self.relative_maximum_charge_state = flow_system.fit_to_model_coords( f'{self.label_full}|relative_maximum_charge_state', self.relative_maximum_charge_state, - needs_extra_timestep=True, ) self.eta_charge = flow_system.fit_to_model_coords(f'{self.label_full}|eta_charge', self.eta_charge) self.eta_discharge = flow_system.fit_to_model_coords(f'{self.label_full}|eta_discharge', self.eta_discharge) @@ -571,10 +576,20 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser ) @property - def relative_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser]: + def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: + relative_minimum_final_charge_state = ( + xr.DataArray([np.min(self.element.relative_minimum_charge_state)], coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, dims=['time'] + ) if self.element.relative_minimum_final_charge_state is None else + self.element.relative_minimum_final_charge_state + ) + relative_maximum_final_charge_state = ( + xr.DataArray([np.max(self.element.relative_maximum_charge_state)], coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, dims=['time'] + ) if self.element.relative_maximum_final_charge_state is None else + self.element.relative_maximum_final_charge_state + ) return ( - self.element.relative_minimum_charge_state, - self.element.relative_maximum_charge_state, + xr.concat([self.element.relative_minimum_charge_state, relative_minimum_final_charge_state], dim='time'), + xr.concat([self.element.relative_maximum_charge_state, relative_maximum_final_charge_state], dim='time'), ) diff --git a/flixopt/effects.py b/flixopt/effects.py index 7fa136f5b..89bc009bf 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -94,7 +94,7 @@ def transform_data(self, flow_system: 'FlowSystem'): f'{self.label_full}|minimum_operation_per_hour', self.minimum_operation_per_hour ) self.maximum_operation_per_hour = flow_system.fit_to_model_coords( - f'{self.label_full}|maximum_operation_per_hour', self.maximum_operation_per_hour, flow_system + f'{self.label_full}|maximum_operation_per_hour', self.maximum_operation_per_hour ) self.specific_share_to_other_effects_operation = flow_system.fit_effects_to_model_coords( diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 2bdfd0bbc..8b412cd07 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -285,7 +285,6 @@ def fit_to_model_coords( self, name: str, data: Optional[NumericDataUser], - needs_extra_timestep: bool = False, ) -> Optional[NumericDataInternal]: """ Fit data to model coordinate system (currently time, but extensible). @@ -293,7 +292,6 @@ def fit_to_model_coords( Args: name: Name of the data data: Data to fit to model coordinates - needs_extra_timestep: Whether to use extended time coordinates Returns: xr.DataArray aligned to model coordinate system @@ -301,20 +299,17 @@ def fit_to_model_coords( if data is None: return None - # Choose appropriate timesteps - target_timesteps = self.timesteps_extra if needs_extra_timestep else self.timesteps - if isinstance(data, TimeSeriesData): try: return TimeSeriesData( - DataConverter.to_dataarray(data, timesteps=target_timesteps), + DataConverter.to_dataarray(data, timesteps=self.timesteps), agg_group=data.agg_group, agg_weight=data.agg_weight ).rename(name) except ConversionError as e: logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' f'Take care to use the correct (time) index.') else: - return DataConverter.to_dataarray(data, timesteps=target_timesteps).rename(name) + return DataConverter.to_dataarray(data, timesteps=self.timesteps).rename(name) def fit_effects_to_model_coords( self, From 5d88fde2a2a6d9c8b6e007e7f4be8a5a64f3d868 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:09:48 +0200 Subject: [PATCH 094/183] Simplify IO of FLowSystem --- flixopt/flow_system.py | 120 +++-------------------------------------- 1 file changed, 6 insertions(+), 114 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 8b412cd07..aa2e261eb 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -129,15 +129,9 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: # Start with Interface base functionality for constructor parameters reference_structure, all_extracted_arrays = super()._create_reference_structure() - # Override timesteps serialization (we need timesteps_extra instead of timesteps) - reference_structure['timesteps_extra'] = [date.isoformat() for date in self.timesteps_extra] - - # Remove timesteps from structure since we're using timesteps_extra + # Remove timesteps, as it's directly stored in dataset index reference_structure.pop('timesteps', None) - # Add timing arrays directly (not handled by Interface introspection) - all_extracted_arrays['hours_per_timestep'] = self.hours_per_timestep - # Extract from components components_structure = {} for comp_label, component in self.components.items(): @@ -193,18 +187,11 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': # Get the reference structure from attrs reference_structure = dict(ds.attrs) - # Extract FlowSystem constructor parameters - timesteps_extra = ds.indexes['time'] - hours_of_previous_timesteps = reference_structure['hours_of_previous_timesteps'] - - # Calculate hours_of_last_timestep from the timesteps - hours_of_last_timestep = float((timesteps_extra[-1] - timesteps_extra[-2]) / pd.Timedelta(hours=1)) - # Create FlowSystem instance with constructor parameters flow_system = cls( - timesteps=timesteps_extra[:-1], - hours_of_last_timestep=hours_of_last_timestep, - hours_of_previous_timesteps=hours_of_previous_timesteps, + timesteps=ds.indexes['time'], + hours_of_last_timestep=reference_structure.get('hours_of_last_timestep'), + hours_of_previous_timesteps=reference_structure.get('hours_of_previous_timesteps'), ) # Create arrays dictionary from dataset variables @@ -549,15 +536,7 @@ def sel(self, **indexers) -> 'FlowSystem': self.connect_and_transform() # Convert to dataset, select, then convert back - dataset = self.to_dataset() - - # Extend time selection and handle NaN preservation - if 'time' in indexers: - indexers = self._extend_time_selection(indexers, dataset) - selected_dataset = dataset.sel(**indexers) - selected_dataset = self._preserve_nan_pattern(selected_dataset, dataset) - else: - selected_dataset = dataset.sel(**indexers) + selected_dataset = self.to_dataset().sel(**indexers) return self.__class__.from_dataset(selected_dataset) @@ -567,94 +546,10 @@ def isel(self, **indexers) -> 'FlowSystem': self.connect_and_transform() # Convert to dataset, select, then convert back - dataset = self.to_dataset() - - # Extend time selection and handle NaN preservation - if 'time' in indexers: - indexers = self._extend_time_iselection(indexers, dataset) - selected_dataset = dataset.isel(**indexers) - selected_dataset = self._preserve_nan_pattern(selected_dataset, dataset) - else: - selected_dataset = dataset.isel(**indexers) + selected_dataset = self.to_dataset().isel(**indexers) return self.__class__.from_dataset(selected_dataset) - def _preserve_nan_pattern(self, processed_dataset: xr.Dataset, original_dataset: xr.Dataset) -> xr.Dataset: - """ - Preserve NaN pattern at the last timestep for arrays that originally had NaN at the end. - Works for both selection and resampling operations. - """ - for var_name, processed_array in processed_dataset.data_vars.items(): - if var_name in original_dataset.data_vars: - original_array = original_dataset.data_vars[var_name] - - # Check if original array had NaN at the last timestep - if len(original_array.time) > 0 and len(processed_array.time) > 0: - last_original = original_array.isel(time=-1) - - if last_original.isnull().all(): # All values at last timestep are NaN - # Set all values at last timestep to NaN - processed_array = processed_array.copy() - processed_array.values[..., -1] = np.nan - processed_dataset[var_name] = processed_array - elif last_original.isnull().any(): # Some values at last timestep are NaN - # Preserve the specific NaN pattern (if dimensions allow) - processed_array = processed_array.copy() - try: - nan_mask = last_original.isnull().values - processed_array.values[..., -1][nan_mask] = np.nan - except (IndexError, ValueError): - # Fallback: set entire last timestep to NaN if dimensions don't match - processed_array.values[..., -1] = np.nan - processed_dataset[var_name] = processed_array - - return processed_dataset - - def _extend_time_selection(self, indexers: dict, dataset: xr.Dataset) -> dict: - """Extend time selection to include the next timestep for proper boundaries.""" - new_indexers = indexers.copy() - time_sel = indexers['time'] - - if isinstance(time_sel, slice): - # For slice, extend the stop point - if time_sel.stop is not None: - time_coord = dataset.coords['time'] - try: - # Find the index of the stop time and add 1 - stop_idx = time_coord.get_index('time').get_indexer([time_sel.stop], method='nearest')[0] - if stop_idx < len(time_coord) - 1: # Don't go beyond bounds - next_time = time_coord.isel(time=stop_idx + 1).values - new_indexers['time'] = slice(time_sel.start, next_time, time_sel.step) - except Exception: - pass # Keep original if extension fails - - return new_indexers - - def _extend_time_iselection(self, indexers: dict, dataset: xr.Dataset) -> dict: - """Extend integer time selection to include the next timestep.""" - new_indexers = indexers.copy() - time_sel = indexers['time'] - - if isinstance(time_sel, slice): - # For slice, extend the stop point by 1 - stop = time_sel.stop - if stop is not None and stop < len(dataset.coords['time']) - 1: - new_indexers['time'] = slice(time_sel.start, stop + 1, time_sel.step) - elif isinstance(time_sel, int): - # For single index, convert to slice including next - if time_sel < len(dataset.coords['time']) - 1: - new_indexers['time'] = slice(time_sel, time_sel + 2) - elif isinstance(time_sel, (list, np.ndarray)): - # For list/array of indices, add next indices - extended_indices = list(time_sel) - max_idx = len(dataset.coords['time']) - 1 - for idx in time_sel: - if isinstance(idx, int) and idx < max_idx and (idx + 1) not in extended_indices: - extended_indices.append(idx + 1) - new_indexers['time'] = sorted(extended_indices) - - return new_indexers - def resample(self, time, method: str = 'mean', **kwargs) -> 'FlowSystem': """ Resample time dimension like dataset.resample(). @@ -676,7 +571,4 @@ def resample(self, time, method: str = 'mean', **kwargs) -> 'FlowSystem': else: raise ValueError(f'Unsupported resampling method: {method}') - # Preserve NaN pattern at the last timestep - resampled_dataset = self._preserve_nan_pattern(resampled_dataset, dataset) - return self.__class__.from_dataset(resampled_dataset) From 1e94de392b57e57b8b3600c4598fdad9a46014a7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:12:53 +0200 Subject: [PATCH 095/183] Remove parameter timesteps from IO --- flixopt/structure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index b4fcf7d38..5a95b0a94 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -156,7 +156,7 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: all_extracted_arrays = {} for name in self._cached_init_params: - if name == 'self': + if name == 'self' or name == 'timesteps': # Skip self and timesteps. Timesteps are directly stored in Datasets continue value = getattr(self, name, None) From e5828ad78f813aff3d116e3696fb832d19b7bbe4 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:26:54 +0200 Subject: [PATCH 096/183] Improve Exceptions and Docstrings --- flixopt/flow_system.py | 2 +- flixopt/structure.py | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index aa2e261eb..1d3bc4aa8 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -167,7 +167,7 @@ def to_dataset(self) -> xr.Dataset: xr.Dataset: Dataset containing all DataArrays with structure in attributes """ if not self._connected_and_transformed: - logger.warning('FlowSystem is not connected_and_transformed..') + logger.warning('FlowSystem is not connected_and_transformed. Connecting and transforming data now.') self.connect_and_transform() return super().to_dataset() diff --git a/flixopt/structure.py b/flixopt/structure.py index 5a95b0a94..3fb0be066 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -380,6 +380,9 @@ def to_dataset(self) -> xr.Dataset: Convert the object to an xarray Dataset representation. All DataArrays become dataset variables, everything else goes to attrs. + Its recommended to only call this method on Interfaces with all numeric data stored as xr.DataArrays. + Interfaces inside a FlowSystem are automatically converted this form after connecting and transforming the FlowSystem. + Returns: xr.Dataset: Dataset containing all DataArrays with basic objects only in attributes @@ -391,7 +394,10 @@ def to_dataset(self) -> xr.Dataset: # Create the dataset with extracted arrays as variables and structure as attrs return xr.Dataset(extracted_arrays, attrs=reference_structure) except Exception as e: - raise ValueError(f'Failed to convert {self.__class__.__name__} to dataset: {e}') from e + raise ValueError( + f'Failed to convert {self.__class__.__name__} to dataset. Its recommended to only call this method on ' + f'a fully connected and transformed FlowSystem, or Interfaces inside such a FlowSystem.' + f'Original Error: {e}') from e def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): """ From 870efeee484f39e63cf4b7adca6bc30000b613ac Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:35:56 +0200 Subject: [PATCH 097/183] Improve isel sel and resample methods --- flixopt/flow_system.py | 66 ++++++++++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 1d3bc4aa8..b146ef06a 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -7,7 +7,7 @@ import pathlib import warnings from io import StringIO -from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union +from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union, Any import numpy as np import pandas as pd @@ -530,34 +530,70 @@ def all_elements(self) -> Dict[str, Element]: def used_in_calculation(self) -> bool: return self._used_in_calculation - def sel(self, **indexers) -> 'FlowSystem': - """Select a subset of the flowsystem like dataset.sel(time=slice('2023-01', '2023-06'))""" + def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp]] = None) -> 'FlowSystem': + """ + Select a subset of the flowsystem by the time coordinate. + + Args: + time: Time selection (e.g., slice('2023-01-01', '2023-12-31'), '2023-06-15', or list of times) + + Returns: + FlowSystem: New FlowSystem with selected data + """ if not self._connected_and_transformed: self.connect_and_transform() - # Convert to dataset, select, then convert back - selected_dataset = self.to_dataset().sel(**indexers) + # Build indexers dict from non-None parameters + indexers = {} + if time is not None: + indexers['time'] = time + + if not indexers: + return self.copy() # Return a copy when no selection + selected_dataset = self.to_dataset().sel(**indexers) return self.__class__.from_dataset(selected_dataset) - def isel(self, **indexers) -> 'FlowSystem': - """Select by integer index like dataset.isel(time=slice(0, 100))""" + def isel(self, time: Optional[Union[int, slice, List[int]]] = None) -> 'FlowSystem': + """ + Select a subset of the flowsystem by integer indices. + + Args: + time: Time selection by integer index (e.g., slice(0, 100), 50, or [0, 5, 10]) + + Returns: + FlowSystem: New FlowSystem with selected data + """ if not self._connected_and_transformed: self.connect_and_transform() - # Convert to dataset, select, then convert back - selected_dataset = self.to_dataset().isel(**indexers) + # Build indexers dict from non-None parameters + indexers = {} + if time is not None: + indexers['time'] = time + if not indexers: + return self.copy() # Return a copy when no selection + + selected_dataset = self.to_dataset().isel(**indexers) return self.__class__.from_dataset(selected_dataset) - def resample(self, time, method: str = 'mean', **kwargs) -> 'FlowSystem': + def resample( + self, + time: str, + method: Literal['mean', 'sum', 'max', 'min', 'first', 'last', 'std', 'var', 'median', 'count'] = 'mean', + **kwargs: Any + ) -> 'FlowSystem': """ - Resample time dimension like dataset.resample(). + Create a resampled FlowSystem by resampling data along the time dimension (like xr.Dataset.resample()). Args: - time: Resampling frequency (e.g., '1H', '1D') - method: Resampling method ('mean', 'sum', 'max', 'min', 'first', 'last') + time: Resampling frequency (e.g., '3h', '2D', '1M') + method: Resampling method. Recommended: 'mean', 'first', 'last', 'max', 'min' **kwargs: Additional arguments passed to xarray.resample() + + Returns: + FlowSystem: New FlowSystem with resampled data """ if not self._connected_and_transformed: self.connect_and_transform() @@ -565,10 +601,10 @@ def resample(self, time, method: str = 'mean', **kwargs) -> 'FlowSystem': dataset = self.to_dataset() resampler = dataset.resample(time=time, **kwargs) - # Apply the specified method if hasattr(resampler, method): resampled_dataset = getattr(resampler, method)() else: - raise ValueError(f'Unsupported resampling method: {method}') + available_methods = ['mean', 'sum', 'max', 'min', 'first', 'last', 'std', 'var', 'median', 'count'] + raise ValueError(f'Unsupported resampling method: {method}. Available: {available_methods}') return self.__class__.from_dataset(resampled_dataset) From e97ec5fcd7ce5085bd1418b4077fc8f35240fbbf Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 10:48:35 +0200 Subject: [PATCH 098/183] Change test --- tests/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 43f9f8bae..b705939cc 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -95,7 +95,8 @@ def simple_flow_system() -> fx.FlowSystem: discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1e4), capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), initial_charge_state=0, - relative_maximum_charge_state=1 / 100 * np.array([80.0, 70.0, 80.0, 80, 80, 80, 80, 80, 80, 80]), + relative_maximum_charge_state=1 / 100 * np.array([80.0, 70.0, 80.0, 80, 80, 80, 80, 80, 80]), + relative_maximum_final_charge_state=0.8, eta_charge=0.9, eta_discharge=1, relative_loss_per_hour=0.08, From f15113efaf16e448a87054741edaf6016eea60dc Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:02:58 +0200 Subject: [PATCH 099/183] Bugfix --- flixopt/components.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index ae8cdfbf0..be86457e6 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -577,16 +577,18 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser @property def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: - relative_minimum_final_charge_state = ( - xr.DataArray([np.min(self.element.relative_minimum_charge_state)], coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, dims=['time'] - ) if self.element.relative_minimum_final_charge_state is None else - self.element.relative_minimum_final_charge_state + relative_minimum_final_charge_state = xr.DataArray( + [self.element.relative_minimum_charge_state.max('time') if self.element.relative_minimum_final_charge_state is None else self.element.relative_minimum_final_charge_state], + coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, + dims=['time'] ) - relative_maximum_final_charge_state = ( - xr.DataArray([np.max(self.element.relative_maximum_charge_state)], coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, dims=['time'] - ) if self.element.relative_maximum_final_charge_state is None else - self.element.relative_maximum_final_charge_state + relative_maximum_final_charge_state = xr.DataArray( + [self.element.relative_maximum_charge_state.max('time') if self.element.relative_maximum_final_charge_state is None else + self.element.relative_maximum_final_charge_state], + coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, + dims=['time'] ) + return ( xr.concat([self.element.relative_minimum_charge_state, relative_minimum_final_charge_state], dim='time'), xr.concat([self.element.relative_maximum_charge_state, relative_maximum_final_charge_state], dim='time'), From 284072e5680f1a2b09c03a53c71fa40e1164aa22 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:24:32 +0200 Subject: [PATCH 100/183] Improve --- flixopt/components.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index be86457e6..fe509c59d 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -237,9 +237,9 @@ def _plausibility_checks(self) -> None: minimum_capacity = self.capacity_in_flow_hours # initial capacity >= allowed min for maximum_size: - minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=1) + minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) # initial capacity <= allowed max for minimum_size: - maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=1) + maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0) if self.initial_charge_state > maximum_inital_capacity: raise ValueError( @@ -577,17 +577,28 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser @property def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: - relative_minimum_final_charge_state = xr.DataArray( - [self.element.relative_minimum_charge_state.max('time') if self.element.relative_minimum_final_charge_state is None else self.element.relative_minimum_final_charge_state], - coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, - dims=['time'] - ) - relative_maximum_final_charge_state = xr.DataArray( - [self.element.relative_maximum_charge_state.max('time') if self.element.relative_maximum_final_charge_state is None else - self.element.relative_maximum_final_charge_state], - coords={'time': [self._model.flow_system.timesteps_extra[-1]]}, - dims=['time'] - ) + coords = {'time': self._model.flow_system.timesteps_extra[-1]} + if self.element.relative_minimum_final_charge_state is None: + relative_minimum_final_charge_state = self.element.relative_minimum_charge_state.isel( + time=-1 + ).assign_coords(time=self._model.flow_system.timesteps_extra[-1]) + else: + relative_minimum_final_charge_state = xr.DataArray( + [self.element.relative_minimum_final_charge_state], + coords=coords, + dims=['time'] + ) + + if self.element.relative_maximum_final_charge_state is None: + relative_maximum_final_charge_state = self.element.relative_maximum_charge_state.isel( + time=-1 + ).assign_coords(coords) + else: + relative_maximum_final_charge_state = xr.DataArray( + [self.element.relative_maximum_final_charge_state], + coords=coords, + dims=['time'] + ) return ( xr.concat([self.element.relative_minimum_charge_state, relative_minimum_final_charge_state], dim='time'), From ebbb5dd61140e5198299c7af1683ef1dddf345f2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:25:47 +0200 Subject: [PATCH 101/183] Improve --- flixopt/components.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index fe509c59d..5e59b8bc5 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -577,33 +577,36 @@ def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser @property def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: - coords = {'time': self._model.flow_system.timesteps_extra[-1]} + """ + Get relative charge state bounds with final timestep values. + + Returns: + Tuple of (minimum_bounds, maximum_bounds) DataArrays extending to final timestep + """ + final_timestep = self._model.flow_system.timesteps_extra[-1] + final_coords = {'time': final_timestep} + + # Get final minimum charge state if self.element.relative_minimum_final_charge_state is None: - relative_minimum_final_charge_state = self.element.relative_minimum_charge_state.isel( - time=-1 - ).assign_coords(time=self._model.flow_system.timesteps_extra[-1]) + min_final = self.element.relative_minimum_charge_state.isel(time=-1).assign_coords(time=final_timestep) else: - relative_minimum_final_charge_state = xr.DataArray( - [self.element.relative_minimum_final_charge_state], - coords=coords, - dims=['time'] + min_final = xr.DataArray( + [self.element.relative_minimum_final_charge_state], coords=final_coords, dims=['time'] ) + # Get final maximum charge state if self.element.relative_maximum_final_charge_state is None: - relative_maximum_final_charge_state = self.element.relative_maximum_charge_state.isel( - time=-1 - ).assign_coords(coords) + max_final = self.element.relative_maximum_charge_state.isel(time=-1).assign_coords(time=final_timestep) else: - relative_maximum_final_charge_state = xr.DataArray( - [self.element.relative_maximum_final_charge_state], - coords=coords, - dims=['time'] + max_final = xr.DataArray( + [self.element.relative_maximum_final_charge_state], coords=final_coords, dims=['time'] ) - return ( - xr.concat([self.element.relative_minimum_charge_state, relative_minimum_final_charge_state], dim='time'), - xr.concat([self.element.relative_maximum_charge_state, relative_maximum_final_charge_state], dim='time'), - ) + # Concatenate with original bounds + min_bounds = xr.concat([self.element.relative_minimum_charge_state, min_final], dim='time') + max_bounds = xr.concat([self.element.relative_maximum_charge_state, max_final], dim='time') + + return min_bounds, max_bounds @register_class_for_io From a501e05b7b06f8d5a916ade24880109ed5b960e6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:25:58 +0200 Subject: [PATCH 102/183] Add test for Storage Bounds --- tests/test_storage.py | 81 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/test_storage.py b/tests/test_storage.py index 472ba4add..1b9b3b875 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -175,6 +175,87 @@ def test_lossy_storage(self, basic_flow_system_linopy): model.variables['TestStorage|charge_state'].isel(time=0) == 0 ) + def test_charge_state_bounds(self, basic_flow_system_linopy): + """Test that basic storage model variables and constraints are correctly generated.""" + flow_system = basic_flow_system_linopy + timesteps = flow_system.timesteps + timesteps_extra = flow_system.timesteps_extra + + # Create a simple storage + storage = fx.Storage( + 'TestStorage', + charging=fx.Flow('Q_th_in', bus='Fernwärme', size=20), + discharging=fx.Flow('Q_th_out', bus='Fernwärme', size=20), + capacity_in_flow_hours=30, # 30 kWh storage capacity + initial_charge_state=3, + prevent_simultaneous_charge_and_discharge=True, + relative_maximum_charge_state=np.array([0.14, 0.22, 0.3 , 0.38, 0.46, 0.54, 0.62, 0.7 , 0.78, 0.86]), + relative_minimum_charge_state=np.array([0.07, 0.11, 0.15, 0.19, 0.23, 0.27, 0.31, 0.35, 0.39, 0.43]), + ) + + flow_system.add_elements(storage) + model = create_linopy_model(flow_system) + + # Check that all expected variables exist - linopy model variables are accessed by indexing + expected_variables = { + 'TestStorage(Q_th_in)|flow_rate', + 'TestStorage(Q_th_in)|total_flow_hours', + 'TestStorage(Q_th_out)|flow_rate', + 'TestStorage(Q_th_out)|total_flow_hours', + 'TestStorage|charge_state', + 'TestStorage|netto_discharge', + } + for var_name in expected_variables: + assert var_name in model.variables, f"Missing variable: {var_name}" + + # Check that all expected constraints exist - linopy model constraints are accessed by indexing + expected_constraints = { + 'TestStorage(Q_th_in)|total_flow_hours', + 'TestStorage(Q_th_out)|total_flow_hours', + 'TestStorage|netto_discharge', + 'TestStorage|charge_state', + 'TestStorage|initial_charge_state', + } + for con_name in expected_constraints: + assert con_name in model.constraints, f"Missing constraint: {con_name}" + + # Check variable properties + assert_var_equal( + model['TestStorage(Q_th_in)|flow_rate'], + model.add_variables(lower=0, upper=20, coords=(timesteps,)) + ) + assert_var_equal( + model['TestStorage(Q_th_out)|flow_rate'], + model.add_variables(lower=0, upper=20, coords=(timesteps,)) + ) + assert_var_equal( + model['TestStorage|charge_state'], + model.add_variables(lower=np.array([0.07, 0.11, 0.15, 0.19, 0.23, 0.27, 0.31, 0.35, 0.39, 0.43, 0.43]) * 30, + upper=np.array([0.14, 0.22, 0.3 , 0.38, 0.46, 0.54, 0.62, 0.7 , 0.78, 0.86, 0.86]) * 30, + coords=(timesteps_extra,)) + ) + + # Check constraint formulations + assert_conequal( + model.constraints['TestStorage|netto_discharge'], + model.variables['TestStorage|netto_discharge'] == + model.variables['TestStorage(Q_th_out)|flow_rate'] - model.variables['TestStorage(Q_th_in)|flow_rate'] + ) + + charge_state = model.variables['TestStorage|charge_state'] + assert_conequal( + model.constraints['TestStorage|charge_state'], + charge_state.isel(time=slice(1, None)) + == charge_state.isel(time=slice(None, -1)) + + model.variables['TestStorage(Q_th_in)|flow_rate'] * model.hours_per_step + - model.variables['TestStorage(Q_th_out)|flow_rate'] * model.hours_per_step, + ) + # Check initial charge state constraint + assert_conequal( + model.constraints['TestStorage|initial_charge_state'], + model.variables['TestStorage|charge_state'].isel(time=0) == 3 + ) + def test_storage_with_investment(self, basic_flow_system_linopy): """Test storage with investment parameters.""" flow_system = basic_flow_system_linopy From 182508914ab9f8c4e4349e709baebd94847ba279 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:34:45 +0200 Subject: [PATCH 103/183] Add test for Storage Bounds --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d692d5e5..545973095 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- `relative_minimum_charge_state` and `relative_maximum_charge_state` dont have an extra timestep anymore. The final charge state can be constrainted by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead. +- FlowSystems can not be shared across multiple Calculations anymore. A copy of the FLowSystem is created instead. THs makes every Calculation independent. +- THe above allowed to remove the intermediate classes `TimeSeries` and `TimeSeriesCollection` classes which orchestratet datahandling. + +### Added +- Added IO for all Interfaces and the FlowSystem +- Added `sel`, `isel` and `resample` methods to FlowSystem, allowing for a flexible data handling. + ## [2.1.2] - 2025-06-14 ### Fixed From 126b07f89dfafb6d53549271f441298e0fc43613 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 11:51:49 +0200 Subject: [PATCH 104/183] CHANGELOG.md --- CHANGELOG.md | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 545973095..bb95b3756 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed -- `relative_minimum_charge_state` and `relative_maximum_charge_state` dont have an extra timestep anymore. The final charge state can be constrainted by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead. -- FlowSystems can not be shared across multiple Calculations anymore. A copy of the FLowSystem is created instead. THs makes every Calculation independent. -- THe above allowed to remove the intermediate classes `TimeSeries` and `TimeSeriesCollection` classes which orchestratet datahandling. +* **BREAKING**: FlowSystems can not be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent +* **BREAKING**: Type system overhaul - replaced `NumericDataTS` with `NumericDataUser` throughout codebase for better clarity +* **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. The final charge state can now be constrained by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead +* FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties +* Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods +* *Internal*: Removed intermediate `TimeSeries` and `TimeSeriesCollection` classes, replaced directly with `xr.DataArray` or `TimeSeriesData` (inheriting from `xr.DataArray`) ### Added -- Added IO for all Interfaces and the FlowSystem -- Added `sel`, `isel` and `resample` methods to FlowSystem, allowing for a flexible data handling. +* **NEW**: Complete serialization infrastructure through `Interface` base class + * IO for all Interfaces and the FlowSystem with round-trip serialization support + * Automatic DataArray extraction and restoration + * NetCDF export/import capabilities for all Interface objects and FlowSystem + * JSON export for documentation purposes + * Recursive handling of nested Interface objects +* **NEW**: FlowSystem data manipulation methods + * `sel()` and `isel()` methods for temporal data selection + * `resample()` method for temporal resampling + * `copy()` method with deep copying support + * `__eq__()` method for FlowSystem comparison +* **NEW**: Storage component enhancements + * `relative_minimum_final_charge_state` parameter for final state control + * `relative_maximum_final_charge_state` parameter for final state control +* *Internal*: Enhanced data handling methods + * `fit_to_model_coords()` method for data alignment + * `fit_effects_to_model_coords()` method for effect data processing + * `connect_and_transform()` method replacing separate operations +* **NEW**: Core data handling improvements + * `get_dataarray_stats()` function for statistical summaries + * Enhanced `DataConverter` class with better TimeSeriesData support + +### Fixed +* Enhanced NetCDF I/O with proper attribute preservation for DataArrays +* Improved error handling and validation in serialization processes +* Better type consistency across all framework components + +### Know Issues +* Plotly >= 6 may raise errors if "nbformat" is not installed. We pinned plotly to <6, but this may be fixed in the future. +* IO for single Interfaces/Elemenets to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arrises from Numeric Data not being stored as xr.DataArray by the user. TO avoid this, always use the `to_dataset()` on Elements inside a FlowSystem thats connected and transformed. ## [2.1.2] - 2025-06-14 From 94d841d9f2222b0736d30473d30403b77e5742f4 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:00:50 +0200 Subject: [PATCH 105/183] ruff check --- flixopt/flow_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index b146ef06a..4ad935dc5 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -7,7 +7,7 @@ import pathlib import warnings from io import StringIO -from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Tuple, Union, Any +from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union import numpy as np import pandas as pd From c19edc8e125e0815aa3c2e5450995e5938970e09 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:26:41 +0200 Subject: [PATCH 106/183] Improve types --- .../example_calculation_types.py | 6 +-- flixopt/calculation.py | 42 +++++++++++++++---- flixopt/components.py | 34 +++++++-------- flixopt/core.py | 18 ++++---- flixopt/effects.py | 41 +++++++++++------- flixopt/elements.py | 26 ++++++------ flixopt/features.py | 32 +++++++------- flixopt/flow_system.py | 14 +++---- flixopt/interface.py | 20 ++++----- flixopt/linear_converters.py | 22 +++++----- flixopt/structure.py | 2 +- 11 files changed, 144 insertions(+), 113 deletions(-) diff --git a/examples/03_Calculation_types/example_calculation_types.py b/examples/03_Calculation_types/example_calculation_types.py index 97b18e3c0..b793e26ce 100644 --- a/examples/03_Calculation_types/example_calculation_types.py +++ b/examples/03_Calculation_types/example_calculation_types.py @@ -164,12 +164,12 @@ if full: calculation = fx.FullCalculation('Full', flow_system) calculation.do_modeling() - calculation.solve(fx.solvers.HighsSolver(0, 60)) + calculation.solve(fx.solvers.GurobiSolver(0.001, 60)) calculations.append(calculation) if segmented: calculation = fx.SegmentedCalculation('Segmented', flow_system, segment_length, overlap_length) - calculation.do_modeling_and_solve(fx.solvers.HighsSolver(0, 60)) + calculation.do_modeling_and_solve(fx.solvers.GurobiSolver(0.001, 60)) calculations.append(calculation) if aggregated: @@ -178,7 +178,7 @@ aggregation_parameters.time_series_for_low_peaks = [TS_electricity_demand, TS_heat_demand] calculation = fx.AggregatedCalculation('Aggregated', flow_system, aggregation_parameters) calculation.do_modeling() - calculation.solve(fx.solvers.HighsSolver(0, 60)) + calculation.solve(fx.solvers.GurobiSolver(0.001, 60)) calculations.append(calculation) # Get solutions for plotting for different calculations diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 251a50075..60163b7a2 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -12,7 +12,8 @@ import math import pathlib import timeit -from typing import Any, Dict, List, Optional, Union +import warnings +from typing import Annotated, Any, Dict, List, Optional, Union import numpy as np import pandas as pd @@ -43,26 +44,39 @@ def __init__( self, name: str, flow_system: FlowSystem, - active_timesteps: Optional[pd.DatetimeIndex] = None, + active_timesteps: Annotated[ + Optional[pd.DatetimeIndex], + "DEPRECATED: Use flow_system.sel(time=...) or flow_system.isel(time=...) instead" + ] = None, folder: Optional[pathlib.Path] = None, ): """ Args: name: name of calculation flow_system: flow_system which should be calculated - active_timesteps: list with indices, which should be used for calculation. If None, then all timesteps are used. folder: folder where results should be saved. If None, then the current working directory is used. """ self.name = name if flow_system.used_in_calculation: - logging.warning(f'FlowSystem {flow_system.name} is already used in a calculation. ' + logging.warning(f'FlowSystem {flow_system} is already used in a calculation. ' f'Creating a copy for Calculation "{self.name}".') flow_system = flow_system.copy() + if active_timesteps is not None: + warnings.warn( + "The 'active_timesteps' parameter is deprecated and will be removed in a future version. " + 'Use flow_system.sel(time=timesteps) or flow_system.isel(time=indices) before passing ' + 'the FlowSystem to the Calculation instead.', + DeprecationWarning, + stacklevel=2, + ) + flow_system = flow_system.sel(time=active_timesteps) + + self.flow_system = flow_system self.flow_system._used_in_calculation = True self.model: Optional[SystemModel] = None - self.active_timesteps = active_timesteps + self._active_timesteps = active_timesteps # deprecated self.durations = {'modeling': 0.0, 'solving': 0.0, 'saving': 0.0} self.folder = pathlib.Path.cwd() / 'results' if folder is None else pathlib.Path(folder) @@ -134,6 +148,15 @@ def summary(self): 'Config': CONFIG.to_dict(), } + @property + def active_timesteps(self) -> pd.DatetimeIndex: + warnings.warn( + "The 'active_timesteps' is deprecated and will be removed in a future version.", + DeprecationWarning, + stacklevel=2, + ) + return self.flow_system.timesteps + class FullCalculation(Calculation): """ @@ -199,7 +222,10 @@ def __init__( flow_system: FlowSystem, aggregation_parameters: AggregationParameters, components_to_clusterize: Optional[List[Component]] = None, - active_timesteps: Optional[pd.DatetimeIndex] = None, + active_timesteps: Annotated[ + Optional[pd.DatetimeIndex], + 'DEPRECATED: Use flow_system.sel(time=...) or flow_system.isel(time=...) instead', + ] = None, folder: Optional[pathlib.Path] = None, ): """ @@ -213,8 +239,6 @@ def __init__( components_to_clusterize: List of Components to perform aggregation on. If None, then all components are aggregated. This means, teh variables in the components are equalized to each other, according to the typical periods computed in the DataAggregation - active_timesteps: pd.DatetimeIndex or None - list with indices, which should be used for calculation. If None, then all timesteps are used. folder: folder where results should be saved. If None, then the current working directory is used. """ super().__init__(name, flow_system, active_timesteps, folder=folder) @@ -370,7 +394,7 @@ def do_modeling_and_solve( ) calculation = FullCalculation( - f'{self.name}-{segment_name}', self.flow_system, active_timesteps=timesteps_of_segment + f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), ) self.sub_calculations.append(calculation) calculation.do_modeling() diff --git a/flixopt/components.py b/flixopt/components.py index 5e59b8bc5..49d6f5b31 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -10,7 +10,7 @@ import xarray as xr from . import utils -from .core import NumericDataUser, PlausibilityError, Scalar +from .core import PlausibilityError, Scalar, TemporalData, TemporalDataUser from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion @@ -35,7 +35,7 @@ def __init__( inputs: List[Flow], outputs: List[Flow], on_off_parameters: OnOffParameters = None, - conversion_factors: List[Dict[str, NumericDataUser]] = None, + conversion_factors: List[Dict[str, TemporalDataUser]] = None, piecewise_conversion: Optional[PiecewiseConversion] = None, meta_data: Optional[Dict] = None, ): @@ -129,16 +129,16 @@ def __init__( charging: Flow, discharging: Flow, capacity_in_flow_hours: Union[Scalar, InvestParameters], - relative_minimum_charge_state: NumericDataUser = 0, - relative_maximum_charge_state: NumericDataUser = 1, + relative_minimum_charge_state: TemporalDataUser = 0, + relative_maximum_charge_state: TemporalDataUser = 1, initial_charge_state: Union[Scalar, Literal['lastValueOfSim']] = 0, minimal_final_charge_state: Optional[Scalar] = None, maximal_final_charge_state: Optional[Scalar] = None, relative_minimum_final_charge_state: Optional[Scalar] = None, relative_maximum_final_charge_state: Optional[Scalar] = None, - eta_charge: NumericDataUser = 1, - eta_discharge: NumericDataUser = 1, - relative_loss_per_hour: NumericDataUser = 0, + eta_charge: TemporalDataUser = 1, + eta_discharge: TemporalDataUser = 1, + relative_loss_per_hour: TemporalDataUser = 0, prevent_simultaneous_charge_and_discharge: bool = True, meta_data: Optional[Dict] = None, ): @@ -181,19 +181,19 @@ def __init__( self.charging = charging self.discharging = discharging self.capacity_in_flow_hours = capacity_in_flow_hours - self.relative_minimum_charge_state: NumericDataUser = relative_minimum_charge_state - self.relative_maximum_charge_state: NumericDataUser = relative_maximum_charge_state + self.relative_minimum_charge_state: TemporalDataUser = relative_minimum_charge_state + self.relative_maximum_charge_state: TemporalDataUser = relative_maximum_charge_state - self.relative_minimum_final_charge_state: NumericDataUser = relative_minimum_final_charge_state - self.relative_maximum_final_charge_state: NumericDataUser = relative_maximum_final_charge_state + self.relative_minimum_final_charge_state: Scalar = relative_minimum_final_charge_state + self.relative_maximum_final_charge_state: Scalar = relative_maximum_final_charge_state self.initial_charge_state = initial_charge_state self.minimal_final_charge_state = minimal_final_charge_state self.maximal_final_charge_state = maximal_final_charge_state - self.eta_charge: NumericDataUser = eta_charge - self.eta_discharge: NumericDataUser = eta_discharge - self.relative_loss_per_hour: NumericDataUser = relative_loss_per_hour + self.eta_charge: TemporalDataUser = eta_charge + self.eta_discharge: TemporalDataUser = eta_discharge + self.relative_loss_per_hour: TemporalDataUser = relative_loss_per_hour self.prevent_simultaneous_charge_and_discharge = prevent_simultaneous_charge_and_discharge def create_model(self, model: SystemModel) -> 'StorageModel': @@ -270,8 +270,8 @@ def __init__( out1: Flow, in2: Optional[Flow] = None, out2: Optional[Flow] = None, - relative_losses: Optional[NumericDataUser] = None, - absolute_losses: Optional[NumericDataUser] = None, + relative_losses: Optional[TemporalDataUser] = None, + absolute_losses: Optional[TemporalDataUser] = None, on_off_parameters: OnOffParameters = None, prevent_simultaneous_flows_in_both_directions: bool = True, meta_data: Optional[Dict] = None, @@ -562,7 +562,7 @@ def _initial_and_final_charge_state(self): ) @property - def absolute_charge_state_bounds(self) -> Tuple[NumericDataUser, NumericDataUser]: + def absolute_charge_state_bounds(self) -> Tuple[TemporalData, TemporalData]: relative_lower_bound, relative_upper_bound = self.relative_charge_state_bounds if not isinstance(self.element.capacity_in_flow_hours, InvestParameters): return ( diff --git a/flixopt/core.py b/flixopt/core.py index 831b90b37..41ee7b799 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -3,12 +3,8 @@ It provides Datatypes, logging functionality, and some functions to transform data structures. """ -import inspect -import json import logging -import pathlib -from collections import Counter -from typing import Any, Dict, Iterator, List, Literal, Optional, Tuple, Union +from typing import Dict, Optional, Union import numpy as np import pandas as pd @@ -19,11 +15,13 @@ Scalar = Union[int, float] """A single number, either integer or float.""" -NumericDataUser = Union[int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, 'TimeSeriesData'] -"""Numeric data accepted in varios types. Will be converted to an xr.DataArray or Scalar internally.""" +TemporalDataUser = Union[ + int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, 'TimeSeriesData' +] +"""User data which might have a time dimension. Internally converted to an xr.DataArray with time dimension.""" -NumericDataInternal = Union[int, float, xr.DataArray, 'TimeSeriesData'] -"""Internally used datatypes for numeric data.""" +TemporalData = Union[xr.DataArray, 'TimeSeriesData'] +"""Internally used datatypes for temporal data.""" class PlausibilityError(Exception): @@ -167,7 +165,7 @@ def _fix_timeseries_data_indexing( return data.copy(deep=True) @staticmethod - def to_dataarray(data: NumericDataUser, timesteps: pd.DatetimeIndex) -> xr.DataArray: + def to_dataarray(data: TemporalData, timesteps: pd.DatetimeIndex) -> xr.DataArray: """Convert data to xarray.DataArray with specified timesteps index.""" if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: raise ValueError(f'Timesteps must be a non-empty DatetimeIndex, got {type(timesteps).__name__}') diff --git a/flixopt/effects.py b/flixopt/effects.py index 89bc009bf..1d1a5216c 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -13,7 +13,7 @@ import numpy as np import pandas as pd -from .core import NumericDataInternal, NumericDataUser, Scalar +from .core import Scalar, TemporalData, TemporalDataUser from .features import ShareAllocationModel from .structure import Element, ElementModel, Interface, Model, SystemModel, register_class_for_io @@ -38,14 +38,14 @@ def __init__( meta_data: Optional[Dict] = None, is_standard: bool = False, is_objective: bool = False, - specific_share_to_other_effects_operation: Optional['EffectValuesUser'] = None, - specific_share_to_other_effects_invest: Optional['EffectValuesUser'] = None, + specific_share_to_other_effects_operation: Optional['TemporalEffectsUser'] = None, + specific_share_to_other_effects_invest: Optional['ScalarEffectsUser'] = None, minimum_operation: Optional[Scalar] = None, maximum_operation: Optional[Scalar] = None, minimum_invest: Optional[Scalar] = None, maximum_invest: Optional[Scalar] = None, - minimum_operation_per_hour: Optional[NumericDataUser] = None, - maximum_operation_per_hour: Optional[NumericDataUser] = None, + minimum_operation_per_hour: Optional[TemporalDataUser] = None, + maximum_operation_per_hour: Optional[TemporalDataUser] = None, minimum_total: Optional[Scalar] = None, maximum_total: Optional[Scalar] = None, ): @@ -76,14 +76,14 @@ def __init__( self.description = description self.is_standard = is_standard self.is_objective = is_objective - self.specific_share_to_other_effects_operation: EffectValuesUser = ( + self.specific_share_to_other_effects_operation: TemporalEffectsUser = ( specific_share_to_other_effects_operation or {} ) - self.specific_share_to_other_effects_invest: EffectValuesUser = specific_share_to_other_effects_invest or {} + self.specific_share_to_other_effects_invest: ScalarEffectsUser = specific_share_to_other_effects_invest or {} self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation - self.minimum_operation_per_hour: NumericDataUser = minimum_operation_per_hour - self.maximum_operation_per_hour: NumericDataUser = maximum_operation_per_hour + self.minimum_operation_per_hour: TemporalDataUser = minimum_operation_per_hour + self.maximum_operation_per_hour: TemporalDataUser = maximum_operation_per_hour self.minimum_invest = minimum_invest self.maximum_invest = maximum_invest self.minimum_total = minimum_total @@ -168,13 +168,19 @@ def do_modeling(self): ) -EffectExpr = Dict[str, linopy.LinearExpression] # Used to create Shares -EffectValuesInternal = Dict[str, NumericDataInternal] # Used internally to index values -EffectValuesUser = Union[NumericDataUser, Dict[str, NumericDataUser]] # User-specified Shares to Effects -""" This datatype is used to define the share to an effect by a certain attribute. """ +TemporalEffectsUser = Union[TemporalDataUser, Dict[str, TemporalDataUser]] # User-specified Shares to Effects +""" This datatype is used to define a temporal share to an effect by a certain attribute. """ + +ScalarEffectsUser = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects +""" This datatype is used to define a scalar share to an effect by a certain attribute. """ + +TemporalEffects = Dict[str, TemporalData] # User-specified Shares to Effects +""" This datatype is used internally to handle temporal shares to an effect. """ -EffectValuesUserScalar = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects -""" This datatype is used to define the share to an effect by a certain attribute. Only scalars are allowed. """ +ScalarEffects = Dict[str, Scalar] +""" This datatype is used internally to handle scalar shares to an effect. """ + +EffectExpr = Dict[str, linopy.LinearExpression] # Used to create Shares class EffectCollection: @@ -206,7 +212,10 @@ def add_effects(self, *effects: Effect) -> None: self._effects[effect.label] = effect logger.info(f'Registered new Effect: {effect.label}') - def create_effect_values_dict(self, effect_values_user: EffectValuesUser) -> Optional[Dict[str, NumericDataUser]]: + def create_effect_values_dict( + self, + effect_values_user: Union[ScalarEffectsUser, TemporalEffectsUser] + ) -> Optional[Dict[str, Union[Scalar, TemporalDataUser]]]: """ Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type. diff --git a/flixopt/elements.py b/flixopt/elements.py index 061a00b65..d596333c3 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -10,8 +10,8 @@ import numpy as np from .config import CONFIG -from .core import NumericDataUser, PlausibilityError, Scalar -from .effects import EffectValuesUser +from .core import PlausibilityError, Scalar, TemporalData, TemporalDataUser +from .effects import TemporalEffectsUser from .features import InvestmentModel, OnOffModel, PreventSimultaneousUsageModel from .interface import InvestParameters, OnOffParameters from .structure import Element, ElementModel, SystemModel, register_class_for_io @@ -90,7 +90,7 @@ class Bus(Element): """ def __init__( - self, label: str, excess_penalty_per_flow_hour: Optional[NumericDataUser] = 1e5, meta_data: Optional[Dict] = None + self, label: str, excess_penalty_per_flow_hour: Optional[TemporalDataUser] = 1e5, meta_data: Optional[Dict] = None ): """ Args: @@ -149,16 +149,16 @@ def __init__( label: str, bus: str, size: Union[Scalar, InvestParameters] = None, - fixed_relative_profile: Optional[NumericDataUser] = None, - relative_minimum: NumericDataUser = 0, - relative_maximum: NumericDataUser = 1, - effects_per_flow_hour: Optional[EffectValuesUser] = None, + fixed_relative_profile: Optional[TemporalDataUser] = None, + relative_minimum: TemporalDataUser = 0, + relative_maximum: TemporalDataUser = 1, + effects_per_flow_hour: Optional[TemporalEffectsUser] = None, on_off_parameters: Optional[OnOffParameters] = None, flow_hours_total_max: Optional[Scalar] = None, flow_hours_total_min: Optional[Scalar] = None, load_factor_min: Optional[Scalar] = None, load_factor_max: Optional[Scalar] = None, - previous_flow_rate: Optional[NumericDataUser] = None, + previous_flow_rate: Optional[TemporalDataUser] = None, meta_data: Optional[Dict] = None, ): r""" @@ -411,7 +411,7 @@ def _create_bounds_for_load_factor(self): ) @property - def flow_rate_bounds_on(self) -> Tuple[NumericDataUser, NumericDataUser]: + def flow_rate_bounds_on(self) -> Tuple[TemporalData, TemporalData]: """Returns absolute flow rate bounds. Important for OnOffModel""" relative_minimum, relative_maximum = self.flow_rate_lower_bound_relative, self.flow_rate_upper_bound_relative size = self.element.size @@ -422,7 +422,7 @@ def flow_rate_bounds_on(self) -> Tuple[NumericDataUser, NumericDataUser]: return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size @property - def flow_rate_lower_bound_relative(self) -> NumericDataUser: + def flow_rate_lower_bound_relative(self) -> TemporalData: """Returns the lower bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: @@ -430,7 +430,7 @@ def flow_rate_lower_bound_relative(self) -> NumericDataUser: return fixed_profile @property - def flow_rate_upper_bound_relative(self) -> NumericDataUser: + def flow_rate_upper_bound_relative(self) -> TemporalData: """ Returns the upper bound of the flow_rate relative to its size""" fixed_profile = self.element.fixed_relative_profile if fixed_profile is None: @@ -438,7 +438,7 @@ def flow_rate_upper_bound_relative(self) -> NumericDataUser: return fixed_profile @property - def flow_rate_lower_bound(self) -> NumericDataUser: + def flow_rate_lower_bound(self) -> TemporalData: """ Returns the minimum bound the flow_rate can reach. Further constraining might be done in OnOffModel and InvestmentModel @@ -452,7 +452,7 @@ def flow_rate_lower_bound(self) -> NumericDataUser: return self.flow_rate_lower_bound_relative * self.element.size @property - def flow_rate_upper_bound(self) -> NumericDataUser: + def flow_rate_upper_bound(self) -> TemporalData: """ Returns the maximum bound the flow_rate can reach. Further constraining might be done in OnOffModel and InvestmentModel diff --git a/flixopt/features.py b/flixopt/features.py index 5bc8f7922..287f4e933 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -11,7 +11,7 @@ from . import utils from .config import CONFIG -from .core import NumericDataUser, Scalar +from .core import Scalar, TemporalData from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel @@ -27,7 +27,7 @@ def __init__( label_of_element: str, parameters: InvestParameters, defining_variable: [linopy.Variable], - relative_bounds_of_defining_variable: Tuple[NumericDataUser, NumericDataUser], + relative_bounds_of_defining_variable: Tuple[TemporalData, TemporalData], label: Optional[str] = None, on_variable: Optional[linopy.Variable] = None, ): @@ -203,12 +203,12 @@ def __init__( model: SystemModel, label_of_element: str, defining_variables: List[linopy.Variable], - defining_bounds: List[Tuple[NumericDataUser, NumericDataUser]], - previous_values: List[Optional[NumericDataUser]] = None, + defining_bounds: List[Tuple[TemporalData, TemporalData]], + previous_values: List[Optional[TemporalData]] = None, use_off: bool = True, - on_hours_total_min: Optional[NumericDataUser] = 0, - on_hours_total_max: Optional[NumericDataUser] = None, - effects_per_running_hour: Dict[str, NumericDataUser] = None, + on_hours_total_min: Optional[TemporalData] = 0, + on_hours_total_max: Optional[TemporalData] = None, + effects_per_running_hour: Dict[str, TemporalData] = None, label: Optional[str] = None, ): """ @@ -344,7 +344,7 @@ def previous_off_states(self): return 1 - self.previous_states @staticmethod - def compute_previous_states(previous_values: List[NumericDataUser], epsilon: float = 1e-5) -> np.ndarray: + def compute_previous_states(previous_values: List[TemporalData], epsilon: float = 1e-5) -> np.ndarray: """Computes the previous states {0, 1} of defining variables as a binary array from their previous values.""" if not previous_values or all([val is None for val in previous_values]): return np.array([0]) @@ -451,9 +451,9 @@ def __init__( model: SystemModel, label_of_element: str, state_variable: linopy.Variable, - minimum_duration: Optional[NumericDataUser] = None, - maximum_duration: Optional[NumericDataUser] = None, - previous_states: Optional[NumericDataUser] = None, + minimum_duration: Optional[TemporalData] = None, + maximum_duration: Optional[TemporalData] = None, + previous_states: Optional[TemporalData] = None, label: Optional[str] = None, ): """ @@ -570,7 +570,7 @@ def previous_duration(self) -> Scalar: @staticmethod def compute_consecutive_hours_in_state( - binary_values: NumericDataUser, hours_per_timestep: Union[int, float, np.ndarray] + binary_values: TemporalData, hours_per_timestep: Union[int, float, np.ndarray] ) -> Scalar: """ Computes the final consecutive duration in state 'on' (=1) in hours, from a binary array. @@ -629,8 +629,8 @@ def __init__( on_off_parameters: OnOffParameters, label_of_element: str, defining_variables: List[linopy.Variable], - defining_bounds: List[Tuple[NumericDataUser, NumericDataUser]], - previous_values: List[Optional[NumericDataUser]], + defining_bounds: List[Tuple[TemporalData, TemporalData]], + previous_values: List[Optional[TemporalData]], label: Optional[str] = None, ): """ @@ -918,8 +918,8 @@ def __init__( label_full: Optional[str] = None, total_max: Optional[Scalar] = None, total_min: Optional[Scalar] = None, - max_per_hour: Optional[NumericDataUser] = None, - min_per_hour: Optional[NumericDataUser] = None, + max_per_hour: Optional[TemporalData] = None, + min_per_hour: Optional[TemporalData] = None, ): super().__init__(model, label_of_element=label_of_element, label=label, label_full=label_full) if not shares_are_time_series: # If the condition is True diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 4ad935dc5..9c181c8d3 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,8 +16,8 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import ConversionError, DataConverter, NumericDataInternal, NumericDataUser, TimeSeriesData -from .effects import Effect, EffectCollection, EffectValuesInternal, EffectValuesUser +from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData +from .effects import Effect, EffectCollection, ScalarEffects, ScalarEffectsUser, TemporalEffects, TemporalEffectsUser from .elements import Bus, Component, Flow from .structure import Element, Interface, SystemModel @@ -271,8 +271,8 @@ def to_json(self, path: Union[str, pathlib.Path]): def fit_to_model_coords( self, name: str, - data: Optional[NumericDataUser], - ) -> Optional[NumericDataInternal]: + data: Optional[TemporalDataUser], + ) -> Optional[TemporalData]: """ Fit data to model coordinate system (currently time, but extensible). @@ -301,9 +301,9 @@ def fit_to_model_coords( def fit_effects_to_model_coords( self, label_prefix: Optional[str], - effect_values: Optional[EffectValuesUser], + effect_values: Optional[TemporalEffectsUser], label_suffix: Optional[str] = None, - ) -> Optional[EffectValuesInternal]: + ) -> Optional[TemporalEffects]: """ Transform EffectValues from the user to Internal Datatypes aligned with model coordinates. """ @@ -530,7 +530,7 @@ def all_elements(self) -> Dict[str, Element]: def used_in_calculation(self) -> bool: return self._used_in_calculation - def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp]] = None) -> 'FlowSystem': + def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp, pd.DatetimeIndex]] = None) -> 'FlowSystem': """ Select a subset of the flowsystem by the time coordinate. diff --git a/flixopt/interface.py b/flixopt/interface.py index e5ee962ed..ad331b904 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Union from .config import CONFIG -from .core import NumericDataUser, Scalar +from .core import Scalar, TemporalDataUser from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports @@ -20,7 +20,7 @@ @register_class_for_io class Piece(Interface): - def __init__(self, start: NumericDataUser, end: NumericDataUser): + def __init__(self, start: TemporalDataUser, end: TemporalDataUser): """ Define a Piece, which is part of a Piecewise object. @@ -175,10 +175,10 @@ def __init__( effects_per_running_hour: Optional['EffectValuesUser'] = None, on_hours_total_min: Optional[int] = None, on_hours_total_max: Optional[int] = None, - consecutive_on_hours_min: Optional[NumericDataUser] = None, - consecutive_on_hours_max: Optional[NumericDataUser] = None, - consecutive_off_hours_min: Optional[NumericDataUser] = None, - consecutive_off_hours_max: Optional[NumericDataUser] = None, + consecutive_on_hours_min: Optional[TemporalDataUser] = None, + consecutive_on_hours_max: Optional[TemporalDataUser] = None, + consecutive_off_hours_min: Optional[TemporalDataUser] = None, + consecutive_off_hours_max: Optional[TemporalDataUser] = None, switch_on_total_max: Optional[int] = None, force_switch_on: bool = False, ): @@ -206,10 +206,10 @@ def __init__( self.effects_per_running_hour: EffectValuesUser = effects_per_running_hour or {} self.on_hours_total_min: Scalar = on_hours_total_min self.on_hours_total_max: Scalar = on_hours_total_max - self.consecutive_on_hours_min: NumericDataUser = consecutive_on_hours_min - self.consecutive_on_hours_max: NumericDataUser = consecutive_on_hours_max - self.consecutive_off_hours_min: NumericDataUser = consecutive_off_hours_min - self.consecutive_off_hours_max: NumericDataUser = consecutive_off_hours_max + self.consecutive_on_hours_min: TemporalDataUser = consecutive_on_hours_min + self.consecutive_on_hours_max: TemporalDataUser = consecutive_on_hours_max + self.consecutive_off_hours_min: TemporalDataUser = consecutive_off_hours_min + self.consecutive_off_hours_max: TemporalDataUser = consecutive_off_hours_max self.switch_on_total_max: Scalar = switch_on_total_max self.force_switch_on: bool = force_switch_on diff --git a/flixopt/linear_converters.py b/flixopt/linear_converters.py index 94463c492..b137ad89a 100644 --- a/flixopt/linear_converters.py +++ b/flixopt/linear_converters.py @@ -8,7 +8,7 @@ import numpy as np from .components import LinearConverter -from .core import NumericDataUser, TimeSeriesData +from .core import TemporalDataUser, TimeSeriesData from .elements import Flow from .interface import OnOffParameters from .structure import register_class_for_io @@ -21,7 +21,7 @@ class Boiler(LinearConverter): def __init__( self, label: str, - eta: NumericDataUser, + eta: TemporalDataUser, Q_fu: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -62,7 +62,7 @@ class Power2Heat(LinearConverter): def __init__( self, label: str, - eta: NumericDataUser, + eta: TemporalDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -104,7 +104,7 @@ class HeatPump(LinearConverter): def __init__( self, label: str, - COP: NumericDataUser, + COP: TemporalDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -146,7 +146,7 @@ class CoolingTower(LinearConverter): def __init__( self, label: str, - specific_electricity_demand: NumericDataUser, + specific_electricity_demand: TemporalDataUser, P_el: Flow, Q_th: Flow, on_off_parameters: OnOffParameters = None, @@ -190,8 +190,8 @@ class CHP(LinearConverter): def __init__( self, label: str, - eta_th: NumericDataUser, - eta_el: NumericDataUser, + eta_th: TemporalDataUser, + eta_el: TemporalDataUser, Q_fu: Flow, P_el: Flow, Q_th: Flow, @@ -251,7 +251,7 @@ class HeatPumpWithSource(LinearConverter): def __init__( self, label: str, - COP: NumericDataUser, + COP: TemporalDataUser, P_el: Flow, Q_ab: Flow, Q_th: Flow, @@ -297,11 +297,11 @@ def COP(self, value): # noqa: N802 def check_bounds( - value: NumericDataUser, + value: TemporalDataUser, parameter_label: str, element_label: str, - lower_bound: NumericDataUser, - upper_bound: NumericDataUser, + lower_bound: TemporalDataUser, + upper_bound: TemporalDataUser, ) -> None: """ Check if the value is within the bounds. The bounds are exclusive. diff --git a/flixopt/structure.py b/flixopt/structure.py index 3fb0be066..cc307a1e8 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -20,7 +20,7 @@ from . import io as fx_io from .config import CONFIG -from .core import NumericDataUser, Scalar, TimeSeriesData, get_dataarray_stats +from .core import Scalar, TemporalDataUser, TimeSeriesData, get_dataarray_stats if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel From 36cf47d5485c4921d5e9209e56312482aadd50ca Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:27:42 +0200 Subject: [PATCH 107/183] CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb95b3756..1871de91a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed * **BREAKING**: FlowSystems can not be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent -* **BREAKING**: Type system overhaul - replaced `NumericDataTS` with `NumericDataUser` throughout codebase for better clarity +* **BREAKING**: Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity * **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. The final charge state can now be constrained by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead * FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties * Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods From 8f1261db381706ce9270fdf571e7a92fdee39a8a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:36:09 +0200 Subject: [PATCH 108/183] Bugfix in Storage --- flixopt/components.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 49d6f5b31..639046cfc 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -584,11 +584,13 @@ def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: Tuple of (minimum_bounds, maximum_bounds) DataArrays extending to final timestep """ final_timestep = self._model.flow_system.timesteps_extra[-1] - final_coords = {'time': final_timestep} + final_coords = {'time': [final_timestep]} # Get final minimum charge state if self.element.relative_minimum_final_charge_state is None: - min_final = self.element.relative_minimum_charge_state.isel(time=-1).assign_coords(time=final_timestep) + min_final = self.element.relative_minimum_charge_state.isel( + time=-1, drop=True + ).assign_coords(time=final_timestep) else: min_final = xr.DataArray( [self.element.relative_minimum_final_charge_state], coords=final_coords, dims=['time'] @@ -596,7 +598,9 @@ def relative_charge_state_bounds(self) -> Tuple[xr.DataArray, xr.DataArray]: # Get final maximum charge state if self.element.relative_maximum_final_charge_state is None: - max_final = self.element.relative_maximum_charge_state.isel(time=-1).assign_coords(time=final_timestep) + max_final = self.element.relative_maximum_charge_state.isel( + time=-1, drop=True + ).assign_coords(time=final_timestep) else: max_final = xr.DataArray( [self.element.relative_maximum_final_charge_state], coords=final_coords, dims=['time'] From 89d69f0280e78220cbf7833dc177ddcfef04bad5 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:38:29 +0200 Subject: [PATCH 109/183] Revert changes in example_calculation_types.py --- examples/03_Calculation_types/example_calculation_types.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/03_Calculation_types/example_calculation_types.py b/examples/03_Calculation_types/example_calculation_types.py index b793e26ce..ee61d6628 100644 --- a/examples/03_Calculation_types/example_calculation_types.py +++ b/examples/03_Calculation_types/example_calculation_types.py @@ -164,12 +164,12 @@ if full: calculation = fx.FullCalculation('Full', flow_system) calculation.do_modeling() - calculation.solve(fx.solvers.GurobiSolver(0.001, 60)) + calculation.solve(fx.solvers.HighsSolver(0.01/100, 60)) calculations.append(calculation) if segmented: calculation = fx.SegmentedCalculation('Segmented', flow_system, segment_length, overlap_length) - calculation.do_modeling_and_solve(fx.solvers.GurobiSolver(0.001, 60)) + calculation.do_modeling_and_solve(fx.solvers.HighsSolver(0.01/100, 60)) calculations.append(calculation) if aggregated: @@ -178,7 +178,7 @@ aggregation_parameters.time_series_for_low_peaks = [TS_electricity_demand, TS_heat_demand] calculation = fx.AggregatedCalculation('Aggregated', flow_system, aggregation_parameters) calculation.do_modeling() - calculation.solve(fx.solvers.GurobiSolver(0.001, 60)) + calculation.solve(fx.solvers.HighsSolver(0.01/100, 60)) calculations.append(calculation) # Get solutions for plotting for different calculations From 76f51a890f19c9d7de072e34bcc0a0736a468e1d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:39:22 +0200 Subject: [PATCH 110/183] Revert changes in simple_example.py --- examples/01_Simple/simple_example.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/01_Simple/simple_example.py b/examples/01_Simple/simple_example.py index da10aed62..45550c9cc 100644 --- a/examples/01_Simple/simple_example.py +++ b/examples/01_Simple/simple_example.py @@ -103,14 +103,9 @@ calculation = fx.FullCalculation(name='Sim1', flow_system=flow_system) calculation.do_modeling() # Translate the model to a solvable form, creating equations and Variables - calculation2 = fx.FullCalculation(name='Sim2', flow_system=flow_system) - calculation2.do_modeling() # Translate the model to a solvable form, creating equations and Variables - # --- Solve the Calculation and Save Results --- calculation.solve(fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=30)) - calculation2.solve(fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=30)) - # --- Analyze Results --- calculation.results['Fernwärme'].plot_node_balance_pie() calculation.results['Fernwärme'].plot_node_balance() From 0ff4d29fd2ac41cdddc7caabd28fe315eb85fa82 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 26 Jun 2025 14:47:34 +0200 Subject: [PATCH 111/183] Add convenient access to Elements in FlowSystem --- flixopt/flow_system.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 9c181c8d3..49321ba82 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -517,6 +517,30 @@ def __eq__(self, other: 'FlowSystem'): return True + def __getitem__(self, item) -> Element: + """Get element by exact label with helpful error messages.""" + if item in self.all_elements: + return self.all_elements[item] + + # Provide helpful error with suggestions + from difflib import get_close_matches + + suggestions = get_close_matches(item, self.all_elements.keys(), n=3, cutoff=0.6) + + if suggestions: + suggestion_str = ', '.join(f"'{s}'" for s in suggestions) + raise KeyError(f"Element '{item}' not found. Did you mean: {suggestion_str}?") + else: + raise KeyError(f"Element '{item}' not found in FlowSystem") + + def __contains__(self, item: str) -> bool: + """Check if element exists in the FlowSystem.""" + return item in self.all_elements + + def __iter__(self): + """Iterate over element labels.""" + return iter(self.all_elements.keys()) + @property def flows(self) -> Dict[str, Flow]: set_of_flows = {flow for comp in self.components.values() for flow in comp.inputs + comp.outputs} From 84c850b5f0f0b9d849becde622b5ba10507d5961 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:23:37 +0200 Subject: [PATCH 112/183] Get Aggregated Calculation Working --- .../example_calculation_types.py | 6 +- flixopt/aggregation.py | 4 +- flixopt/calculation.py | 50 +++++++++++++--- flixopt/core.py | 58 ++++++++++++------- flixopt/flow_system.py | 3 +- tests/conftest.py | 6 +- 6 files changed, 89 insertions(+), 38 deletions(-) diff --git a/examples/03_Calculation_types/example_calculation_types.py b/examples/03_Calculation_types/example_calculation_types.py index ee61d6628..cac628042 100644 --- a/examples/03_Calculation_types/example_calculation_types.py +++ b/examples/03_Calculation_types/example_calculation_types.py @@ -48,9 +48,9 @@ # TimeSeriesData objects TS_heat_demand = fx.TimeSeriesData(heat_demand) - TS_electricity_demand = fx.TimeSeriesData(electricity_demand, agg_weight=0.7) - TS_electricity_price_sell = fx.TimeSeriesData(-(electricity_demand - 0.5), agg_group='p_el') - TS_electricity_price_buy = fx.TimeSeriesData(electricity_price + 0.5, agg_group='p_el') + TS_electricity_demand = fx.TimeSeriesData(electricity_demand, aggregation_weight=0.7) + TS_electricity_price_sell = fx.TimeSeriesData(-(electricity_demand - 0.5), aggregation_group='p_el') + TS_electricity_price_buy = fx.TimeSeriesData(electricity_price + 0.5, aggregation_group='p_el') flow_system = fx.FlowSystem(timesteps) flow_system.add_elements( diff --git a/flixopt/aggregation.py b/flixopt/aggregation.py index f149d5f20..d47a42997 100644 --- a/flixopt/aggregation.py +++ b/flixopt/aggregation.py @@ -274,11 +274,11 @@ def use_extreme_periods(self): @property def labels_for_high_peaks(self) -> List[str]: - return [ts.label for ts in self.time_series_for_high_peaks] + return [ts.name for ts in self.time_series_for_high_peaks] @property def labels_for_low_peaks(self) -> List[str]: - return [ts.label for ts in self.time_series_for_low_peaks] + return [ts.name for ts in self.time_series_for_low_peaks] @property def use_low_peaks(self): diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 60163b7a2..43884632f 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -14,17 +14,19 @@ import timeit import warnings from typing import Annotated, Any, Dict, List, Optional, Union +from collections import Counter import numpy as np import pandas as pd import yaml +import xarray as xr from . import io as fx_io from . import utils as utils from .aggregation import AggregationModel, AggregationParameters from .components import Storage from .config import CONFIG -from .core import Scalar +from .core import Scalar, DataConverter, drop_constant_arrays, TimeSeriesData from .elements import Component from .features import InvestmentModel from .flow_system import FlowSystem @@ -294,15 +296,17 @@ def _perform_aggregation(self): logger.info(f'{"":#^80}') logger.info(f'{" Aggregating TimeSeries Data ":#^80}') + ds = self.flow_system.to_dataset() + + temporaly_changing_ds = drop_constant_arrays(ds, dim='time') + # Aggregation - creation of aggregated timeseries: self.aggregation = Aggregation( - original_data=self.flow_system.to_dataframe( - include_extra_timestep=False - ), # Exclude last row (NaN) + original_data=temporaly_changing_ds.to_dataframe(), hours_per_time_step=float(dt_min), hours_per_period=self.aggregation_parameters.hours_per_period, nr_of_periods=self.aggregation_parameters.nr_of_periods, - weights=self.flow_system.calculate_aggregation_weights(), + weights=self.calculate_aggregation_weights(temporaly_changing_ds), time_series_for_high_peaks=self.aggregation_parameters.labels_for_high_peaks, time_series_for_low_peaks=self.aggregation_parameters.labels_for_low_peaks, ) @@ -310,11 +314,41 @@ def _perform_aggregation(self): self.aggregation.cluster() self.aggregation.plot(show=True, save=self.folder / 'aggregation.html') if self.aggregation_parameters.aggregate_data_and_fix_non_binary_vars: - self.flow_system.insert_new_data( - self.aggregation.aggregated_data, include_extra_timestep=False - ) + ds = self.flow_system.to_dataset() + for name, series in self.aggregation.aggregated_data.items(): + da = DataConverter.to_dataarray(series, timesteps=self.flow_system.timesteps).rename(name).assign_attrs(ds[name].attrs) + if TimeSeriesData.is_timeseries_data(da): + da = TimeSeriesData.from_dataarray(da) + + ds[name] = da + + self.flow_system = FlowSystem.from_dataset(ds) + self.flow_system.connect_and_transform() self.durations['aggregation'] = round(timeit.default_timer() - t_start_agg, 2) + @classmethod + def calculate_aggregation_weights(cls, ds: xr.Dataset) -> Dict[str, float]: + """Calculate weights for all datavars in the dataset. Weights are pulled from the attrs of the datavars.""" + + groups = [da.attrs['aggregation_group'] for da in ds.values() if 'aggregation_group' in da.attrs] + group_counts = Counter(groups) + + # Calculate weight for each group (1/count) + group_weights = {group: 1 / count for group, count in group_counts.items()} + + weights = {} + for name, da in ds.data_vars.items(): + group_weight = group_weights.get(da.attrs.get('aggregation_group')) + if group_weight is not None: + weights[name] = group_weight + else: + weights[name] = da.attrs.get('aggregation_weight', 1) + + if np.all(np.isclose(list(weights.values()), 1, atol=1e-6)): + logger.info('All Aggregation weights were set to 1') + + return weights + class SegmentedCalculation(Calculation): def __init__( diff --git a/flixopt/core.py b/flixopt/core.py index 41ee7b799..5bba418be 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -41,45 +41,45 @@ class TimeSeriesData(xr.DataArray): __slots__ = () # No additional instance attributes - everything goes in attrs - def __init__(self, *args, agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): + def __init__(self, *args, aggregation_group: Optional[str] = None, aggregation_weight: Optional[float] = None, **kwargs): """ Args: *args: Arguments passed to DataArray - agg_group: Aggregation group name - agg_weight: Aggregation weight (0-1) + aggregation_group: Aggregation group name + aggregation_weight: Aggregation weight (0-1) **kwargs: Additional arguments passed to DataArray """ - if (agg_group is not None) and (agg_weight is not None): - raise ValueError('Use either agg_group or agg_weight, not both') + if (aggregation_group is not None) and (aggregation_weight is not None): + raise ValueError('Use either aggregation_group or aggregation_weight, not both') # Let xarray handle all the initialization complexity super().__init__(*args, **kwargs) # Add our metadata to attrs after initialization - if agg_group is not None: - self.attrs['agg_group'] = agg_group - if agg_weight is not None: - self.attrs['agg_weight'] = agg_weight + if aggregation_group is not None: + self.attrs['aggregation_group'] = aggregation_group + if aggregation_weight is not None: + self.attrs['aggregation_weight'] = aggregation_weight # Always mark as TimeSeriesData self.attrs['__timeseries_data__'] = True @property - def agg_group(self) -> Optional[str]: - return self.attrs.get('agg_group') + def aggregation_group(self) -> Optional[str]: + return self.attrs.get('aggregation_group') @property - def agg_weight(self) -> Optional[float]: - return self.attrs.get('agg_weight') + def aggregation_weight(self) -> Optional[float]: + return self.attrs.get('aggregation_weight') @classmethod - def from_dataarray(cls, da: xr.DataArray, agg_group: Optional[str] = None, agg_weight: Optional[float] = None): + def from_dataarray(cls, da: xr.DataArray, aggregation_group: Optional[str] = None, aggregation_weight: Optional[float] = None): """Create TimeSeriesData from DataArray, extracting metadata from attrs.""" # Get aggregation metadata from attrs or parameters - final_agg_group = agg_group if agg_group is not None else da.attrs.get('agg_group') - final_agg_weight = agg_weight if agg_weight is not None else da.attrs.get('agg_weight') + final_aggregation_group = aggregation_group if aggregation_group is not None else da.attrs.get('aggregation_group') + final_aggregation_weight = aggregation_weight if aggregation_weight is not None else da.attrs.get('aggregation_weight') - return cls(da, agg_group=final_agg_group, agg_weight=final_agg_weight) + return cls(da, aggregation_group=final_aggregation_group, aggregation_weight=final_aggregation_weight) @classmethod def is_timeseries_data(cls, obj) -> bool: @@ -88,10 +88,10 @@ def is_timeseries_data(cls, obj) -> bool: def __repr__(self): agg_info = [] - if self.agg_group: - agg_info.append(f"agg_group='{self.agg_group}'") - if self.agg_weight is not None: - agg_info.append(f'agg_weight={self.agg_weight}') + if self.aggregation_group: + agg_info.append(f"aggregation_group='{self.aggregation_group}'") + if self.aggregation_weight is not None: + agg_info.append(f'aggregation_weight={self.aggregation_weight}') info_str = f'TimeSeriesData({", ".join(agg_info)})' if agg_info else 'TimeSeriesData' return f'{info_str}\n{super().__repr__()}' @@ -255,3 +255,19 @@ def get_dataarray_stats(arr: xr.DataArray) -> Dict: pass return stats + + +def drop_constant_arrays(ds: xr.Dataset, dim='time', drop_arrays_without_dim: bool = True): + """Drop variables with very low variance (near-constant).""" + drop_vars = [] + + for name, da in ds.data_vars.items(): + if dim in da.dims: + if da.max(dim) == da.min(dim): + drop_vars.append(name) + continue + elif drop_arrays_without_dim: + drop_vars.append(name) + + logger.debug(f'Dropping {len(drop_vars)} arrays with constant values') + return ds.drop_vars(drop_vars) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 49321ba82..560d740bd 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -288,9 +288,10 @@ def fit_to_model_coords( if isinstance(data, TimeSeriesData): try: + data.name = name # Set name of previous object! return TimeSeriesData( DataConverter.to_dataarray(data, timesteps=self.timesteps), - agg_group=data.agg_group, agg_weight=data.agg_weight + aggregation_group=data.aggregation_group, aggregation_weight=data.aggregation_weight ).rename(name) except ConversionError as e: logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' diff --git a/tests/conftest.py b/tests/conftest.py index b705939cc..074c56efe 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -327,11 +327,11 @@ def flow_system_long(): thermal_load_ts, electrical_load_ts = ( fx.TimeSeriesData(thermal_load), - fx.TimeSeriesData(electrical_load, agg_weight=0.7), + fx.TimeSeriesData(electrical_load, aggregation_weight=0.7), ) p_feed_in, p_sell = ( - fx.TimeSeriesData(-(p_el - 0.5), agg_group='p_el'), - fx.TimeSeriesData(p_el + 0.5, agg_group='p_el'), + fx.TimeSeriesData(-(p_el - 0.5), aggregation_group='p_el'), + fx.TimeSeriesData(p_el + 0.5, aggregation_group='p_el'), ) flow_system = fx.FlowSystem(pd.DatetimeIndex(data.index)) From 8b9dabb7f917b8e06f2d30deffe740ab16df32de Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:46:23 +0200 Subject: [PATCH 113/183] Segmented running with wrong results --- flixopt/calculation.py | 47 +++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 43884632f..5a1437ba9 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -385,8 +385,6 @@ def __init__( self.nr_of_previous_values = nr_of_previous_values self.sub_calculations: List[FullCalculation] = [] - self.all_timesteps = self.flow_system.all_timesteps - self.all_timesteps_extra = self.flow_system.all_timesteps_extra self.segment_names = [ f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment)) @@ -419,22 +417,22 @@ def do_modeling_and_solve( for i, (segment_name, timesteps_of_segment) in enumerate( zip(self.segment_names, self.active_timesteps_per_segment, strict=False) ): - if self.sub_calculations: - self._transfer_start_values(i) + calculation = FullCalculation( + f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), + ) + self.sub_calculations.append(calculation) logger.info( f'{segment_name} [{i + 1:>2}/{len(self.segment_names):<2}] ' f'({timesteps_of_segment[0]} -> {timesteps_of_segment[-1]}):' ) + if len(self.sub_calculations) >= 2: + self._transfer_start_values(i) - calculation = FullCalculation( - f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), - ) - self.sub_calculations.append(calculation) calculation.do_modeling() invest_elements = [ model.label_full - for component in self.flow_system.components.values() + for component in calculation.flow_system.components.values() for model in component.model.all_sub_models if isinstance(model, InvestmentModel) ] @@ -449,8 +447,6 @@ def do_modeling_and_solve( log_main_results=log_main_results, ) - self._reset_start_values() - for calc in self.sub_calculations: for key, value in calc.durations.items(): self.durations[key] += value @@ -471,27 +467,22 @@ def _transfer_start_values(self, segment_index: int): logger.debug( f'start of next segment: {start}. indices of previous values: {start_previous_values}:{end_previous_values}' ) + current_flow_system = self.sub_calculations[segment_index -1].flow_system + next_flow_system = self.sub_calculations[segment_index].flow_system + start_values_of_this_segment = {} - for flow in self.flow_system.flows.values(): - flow.previous_flow_rate = flow.model.flow_rate.solution.sel( + for current_flow, next_flow in zip(current_flow_system.flows.values(), next_flow_system.flows.values()): + next_flow.previous_flow_rate = current_flow.model.flow_rate.solution.sel( time=slice(start_previous_values, end_previous_values) ).values - start_values_of_this_segment[flow.label_full] = flow.previous_flow_rate - for comp in self.flow_system.components.values(): - if isinstance(comp, Storage): - comp.initial_charge_state = comp.model.charge_state.solution.sel(time=start).item() - start_values_of_this_segment[comp.label_full] = comp.initial_charge_state + start_values_of_this_segment[current_flow.label_full] = next_flow.previous_flow_rate + for current_comp, next_comp in zip(current_flow_system.components.values(), next_flow_system.components.values()): + if isinstance(next_comp, Storage): + next_comp.initial_charge_state = current_comp.model.charge_state.solution.sel(time=start).item() + start_values_of_this_segment[current_comp.label_full] = next_comp.initial_charge_state self._transfered_start_values.append(start_values_of_this_segment) - def _reset_start_values(self): - """This resets the start values of all Elements to its original state""" - for flow in self.flow_system.flows.values(): - flow.previous_flow_rate = self._original_start_values[flow.label_full] - for comp in self.flow_system.components.values(): - if isinstance(comp, Storage): - comp.initial_charge_state = self._original_start_values[comp.label_full] - def _calculate_timesteps_of_segment(self) -> List[pd.DatetimeIndex]: active_timesteps_per_segment = [] for i, _ in enumerate(self.segment_names): @@ -511,3 +502,7 @@ def start_values_of_segments(self) -> Dict[int, Dict[str, Any]]: 0: {element.label_full: value for element, value in self._original_start_values.items()}, **{i: start_values for i, start_values in enumerate(self._transfered_start_values, 1)}, } + + @property + def all_timesteps(self) -> pd.DatetimeIndex: + return self.flow_system.timesteps \ No newline at end of file From 7e72ab56d6f66b422212bb4c9468af52dcb86e85 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 09:58:23 +0200 Subject: [PATCH 114/183] Use new persistent FLowSystem to create Calculations upfront --- flixopt/calculation.py | 59 ++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 5a1437ba9..fb5686e15 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -396,7 +396,7 @@ def __init__( f'{self.timesteps_per_segment_with_overlap=} cant be greater than the total length {len(self.all_timesteps)}' ) - self.flow_system._connect_network() # Connect network to ensure that all FLows know their Component + self.flow_system._connect_network() # Connect network to ensure that all Flows know their Component # Storing all original start values self._original_start_values = { **{flow.label_full: flow.previous_flow_rate for flow in self.flow_system.flows.values()}, @@ -408,39 +408,52 @@ def __init__( } self._transfered_start_values: List[Dict[str, Any]] = [] - def do_modeling_and_solve( - self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = False - ): - logger.info(f'{"":#^80}') - logger.info(f'{" Segmented Solving ":#^80}') - + def _create_sub_calculations(self): for i, (segment_name, timesteps_of_segment) in enumerate( zip(self.segment_names, self.active_timesteps_per_segment, strict=False) ): - calculation = FullCalculation( - f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), + self.sub_calculations.append( + FullCalculation( + f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), + folder=self.folder / segment_name + ) ) - self.sub_calculations.append(calculation) - logger.info( f'{segment_name} [{i + 1:>2}/{len(self.segment_names):<2}] ' f'({timesteps_of_segment[0]} -> {timesteps_of_segment[-1]}):' ) + + def do_modeling_and_solve( + self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = False + ): + logger.info(f'{"":#^80}') + logger.info(f'{" Segmented Solving ":#^80}') + + for i, calculation in enumerate(self.sub_calculations): + logger.info( + f'{self.segment_names[i]} [{i + 1:>2}/{len(self.segment_names):<2}] ' + f'({calculation.flow_system.timesteps[0]} -> {calculation.flow_system.timesteps[-1]}):' + ) + if len(self.sub_calculations) >= 2: self._transfer_start_values(i) calculation.do_modeling() - invest_elements = [ - model.label_full - for component in calculation.flow_system.components.values() - for model in component.model.all_sub_models - if isinstance(model, InvestmentModel) - ] - if invest_elements: - logger.critical( - f'Investments are not supported in Segmented Calculation! ' - f'Following InvestmentModels were found: {invest_elements}' - ) + + # Warn about Investments, but only in fist run + if i == 0: + invest_elements = [ + model.label_full + for component in calculation.flow_system.components.values() + for model in component.model.all_sub_models + if isinstance(model, InvestmentModel) + ] + if invest_elements: + logger.critical( + f'Investments are not supported in Segmented Calculation! ' + f'Following InvestmentModels were found: {invest_elements}' + ) + calculation.solve( solver, log_file=pathlib.Path(log_file) if log_file is not None else self.folder / f'{self.name}.log', @@ -458,7 +471,7 @@ def _transfer_start_values(self, segment_index: int): This function gets the last values of the previous solved segment and inserts them as start values for the next segment """ - timesteps_of_prior_segment = self.active_timesteps_per_segment[segment_index - 1] + timesteps_of_prior_segment = self.sub_calculations[segment_index - 1].flow_system.timesteps_extra start = self.active_timesteps_per_segment[segment_index][0] start_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - self.nr_of_previous_values] From 17632f36895e82f471b4f1562e543431838b6006 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 10:22:52 +0200 Subject: [PATCH 115/183] Improve SegmentedCalcualtion --- flixopt/calculation.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index fb5686e15..b0f71a40e 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -389,7 +389,7 @@ def __init__( self.segment_names = [ f'Segment_{i + 1}' for i in range(math.ceil(len(self.all_timesteps) / self.timesteps_per_segment)) ] - self.active_timesteps_per_segment = self._calculate_timesteps_of_segment() + self._timesteps_per_segment = self._calculate_timesteps_per_segment() assert timesteps_per_segment > 2, 'The Segment length must be greater 2, due to unwanted internal side effects' assert self.timesteps_per_segment_with_overlap <= len(self.all_timesteps), ( @@ -410,12 +410,11 @@ def __init__( def _create_sub_calculations(self): for i, (segment_name, timesteps_of_segment) in enumerate( - zip(self.segment_names, self.active_timesteps_per_segment, strict=False) + zip(self.segment_names, self._timesteps_per_segment, strict=False) ): self.sub_calculations.append( FullCalculation( f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), - folder=self.folder / segment_name ) ) logger.info( @@ -428,6 +427,7 @@ def do_modeling_and_solve( ): logger.info(f'{"":#^80}') logger.info(f'{" Segmented Solving ":#^80}') + self._create_sub_calculations() for i, calculation in enumerate(self.sub_calculations): logger.info( @@ -435,7 +435,7 @@ def do_modeling_and_solve( f'({calculation.flow_system.timesteps[0]} -> {calculation.flow_system.timesteps[-1]}):' ) - if len(self.sub_calculations) >= 2: + if i > 0: self._transfer_start_values(i) calculation.do_modeling() @@ -466,22 +466,22 @@ def do_modeling_and_solve( self.results = SegmentedCalculationResults.from_calculation(self) - def _transfer_start_values(self, segment_index: int): + def _transfer_start_values(self, i: int): """ This function gets the last values of the previous solved segment and inserts them as start values for the next segment """ - timesteps_of_prior_segment = self.sub_calculations[segment_index - 1].flow_system.timesteps_extra + timesteps_of_prior_segment = self.sub_calculations[i - 1].flow_system.timesteps_extra - start = self.active_timesteps_per_segment[segment_index][0] + start = self.sub_calculations[i].flow_system.timesteps[0] start_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - self.nr_of_previous_values] end_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - 1] logger.debug( f'start of next segment: {start}. indices of previous values: {start_previous_values}:{end_previous_values}' ) - current_flow_system = self.sub_calculations[segment_index -1].flow_system - next_flow_system = self.sub_calculations[segment_index].flow_system + current_flow_system = self.sub_calculations[i -1].flow_system + next_flow_system = self.sub_calculations[i].flow_system start_values_of_this_segment = {} for current_flow, next_flow in zip(current_flow_system.flows.values(), next_flow_system.flows.values()): @@ -496,25 +496,24 @@ def _transfer_start_values(self, segment_index: int): self._transfered_start_values.append(start_values_of_this_segment) - def _calculate_timesteps_of_segment(self) -> List[pd.DatetimeIndex]: - active_timesteps_per_segment = [] + def _calculate_timesteps_per_segment(self) -> List[pd.DatetimeIndex]: + timesteps_per_segment = [] for i, _ in enumerate(self.segment_names): start = self.timesteps_per_segment * i end = min(start + self.timesteps_per_segment_with_overlap, len(self.all_timesteps)) - active_timesteps_per_segment.append(self.all_timesteps[start:end]) - return active_timesteps_per_segment + timesteps_per_segment.append(self.all_timesteps[start:end]) + return timesteps_per_segment @property def timesteps_per_segment_with_overlap(self): return self.timesteps_per_segment + self.overlap_timesteps @property - def start_values_of_segments(self) -> Dict[int, Dict[str, Any]]: + def start_values_of_segments(self) -> List[Dict[str, Any]]: """Gives an overview of the start values of all Segments""" - return { - 0: {element.label_full: value for element, value in self._original_start_values.items()}, - **{i: start_values for i, start_values in enumerate(self._transfered_start_values, 1)}, - } + return [ + {name: value for name, value in self._original_start_values.items()} + ] + [start_values for start_values in self._transfered_start_values] @property def all_timesteps(self) -> pd.DatetimeIndex: From 3c355c9c70a2e67ff7c1208f6854c75dddf7d23f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:32:47 +0200 Subject: [PATCH 116/183] Improve SegmentedCalcualtion --- flixopt/calculation.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index b0f71a40e..8e4a57100 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -410,13 +410,12 @@ def __init__( def _create_sub_calculations(self): for i, (segment_name, timesteps_of_segment) in enumerate( - zip(self.segment_names, self._timesteps_per_segment, strict=False) + zip(self.segment_names, self._timesteps_per_segment, strict=True) ): - self.sub_calculations.append( - FullCalculation( - f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment), - ) - ) + calc = FullCalculation(f'{self.name}-{segment_name}', self.flow_system.sel(timesteps_of_segment)) + calc.flow_system._connect_network() # Connect to have Correct names of Flows! + + self.sub_calculations.append(calc) logger.info( f'{segment_name} [{i + 1:>2}/{len(self.segment_names):<2}] ' f'({timesteps_of_segment[0]} -> {timesteps_of_segment[-1]}):' @@ -435,7 +434,7 @@ def do_modeling_and_solve( f'({calculation.flow_system.timesteps[0]} -> {calculation.flow_system.timesteps[-1]}):' ) - if i > 0: + if i > 0 and self.nr_of_previous_values > 0: self._transfer_start_values(i) calculation.do_modeling() @@ -478,18 +477,22 @@ def _transfer_start_values(self, i: int): end_previous_values = timesteps_of_prior_segment[self.timesteps_per_segment - 1] logger.debug( - f'start of next segment: {start}. indices of previous values: {start_previous_values}:{end_previous_values}' + f'Start of next segment: {start}. Indices of previous values: {start_previous_values} -> {end_previous_values}' ) current_flow_system = self.sub_calculations[i -1].flow_system next_flow_system = self.sub_calculations[i].flow_system start_values_of_this_segment = {} - for current_flow, next_flow in zip(current_flow_system.flows.values(), next_flow_system.flows.values()): + + for current_flow in current_flow_system.flows.values(): + next_flow = next_flow_system.flows[current_flow.label_full] next_flow.previous_flow_rate = current_flow.model.flow_rate.solution.sel( time=slice(start_previous_values, end_previous_values) ).values start_values_of_this_segment[current_flow.label_full] = next_flow.previous_flow_rate - for current_comp, next_comp in zip(current_flow_system.components.values(), next_flow_system.components.values()): + + for current_comp in current_flow_system.components.values(): + next_comp = next_flow_system.components[current_comp.label_full] if isinstance(next_comp, Storage): next_comp.initial_charge_state = current_comp.model.charge_state.solution.sel(time=start).item() start_values_of_this_segment[current_comp.label_full] = next_comp.initial_charge_state From f473ce523adac4813648a8da11e52acbeacca0a0 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:34:36 +0200 Subject: [PATCH 117/183] Fix SegmentedResults IO --- flixopt/results.py | 4 ++-- tests/test_integration.py | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flixopt/results.py b/flixopt/results.py index e13cb0785..1dee9ac02 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -659,7 +659,7 @@ def from_file(cls, folder: Union[str, pathlib.Path], name: str): with open(path.with_suffix('.json'), 'r', encoding='utf-8') as f: meta_data = json.load(f) return cls( - [CalculationResults.from_file(folder, name) for name in meta_data['sub_calculations']], + [CalculationResults.from_file(folder, sub_name) for sub_name in meta_data['sub_calculations']], all_timesteps=pd.DatetimeIndex( [datetime.datetime.fromisoformat(date) for date in meta_data['all_timesteps']], name='time' ), @@ -756,7 +756,7 @@ def to_file( f'Folder {folder} and its parent do not exist. Please create them first.' ) from e for segment in self.segment_results: - segment.to_file(folder=folder, name=f'{name}-{segment.name}', compression=compression) + segment.to_file(folder=folder, name=segment.name, compression=compression) with open(path.with_suffix('.json'), 'w', encoding='utf-8') as f: json.dump(self.meta_data, f, indent=4, ensure_ascii=False) diff --git a/tests/test_integration.py b/tests/test_integration.py index dc203c33e..da473b4e6 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -420,6 +420,12 @@ def test_modeling_types_costs(self, modeling_calculation): f'Costs do not match for {modeling_type} modeling type', ) + def test_segmented_io(self, modeling_calculation): + calc, modeling_type = modeling_calculation + if modeling_type == 'segmented': + calc.results.to_file() + _ = fx.results.SegmentedCalculationResults.from_file(calc.folder, calc.name) + if __name__ == '__main__': pytest.main(['-v']) From 7869a7249617c686ccc39ad0794fc2cebe218d34 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:47:30 +0200 Subject: [PATCH 118/183] ruff check --- flixopt/calculation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 8e4a57100..0c844f78f 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -13,20 +13,20 @@ import pathlib import timeit import warnings -from typing import Annotated, Any, Dict, List, Optional, Union from collections import Counter +from typing import Annotated, Any, Dict, List, Optional, Union import numpy as np import pandas as pd -import yaml import xarray as xr +import yaml from . import io as fx_io from . import utils as utils from .aggregation import AggregationModel, AggregationParameters from .components import Storage from .config import CONFIG -from .core import Scalar, DataConverter, drop_constant_arrays, TimeSeriesData +from .core import DataConverter, Scalar, TimeSeriesData, drop_constant_arrays from .elements import Component from .features import InvestmentModel from .flow_system import FlowSystem @@ -520,4 +520,4 @@ def start_values_of_segments(self) -> List[Dict[str, Any]]: @property def all_timesteps(self) -> pd.DatetimeIndex: - return self.flow_system.timesteps \ No newline at end of file + return self.flow_system.timesteps From bb29ef254a6c410d43ddd72011060f7488d1ee35 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 12:08:27 +0200 Subject: [PATCH 119/183] Update example --- examples/01_Simple/simple_example.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/01_Simple/simple_example.py b/examples/01_Simple/simple_example.py index 45550c9cc..963f2fbe1 100644 --- a/examples/01_Simple/simple_example.py +++ b/examples/01_Simple/simple_example.py @@ -67,7 +67,8 @@ discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1000), capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), initial_charge_state=0, # Initial storage state: empty - relative_maximum_charge_state=1 / 100 * np.array([80, 70, 80, 80, 80, 80, 80, 80, 80, 80]), + relative_maximum_charge_state=1 / 100 * np.array([80, 70, 80, 80, 80, 80, 80, 80, 80]), + relative_maximum_final_charge_state=0.8, eta_charge=0.9, eta_discharge=1, # Efficiency factors for charging/discharging relative_loss_per_hour=0.08, # 8% loss per hour. Absolute loss depends on current charge state From 8d96a49b3a259ceb28540d526e0eb7c4c0f69613 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 13:46:05 +0200 Subject: [PATCH 120/183] Updated logger essages to use .label_full instead of .label --- flixopt/elements.py | 8 ++++---- flixopt/flow_system.py | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index d596333c3..a49a12f0d 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -117,7 +117,7 @@ def transform_data(self, flow_system: 'FlowSystem'): def _plausibility_checks(self) -> None: if self.excess_penalty_per_flow_hour is not None and (self.excess_penalty_per_flow_hour == 0).all(): - logger.warning(f'In Bus {self.label}, the excess_penalty_per_flow_hour is 0. Use "None" or a value > 0.') + logger.warning(f'In Bus {self.label_full}, the excess_penalty_per_flow_hour is 0. Use "None" or a value > 0.') @property def with_excess(self) -> bool: @@ -256,21 +256,21 @@ def _plausibility_checks(self) -> None: self.size == CONFIG.modeling.BIG and self.fixed_relative_profile is not None ): # Default Size --> Most likely by accident logger.warning( - f'Flow "{self.label}" has no size assigned, but a "fixed_relative_profile". ' + f'Flow "{self.label_full}" has no size assigned, but a "fixed_relative_profile". ' f'The default size is {CONFIG.modeling.BIG}. As "flow_rate = size * fixed_relative_profile", ' f'the resulting flow_rate will be very high. To fix this, assign a size to the Flow {self}.' ) if self.fixed_relative_profile is not None and self.on_off_parameters is not None: raise ValueError( - f'Flow {self.label} has both a fixed_relative_profile and an on_off_parameters. This is not supported. ' + f'Flow {self.label_full} has both a fixed_relative_profile and an on_off_parameters. This is not supported. ' f'Use relative_minimum and relative_maximum instead, ' f'if you want to allow flows to be switched on and off.' ) if (self.relative_minimum > 0).any() and self.on_off_parameters is None: logger.warning( - f'Flow {self.label} has a relative_minimum of {self.relative_minimum} and no on_off_parameters. ' + f'Flow {self.label_full} has a relative_minimum of {self.relative_minimum} and no on_off_parameters. ' f'This prevents the flow_rate from switching off (flow_rate = 0). ' f'Consider using on_off_parameters to allow the flow to be switched on and off.' ) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 560d740bd..306872674 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -409,25 +409,25 @@ def _check_if_element_is_unique(self, element: Element) -> None: element: new element to check """ if element in self.all_elements.values(): - raise ValueError(f'Element {element.label} already added to FlowSystem!') + raise ValueError(f'Element {element.label_full} already added to FlowSystem!') # check if name is already used: if element.label_full in self.all_elements: - raise ValueError(f'Label of Element {element.label} already used in another element!') + raise ValueError(f'Label of Element {element.label_full} already used in another element!') def _add_effects(self, *args: Effect) -> None: self.effects.add_effects(*args) def _add_components(self, *components: Component) -> None: for new_component in list(components): - logger.info(f'Registered new Component: {new_component.label}') + logger.info(f'Registered new Component: {new_component.label_full}') self._check_if_element_is_unique(new_component) # check if already exists: - self.components[new_component.label] = new_component # Add to existing components + self.components[new_component.label_full] = new_component # Add to existing components def _add_buses(self, *buses: Bus): for new_bus in list(buses): - logger.info(f'Registered new Bus: {new_bus.label}') + logger.info(f'Registered new Bus: {new_bus.label_full}') self._check_if_element_is_unique(new_bus) # check if already exists: - self.buses[new_bus.label] = new_bus # Add to existing components + self.buses[new_bus.label_full] = new_bus # Add to existing components def _connect_network(self): """Connects the network of components and buses. Can be rerun without changes if no elements were added""" @@ -440,7 +440,7 @@ def _connect_network(self): if flow._bus_object is not None and flow._bus_object not in self.buses.values(): self._add_buses(flow._bus_object) warnings.warn( - f'The Bus {flow._bus_object.label} was added to the FlowSystem from {flow.label_full}.' + f'The Bus {flow._bus_object.label_full} was added to the FlowSystem from {flow.label_full}.' f'This is deprecated and will be removed in the future. ' f'Please pass the Bus.label to the Flow and the Bus to the FlowSystem instead.', UserWarning, From 8240da1046b979e0925a8e14ebd1fe7850b9ac1a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 13:59:02 +0200 Subject: [PATCH 121/183] Re-add parameters. Use deprecation warning instead --- flixopt/calculation.py | 2 +- flixopt/core.py | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 0c844f78f..66a33497b 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -74,9 +74,9 @@ def __init__( ) flow_system = flow_system.sel(time=active_timesteps) + flow_system._used_in_calculation = True self.flow_system = flow_system - self.flow_system._used_in_calculation = True self.model: Optional[SystemModel] = None self._active_timesteps = active_timesteps # deprecated diff --git a/flixopt/core.py b/flixopt/core.py index 5bba418be..1aa175ed0 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -4,6 +4,7 @@ """ import logging +import warnings from typing import Dict, Optional, Union import numpy as np @@ -41,14 +42,24 @@ class TimeSeriesData(xr.DataArray): __slots__ = () # No additional instance attributes - everything goes in attrs - def __init__(self, *args, aggregation_group: Optional[str] = None, aggregation_weight: Optional[float] = None, **kwargs): + def __init__(self, *args, aggregation_group: Optional[str] = None, aggregation_weight: Optional[float] = None, + agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): """ Args: *args: Arguments passed to DataArray aggregation_group: Aggregation group name aggregation_weight: Aggregation weight (0-1) + agg_group: Deprecated, use aggregation_group instead + agg_weight: Deprecated, use aggregation_weight instead **kwargs: Additional arguments passed to DataArray """ + if agg_group is not None: + warnings.warn('agg_group is deprecated, use aggregation_group instead', DeprecationWarning, stacklevel=2) + aggregation_group = agg_group + if agg_weight is not None: + warnings.warn('agg_weight is deprecated, use aggregation_weight instead', DeprecationWarning, stacklevel=2) + aggregation_weight = agg_weight + if (aggregation_group is not None) and (aggregation_weight is not None): raise ValueError('Use either aggregation_group or aggregation_weight, not both') @@ -96,6 +107,16 @@ def __repr__(self): info_str = f'TimeSeriesData({", ".join(agg_info)})' if agg_info else 'TimeSeriesData' return f'{info_str}\n{super().__repr__()}' + @property + def agg_group(self): + warnings.warn('agg_group is deprecated, use aggregation_group instead', DeprecationWarning, stacklevel=2) + return self._aggregation_group + + @property + def agg_weight(self): + warnings.warn('agg_weight is deprecated, use aggregation_weight instead', DeprecationWarning, stacklevel=2) + return self._aggregation_weight + class DataConverter: """ From 8ac2664ce99b1a507f2314db55ab12b8bd88c331 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:05:58 +0200 Subject: [PATCH 122/183] Update changelog --- CHANGELOG.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1871de91a..45121534f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,12 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed -* **BREAKING**: FlowSystems can not be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent -* **BREAKING**: Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity * **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. The final charge state can now be constrained by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead +* FlowSystems can not be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent +* Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity * FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties * Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods -* *Internal*: Removed intermediate `TimeSeries` and `TimeSeriesCollection` classes, replaced directly with `xr.DataArray` or `TimeSeriesData` (inheriting from `xr.DataArray`) ### Added * **NEW**: Complete serialization infrastructure through `Interface` base class @@ -25,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * **NEW**: FlowSystem data manipulation methods * `sel()` and `isel()` methods for temporal data selection * `resample()` method for temporal resampling - * `copy()` method with deep copying support + * `copy()` method to create a copy of a FlowSystem, including all underlying Elements and their data * `__eq__()` method for FlowSystem comparison * **NEW**: Storage component enhancements * `relative_minimum_final_charge_state` parameter for final state control @@ -47,6 +46,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Plotly >= 6 may raise errors if "nbformat" is not installed. We pinned plotly to <6, but this may be fixed in the future. * IO for single Interfaces/Elemenets to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arrises from Numeric Data not being stored as xr.DataArray by the user. TO avoid this, always use the `to_dataset()` on Elements inside a FlowSystem thats connected and transformed. +### Deprecated +* The `agg_group` and `agg_weight` parameters of `TimeSeriesData` are deprecated and will be removed in a future version. Use `aggregation_group` and `aggregation_weight` instead. +* The `active_timesteps` parameter of `Calculation` is deprecated and will be removed in a future version. Use the new `sel(time=...)` method on the FlowSystem instead. +* The assignment of Bus Objects to Flow.bus is deprecated and will be removed in a future version. Use the label of the Bus instead. +* The usage of Effects objects in Dicts to assign shares to Effects is deprecated and will be removed in a future version. Use the label of the Effect instead. + ## [2.1.2] - 2025-06-14 ### Fixed From 43a64eaf3f04f531217c2d560fc9813352a8a754 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 14:17:02 +0200 Subject: [PATCH 123/183] Improve warning message --- flixopt/core.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/flixopt/core.py b/flixopt/core.py index 1aa175ed0..121c7fb12 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -148,7 +148,10 @@ def _fix_timeseries_data_indexing( # Check if dimensions match if data.dims != tuple(dims): - logger.warning(f'TimeSeriesData has dimensions {data.dims}, expected {dims}. Reshaping to match timesteps.') + logger.warning( + f'TimeSeriesData has dimensions {data.dims}, expected {dims}. Reshaping to match timesteps. To avoid ' + f'this warning, create a correctly shaped DataArray with the correct dimensions in the first place.' + ) # Try to reshape the data to match expected dimensions if data.size != len(timesteps): raise ConversionError( From b3fe443ea576db57ab452ab667cffa58a6e0eea9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Fri, 27 Jun 2025 15:17:24 +0200 Subject: [PATCH 124/183] Merge --- flixopt/components.py | 2 +- flixopt/core.py | 12 ++++++------ flixopt/effects.py | 18 +++++++++--------- flixopt/features.py | 20 ++++++++++---------- flixopt/flow_system.py | 23 ++++++++++++----------- flixopt/interface.py | 39 ++++++++++++++++++--------------------- flixopt/structure.py | 19 ++++++++----------- tests/conftest.py | 2 +- 8 files changed, 65 insertions(+), 70 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index db92067f5..c2787ef9a 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -10,7 +10,7 @@ import xarray as xr from . import utils -from .core import PlausibilityError, Scalar, TemporalData, TemporalDataUser, ScenarioData +from .core import PlausibilityError, Scalar, TemporalData, TemporalDataUser, NonTemporalDataUser from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion diff --git a/flixopt/core.py b/flixopt/core.py index 1c58ed290..9c9d6f891 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -5,7 +5,7 @@ import logging import warnings -from typing import Dict, Optional, Union +from typing import Dict, Optional, Union, Tuple import numpy as np import pandas as pd @@ -24,11 +24,11 @@ TemporalData = Union[xr.DataArray, 'TimeSeriesData'] """Internally used datatypes for temporal data.""" -TimestepData = NumericData -"""Represents any form of numeric data that corresponds to timesteps.""" +NonTemporalDataUser = Union[int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray] +"""User data which has no time dimension. Internally converted to an xr.DataArray without a time dimension.""" -ScenarioData = NumericData -"""Represents any form of numeric data that corresponds to scenarios.""" +NonTemporalData = Union[Scalar, xr.DataArray] +"""Internally used datatypes for non-temporal data.""" class PlausibilityError(Exception): @@ -196,7 +196,7 @@ def _fix_timeseries_data_indexing( @staticmethod def to_dataarray( - data: TimestepData, timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None + data: Union[TemporalData, NonTemporalData], timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None ) -> xr.DataArray: """ Convert data to xarray.DataArray with specified dimensions. diff --git a/flixopt/effects.py b/flixopt/effects.py index 5270bf7fd..dc1d7d07f 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -39,7 +39,7 @@ def __init__( is_standard: bool = False, is_objective: bool = False, specific_share_to_other_effects_operation: Optional['TemporalEffectsUser'] = None, - specific_share_to_other_effects_invest: Optional['ScalarEffectsUser'] = None, + specific_share_to_other_effects_invest: Optional['NonTemporalEffectsUser'] = None, minimum_operation: Optional[Scalar] = None, maximum_operation: Optional[Scalar] = None, minimum_invest: Optional[Scalar] = None, @@ -79,7 +79,7 @@ def __init__( self.specific_share_to_other_effects_operation: TemporalEffectsUser = ( specific_share_to_other_effects_operation or {} ) - self.specific_share_to_other_effects_invest: ScalarEffectsUser = specific_share_to_other_effects_invest or {} + self.specific_share_to_other_effects_invest: NonTemporalEffectsUser = specific_share_to_other_effects_invest or {} self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation self.minimum_operation_per_hour: TemporalDataUser = minimum_operation_per_hour @@ -148,8 +148,8 @@ def __init__(self, model: SystemModel, element: Effect): label_of_element=self.label_of_element, label='invest', label_full=f'{self.label_full}(invest)', - total_max=extract_data(self.element.maximum_invest), - total_min=extract_data(self.element.minimum_invest), + total_max=self.element.maximum_invest, + total_min=self.element.minimum_invest, ) ) @@ -197,13 +197,13 @@ def do_modeling(self): TemporalEffectsUser = Union[TemporalDataUser, Dict[str, TemporalDataUser]] # User-specified Shares to Effects """ This datatype is used to define a temporal share to an effect by a certain attribute. """ -ScalarEffectsUser = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects +NonTemporalEffectsUser = Union[Scalar, Dict[str, Scalar]] # User-specified Shares to Effects """ This datatype is used to define a scalar share to an effect by a certain attribute. """ TemporalEffects = Dict[str, TemporalData] # User-specified Shares to Effects """ This datatype is used internally to handle temporal shares to an effect. """ -ScalarEffects = Dict[str, Scalar] +NonTemporalEffects = Dict[str, Scalar] """ This datatype is used internally to handle scalar shares to an effect. """ EffectExpr = Dict[str, linopy.LinearExpression] # Used to create Shares @@ -240,7 +240,7 @@ def add_effects(self, *effects: Effect) -> None: def create_effect_values_dict( self, - effect_values_user: Union[ScalarEffectsUser, TemporalEffectsUser] + effect_values_user: Union[NonTemporalEffectsUser, TemporalEffectsUser] ) -> Optional[Dict[str, Union[Scalar, TemporalDataUser]]]: """ Converts effect values into a dictionary. If a scalar is provided, it is associated with a default effect type. @@ -366,7 +366,7 @@ def calculate_effect_share_factors(self) -> Tuple[ for name, effect in self.effects.items(): if effect.specific_share_to_other_effects_invest: shares_invest[name] = { - target: extract_data(data) + target: data for target, data in effect.specific_share_to_other_effects_invest.items() } shares_invest = calculate_all_conversion_paths(shares_invest) @@ -375,7 +375,7 @@ def calculate_effect_share_factors(self) -> Tuple[ for name, effect in self.effects.items(): if effect.specific_share_to_other_effects_operation: shares_operation[name] = { - target: extract_data(data) + target: data for target, data in effect.specific_share_to_other_effects_operation.items() } shares_operation = calculate_all_conversion_paths(shares_operation) diff --git a/flixopt/features.py b/flixopt/features.py index 8b80e94d4..3a2744dee 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -45,8 +45,8 @@ def __init__( def do_modeling(self): self.size = self.add( self._model.add_variables( - lower=0 if self.parameters.optional else extract_data(self.parameters.minimum_size), - upper=extract_data(self.parameters.maximum_size), + lower=0 if self.parameters.optional else self.parameters.minimum_size, + upper=self.parameters.maximum_size, name=f'{self.label_full}|size', coords=self._model.get_coords(time_dim=False), ), @@ -292,8 +292,8 @@ def do_modeling(self): self.total_on_hours = self.add( self._model.add_variables( - lower=extract_data(self._on_hours_total_min), - upper=extract_data(self._on_hours_total_max), + lower=self._on_hours_total_min, + upper=self._on_hours_total_max, coords=self._model.get_coords(time_dim=False), name=f'{self.label_full}|on_hours_total', ), @@ -437,7 +437,7 @@ def do_modeling(self): # Create count variable for number of switches self.switch_on_nr = self.add( self._model.add_variables( - upper=extract_data(self._switch_on_max), + upper=self._switch_on_max, lower=0, name=f'{self.label_full}|switch_on_nr', ), @@ -526,7 +526,7 @@ def do_modeling(self): self.duration = self.add( self._model.add_variables( lower=0, - upper=extract_data(self._maximum_duration, mega), + upper=self._maximum_duration if self._maximum_duration is not None else mega, coords=self._model.get_coords(), name=f'{self.label_full}|hours', ), @@ -707,8 +707,8 @@ def do_modeling(self): defining_bounds=self._defining_bounds, previous_values=self._previous_values, use_off=self.parameters.use_off, - on_hours_total_min=extract_data(self.parameters.on_hours_total_min), - on_hours_total_max=extract_data(self.parameters.on_hours_total_max), + on_hours_total_min=self.parameters.on_hours_total_min, + on_hours_total_max=self.parameters.on_hours_total_max, effects_per_running_hour=self.parameters.effects_per_running_hour, ) self.add(self.state_model) @@ -1001,8 +1001,8 @@ def do_modeling(self): if self._has_time_dim: self.total_per_timestep = self.add( self._model.add_variables( - lower=-np.inf if (self._min_per_hour is None) else extract_data(self._min_per_hour) * self._model.hours_per_step, - upper=np.inf if (self._max_per_hour is None) else extract_data(self._max_per_hour) * self._model.hours_per_step, + lower=-np.inf if (self._min_per_hour is None) else self._min_per_hour * self._model.hours_per_step, + upper=np.inf if (self._max_per_hour is None) else self._max_per_hour * self._model.hours_per_step, coords=self._model.get_coords(time_dim=True, scenario_dim=self._has_scenario_dim), name=f'{self.label_full}|total_per_timestep', ), diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 5734c6a1d..52371c9cb 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,8 +16,8 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData -from .effects import Effect, EffectCollection, ScalarEffects, ScalarEffectsUser, TemporalEffects, TemporalEffectsUser +from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser +from .effects import Effect, EffectCollection, NonTemporalEffects, NonTemporalEffectsUser, TemporalEffects, TemporalEffectsUser from .elements import Bus, Component, Flow from .structure import Element, Interface, SystemModel @@ -49,7 +49,7 @@ def __init__( scenarios: Optional[pd.Index] = None, hours_of_last_timestep: Optional[float] = None, hours_of_previous_timesteps: Optional[Union[int, float, np.ndarray]] = None, - scenario_weights: Optional[ScenarioData] = None, + scenario_weights: Optional[NonTemporalDataUser] = None, ): """ Args: @@ -69,9 +69,8 @@ def __init__( self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps ) - self.scenario_weights = self.create_time_series( - 'scenario_weights', scenario_weights, has_time_dim=False, has_scenario_dim=True - ) + self.scenarios = scenarios + self.scenario_weights = self.fit_to_model_coords('scenario_weights', scenario_weights, has_time_dim=False) # Element collections self.components: Dict[str, Component] = {} @@ -279,6 +278,7 @@ def fit_to_model_coords( self, name: str, data: Optional[TemporalDataUser], + has_time_dim: bool = True, ) -> Optional[TemporalData]: """ Fit data to model coordinate system (currently time, but extensible). @@ -297,21 +297,22 @@ def fit_to_model_coords( try: data.name = name # Set name of previous object! return TimeSeriesData( - DataConverter.to_dataarray(data, timesteps=self.timesteps), + DataConverter.to_dataarray(data, timesteps=self.timesteps, scenarios=self.scenarios), aggregation_group=data.aggregation_group, aggregation_weight=data.aggregation_weight ).rename(name) except ConversionError as e: logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' f'Take care to use the correct (time) index.') else: - return DataConverter.to_dataarray(data, timesteps=self.timesteps).rename(name) + return DataConverter.to_dataarray(data, timesteps=self.timesteps if has_time_dim else None, scenarios=self.scenarios).rename(name) def fit_effects_to_model_coords( self, label_prefix: Optional[str], effect_values: Optional[TemporalEffectsUser], label_suffix: Optional[str] = None, - ) -> Optional[TemporalEffects]: + has_time_dim: bool = True, + ) -> Optional[Union[TemporalEffects, NonTemporalEffects]]: """ Transform EffectValues from the user to Internal Datatypes aligned with model coordinates. """ @@ -321,14 +322,14 @@ def fit_effects_to_model_coords( effect_values_dict = self.effects.create_effect_values_dict(effect_values) return { - effect: self.fit_to_model_coords('|'.join(filter(None, [label_prefix, effect, label_suffix])), value) + effect: self.fit_to_model_coords('|'.join(filter(None, [label_prefix, effect, label_suffix])), value, has_time_dim=has_time_dim) for effect, value in effect_values_dict.items() } def connect_and_transform(self): """Transform data for all elements using the new simplified approach.""" self.scenario_weights = self.fit_to_model_coords( - 'scenario_weights', self.scenario_weights, has_time_dim=False, has_scenario_dim=True + 'scenario_weights', self.scenario_weights, has_time_dim=False ) if not self._connected_and_transformed: self._connect_network() diff --git a/flixopt/interface.py b/flixopt/interface.py index a8f209c8f..eb84a45e3 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union from .config import CONFIG -from .core import Scalar, TemporalDataUser +from .core import Scalar, TemporalDataUser, NonTemporalDataUser from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports @@ -33,8 +33,8 @@ def __init__(self, start: TemporalDataUser, end: TemporalDataUser): self.has_time_dim = False def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): - self.start = flow_system.fit_to_model_coords(f'{name_prefix}|start', self.start, has_time_dim=self.has_time_dim, has_scenario_dim=True) - self.end = flow_system.fit_to_model_coords(f'{name_prefix}|end', self.end, has_time_dim=self.has_time_dim, has_scenario_dim=True) + self.start = flow_system.fit_to_model_coords(f'{name_prefix}|start', self.start, has_time_dim=self.has_time_dim) + self.end = flow_system.fit_to_model_coords(f'{name_prefix}|end', self.end, has_time_dim=self.has_time_dim) @register_class_for_io @@ -146,9 +146,9 @@ class InvestParameters(Interface): def __init__( self, - fixed_size: Optional[ScenarioData] = None, - minimum_size: Optional[ScenarioData] = None, - maximum_size: Optional[ScenarioData] = None, + fixed_size: Optional[NonTemporalDataUser] = None, + minimum_size: Optional[NonTemporalDataUser] = None, + maximum_size: Optional[NonTemporalDataUser] = None, optional: bool = True, # Investition ist weglassbar fix_effects: Optional['EffectValuesUserScenario'] = None, specific_effects: Optional['EffectValuesUserScenario'] = None, # costs per Flow-Unit/Storage-Size/... @@ -185,40 +185,37 @@ def __init__( def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self._plausibility_checks(flow_system) - self.fix_effects = flow_system.create_effect_time_series( + self.fix_effects = flow_system.fit_effects_to_model_coords( label_prefix=name_prefix, effect_values=self.fix_effects, label_suffix='fix_effects', has_time_dim=False, - has_scenario_dim=True, ) - self.divest_effects = flow_system.create_effect_time_series( + self.divest_effects = flow_system.fit_effects_to_model_coords( label_prefix=name_prefix, effect_values=self.divest_effects, label_suffix='divest_effects', has_time_dim=False, - has_scenario_dim=True, ) - self.specific_effects = flow_system.create_effect_time_series( + self.specific_effects = flow_system.fit_effects_to_model_coords( label_prefix=name_prefix, effect_values=self.specific_effects, label_suffix='specific_effects', has_time_dim=False, - has_scenario_dim=True, ) if self.piecewise_effects is not None: self.piecewise_effects.has_time_dim = False self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') - self._minimum_size = flow_system.create_time_series( - f'{name_prefix}|minimum_size', self.minimum_size, has_time_dim=False, has_scenario_dim=True + self._minimum_size = flow_system.fit_to_model_coords( + f'{name_prefix}|minimum_size', self.minimum_size, has_time_dim=False ) - self._maximum_size = flow_system.create_time_series( - f'{name_prefix}|maximum_size', self.maximum_size, has_time_dim=False, has_scenario_dim=True + self._maximum_size = flow_system.fit_to_model_coords( + f'{name_prefix}|maximum_size', self.maximum_size, has_time_dim=False ) if self.fixed_size is not None: - self.fixed_size = flow_system.create_time_series( - f'{name_prefix}|fixed_size', self.fixed_size, has_time_dim=False, has_scenario_dim=True + self.fixed_size = flow_system.fit_to_model_coords( + f'{name_prefix}|fixed_size', self.fixed_size, has_time_dim=False ) def _plausibility_checks(self, flow_system): @@ -310,13 +307,13 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.consecutive_off_hours_max = flow_system.fit_to_model_coords( f'{name_prefix}|consecutive_off_hours_max', self.consecutive_off_hours_max ) - self.on_hours_total_max = flow_system.create_time_series( + self.on_hours_total_max = flow_system.fit_to_model_coords( f'{name_prefix}|on_hours_total_max', self.on_hours_total_max, has_time_dim=False ) - self.on_hours_total_min = flow_system.create_time_series( + self.on_hours_total_min = flow_system.fit_to_model_coords( f'{name_prefix}|on_hours_total_min', self.on_hours_total_min, has_time_dim=False ) - self.switch_on_total_max = flow_system.create_time_series( + self.switch_on_total_max = flow_system.fit_to_model_coords( f'{name_prefix}|switch_on_total_max', self.switch_on_total_max, has_time_dim=False ) diff --git a/flixopt/structure.py b/flixopt/structure.py index 9fc12bcd6..d02a8d4e9 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -7,7 +7,6 @@ import json import logging import pathlib -from datetime import datetime from io import StringIO from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union @@ -20,7 +19,7 @@ from . import io as fx_io from .config import CONFIG -from .core import Scalar, TemporalDataUser, TimeSeriesData, get_dataarray_stats +from .core import Scalar, TemporalDataUser, TimeSeriesData, get_dataarray_stats, NonTemporalData if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel @@ -70,21 +69,19 @@ def do_modeling(self): for bus_model in bus_models: # Buses after Components, because FlowModels are created in ComponentModels bus_model.do_modeling() - def _calculate_scenario_weights(self, weights: Optional[TimeSeries] = None) -> xr.DataArray: + def _calculate_scenario_weights(self, weights: Optional[NonTemporalData] = None) -> xr.DataArray: """Calculates the weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. If no scenarios are present, s single weight of 1 is returned. """ - if weights is not None and not isinstance(weights, TimeSeries): - raise TypeError(f'Weights must be a TimeSeries or None, got {type(weights)}') - if self.time_series_collection.scenarios is None: + if weights is not None and not isinstance(weights, xr.DataArray): + raise TypeError(f'Weights must be a xr.DataArray or None, got {type(weights)}') + if self.flow_system.scenarios is None: return xr.DataArray(1) if weights is None: weights = xr.DataArray( - np.ones(len(self.time_series_collection.scenarios)), - coords={'scenario': self.time_series_collection.scenarios} + np.ones(len(self.flow_system.scenarios)), + coords={'scenario': self.flow_system.scenarios} ) - elif isinstance(weights, TimeSeries): - weights = weights.selected_data return weights / weights.sum() @@ -137,7 +134,7 @@ def get_coords( """ if not scenario_dim and not time_dim: return None - scenarios = self.time_series_collection.scenarios + scenarios = self.flow_system.scenarios timesteps = ( self.flow_system.timesteps if not extra_timestep else self.flow_system.timesteps_extra ) diff --git a/tests/conftest.py b/tests/conftest.py index 198b9a92b..ae2cb8c25 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ from flixopt.structure import SystemModel -@pytest.fixture() +@pytest.fixture( def highs_solver(): return fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=300) From 483ba12c9099113b17000042e9f6f886992d520c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:40:06 +0200 Subject: [PATCH 125/183] Merge --- flixopt/components.py | 16 ++++++++-------- flixopt/results.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index c2787ef9a..5b902b65b 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -257,20 +257,20 @@ def _plausibility_checks(self) -> None: maximum_capacity = self.capacity_in_flow_hours minimum_capacity = self.capacity_in_flow_hours - # initial capacity >= allowed min for maximum_size: - minimum_inital_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) - # initial capacity <= allowed max for minimum_size: - maximum_inital_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0) + # initial capacity >= allowed min for maximum_size: + minimum_initial_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) + # initial capacity <= allowed max for minimum_size: + maximum_initial_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0) - if (self.initial_charge_state > maximum_inital_capacity).any(): + if (self.initial_charge_state > maximum_initial_capacity).any(): raise ValueError( f'{self.label_full}: {self.initial_charge_state=} ' - f'is above allowed maximum charge_state {maximum_inital_capacity}' + f'is above allowed maximum charge_state {maximum_initial_capacity}' ) - if (self.initial_charge_state < minimum_inital_capacity).any(): + if (self.initial_charge_state < minimum_initial_capacity).any(): raise ValueError( f'{self.label_full}: {self.initial_charge_state=} ' - f'is below allowed minimum charge_state {minimum_inital_capacity}' + f'is below allowed minimum charge_state {minimum_initial_capacity}' ) if self.balanced: diff --git a/flixopt/results.py b/flixopt/results.py index 3eba91c8a..7a6f7926e 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -124,7 +124,7 @@ def from_calculation(cls, calculation: 'Calculation'): """ return cls( solution=calculation.model.solution, - flow_system=calculation.flow_system.to_dataset(), + flow_system_data=calculation.flow_system.to_dataset(), summary=calculation.summary, model=calculation.model, name=calculation.name, From dee1de4ebb0418f30cfcc54ba634f21c31fba0c6 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:40:48 +0200 Subject: [PATCH 126/183] Fit scenario weights to model coords when transforming --- flixopt/flow_system.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 52371c9cb..dea0a40e6 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -70,7 +70,7 @@ def __init__( timesteps, hours_of_previous_timesteps ) self.scenarios = scenarios - self.scenario_weights = self.fit_to_model_coords('scenario_weights', scenario_weights, has_time_dim=False) + self.scenario_weights = scenario_weights # Element collections self.components: Dict[str, Component] = {} From 4ec39149705b5fb60886db33901bd47f05ba9ea9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:40:57 +0200 Subject: [PATCH 127/183] Merge --- flixopt/features.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/features.py b/flixopt/features.py index 3a2744dee..e9d936b74 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -66,7 +66,7 @@ def do_modeling(self): self._create_bounds_for_optional_investment() - if self._model.time_series_collection.scenarios is not None: + if self._model.flow_system.scenarios is not None: self._create_bounds_for_scenarios() # Bounds for defining variable From 2554d8a79f38eef6b1c89983137453f6a51eb3b8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:41:32 +0200 Subject: [PATCH 128/183] Removing logic between minimum, maximum and fixed size from InvestParameters --- flixopt/interface.py | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/flixopt/interface.py b/flixopt/interface.py index eb84a45e3..f8bf0b1bf 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -179,8 +179,8 @@ def __init__( self.optional = optional self.specific_effects: EffectValuesUserScenario = specific_effects if specific_effects is not None else {} self.piecewise_effects = piecewise_effects - self._minimum_size = minimum_size if minimum_size is not None else CONFIG.modeling.EPSILON - self._maximum_size = maximum_size if maximum_size is not None else CONFIG.modeling.BIG # default maximum + self.minimum_size = minimum_size if minimum_size is not None else CONFIG.modeling.EPSILON + self.maximum_size = maximum_size if maximum_size is not None else CONFIG.modeling.BIG # default maximum self.investment_scenarios = investment_scenarios def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): @@ -207,10 +207,10 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): self.piecewise_effects.has_time_dim = False self.piecewise_effects.transform_data(flow_system, f'{name_prefix}|PiecewiseEffects') - self._minimum_size = flow_system.fit_to_model_coords( + self.minimum_size = flow_system.fit_to_model_coords( f'{name_prefix}|minimum_size', self.minimum_size, has_time_dim=False ) - self._maximum_size = flow_system.fit_to_model_coords( + self.maximum_size = flow_system.fit_to_model_coords( f'{name_prefix}|maximum_size', self.maximum_size, has_time_dim=False ) if self.fixed_size is not None: @@ -220,10 +220,10 @@ def transform_data(self, flow_system: 'FlowSystem', name_prefix: str): def _plausibility_checks(self, flow_system): if isinstance(self.investment_scenarios, list): - if not set(self.investment_scenarios).issubset(flow_system.time_series_collection.scenarios): + if not set(self.investment_scenarios).issubset(flow_system.scenarios): raise ValueError( f'Some scenarios in investment_scenarios are not present in the time_series_collection: ' - f'{set(self.investment_scenarios) - set(flow_system.time_series_collection.scenarios)}' + f'{set(self.investment_scenarios) - set(flow_system.scenarios)}' ) if self.investment_scenarios is not None: if not self.optional: @@ -233,14 +233,6 @@ def _plausibility_checks(self, flow_system): 'Otherwise the investment cannot be 0 incertain scenarios while being non-zero in others.' ) - @property - def minimum_size(self): - return self.fixed_size if self.fixed_size is not None else self._minimum_size - - @property - def maximum_size(self): - return self.fixed_size if self.fixed_size is not None else self._maximum_size - @register_class_for_io class OnOffParameters(Interface): From d062727476015049e0b36b274316acf7312c8062 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:45:11 +0200 Subject: [PATCH 129/183] Remove selected_timesteps --- flixopt/calculation.py | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 5796cd70e..88e509438 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -46,30 +46,19 @@ def __init__( self, name: str, flow_system: FlowSystem, - selected_timesteps: Annotated[ + active_timesteps: Annotated[ Optional[pd.DatetimeIndex], 'DEPRECATED: Use flow_system.sel(time=...) or flow_system.isel(time=...) instead', ] = None, - selected_scenarios: Optional[pd.Index] = None, folder: Optional[pathlib.Path] = None, - active_timesteps: Optional[pd.DatetimeIndex] = None, ): """ Args: name: name of calculation flow_system: flow_system which should be calculated - selected_timesteps: timesteps which should be used for calculation. If None, then all timesteps are used. - selected_scenarios: scenarios which should be used for calculation. If None, then all scenarios are used. folder: folder where results should be saved. If None, then the current working directory is used. - active_timesteps: Deprecated. Use selected_timesteps instead. + active_timesteps: Deprecated. Use FLowSystem.sel(time=...) or FlowSystem.isel(time=...) instead. """ - if active_timesteps is not None: - warnings.warn( - 'active_timesteps is deprecated. Use selected_timesteps instead.', - DeprecationWarning, - stacklevel=2, - ) - selected_timesteps = active_timesteps self.name = name if flow_system.used_in_calculation: logging.warning(f'FlowSystem {flow_system} is already used in a calculation. ' @@ -85,14 +74,12 @@ def __init__( stacklevel=2, ) flow_system = flow_system.sel(time=active_timesteps) + self._active_timesteps = active_timesteps # deprecated flow_system._used_in_calculation = True self.flow_system = flow_system self.model: Optional[SystemModel] = None - self.selected_timesteps = selected_timesteps - self.selected_scenarios = selected_scenarios - self._active_timesteps = active_timesteps # deprecated self.durations = {'modeling': 0.0, 'solving': 0.0, 'saving': 0.0} self.folder = pathlib.Path.cwd() / 'results' if folder is None else pathlib.Path(folder) @@ -169,11 +156,11 @@ def summary(self): @property def active_timesteps(self) -> pd.DatetimeIndex: warnings.warn( - 'active_timesteps is deprecated. Use selected_timesteps instead.', + 'active_timesteps is deprecated. Use active_timesteps instead.', DeprecationWarning, stacklevel=2, ) - return self.selected_timesteps + return self._active_timesteps class FullCalculation(Calculation): @@ -240,7 +227,6 @@ def __init__( flow_system: FlowSystem, aggregation_parameters: AggregationParameters, components_to_clusterize: Optional[List[Component]] = None, - selected_timesteps: Optional[pd.DatetimeIndex] = None, active_timesteps: Annotated[ Optional[pd.DatetimeIndex], 'DEPRECATED: Use flow_system.sel(time=...) or flow_system.isel(time=...) instead', @@ -258,13 +244,13 @@ def __init__( components_to_clusterize: List of Components to perform aggregation on. If None, then all components are aggregated. This means, teh variables in the components are equalized to each other, according to the typical periods computed in the DataAggregation - selected_timesteps: pd.DatetimeIndex or None + active_timesteps: pd.DatetimeIndex or None list with indices, which should be used for calculation. If None, then all timesteps are used. folder: folder where results should be saved. If None, then the current working directory is used. """ if flow_system.scenarios is not None: raise ValueError('Aggregation is not supported for scenarios yet. Please use FullCalculation instead.') - super().__init__(name, flow_system, selected_timesteps, folder=folder, active_timesteps=active_timesteps) + super().__init__(name, flow_system, folder=folder, active_timesteps=active_timesteps) self.aggregation_parameters = aggregation_parameters self.components_to_clusterize = components_to_clusterize self.aggregation = None From 6dc23f587f4ff7517ffb0c551f0270214cd85190 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 21:49:01 +0200 Subject: [PATCH 130/183] Improve TypeHints --- flixopt/core.py | 6 +++--- flixopt/flow_system.py | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 9c9d6f891..648344098 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -22,13 +22,13 @@ """User data which might have a time dimension. Internally converted to an xr.DataArray with time dimension.""" TemporalData = Union[xr.DataArray, 'TimeSeriesData'] -"""Internally used datatypes for temporal data.""" +"""Internally used datatypes for temporal data (data with a time dimension).""" NonTemporalDataUser = Union[int, float, np.integer, np.floating, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray] -"""User data which has no time dimension. Internally converted to an xr.DataArray without a time dimension.""" +"""User data which has no time dimension. Internally converted to a Scalar or an xr.DataArray without a time dimension.""" NonTemporalData = Union[Scalar, xr.DataArray] -"""Internally used datatypes for non-temporal data.""" +"""Internally used datatypes for non-temporal data. Can be a Scalar or an xr.DataArray.""" class PlausibilityError(Exception): diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index dea0a40e6..7849cdcea 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,7 +16,7 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser +from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser, NonTemporalData from .effects import Effect, EffectCollection, NonTemporalEffects, NonTemporalEffectsUser, TemporalEffects, TemporalEffectsUser from .elements import Bus, Component, Flow from .structure import Element, Interface, SystemModel @@ -277,15 +277,16 @@ def to_json(self, path: Union[str, pathlib.Path]): def fit_to_model_coords( self, name: str, - data: Optional[TemporalDataUser], + data: Optional[Union[TemporalDataUser, NonTemporalDataUser]], has_time_dim: bool = True, - ) -> Optional[TemporalData]: + ) -> Optional[Union[TemporalData, NonTemporalData]]: """ Fit data to model coordinate system (currently time, but extensible). Args: name: Name of the data data: Data to fit to model coordinates + has_time_dim: Whether the data has a time dimension Returns: xr.DataArray aligned to model coordinate system @@ -309,7 +310,7 @@ def fit_to_model_coords( def fit_effects_to_model_coords( self, label_prefix: Optional[str], - effect_values: Optional[TemporalEffectsUser], + effect_values: Optional[Union[TemporalEffectsUser, NonTemporalEffectsUser]], label_suffix: Optional[str] = None, has_time_dim: bool = True, ) -> Optional[Union[TemporalEffects, NonTemporalEffects]]: From e6100d66d64533bddc29de98e84b2a7650807c68 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:05:42 +0200 Subject: [PATCH 131/183] New property on InvestParameters for min/max/fixed size --- flixopt/elements.py | 6 +++--- flixopt/features.py | 8 ++++---- flixopt/interface.py | 10 +++++++++- tests/conftest.py | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index 9d0f91885..721c8d50d 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -443,7 +443,7 @@ def flow_rate_bounds_on(self) -> Tuple[TemporalData, TemporalData]: if size.fixed_size is not None: return relative_minimum * size.fixed_size, relative_maximum * size.fixed_size - return relative_minimum * size.minimum_size, relative_maximum * size.maximum_size + return relative_minimum * size.minimum_or_fixed_size, relative_maximum * size.maximum_or_fixed_size @property def flow_rate_lower_bound_relative(self) -> TemporalData: @@ -472,7 +472,7 @@ def flow_rate_lower_bound(self) -> TemporalData: if isinstance(self.element.size, InvestParameters): if self.element.size.optional: return 0 - return self.flow_rate_lower_bound_relative * self.element.size.minimum_size + return self.flow_rate_lower_bound_relative * self.element.size.minimum_or_fixed_size return self.flow_rate_lower_bound_relative * self.element.size @property @@ -482,7 +482,7 @@ def flow_rate_upper_bound(self) -> TemporalData: Further constraining might be done in OnOffModel and InvestmentModel """ if isinstance(self.element.size, InvestParameters): - return self.flow_rate_upper_bound_relative * self.element.size.maximum_size + return self.flow_rate_upper_bound_relative * self.element.size.maximum_or_fixed_size return self.flow_rate_upper_bound_relative * self.element.size diff --git a/flixopt/features.py b/flixopt/features.py index e9d936b74..5e0ed0e80 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import Scalar, TemporalData +from .core import Scalar, TemporalData, NonTemporalData from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel @@ -45,8 +45,8 @@ def __init__( def do_modeling(self): self.size = self.add( self._model.add_variables( - lower=0 if self.parameters.optional else self.parameters.minimum_size, - upper=self.parameters.maximum_size, + lower=0 if self.parameters.optional else self.parameters.minimum_or_fixed_size, + upper=self.parameters.maximum_or_fixed_size, name=f'{self.label_full}|size', coords=self._model.get_coords(time_dim=False), ), @@ -138,7 +138,7 @@ def _create_bounds_for_optional_investment(self): # eq2: P_invest >= isInvested * max(epsilon, investSize_min) self.add( self._model.add_constraints( - self.size >= self.is_invested * np.maximum(CONFIG.modeling.EPSILON, self.parameters.minimum_size), + self.size >= self.is_invested * np.maximum(CONFIG.modeling.EPSILON, self.parameters.minimum_or_fixed_size), name=f'{self.label_full}|is_invested_lb', ), 'is_invested_lb', diff --git a/flixopt/interface.py b/flixopt/interface.py index f8bf0b1bf..81f351ebe 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union from .config import CONFIG -from .core import Scalar, TemporalDataUser, NonTemporalDataUser +from .core import Scalar, TemporalDataUser, NonTemporalDataUser, NonTemporalData from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports @@ -233,6 +233,14 @@ def _plausibility_checks(self, flow_system): 'Otherwise the investment cannot be 0 incertain scenarios while being non-zero in others.' ) + @property + def minimum_or_fixed_size(self) -> NonTemporalData: + return self.fixed_size if self.fixed_size is not None else self.minimum_size + + @property + def maximum_or_fixed_size(self) -> NonTemporalData: + return self.fixed_size if self.fixed_size is not None else self.maximum_size + @register_class_for_io class OnOffParameters(Interface): diff --git a/tests/conftest.py b/tests/conftest.py index ae2cb8c25..198b9a92b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ from flixopt.structure import SystemModel -@pytest.fixture( +@pytest.fixture() def highs_solver(): return fx.solvers.HighsSolver(mip_gap=0, time_limit_seconds=300) From 46f20358e61236317fe95631a95c20bfee0b1c86 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:29:06 +0200 Subject: [PATCH 132/183] Move logic for InvestParameters in Transmission to from Model to Interface --- flixopt/components.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 5b902b65b..2f566963b 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -358,7 +358,17 @@ def _plausibility_checks(self): if flow is not None and isinstance(flow.size, InvestParameters): raise ValueError( 'Transmission currently does not support separate InvestParameters for Flows. ' - 'Please use Flow in1. The size of in2 is equal to in1. THis is handled internally' + 'Please use Flow in1. The size of in2 is equal to in1. This is handled internally' + ) + + # Make sure either None or both in Flows have InvestParameters + if self.in2 is not None: + if isinstance(self.in1.size, InvestParameters) and not isinstance( + self.in2.size, InvestParameters + ): + array_name = self.in1.size.maximum_size.name.replace(self.in1, self.in2) + self.in2.size = InvestParameters( + maximum_size=self.in1.size.maximum_size.rename(array_name) ) def create_model(self, model) -> 'TransmissionModel': @@ -390,13 +400,6 @@ def do_modeling(self): if flow.on_off_parameters is None: flow.on_off_parameters = OnOffParameters() - # Make sure either None or both in Flows have InvestParameters - if self.element.in2 is not None: - if isinstance(self.element.in1.size, InvestParameters) and not isinstance( - self.element.in2.size, InvestParameters - ): - self.element.in2.size = InvestParameters(maximum_size=self.element.in1.size.maximum_size) - super().do_modeling() # first direction From 6baeb8e5c0837bcea51add5914594a68347b3a98 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:29:46 +0200 Subject: [PATCH 133/183] Make transformation of data more hierarchical (Flows after Components) --- flixopt/elements.py | 3 +++ flixopt/flow_system.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index 721c8d50d..9df367eec 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -72,6 +72,9 @@ def transform_data(self, flow_system: 'FlowSystem') -> None: if self.on_off_parameters is not None: self.on_off_parameters.transform_data(flow_system, self.label_full) + for flow in self.inputs + self.outputs: + flow.transform_data(flow_system) + def _check_unique_flow_labels(self): all_flow_labels = [flow.label for flow in self.inputs + self.outputs] diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 7849cdcea..200e8eb60 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -334,7 +334,7 @@ def connect_and_transform(self): ) if not self._connected_and_transformed: self._connect_network() - for element in self.all_elements.values(): + for element in list(self.components.values()) + list(self.effects.effects.values()) + list(self.buses.values()): element.transform_data(self) self._connected_and_transformed = True From aeaaa8356e60cabfccd06b27725c60660f6845e3 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:47:23 +0200 Subject: [PATCH 134/183] Add scenario validation --- flixopt/flow_system.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 200e8eb60..b513372bf 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -69,7 +69,7 @@ def __init__( self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( timesteps, hours_of_previous_timesteps ) - self.scenarios = scenarios + self.scenarios = None if scenarios is None else self._validate_scenarios(scenarios) self.scenario_weights = scenario_weights # Element collections @@ -94,6 +94,22 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: raise ValueError('timesteps must be sorted') return timesteps + @staticmethod + def _validate_scenarios(scenarios: pd.Index) -> pd.Index: + """ + Validate and prepare scenario index. + + Args: + scenarios: The scenario index to validate + """ + if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: + raise ConversionError('Scenarios must be a non-empty Index') + + if not scenarios.name == 'scenario': + raise ConversionError(f'Scenarios must be named "scenario", got "{scenarios.name}"') + + return scenarios + @staticmethod def _create_timesteps_with_extra( timesteps: pd.DatetimeIndex, hours_of_last_timestep: Optional[float] From 15fd1241f2dc0fb93880b077b5272ce4eb837571 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:57:10 +0200 Subject: [PATCH 135/183] Change Transmission to have a "balanced" attribute. Change Tests accordingly --- flixopt/components.py | 32 +++---- tests/test_component.py | 175 +++++++++++++++++++++++++++++++++++++- tests/test_integration.py | 104 +--------------------- 3 files changed, 191 insertions(+), 120 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index 2f566963b..cf05af0ed 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -305,6 +305,7 @@ def __init__( absolute_losses: Optional[TemporalDataUser] = None, on_off_parameters: OnOffParameters = None, prevent_simultaneous_flows_in_both_directions: bool = True, + balanced: bool = False, meta_data: Optional[Dict] = None, ): """ @@ -322,6 +323,7 @@ def __init__( absolute_losses: The absolute loss, occur only when the Flow is on. Induces the creation of the ON-Variable on_off_parameters: Parameters defining the on/off behavior of the component. prevent_simultaneous_flows_in_both_directions: If True, inflow and outflow are not allowed to be both non-zero at same timestep. + balanced: Wether to equate the size of the in1 and in2 Flow. Needs InvestParameters in both Flows. meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types. """ super().__init__( @@ -341,6 +343,7 @@ def __init__( self.relative_losses = relative_losses self.absolute_losses = absolute_losses + self.balanced = balanced def _plausibility_checks(self): super()._plausibility_checks() @@ -353,23 +356,20 @@ def _plausibility_checks(self): assert self.out2.bus == self.in1.bus, ( f'Input 1 and Output 2 do not start/end at the same Bus: {self.in1.bus=}, {self.out2.bus=}' ) - # Check Investments - for flow in [self.out1, self.in2, self.out2]: - if flow is not None and isinstance(flow.size, InvestParameters): - raise ValueError( - 'Transmission currently does not support separate InvestParameters for Flows. ' - 'Please use Flow in1. The size of in2 is equal to in1. This is handled internally' - ) - # Make sure either None or both in Flows have InvestParameters - if self.in2 is not None: - if isinstance(self.in1.size, InvestParameters) and not isinstance( - self.in2.size, InvestParameters + if self.balanced: + if self.in2 is None: + raise ValueError('Balanced Transmission needs InvestParameters in both in-Flows') + if not isinstance(self.in1.size, InvestParameters) or not isinstance(self.in2.size, InvestParameters): + raise ValueError('Balanced Transmission needs InvestParameters in both in-Flows') + if ( + (self.in1.size.minimum_or_fixed_size > self.in2.size.maximum_or_fixed_size).any() or + (self.in1.size.maximum_or_fixed_size < self.in2.size.minimum_or_fixed_size).any() ): - array_name = self.in1.size.maximum_size.name.replace(self.in1, self.in2) - self.in2.size = InvestParameters( - maximum_size=self.in1.size.maximum_size.rename(array_name) - ) + raise ValueError( + f'Balanced Transmission needs compatible minimum and maximum sizes.' + f'Got: {self.in1.size.minimum_size=}, {self.in1.size.maximum_size=}, {self.in1.size.fixed_size=} and ' + f'{self.in2.size.minimum_size=}, {self.in2.size.maximum_size=}, {self.in2.size.fixed_size=}.') def create_model(self, model) -> 'TransmissionModel': self._plausibility_checks() @@ -410,7 +410,7 @@ def do_modeling(self): self.create_transmission_equation('dir2', self.element.in2, self.element.out2) # equate size of both directions - if isinstance(self.element.in1.size, InvestParameters) and self.element.in2 is not None: + if self.element.balanced: # eq: in1.size = in2.size self.add( self._model.add_constraints( diff --git a/tests/test_component.py b/tests/test_component.py index 18ceb717a..8a99b5d5b 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -6,7 +6,7 @@ import flixopt as fx import flixopt.elements -from .conftest import assert_conequal, assert_var_equal, create_linopy_model +from .conftest import assert_conequal, assert_var_equal, create_linopy_model, create_calculation_and_solve, assert_almost_equal_numeric class TestComponentModel: @@ -182,3 +182,176 @@ def test_on_with_single_flow(self, basic_flow_system_linopy): model.variables['TestComponent|on'] * 100 >= model.variables['TestComponent(In1)|flow_rate'], ) + +class TestTransmissionModel: + def test_transmission_basic(self, basic_flow_system, highs_solver): + """Test basic transmission functionality""" + flow_system = basic_flow_system + flow_system.add_elements(fx.Bus('Wärme lokal')) + + boiler = fx.linear_converters.Boiler( + 'Boiler', eta=0.5, Q_th=fx.Flow('Q_th', bus='Wärme lokal'), Q_fu=fx.Flow('Q_fu', bus='Gas') + ) + + transmission = fx.Transmission( + 'Rohr', + relative_losses=0.2, + absolute_losses=20, + in1=fx.Flow('Rohr1', 'Wärme lokal', size=fx.InvestParameters(specific_effects=5, maximum_size=1e6)), + out1=fx.Flow('Rohr2', 'Fernwärme', size=1000), + ) + + flow_system.add_elements(transmission, boiler) + + _ = create_calculation_and_solve(flow_system, highs_solver, 'test_transmission_basic') + + # Assertions + assert_almost_equal_numeric( + transmission.in1.model.on_off.on.solution.values, + np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), + 'On does not work properly', + ) + + assert_almost_equal_numeric( + transmission.in1.model.flow_rate.solution.values * 0.8 - 20, + transmission.out1.model.flow_rate.solution.values, + 'Losses are not computed correctly', + ) + + def test_transmission_balanced(self, basic_flow_system, highs_solver): + """Test advanced transmission functionality""" + flow_system = basic_flow_system + flow_system.add_elements(fx.Bus('Wärme lokal')) + + boiler = fx.linear_converters.Boiler( + 'Boiler_Standard', + eta=0.9, + Q_th=fx.Flow('Q_th', bus='Fernwärme', relative_maximum=np.array([0, 0, 0, 1, 1, 1, 1, 1, 1, 1])), + Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + + boiler2 = fx.linear_converters.Boiler( + 'Boiler_backup', eta=0.4, Q_th=fx.Flow('Q_th', bus='Wärme lokal'), Q_fu=fx.Flow('Q_fu', bus='Gas') + ) + + last2 = fx.Sink( + 'Wärmelast2', + sink=fx.Flow( + 'Q_th_Last', + bus='Wärme lokal', + size=1, + fixed_relative_profile=flow_system.components['Wärmelast'].sink.fixed_relative_profile + * np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), + ), + ) + + transmission = fx.Transmission( + 'Rohr', + relative_losses=0.2, + absolute_losses=20, + in1=fx.Flow('Rohr1a', bus='Wärme lokal', size=fx.InvestParameters(specific_effects=5, maximum_size=1000)), + out1=fx.Flow('Rohr1b', 'Fernwärme', size=1000), + in2=fx.Flow('Rohr2a', 'Fernwärme', size=fx.InvestParameters()), + out2=fx.Flow('Rohr2b', bus='Wärme lokal', size=1000), + balanced=True, + ) + + flow_system.add_elements(transmission, boiler, boiler2, last2) + + calculation = create_calculation_and_solve(flow_system, highs_solver, 'test_transmission_advanced') + + # Assertions + assert_almost_equal_numeric( + transmission.in1.model.on_off.on.solution.values, + np.array([1, 1, 1, 0, 0, 0, 0, 0, 0, 0]), + 'On does not work properly', + ) + + assert_almost_equal_numeric( + calculation.results.model.variables['Rohr(Rohr1b)|flow_rate'].solution.values, + transmission.out1.model.flow_rate.solution.values, + 'Flow rate of Rohr__Rohr1b is not correct', + ) + + assert_almost_equal_numeric( + transmission.in1.model.flow_rate.solution.values * 0.8 + - np.array([20 if val > 0.1 else 0 for val in transmission.in1.model.flow_rate.solution.values]), + transmission.out1.model.flow_rate.solution.values, + 'Losses are not computed correctly', + ) + + assert_almost_equal_numeric( + transmission.in1.model._investment.size.solution.item(), + transmission.in2.model._investment.size.solution.item(), + 'The Investments are not equated correctly', + ) + + def test_transmission_unbalanced(self, basic_flow_system, highs_solver): + """Test advanced transmission functionality""" + flow_system = basic_flow_system + flow_system.add_elements(fx.Bus('Wärme lokal')) + + boiler = fx.linear_converters.Boiler( + 'Boiler_Standard', + eta=0.9, + Q_th=fx.Flow('Q_th', bus='Fernwärme', relative_maximum=np.array([0, 0, 0, 1, 1, 1, 1, 1, 1, 1])), + Q_fu=fx.Flow('Q_fu', bus='Gas'), + ) + + boiler2 = fx.linear_converters.Boiler( + 'Boiler_backup', eta=0.4, Q_th=fx.Flow('Q_th', bus='Wärme lokal'), Q_fu=fx.Flow('Q_fu', bus='Gas') + ) + + last2 = fx.Sink( + 'Wärmelast2', + sink=fx.Flow( + 'Q_th_Last', + bus='Wärme lokal', + size=1, + fixed_relative_profile=flow_system.components['Wärmelast'].sink.fixed_relative_profile + * np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), + ), + ) + + transmission = fx.Transmission( + 'Rohr', + relative_losses=0.2, + absolute_losses=20, + in1=fx.Flow('Rohr1a', bus='Wärme lokal', size=fx.InvestParameters(specific_effects=50, maximum_size=1000)), + out1=fx.Flow('Rohr1b', 'Fernwärme', size=1000), + in2=fx.Flow('Rohr2a', 'Fernwärme', size=fx.InvestParameters(specific_effects=100, minimum_size=10, optional=False)), + out2=fx.Flow('Rohr2b', bus='Wärme lokal', size=1000), + balanced=False, + ) + + flow_system.add_elements(transmission, boiler, boiler2, last2) + + calculation = create_calculation_and_solve(flow_system, highs_solver, 'test_transmission_advanced') + + # Assertions + assert_almost_equal_numeric( + transmission.in1.model.on_off.on.solution.values, + np.array([1, 1, 1, 0, 0, 0, 0, 0, 0, 0]), + 'On does not work properly', + ) + + assert_almost_equal_numeric( + calculation.results.model.variables['Rohr(Rohr1b)|flow_rate'].solution.values, + transmission.out1.model.flow_rate.solution.values, + 'Flow rate of Rohr__Rohr1b is not correct', + ) + + assert_almost_equal_numeric( + transmission.in1.model.flow_rate.solution.values * 0.8 + - np.array([20 if val > 0.1 else 0 for val in transmission.in1.model.flow_rate.solution.values]), + transmission.out1.model.flow_rate.solution.values, + 'Losses are not computed correctly', + ) + + assert transmission.in1.model._investment.size.solution.item() > 11 + + assert_almost_equal_numeric( + transmission.in2.model._investment.size.solution.item(), + 10, + 'Sizing does not work properly', + ) diff --git a/tests/test_integration.py b/tests/test_integration.py index da473b4e6..e3d44d764 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -73,109 +73,6 @@ def test_results_persistence(self, simple_flow_system, highs_solver): assert_almost_equal_numeric(results.solution['CO2|total'].values, 255.09184, 'CO2 doesnt match expected value') -class TestComponents: - def test_transmission_basic(self, basic_flow_system, highs_solver): - """Test basic transmission functionality""" - flow_system = basic_flow_system - flow_system.add_elements(fx.Bus('Wärme lokal')) - - boiler = fx.linear_converters.Boiler( - 'Boiler', eta=0.5, Q_th=fx.Flow('Q_th', bus='Wärme lokal'), Q_fu=fx.Flow('Q_fu', bus='Gas') - ) - - transmission = fx.Transmission( - 'Rohr', - relative_losses=0.2, - absolute_losses=20, - in1=fx.Flow('Rohr1', 'Wärme lokal', size=fx.InvestParameters(specific_effects=5, maximum_size=1e6)), - out1=fx.Flow('Rohr2', 'Fernwärme', size=1000), - ) - - flow_system.add_elements(transmission, boiler) - - _ = create_calculation_and_solve(flow_system, highs_solver, 'test_transmission_basic') - - # Assertions - assert_almost_equal_numeric( - transmission.in1.model.on_off.on.solution.values, - np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), - 'On does not work properly', - ) - - assert_almost_equal_numeric( - transmission.in1.model.flow_rate.solution.values * 0.8 - 20, - transmission.out1.model.flow_rate.solution.values, - 'Losses are not computed correctly', - ) - - def test_transmission_advanced(self, basic_flow_system, highs_solver): - """Test advanced transmission functionality""" - flow_system = basic_flow_system - flow_system.add_elements(fx.Bus('Wärme lokal')) - - boiler = fx.linear_converters.Boiler( - 'Boiler_Standard', - eta=0.9, - Q_th=fx.Flow('Q_th', bus='Fernwärme', relative_maximum=np.array([0, 0, 0, 1, 1, 1, 1, 1, 1, 1])), - Q_fu=fx.Flow('Q_fu', bus='Gas'), - ) - - boiler2 = fx.linear_converters.Boiler( - 'Boiler_backup', eta=0.4, Q_th=fx.Flow('Q_th', bus='Wärme lokal'), Q_fu=fx.Flow('Q_fu', bus='Gas') - ) - - last2 = fx.Sink( - 'Wärmelast2', - sink=fx.Flow( - 'Q_th_Last', - bus='Wärme lokal', - size=1, - fixed_relative_profile=flow_system.components['Wärmelast'].sink.fixed_relative_profile - * np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), - ), - ) - - transmission = fx.Transmission( - 'Rohr', - relative_losses=0.2, - absolute_losses=20, - in1=fx.Flow('Rohr1a', bus='Wärme lokal', size=fx.InvestParameters(specific_effects=5, maximum_size=1000)), - out1=fx.Flow('Rohr1b', 'Fernwärme', size=1000), - in2=fx.Flow('Rohr2a', 'Fernwärme', size=1000), - out2=fx.Flow('Rohr2b', bus='Wärme lokal', size=1000), - ) - - flow_system.add_elements(transmission, boiler, boiler2, last2) - - calculation = create_calculation_and_solve(flow_system, highs_solver, 'test_transmission_advanced') - - # Assertions - assert_almost_equal_numeric( - transmission.in1.model.on_off.on.solution.values, - np.array([1, 1, 1, 0, 0, 0, 0, 0, 0, 0]), - 'On does not work properly', - ) - - assert_almost_equal_numeric( - calculation.results.model.variables['Rohr(Rohr1b)|flow_rate'].solution.values, - transmission.out1.model.flow_rate.solution.values, - 'Flow rate of Rohr__Rohr1b is not correct', - ) - - assert_almost_equal_numeric( - transmission.in1.model.flow_rate.solution.values * 0.8 - - np.array([20 if val > 0.1 else 0 for val in transmission.in1.model.flow_rate.solution.values]), - transmission.out1.model.flow_rate.solution.values, - 'Losses are not computed correctly', - ) - - assert_almost_equal_numeric( - transmission.in1.model._investment.size.solution.item(), - transmission.in2.model._investment.size.solution.item(), - 'The Investments are not equated correctly', - ) - - class TestComplex: def test_basic_flow_system(self, flow_system_base, highs_solver): calculation = create_calculation_and_solve(flow_system_base, highs_solver, 'test_basic_flow_system') @@ -354,6 +251,7 @@ def test_piecewise_conversion(self, flow_system_piecewise_conversion, highs_solv 'Speicher investCosts_segmented_costs doesnt match expected value', ) + @pytest.mark.slow class TestModelingTypes: @pytest.fixture(params=['full', 'segmented', 'aggregated']) From d0b231dfba896b9b57045ce0d4b3fec0fe596fac Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 28 Jun 2025 22:57:23 +0200 Subject: [PATCH 136/183] Improve index validations --- flixopt/core.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 648344098..3b320276d 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -246,7 +246,8 @@ def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: raise ConversionError('Timesteps must be a non-empty DatetimeIndex') if not timesteps.name == 'time': - raise ConversionError(f'Scenarios must be named "time", got "{timesteps.name}"') + logger.warning(f'Timesteps must be named "time", got "{timesteps.name}". Renaming to "time".') + timesteps = timesteps.rename('time') return timesteps @@ -262,7 +263,8 @@ def _validate_scenarios(scenarios: pd.Index) -> pd.Index: raise ConversionError('Scenarios must be a non-empty Index') if not scenarios.name == 'scenario': - raise ConversionError(f'Scenarios must be named "scenario", got "{scenarios.name}"') + logger.warning(f'Scenarios must be named "scenario", got "{scenarios.name}". Renaming to "scenario".') + scenarios = scenarios.rename('scenario') return scenarios From 4ebe6a5343f8a303a9286a31399e568b437239a2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 20:50:58 +0200 Subject: [PATCH 137/183] rename method in tests --- tests/test_dataconverter.py | 151 ++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 76 deletions(-) diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 0484d4aac..1ad41a0d2 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -6,7 +6,6 @@ from flixopt.core import ( # Adjust this import to match your project structure ConversionError, DataConverter, - TimeSeries, ) @@ -32,39 +31,39 @@ class TestSingleDimensionConversion: def test_scalar_conversion(self, sample_time_index): """Test converting a scalar value.""" # Test with integer - result = DataConverter.as_dataarray(42, sample_time_index) + result = DataConverter.to_dataarray(42, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (len(sample_time_index),) assert result.dims == ('time',) assert np.all(result.values == 42) # Test with float - result = DataConverter.as_dataarray(42.5, sample_time_index) + result = DataConverter.to_dataarray(42.5, sample_time_index) assert np.all(result.values == 42.5) # Test with numpy scalar types - result = DataConverter.as_dataarray(np.int64(42), sample_time_index) + result = DataConverter.to_dataarray(np.int64(42), sample_time_index) assert np.all(result.values == 42) - result = DataConverter.as_dataarray(np.float32(42.5), sample_time_index) + result = DataConverter.to_dataarray(np.float32(42.5), sample_time_index) assert np.all(result.values == 42.5) def test_ndarray_conversion(self, sample_time_index): """Test converting a numpy ndarray.""" # Test with integer 1D array arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index) assert result.shape == (5,) assert result.dims == ('time',) assert np.array_equal(result.values, arr_1d) # Test with float 1D array arr_1d = np.array([1.1, 2.2, 3.3, 4.4, 5.5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index) assert np.array_equal(result.values, arr_1d) # Test with array containing NaN arr_1d = np.array([1, np.nan, 3, np.nan, 5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index) assert np.array_equal(np.isnan(result.values), np.isnan(arr_1d)) assert np.array_equal(result.values[~np.isnan(result.values)], arr_1d[~np.isnan(arr_1d)]) @@ -74,7 +73,7 @@ def test_dataarray_conversion(self, sample_time_index): original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) # Convert and check - result = DataConverter.as_dataarray(original, sample_time_index) + result = DataConverter.to_dataarray(original, sample_time_index) assert result.shape == (5,) assert result.dims == ('time',) assert np.array_equal(result.values, original.values) @@ -89,7 +88,7 @@ def test_dataarray_conversion(self, sample_time_index): # Should raise an error for mismatched time coordinates with pytest.raises(ConversionError): - DataConverter.as_dataarray(original, sample_time_index) + DataConverter.to_dataarray(original, sample_time_index) class TestMultiDimensionConversion: @@ -98,7 +97,7 @@ class TestMultiDimensionConversion: def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): """Test converting scalar values with scenario dimension.""" # Test with integer - result = DataConverter.as_dataarray(42, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(42, sample_time_index, sample_scenario_index) assert isinstance(result, xr.DataArray) assert result.shape == (len(sample_time_index), len(sample_scenario_index)) @@ -108,7 +107,7 @@ def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): assert set(result.coords['time'].values) == set(sample_time_index.values) # Test with float - result = DataConverter.as_dataarray(42.5, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(42.5, sample_time_index, sample_scenario_index) assert np.all(result.values == 42.5) def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index): @@ -117,7 +116,7 @@ def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index) arr_1d = np.array([1, 2, 3, 4, 5]) # Convert with scenarios - result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) assert result.shape == (len(sample_time_index), len(sample_scenario_index)) assert result.dims == ('time', 'scenario') @@ -139,7 +138,7 @@ def test_2d_array_with_scenarios(self, sample_time_index, sample_scenario_index) ) # Convert to DataArray - result = DataConverter.as_dataarray(arr_2d.T, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(arr_2d.T, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') @@ -159,7 +158,7 @@ def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index ) # Test conversion - result = DataConverter.as_dataarray(original, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(original, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') @@ -179,7 +178,7 @@ def test_series_single_dimension(self, sample_time_index): series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) # Convert and check - result = DataConverter.as_dataarray(series, sample_time_index) + result = DataConverter.to_dataarray(series, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) @@ -190,7 +189,7 @@ def test_series_single_dimension(self, sample_time_index): scenario_index = pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') series = pd.Series([100, 200, 300], index=scenario_index) - result = DataConverter.as_dataarray(series, scenarios=scenario_index) + result = DataConverter.to_dataarray(series, scenarios=scenario_index) assert result.shape == (3,) assert result.dims == ('scenario',) assert np.array_equal(result.values, series.values) @@ -204,7 +203,7 @@ def test_series_mismatched_index(self, sample_time_index): # Should raise error for mismatched index with pytest.raises(ConversionError): - DataConverter.as_dataarray(series, sample_time_index) + DataConverter.to_dataarray(series, sample_time_index) def test_series_broadcast_to_scenarios(self, sample_time_index, sample_scenario_index): """Test broadcasting a time-indexed Series across scenarios.""" @@ -212,7 +211,7 @@ def test_series_broadcast_to_scenarios(self, sample_time_index, sample_scenario_ series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) # Convert with scenarios - result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(series, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') @@ -228,7 +227,7 @@ def test_series_broadcast_to_time(self, sample_time_index, sample_scenario_index series = pd.Series([100, 200, 300], index=sample_scenario_index) # Convert with time - result = DataConverter.as_dataarray(series, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(series, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') @@ -264,7 +263,7 @@ def test_dataframe_single_column(self, sample_time_index): df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) # Convert and check - result = DataConverter.as_dataarray(df, sample_time_index) + result = DataConverter.to_dataarray(df, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) @@ -277,7 +276,7 @@ def test_dataframe_multi_column_fails(self, sample_time_index): # Should raise error with pytest.raises(ConversionError): - DataConverter.as_dataarray(df, sample_time_index) + DataConverter.to_dataarray(df, sample_time_index) def test_dataframe_time_scenario(self, sample_time_index, sample_scenario_index): """Test converting a DataFrame with time index and scenario columns.""" @@ -289,7 +288,7 @@ def test_dataframe_time_scenario(self, sample_time_index, sample_scenario_index) df.columns.name = 'scenario' # Convert and check - result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') @@ -309,7 +308,7 @@ def test_dataframe_mismatched_coordinates(self, sample_time_index, sample_scenar # Should raise error with pytest.raises(ConversionError): - DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) # Create DataFrame with different scenario columns different_scenarios = pd.Index(['scenario1', 'scenario2', 'scenario3'], name='scenario') @@ -319,7 +318,7 @@ def test_dataframe_mismatched_coordinates(self, sample_time_index, sample_scenar # Should raise error with pytest.raises(ConversionError): - DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) def test_ensure_copy(self, sample_time_index, sample_scenario_index): """Test that the returned DataArray is a copy.""" @@ -329,7 +328,7 @@ def test_ensure_copy(self, sample_time_index, sample_scenario_index): df.columns = sample_scenario_index # Convert - result = DataConverter.as_dataarray(df, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) # Modify the result result.loc[dict(time=sample_time_index[0], scenario='baseline')] = 999 @@ -346,51 +345,51 @@ def test_time_index_validation(self): # Test with unnamed index unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, unnamed_index) + DataConverter.to_dataarray(42, unnamed_index) # Test with empty index empty_index = pd.DatetimeIndex([], name='time') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, empty_index) + DataConverter.to_dataarray(42, empty_index) # Test with non-DatetimeIndex wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, wrong_type_index) + DataConverter.to_dataarray(42, wrong_type_index) def test_scenario_index_validation(self, sample_time_index): """Test validation of scenario index.""" # Test with unnamed scenario index unnamed_index = pd.Index(['baseline', 'high_demand']) with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, sample_time_index, unnamed_index) + DataConverter.to_dataarray(42, sample_time_index, unnamed_index) # Test with empty scenario index empty_index = pd.Index([], name='scenario') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, sample_time_index, empty_index) + DataConverter.to_dataarray(42, sample_time_index, empty_index) # Test with non-Index scenario with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, sample_time_index, ['baseline', 'high_demand']) + DataConverter.to_dataarray(42, sample_time_index, ['baseline', 'high_demand']) def test_invalid_data_types(self, sample_time_index, sample_scenario_index): """Test handling of invalid data types.""" # Test invalid input type (string) with pytest.raises(ConversionError): - DataConverter.as_dataarray('invalid_string', sample_time_index) + DataConverter.to_dataarray('invalid_string', sample_time_index) # Test invalid input type with scenarios with pytest.raises(ConversionError): - DataConverter.as_dataarray('invalid_string', sample_time_index, sample_scenario_index) + DataConverter.to_dataarray('invalid_string', sample_time_index, sample_scenario_index) # Test unsupported complex object with pytest.raises(ConversionError): - DataConverter.as_dataarray(object(), sample_time_index) + DataConverter.to_dataarray(object(), sample_time_index) # Test None value with pytest.raises(ConversionError): - DataConverter.as_dataarray(None, sample_time_index) + DataConverter.to_dataarray(None, sample_time_index) def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_index): """Test handling of mismatched input dimensions.""" @@ -399,16 +398,16 @@ def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_in [1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D', name='time') ) with pytest.raises(ConversionError): - DataConverter.as_dataarray(mismatched_series, sample_time_index) + DataConverter.to_dataarray(mismatched_series, sample_time_index) # Test DataFrame with multiple columns df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) with pytest.raises(ConversionError): - DataConverter.as_dataarray(df_multi_col, sample_time_index) + DataConverter.to_dataarray(df_multi_col, sample_time_index) # Test mismatched array shape for time-only with pytest.raises(ConversionError): - DataConverter.as_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length + DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length # Test mismatched array shape for scenario × time # Array shape should be (n_scenarios, n_timesteps) @@ -420,24 +419,24 @@ def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_in ] ) with pytest.raises(ConversionError): - DataConverter.as_dataarray(wrong_shape_array, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(wrong_shape_array, sample_time_index, sample_scenario_index) # Test array with too many dimensions with pytest.raises(ConversionError): # 3D array not allowed - DataConverter.as_dataarray(np.ones((3, 5, 2)), sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(np.ones((3, 5, 2)), sample_time_index, sample_scenario_index) def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_index): """Test handling of mismatched DataArray dimensions.""" # Create DataArray with wrong dimensions wrong_dims = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'wrong_dim': range(5)}, dims=['wrong_dim']) with pytest.raises(ConversionError): - DataConverter.as_dataarray(wrong_dims, sample_time_index) + DataConverter.to_dataarray(wrong_dims, sample_time_index) # Create DataArray with scenario but no time wrong_dims_2 = xr.DataArray(data=np.array([1, 2, 3]), coords={'scenario': ['a', 'b', 'c']}, dims=['scenario']) with pytest.raises(ConversionError): - DataConverter.as_dataarray(wrong_dims_2, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(wrong_dims_2, sample_time_index, sample_scenario_index) # Create DataArray with right dims but wrong length wrong_length = xr.DataArray( @@ -449,7 +448,7 @@ def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_i dims=['scenario', 'time'], ) with pytest.raises(ConversionError): - DataConverter.as_dataarray(wrong_length, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(wrong_length, sample_time_index, sample_scenario_index) class TestDataArrayBroadcasting: """Tests for broadcasting DataArrays.""" @@ -458,7 +457,7 @@ def test_broadcast_1d_array_to_2d(self, sample_time_index, sample_scenario_index arr_1d = np.array([1, 2, 3, 4, 5]) xr.testing.assert_equal( - DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index), + DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index), xr.DataArray( np.array([arr_1d] * 3).T, coords=(sample_time_index, sample_scenario_index) @@ -467,7 +466,7 @@ def test_broadcast_1d_array_to_2d(self, sample_time_index, sample_scenario_index arr_1d = np.array([1, 2, 3]) xr.testing.assert_equal( - DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index), + DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index), xr.DataArray( np.array([arr_1d] * 5), coords=(sample_time_index, sample_scenario_index) @@ -479,7 +478,7 @@ def test_broadcast_1d_array_to_1d(self, sample_time_index,): arr_1d = np.array([1, 2, 3, 4, 5]) xr.testing.assert_equal( - DataConverter.as_dataarray(arr_1d, sample_time_index), + DataConverter.to_dataarray(arr_1d, sample_time_index), xr.DataArray( arr_1d, coords=(sample_time_index,) @@ -488,7 +487,7 @@ def test_broadcast_1d_array_to_1d(self, sample_time_index,): arr_1d = np.array([1, 2, 3]) with pytest.raises(ConversionError): - DataConverter.as_dataarray(arr_1d, sample_time_index) + DataConverter.to_dataarray(arr_1d, sample_time_index) class TestEdgeCases: @@ -500,12 +499,12 @@ def test_single_timestep(self, sample_scenario_index): single_timestep = pd.DatetimeIndex(['2024-01-01'], name='time') # Scalar conversion - result = DataConverter.as_dataarray(42, single_timestep) + result = DataConverter.to_dataarray(42, single_timestep) assert result.shape == (1,) assert result.dims == ('time',) # With scenarios - result_with_scenarios = DataConverter.as_dataarray(42, single_timestep, sample_scenario_index) + result_with_scenarios = DataConverter.to_dataarray(42, single_timestep, sample_scenario_index) assert result_with_scenarios.shape == (1, len(sample_scenario_index)) assert result_with_scenarios.dims == ('time', 'scenario') @@ -515,19 +514,19 @@ def test_single_scenario(self, sample_time_index): single_scenario = pd.Index(['baseline'], name='scenario') # Scalar conversion with single scenario - result = DataConverter.as_dataarray(42, sample_time_index, single_scenario) + result = DataConverter.to_dataarray(42, sample_time_index, single_scenario) assert result.shape == (len(sample_time_index), 1) assert result.dims == ('time', 'scenario') # Array conversion with single scenario arr = np.array([1, 2, 3, 4, 5]) - result_arr = DataConverter.as_dataarray(arr, sample_time_index, single_scenario) + result_arr = DataConverter.to_dataarray(arr, sample_time_index, single_scenario) assert result_arr.shape == (5, 1) assert np.array_equal(result_arr.sel(scenario='baseline').values, arr) # 2D array with single scenario arr_2d = np.array([[1, 2, 3, 4, 5]]) # Note the extra dimension - result_arr_2d = DataConverter.as_dataarray(arr_2d.T, sample_time_index, single_scenario) + result_arr_2d = DataConverter.to_dataarray(arr_2d.T, sample_time_index, single_scenario) assert result_arr_2d.shape == (5, 1) assert np.array_equal(result_arr_2d.sel(scenario='baseline').values, arr_2d[0]) @@ -546,12 +545,12 @@ def test_different_scenario_order(self, sample_time_index): ] ).T - result1 = DataConverter.as_dataarray(data, sample_time_index, scenarios1) + result1 = DataConverter.to_dataarray(data, sample_time_index, scenarios1) assert np.array_equal(result1.sel(scenario='a').values, [1, 2, 3, 4, 5]) assert np.array_equal(result1.sel(scenario='c').values, [11, 12, 13, 14, 15]) # Create DataArray with second order - result2 = DataConverter.as_dataarray(data, sample_time_index, scenarios2) + result2 = DataConverter.to_dataarray(data, sample_time_index, scenarios2) # First row should match 'c' now assert np.array_equal(result2.sel(scenario='c').values, [1, 2, 3, 4, 5]) # Last row should match 'a' now @@ -561,16 +560,16 @@ def test_all_nan_data(self, sample_time_index, sample_scenario_index): """Test handling of all-NaN data.""" # Create array of all NaNs all_nan_array = np.full(5, np.nan) - result = DataConverter.as_dataarray(all_nan_array, sample_time_index) + result = DataConverter.to_dataarray(all_nan_array, sample_time_index) assert np.all(np.isnan(result.values)) # With scenarios - result = DataConverter.as_dataarray(all_nan_array, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(all_nan_array, sample_time_index, sample_scenario_index) assert result.shape == (len(sample_time_index), len(sample_scenario_index)) assert np.all(np.isnan(result.values)) # Series of all NaNs - result = DataConverter.as_dataarray( + result = DataConverter.to_dataarray( np.array([np.nan, np.nan, np.nan, np.nan, np.nan]), sample_time_index, sample_scenario_index ) assert np.all(np.isnan(result.values)) @@ -579,14 +578,14 @@ def test_mixed_data_types(self, sample_time_index, sample_scenario_index): """Test conversion of mixed integer and float data.""" # Create array with mixed types mixed_array = np.array([1, 2.5, 3, 4.5, 5]) - result = DataConverter.as_dataarray(mixed_array, sample_time_index) + result = DataConverter.to_dataarray(mixed_array, sample_time_index) # Result should be float dtype assert np.issubdtype(result.dtype, np.floating) assert np.array_equal(result.values, mixed_array) # With scenarios - result = DataConverter.as_dataarray(mixed_array, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(mixed_array, sample_time_index, sample_scenario_index) assert np.issubdtype(result.dtype, np.floating) for scenario in sample_scenario_index: assert np.array_equal(result.sel(scenario=scenario).values, mixed_array) @@ -609,7 +608,7 @@ def test_large_dataset(self, sample_scenario_index): large_data = np.random.rand(len(sample_scenario_index), len(large_timesteps)) # Convert and check - result = DataConverter.as_dataarray(large_data.T, large_timesteps, sample_scenario_index) + result = DataConverter.to_dataarray(large_data.T, large_timesteps, sample_scenario_index) assert result.shape == (len(large_timesteps), len(sample_scenario_index)) assert result.dims == ('time', 'scenario') @@ -622,7 +621,7 @@ class TestMultiScenarioArrayConversion: def test_1d_array_broadcasting(self, sample_time_index, sample_scenario_index): """Test that 1D arrays are properly broadcast to all scenarios.""" arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.as_dataarray(arr_1d, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) assert result.shape == (len(sample_time_index), len(sample_scenario_index)) @@ -643,14 +642,14 @@ def test_2d_array_different_shapes(self, sample_time_index): single_scenario = pd.Index(['baseline'], name='scenario') arr_1_scenario = np.array([[1, 2, 3, 4, 5]]) - result = DataConverter.as_dataarray(arr_1_scenario.T, sample_time_index, single_scenario) + result = DataConverter.to_dataarray(arr_1_scenario.T, sample_time_index, single_scenario) assert result.shape == (len(sample_time_index), 1) # Test with 2 scenarios two_scenarios = pd.Index(['baseline', 'high_demand'], name='scenario') arr_2_scenarios = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) - result = DataConverter.as_dataarray(arr_2_scenarios.T, sample_time_index, two_scenarios) + result = DataConverter.to_dataarray(arr_2_scenarios.T, sample_time_index, two_scenarios) assert result.shape == (len(sample_time_index), 2) assert np.array_equal(result.sel(scenario='baseline').values, arr_2_scenarios[0]) assert np.array_equal(result.sel(scenario='high_demand').values, arr_2_scenarios[1]) @@ -658,19 +657,19 @@ def test_2d_array_different_shapes(self, sample_time_index): # Test mismatched scenarios count three_scenarios = pd.Index(['a', 'b', 'c'], name='scenario') with pytest.raises(ConversionError): - DataConverter.as_dataarray(arr_2_scenarios, sample_time_index, three_scenarios) + DataConverter.to_dataarray(arr_2_scenarios, sample_time_index, three_scenarios) def test_array_handling_edge_cases(self, sample_time_index, sample_scenario_index): """Test array edge cases.""" # Test with boolean array bool_array = np.array([True, False, True, False, True]) - result = DataConverter.as_dataarray(bool_array, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(bool_array, sample_time_index, sample_scenario_index) assert result.dtype == bool assert result.shape == (len(sample_time_index), len(sample_scenario_index)) # Test with array containing infinite values inf_array = np.array([1, np.inf, 3, -np.inf, 5]) - result = DataConverter.as_dataarray(inf_array, sample_time_index, sample_scenario_index) + result = DataConverter.to_dataarray(inf_array, sample_time_index, sample_scenario_index) for scenario in sample_scenario_index: scenario_data = result.sel(scenario=scenario) assert np.isinf(scenario_data[1].item()) @@ -696,7 +695,7 @@ def test_preserving_scenario_order(self, sample_time_index): ) # Convert to DataArray - result = DataConverter.as_dataarray(data.T, sample_time_index, scenarios) + result = DataConverter.to_dataarray(data.T, sample_time_index, scenarios) # Verify order of scenarios is preserved assert list(result.coords['scenario'].values) == list(scenarios) @@ -714,42 +713,42 @@ def test_preserving_scenario_order(self, sample_time_index): def test_invalid_inputs(sample_time_index): # Test invalid input type with pytest.raises(ConversionError): - DataConverter.as_dataarray('invalid_string', sample_time_index) + DataConverter.to_dataarray('invalid_string', sample_time_index) # Test mismatched Series index mismatched_series = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D')) with pytest.raises(ConversionError): - DataConverter.as_dataarray(mismatched_series, sample_time_index) + DataConverter.to_dataarray(mismatched_series, sample_time_index) # Test DataFrame with multiple columns df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) with pytest.raises(ConversionError): - DataConverter.as_dataarray(df_multi_col, sample_time_index) + DataConverter.to_dataarray(df_multi_col, sample_time_index) # Test mismatched array shape with pytest.raises(ConversionError): - DataConverter.as_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length + DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length # Test multi-dimensional array with pytest.raises(ConversionError): - DataConverter.as_dataarray(np.array([[1, 2], [3, 4]]), sample_time_index) # 2D array not allowed + DataConverter.to_dataarray(np.array([[1, 2], [3, 4]]), sample_time_index) # 2D array not allowed def test_time_index_validation(): # Test with unnamed index unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, unnamed_index) + DataConverter.to_dataarray(42, unnamed_index) # Test with empty index empty_index = pd.DatetimeIndex([], name='time') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, empty_index) + DataConverter.to_dataarray(42, empty_index) # Test with non-DatetimeIndex wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') with pytest.raises(ConversionError): - DataConverter.as_dataarray(42, wrong_type_index) + DataConverter.to_dataarray(42, wrong_type_index) if __name__ == '__main__': From 6b56dac3818771df98e4ace468a79a258d509e71 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 20:52:42 +0200 Subject: [PATCH 138/183] Update DataConverter --- flixopt/core.py | 516 ++++++++--------------------------- tests/test_dataconverter.py | 522 ++++++++++-------------------------- 2 files changed, 255 insertions(+), 783 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 3b320276d..9ab71cab9 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -126,178 +126,77 @@ def agg_weight(self): class DataConverter: """ - Converts various data types into xarray.DataArray with optional time and scenario dimension. + Converts scalars and 1D data into xarray.DataArray with optional time and scenario dimensions. - Supports: scalars, arrays, Series, DataFrames, DataArrays, and TimeSeriesData. + Only handles: + - Scalars (int, float, np.number) + - 1D arrays (np.ndarray, pd.Series) + - xr.DataArray (for broadcasting/checking) """ - @staticmethod - def _fix_timeseries_data_indexing( - data: TimeSeriesData, timesteps: pd.DatetimeIndex, dims: list, coords: list - ) -> TimeSeriesData: - """ - Fix TimeSeriesData indexing issues and return properly indexed TimeSeriesData. - - Args: - data: TimeSeriesData that might have indexing issues - timesteps: Target timesteps - dims: Expected dimensions - coords: Expected coordinates - - Returns: - TimeSeriesData with correct indexing - - Raises: - ConversionError: If data cannot be fixed to match expected indexing - """ - expected_shape = (len(timesteps),) - - # Check if dimensions match - if data.dims != tuple(dims): - logger.warning( - f'TimeSeriesData has dimensions {data.dims}, expected {dims}. Reshaping to match timesteps. To avoid ' - f'this warning, create a correctly shaped DataArray with the correct dimensions in the first place.' - ) - # Try to reshape the data to match expected dimensions - if data.size != len(timesteps): - raise ConversionError( - f'TimeSeriesData has {data.size} elements, cannot reshape to match {len(timesteps)} timesteps' - ) - # Create new DataArray with correct coordinates, preserving metadata - reshaped_data = xr.DataArray( - data.values.reshape(expected_shape), coords=coords, dims=dims, name=data.name, attrs=data.attrs.copy() - ) - return TimeSeriesData(reshaped_data) - - # Check if time coordinate length matches - elif data.sizes[dims[0]] != len(coords[0]): - logger.warning( - f'TimeSeriesData has {data.sizes[dims[0]]} time points, ' - f"expected {len(coords[0])}. Cannot reindex - lengths don't match." - ) - raise ConversionError( - f"TimeSeriesData length {data.sizes[dims[0]]} doesn't match expected {len(coords[0])}" - ) - - # Check if time coordinates are identical - elif not data.coords['time'].equals(timesteps): - logger.warning( - 'TimeSeriesData has different time coordinates than expected. Replacing with provided timesteps.' - ) - # Replace time coordinates while preserving data and metadata - recoordinated_data = xr.DataArray( - data.values, coords=coords, dims=dims, name=data.name, attrs=data.attrs.copy() - ) - return TimeSeriesData(recoordinated_data) - - else: - # Everything matches - return copy to avoid modifying original - return data.copy(deep=True) - @staticmethod def to_dataarray( - data: Union[TemporalData, NonTemporalData], timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None + data: Union[Scalar, np.ndarray, pd.Series, xr.DataArray, TimeSeriesData], + timesteps: Optional[pd.DatetimeIndex] = None, + scenarios: Optional[pd.Index] = None, ) -> xr.DataArray: """ Convert data to xarray.DataArray with specified dimensions. Args: - data: The data to convert (scalar, array, or DataArray) + data: Scalar, 1D array/Series, or existing DataArray timesteps: Optional DatetimeIndex for time dimension scenarios: Optional Index for scenario dimension Returns: DataArray with the converted data """ - # Prepare dimensions and coordinates coords, dims = DataConverter._prepare_dimensions(timesteps, scenarios) - # Select appropriate converter based on data type + # Handle scalars if isinstance(data, (int, float, np.integer, np.floating)): return DataConverter._convert_scalar(data, coords, dims) - elif isinstance(data, xr.DataArray): - return DataConverter._convert_dataarray(data, coords, dims) - + # Handle 1D numpy arrays elif isinstance(data, np.ndarray): - return DataConverter._convert_ndarray(data, coords, dims) + if data.ndim != 1: + raise ConversionError(f'Only 1D arrays supported, got {data.ndim}D array') + return DataConverter._convert_1d_array(data, coords, dims) + # Handle pandas Series elif isinstance(data, pd.Series): return DataConverter._convert_series(data, coords, dims) - elif isinstance(data, pd.DataFrame): - return DataConverter._convert_dataframe(data, coords, dims) + # Handle existing DataArrays (including TimeSeriesData) + elif isinstance(data, xr.DataArray): + return DataConverter._handle_dataarray(data, coords, dims) else: - raise ConversionError(f'Unsupported data type: {type(data).__name__}') - - @staticmethod - def _validate_timesteps(timesteps: pd.DatetimeIndex) -> pd.DatetimeIndex: - """ - Validate and prepare time index. - - Args: - timesteps: The time index to validate - - Returns: - Validated time index - """ - if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: - raise ConversionError('Timesteps must be a non-empty DatetimeIndex') - - if not timesteps.name == 'time': - logger.warning(f'Timesteps must be named "time", got "{timesteps.name}". Renaming to "time".') - timesteps = timesteps.rename('time') - - return timesteps - - @staticmethod - def _validate_scenarios(scenarios: pd.Index) -> pd.Index: - """ - Validate and prepare scenario index. - - Args: - scenarios: The scenario index to validate - """ - if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: - raise ConversionError('Scenarios must be a non-empty Index') - - if not scenarios.name == 'scenario': - logger.warning(f'Scenarios must be named "scenario", got "{scenarios.name}". Renaming to "scenario".') - scenarios = scenarios.rename('scenario') - - return scenarios + raise ConversionError( + f'Unsupported data type: {type(data).__name__}. Only scalars, 1D arrays, Series, and DataArrays are supported.' + ) @staticmethod def _prepare_dimensions( timesteps: Optional[pd.DatetimeIndex], scenarios: Optional[pd.Index] ) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: - """ - Prepare coordinates and dimensions for the DataArray. - - Args: - timesteps: Optional time index - scenarios: Optional scenario index - - Returns: - Tuple of (coordinates dict, dimensions tuple) - """ - # Validate inputs if provided - if timesteps is not None: - timesteps = DataConverter._validate_timesteps(timesteps) - - if scenarios is not None: - scenarios = DataConverter._validate_scenarios(scenarios) - - # Build coordinates and dimensions + """Prepare coordinates and dimensions.""" coords = {} dims = [] if timesteps is not None: + if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: + raise ConversionError('Timesteps must be a non-empty DatetimeIndex') + if timesteps.name != 'time': + timesteps = timesteps.rename('time') coords['time'] = timesteps dims.append('time') if scenarios is not None: + if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: + raise ConversionError('Scenarios must be a non-empty Index') + if scenarios.name != 'scenario': + scenarios = scenarios.rename('scenario') coords['scenario'] = scenarios dims.append('scenario') @@ -307,322 +206,129 @@ def _prepare_dimensions( def _convert_scalar( data: Union[int, float, np.integer, np.floating], coords: Dict[str, pd.Index], dims: Tuple[str, ...] ) -> xr.DataArray: - """ - Convert a scalar value to a DataArray. - - Args: - data: The scalar value - coords: Coordinate dictionary - dims: Dimension names - - Returns: - DataArray with the scalar value - """ + """Convert scalar to DataArray, broadcasting to all dimensions.""" if isinstance(data, (np.integer, np.floating)): data = data.item() return xr.DataArray(data, coords=coords, dims=dims) @staticmethod - def _convert_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """ - Convert an existing DataArray to desired dimensions. - - Args: - data: The source DataArray - coords: Target coordinates - dims: Target dimensions - - Returns: - DataArray with the target dimensions - """ - # No dimensions case - if len(dims) == 0: - if data.size != 1: - raise ConversionError('When converting to dimensionless DataArray, source must be scalar') - return xr.DataArray(data.values.item()) - - # Check if data already has matching dimensions and coordinates - if set(data.dims) == set(dims): - # Check if coordinates match - is_compatible = True - for dim in dims: - if dim in data.dims and not np.array_equal(data.coords[dim].values, coords[dim].values): - is_compatible = False - break - - if is_compatible: - # Ensure dimensions are in the correct order - if data.dims != dims: - # Transpose to get dimensions in the right order - return data.transpose(*dims).copy(deep=True) - else: - # Return existing DataArray if compatible and order is correct - return data.copy(deep=True) - - # Handle dimension broadcasting - if len(data.dims) == 1 and len(dims) == 2: - # Single dimension to two dimensions - if data.dims[0] == 'time' and 'scenario' in dims: - # Broadcast time dimension to include scenarios - return DataConverter._broadcast_time_to_scenarios(data, coords, dims) - - elif data.dims[0] == 'scenario' and 'time' in dims: - # Broadcast scenario dimension to include time - return DataConverter._broadcast_scenario_to_time(data, coords, dims) - - raise ConversionError( - f'Cannot convert {data.dims} to {dims}. Source coordinates: {data.coords}, Target coordinates: {coords}' - ) - @staticmethod - def _broadcast_time_to_scenarios( - data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: - """ - Broadcast a time-only DataArray to include scenarios. - - Args: - data: The time-indexed DataArray - coords: Target coordinates - dims: Target dimensions - - Returns: - DataArray with time and scenario dimensions - """ - # Check compatibility - if not np.array_equal(data.coords['time'].values, coords['time'].values): - raise ConversionError("Source time coordinates don't match target time coordinates") - - if len(coords['scenario']) <= 1: - return data.copy(deep=True) - - # Broadcast values - values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - @staticmethod - def _broadcast_scenario_to_time( - data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: - """ - Broadcast a scenario-only DataArray to include time. - - Args: - data: The scenario-indexed DataArray - coords: Target coordinates - dims: Target dimensions - - Returns: - DataArray with time and scenario dimensions - """ - # Check compatibility - if not np.array_equal(data.coords['scenario'].values, coords['scenario'].values): - raise ConversionError("Source scenario coordinates don't match target scenario coordinates") - - # Broadcast values - values = np.repeat(data.values[:, np.newaxis], len(coords['time']), axis=1).T - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - @staticmethod - def _convert_ndarray(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """ - Convert a NumPy array to a DataArray. - - Args: - data: The NumPy array - coords: Target coordinates - dims: Target dimensions - - Returns: - DataArray from the NumPy array - """ - # Handle dimensionless case + def _convert_1d_array(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """Convert 1D array to DataArray.""" if len(dims) == 0: - if data.size != 1: - raise ConversionError('Without dimensions, can only convert scalar arrays') - return xr.DataArray(data.item()) + if len(data) != 1: + raise ConversionError('Cannot convert multi-element array without dimensions') + return xr.DataArray(data[0]) - # Handle single dimension elif len(dims) == 1: - return DataConverter._convert_ndarray_single_dim(data, coords, dims) - - # Handle two dimensions - elif len(dims) == 2: - return DataConverter._convert_ndarray_two_dims(data, coords, dims) - - else: - raise ConversionError('Maximum 2 dimensions supported') - - @staticmethod - def _convert_ndarray_single_dim( - data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: - """ - Convert a NumPy array to a single-dimension DataArray. - - Args: - data: The NumPy array - coords: Target coordinates - dims: Target dimensions (length 1) - - Returns: - DataArray with single dimension - """ - dim_name = dims[0] - dim_length = len(coords[dim_name]) - - if data.ndim == 1: - # 1D array must match dimension length - if data.shape[0] != dim_length: - raise ConversionError(f"Array length {data.shape[0]} doesn't match {dim_name} length {dim_length}") + dim_name = dims[0] + if len(data) != len(coords[dim_name]): + raise ConversionError( + f'Array length {len(data)} does not match {dim_name} length {len(coords[dim_name])}' + ) return xr.DataArray(data, coords=coords, dims=dims) - else: - raise ConversionError(f'Expected 1D array for single dimension, got {data.ndim}D') - - @staticmethod - def _convert_ndarray_two_dims(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """ - Convert a NumPy array to a two-dimension DataArray. - Args: - data: The NumPy array - coords: Target coordinates - dims: Target dimensions (length 2) - - Returns: - DataArray with two dimensions - """ - scenario_length = len(coords['scenario']) - time_length = len(coords['time']) + elif len(dims) == 2: + # Broadcast 1D array to 2D based on which dimension it matches + time_len = len(coords['time']) + scenario_len = len(coords['scenario']) - if data.ndim == 1: - # For 1D array, create 2D array based on which dimension it matches - if data.shape[0] == time_length: + if len(data) == time_len: # Broadcast across scenarios - values = np.repeat(data[:, np.newaxis], scenario_length, axis=1) + values = np.repeat(data[:, np.newaxis], scenario_len, axis=1) return xr.DataArray(values, coords=coords, dims=dims) - elif data.shape[0] == scenario_length: + elif len(data) == scenario_len: # Broadcast across time - values = np.repeat(data[np.newaxis, :], time_length, axis=0) + values = np.repeat(data[np.newaxis, :], time_len, axis=0) return xr.DataArray(values, coords=coords, dims=dims) else: - raise ConversionError(f"1D array length {data.shape[0]} doesn't match either dimension") - - elif data.ndim == 2: - # For 2D array, shape must match dimensions - expected_shape = (time_length, scenario_length) - if data.shape != expected_shape: - raise ConversionError(f"2D array shape {data.shape} doesn't match expected shape {expected_shape}") - return xr.DataArray(data, coords=coords, dims=dims) + raise ConversionError( + f'Array length {len(data)} matches neither time ({time_len}) nor scenario ({scenario_len}) dimensions' + ) else: - raise ConversionError(f'Expected 1D or 2D array for two dimensions, got {data.ndim}D') + raise ConversionError('Maximum 2 dimensions supported') @staticmethod def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """ - Convert pandas Series to xarray DataArray. - - Args: - data: pandas Series to convert - coords: Target coordinates - dims: Target dimensions + """Convert pandas Series to DataArray.""" + if len(dims) == 0: + if len(data) != 1: + raise ConversionError('Cannot convert multi-element Series without dimensions') + return xr.DataArray(data.iloc[0]) - Returns: - DataArray from the pandas Series - """ - # Handle single dimension case - if len(dims) == 1: + elif len(dims) == 1: dim_name = dims[0] + if not data.index.equals(coords[dim_name]): + raise ConversionError(f'Series index does not match {dim_name} coordinates') + return xr.DataArray(data.values, coords=coords, dims=dims) - # Check if series index matches the dimension - if data.index.equals(coords[dim_name]): - return xr.DataArray(data.values.copy(), coords=coords, dims=dims) - else: - raise ConversionError( - f"Series index doesn't match {dim_name} coordinates.\n" - f'Series index: {data.index}\n' - f'Target {dim_name} coordinates: {coords[dim_name]}' - ) - - # Handle two dimensions case elif len(dims) == 2: - # Check if dimensions are time and scenario - if dims != ('time', 'scenario'): - raise ConversionError( - f'Two-dimensional conversion only supports time and scenario dimensions, got {dims}' - ) - - # Case 1: Series is indexed by time + # Check which dimension the Series index matches if data.index.equals(coords['time']): # Broadcast across scenarios values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - # Case 2: Series is indexed by scenario + return xr.DataArray(values, coords=coords, dims=dims) elif data.index.equals(coords['scenario']): # Broadcast across time values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - + return xr.DataArray(values, coords=coords, dims=dims) else: - raise ConversionError( - "Series index must match either 'time' or 'scenario' coordinates.\n" - f'Series index: {data.index}\n' - f'Target time coordinates: {coords["time"]}\n' - f'Target scenario coordinates: {coords["scenario"]}' - ) + raise ConversionError('Series index must match either time or scenario coordinates') else: - raise ConversionError(f'Maximum 2 dimensions supported, got {len(dims)}') + raise ConversionError('Maximum 2 dimensions supported') @staticmethod - def _convert_dataframe(data: pd.DataFrame, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """ - Convert pandas DataFrame to xarray DataArray. - Only allows time as index and scenarios as columns. + def _handle_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """Handle existing DataArray - check compatibility or broadcast.""" + # If no target dimensions, data must be scalar + if len(dims) == 0: + if data.size != 1: + raise ConversionError('DataArray must be scalar when no dimensions specified') + return xr.DataArray(data.values.item()) - Args: - data: pandas DataFrame to convert - coords: Target coordinates - dims: Target dimensions + # Check if already compatible + if data.dims == dims: + # Check if coordinates match + compatible = True + for dim in dims: + if not np.array_equal(data.coords[dim].values, coords[dim].values): + compatible = False + break + if compatible: + return data.copy() - Returns: - DataArray from the pandas DataFrame - """ - # Single dimension case - if len(dims) == 1: - # If DataFrame has one column, treat it like a Series - if len(data.columns) == 1: - series = data.iloc[:, 0] - return DataConverter._convert_series(series, coords, dims) + # Handle broadcasting from smaller to larger dimensions + if len(data.dims) < len(dims): + return DataConverter._broadcast_dataarray(data, coords, dims) - raise ConversionError( - f'When converting DataFrame to single-dimension DataArray, DataFrame must have exactly one column, got {len(data.columns)}' - ) + # If dimensions don't match and can't broadcast, raise error + raise ConversionError(f'Cannot convert DataArray with dims {data.dims} to target dims {dims}') - # Two dimensions case - elif len(dims) == 2: - # Check if dimensions are time and scenario - if dims != ('time', 'scenario'): - raise ConversionError( - f'Two-dimensional conversion only supports time and scenario dimensions, got {dims}' - ) + @staticmethod + def _broadcast_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """Broadcast DataArray to target dimensions.""" + if len(data.dims) == 0: + # Scalar DataArray - broadcast to all dimensions + return xr.DataArray(data.values.item(), coords=coords, dims=dims) - # DataFrame must have time as index and scenarios as columns - if data.index.equals(coords['time']) and data.columns.equals(coords['scenario']): - # Create DataArray with proper dimension order - return xr.DataArray(data.values.copy(), coords=coords, dims=dims) - else: - raise ConversionError( - 'DataFrame must have time as index and scenarios as columns.\n' - f'DataFrame index: {data.index}\n' - f'DataFrame columns: {data.columns}\n' - f'Target time coordinates: {coords["time"]}\n' - f'Target scenario coordinates: {coords["scenario"]}' - ) + elif len(data.dims) == 1 and len(dims) == 2: + source_dim = data.dims[0] - else: - raise ConversionError(f'Maximum 2 dimensions supported, got {len(dims)}') + # Check coordinate compatibility + if not np.array_equal(data.coords[source_dim].values, coords[source_dim].values): + raise ConversionError(f'Source {source_dim} coordinates do not match target coordinates') + + if source_dim == 'time': + # Broadcast time to include scenarios + values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) + return xr.DataArray(values, coords=coords, dims=dims) + elif source_dim == 'scenario': + # Broadcast scenario to include time + values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) + return xr.DataArray(values, coords=coords, dims=dims) + + raise ConversionError(f'Cannot broadcast from {data.dims} to {dims}') def get_dataarray_stats(arr: xr.DataArray) -> Dict: diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 1ad41a0d2..d92077307 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -6,6 +6,7 @@ from flixopt.core import ( # Adjust this import to match your project structure ConversionError, DataConverter, + TimeSeriesData, ) @@ -19,12 +20,6 @@ def sample_scenario_index(): return pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') -@pytest.fixture -def multi_index(sample_time_index, sample_scenario_index): - """Create a sample MultiIndex combining scenarios and times.""" - return pd.MultiIndex.from_product([sample_scenario_index, sample_time_index], names=['scenario', 'time']) - - class TestSingleDimensionConversion: """Tests for converting data without scenarios (1D: time only).""" @@ -110,8 +105,8 @@ def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): result = DataConverter.to_dataarray(42.5, sample_time_index, sample_scenario_index) assert np.all(result.values == 42.5) - def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting 1D array with scenario dimension (broadcasting).""" + def test_1d_array_with_scenarios_time_broadcast(self, sample_time_index, sample_scenario_index): + """Test converting 1D array matching time dimension (broadcasting across scenarios).""" # Create 1D array matching timesteps length arr_1d = np.array([1, 2, 3, 4, 5]) @@ -126,35 +121,29 @@ def test_1d_array_with_scenarios(self, sample_time_index, sample_scenario_index) scenario_slice = result.sel(scenario=scenario) assert np.array_equal(scenario_slice.values, arr_1d) - def test_2d_array_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting 2D array with scenario dimension.""" - # Create 2D array with different values per scenario - arr_2d = np.array( - [ - [1, 2, 3, 4, 5], # baseline scenario - [6, 7, 8, 9, 10], # high_demand scenario - [11, 12, 13, 14, 15], # low_price scenario - ] - ) + def test_1d_array_with_scenarios_scenario_broadcast(self, sample_time_index, sample_scenario_index): + """Test converting 1D array matching scenario dimension (broadcasting across time).""" + # Create 1D array matching scenario length + arr_1d = np.array([10, 20, 30]) # 3 scenarios - # Convert to DataArray - result = DataConverter.to_dataarray(arr_2d.T, sample_time_index, sample_scenario_index) + # Convert with time and scenarios + result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) - assert result.shape == (5, 3) + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) assert result.dims == ('time', 'scenario') - # Check that each scenario has correct values - assert np.array_equal(result.sel(scenario='baseline').values, arr_2d[0]) - assert np.array_equal(result.sel(scenario='high_demand').values, arr_2d[1]) - assert np.array_equal(result.sel(scenario='low_price').values, arr_2d[2]) + # Each time step should have the same scenario values (broadcasting) + for time in sample_time_index: + time_slice = result.sel(time=time) + assert np.array_equal(time_slice.values, arr_1d) def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index): """Test converting an existing DataArray with scenarios.""" - # Create a multi-scenario DataArray + # Create a multi-scenario DataArray with dims in (time, scenario) order original = xr.DataArray( - data=np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15]]), - coords={'scenario': sample_scenario_index, 'time': sample_time_index}, - dims=['scenario', 'time'], + data=np.array([[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]), + coords={'time': sample_time_index, 'scenario': sample_scenario_index}, + dims=['time', 'scenario'], ) # Test conversion @@ -162,7 +151,7 @@ def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') - assert np.array_equal(result.values, original.values.T) + assert np.array_equal(result.values, original.values) # Ensure it's a copy result.loc[:, 'baseline'] = 999 @@ -172,7 +161,7 @@ def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index class TestSeriesConversion: """Tests for converting pandas Series to DataArray.""" - def test_series_single_dimension(self, sample_time_index): + def test_series_single_dimension_time(self, sample_time_index): """Test converting a pandas Series with time index.""" # Create a Series with matching time index series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) @@ -185,15 +174,16 @@ def test_series_single_dimension(self, sample_time_index): assert np.array_equal(result.values, series.values) assert np.array_equal(result.coords['time'].values, sample_time_index.values) - # Test with scenario index - scenario_index = pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') - series = pd.Series([100, 200, 300], index=scenario_index) + def test_series_single_dimension_scenario(self, sample_scenario_index): + """Test converting a pandas Series with scenario index.""" + # Create a Series with scenario index + series = pd.Series([100, 200, 300], index=sample_scenario_index) - result = DataConverter.to_dataarray(series, scenarios=scenario_index) + result = DataConverter.to_dataarray(series, scenarios=sample_scenario_index) assert result.shape == (3,) assert result.dims == ('scenario',) assert np.array_equal(result.values, series.values) - assert np.array_equal(result.coords['scenario'].values, scenario_index.values) + assert np.array_equal(result.coords['scenario'].values, sample_scenario_index.values) def test_series_mismatched_index(self, sample_time_index): """Test converting a Series with mismatched index.""" @@ -237,104 +227,39 @@ def test_series_broadcast_to_time(self, sample_time_index, sample_scenario_index time_slice = result.sel(time=time) assert np.array_equal(time_slice.values, series.values) - def test_series_dimension_order(self, sample_time_index, sample_scenario_index): - """Test that dimension order is respected with Series conversions.""" - # Create custom dimensions tuple with reversed order - dims = ('scenario', 'time',) - coords = {'time': sample_time_index, 'scenario': sample_scenario_index} - - # Time-indexed series - series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) - with pytest.raises(ConversionError, match="only supports time and scenario dimensions"): - _ = DataConverter._convert_series(series, coords, dims) - - # Scenario-indexed series - series = pd.Series([100, 200, 300], index=sample_scenario_index) - with pytest.raises(ConversionError, match="only supports time and scenario dimensions"): - _ = DataConverter._convert_series(series, coords, dims) +class TestTimeSeriesDataConversion: + """Tests for converting TimeSeriesData objects.""" -class TestDataFrameConversion: - """Tests for converting pandas DataFrame to DataArray.""" + def test_timeseries_data_conversion(self, sample_time_index): + """Test converting TimeSeriesData.""" + # Create TimeSeriesData + data_array = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_time_index}, dims=['time']) + ts_data = TimeSeriesData(data_array, aggregation_group='test_group') - def test_dataframe_single_column(self, sample_time_index): - """Test converting a DataFrame with a single column.""" - # Create DataFrame with one column - df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + # Convert + result = DataConverter.to_dataarray(ts_data, sample_time_index) - # Convert and check - result = DataConverter.to_dataarray(df, sample_time_index) assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) - assert np.array_equal(result.values, df['value'].values) - - def test_dataframe_multi_column_fails(self, sample_time_index): - """Test that converting a multi-column DataFrame to 1D fails.""" - # Create DataFrame with multiple columns - df = pd.DataFrame({'val1': [10, 20, 30, 40, 50], 'val2': [15, 25, 35, 45, 55]}, index=sample_time_index) + assert np.array_equal(result.values, [1, 2, 3, 4, 5]) - # Should raise error - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df, sample_time_index) - - def test_dataframe_time_scenario(self, sample_time_index, sample_scenario_index): - """Test converting a DataFrame with time index and scenario columns.""" - # Create DataFrame with time as index and scenarios as columns - data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} - df = pd.DataFrame(data, index=sample_time_index) + def test_timeseries_data_with_scenarios(self, sample_time_index, sample_scenario_index): + """Test converting TimeSeriesData with broadcasting to scenarios.""" + # Create 1D TimeSeriesData + data_array = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_time_index}, dims=['time']) + ts_data = TimeSeriesData(data_array) - # Make sure columns are named properly - df.columns.name = 'scenario' - - # Convert and check - result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) + # Convert with scenarios (should broadcast) + result = DataConverter.to_dataarray(ts_data, sample_time_index, sample_scenario_index) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') - assert np.array_equal(result.values, df.values) - # Check values for specific scenarios - assert np.array_equal(result.sel(scenario='baseline').values, df['baseline'].values) - assert np.array_equal(result.sel(scenario='high_demand').values, df['high_demand'].values) - - def test_dataframe_mismatched_coordinates(self, sample_time_index, sample_scenario_index): - """Test conversion fails with mismatched coordinates.""" - # Create DataFrame with different time index - different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') - data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} - df = pd.DataFrame(data, index=different_times) - df.columns = sample_scenario_index - - # Should raise error - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) - - # Create DataFrame with different scenario columns - different_scenarios = pd.Index(['scenario1', 'scenario2', 'scenario3'], name='scenario') - data = {'scenario1': [10, 20, 30, 40, 50], 'scenario2': [15, 25, 35, 45, 55], 'scenario3': [5, 15, 25, 35, 45]} - df = pd.DataFrame(data, index=sample_time_index) - df.columns = different_scenarios - - # Should raise error - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) - - def test_ensure_copy(self, sample_time_index, sample_scenario_index): - """Test that the returned DataArray is a copy.""" - # Create DataFrame - data = {'baseline': [10, 20, 30, 40, 50], 'high_demand': [15, 25, 35, 45, 55], 'low_price': [5, 15, 25, 35, 45]} - df = pd.DataFrame(data, index=sample_time_index) - df.columns = sample_scenario_index - - # Convert - result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) - - # Modify the result - result.loc[dict(time=sample_time_index[0], scenario='baseline')] = 999 - - # Original should be unchanged - assert df.loc[sample_time_index[0], 'baseline'] == 10 + # Each scenario should have the same values + for scenario in sample_scenario_index: + assert np.array_equal(result.sel(scenario=scenario).values, [1, 2, 3, 4, 5]) class TestInvalidInputs: @@ -344,8 +269,9 @@ def test_time_index_validation(self): """Test validation of time index.""" # Test with unnamed index unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, unnamed_index) + # Should automatically rename to 'time' with a warning, not raise error + result = DataConverter.to_dataarray(42, unnamed_index) + assert result.coords['time'].name == 'time' # Test with empty index empty_index = pd.DatetimeIndex([], name='time') @@ -361,8 +287,9 @@ def test_scenario_index_validation(self, sample_time_index): """Test validation of scenario index.""" # Test with unnamed scenario index unnamed_index = pd.Index(['baseline', 'high_demand']) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, sample_time_index, unnamed_index) + # Should automatically rename to 'scenario' with a warning, not raise error + result = DataConverter.to_dataarray(42, sample_time_index, unnamed_index) + assert result.coords['scenario'].name == 'scenario' # Test with empty scenario index empty_index = pd.Index([], name='scenario') @@ -391,6 +318,18 @@ def test_invalid_data_types(self, sample_time_index, sample_scenario_index): with pytest.raises(ConversionError): DataConverter.to_dataarray(None, sample_time_index) + def test_multidimensional_array_rejection(self, sample_time_index, sample_scenario_index): + """Test that multidimensional arrays are rejected.""" + # Test 2D array (not supported in simplified version) + arr_2d = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) + with pytest.raises(ConversionError, match="Only 1D arrays supported"): + DataConverter.to_dataarray(arr_2d, sample_time_index) + + # Test 3D array + arr_3d = np.ones((2, 3, 4)) + with pytest.raises(ConversionError, match="Only 1D arrays supported"): + DataConverter.to_dataarray(arr_3d, sample_time_index, sample_scenario_index) + def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_index): """Test handling of mismatched input dimensions.""" # Test mismatched Series index @@ -400,31 +339,14 @@ def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_in with pytest.raises(ConversionError): DataConverter.to_dataarray(mismatched_series, sample_time_index) - # Test DataFrame with multiple columns - df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df_multi_col, sample_time_index) - - # Test mismatched array shape for time-only + # Test mismatched array length for time-only with pytest.raises(ConversionError): DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length - # Test mismatched array shape for scenario × time - # Array shape should be (n_scenarios, n_timesteps) - wrong_shape_array = np.array( - [ - [1, 2, 3, 4], # Missing a timestep - [5, 6, 7, 8], - [9, 10, 11, 12], - ] - ) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_shape_array, sample_time_index, sample_scenario_index) - - # Test array with too many dimensions + # Test array that doesn't match either dimension + wrong_length_array = np.array([1, 2, 3, 4]) # Doesn't match time (5) or scenario (3) with pytest.raises(ConversionError): - # 3D array not allowed - DataConverter.to_dataarray(np.ones((3, 5, 2)), sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(wrong_length_array, sample_time_index, sample_scenario_index) def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_index): """Test handling of mismatched DataArray dimensions.""" @@ -433,61 +355,58 @@ def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_i with pytest.raises(ConversionError): DataConverter.to_dataarray(wrong_dims, sample_time_index) - # Create DataArray with scenario but no time - wrong_dims_2 = xr.DataArray(data=np.array([1, 2, 3]), coords={'scenario': ['a', 'b', 'c']}, dims=['scenario']) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_dims_2, sample_time_index, sample_scenario_index) - - # Create DataArray with right dims but wrong length - wrong_length = xr.DataArray( - data=np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]), - coords={ - 'scenario': sample_scenario_index, - 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), - }, - dims=['scenario', 'time'], + # Create DataArray with right dims but wrong coordinate values + wrong_coords = xr.DataArray( + data=np.array([1, 2, 3, 4, 5]), + coords={'time': pd.date_range('2025-01-01', periods=5, freq='D', name='time')}, + dims=['time'] ) with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_length, sample_time_index, sample_scenario_index) + DataConverter.to_dataarray(wrong_coords, sample_time_index) + class TestDataArrayBroadcasting: """Tests for broadcasting DataArrays.""" - def test_broadcast_1d_array_to_2d(self, sample_time_index, sample_scenario_index): - """Test broadcasting a 1D array to all scenarios.""" + + def test_broadcast_1d_array_to_2d_time(self, sample_time_index, sample_scenario_index): + """Test broadcasting a 1D array (time) to 2D.""" arr_1d = np.array([1, 2, 3, 4, 5]) - xr.testing.assert_equal( - DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index), - xr.DataArray( - np.array([arr_1d] * 3).T, - coords=(sample_time_index, sample_scenario_index) - ) - ) + result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) - arr_1d = np.array([1, 2, 3]) - xr.testing.assert_equal( - DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index), - xr.DataArray( - np.array([arr_1d] * 5), - coords=(sample_time_index, sample_scenario_index) - ) - ) + # Should broadcast across scenarios + expected = np.repeat(arr_1d[:, np.newaxis], len(sample_scenario_index), axis=1) + assert np.array_equal(result.values, expected) + assert result.dims == ('time', 'scenario') - def test_broadcast_1d_array_to_1d(self, sample_time_index,): - """Test broadcasting a 1D array to all scenarios.""" + def test_broadcast_1d_array_to_2d_scenario(self, sample_time_index, sample_scenario_index): + """Test broadcasting a 1D array (scenario) to 2D.""" + arr_1d = np.array([1, 2, 3]) # Matches scenario length + + result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) + + # Should broadcast across time + expected = np.repeat(arr_1d[np.newaxis, :], len(sample_time_index), axis=0) + assert np.array_equal(result.values, expected) + assert result.dims == ('time', 'scenario') + + def test_broadcast_1d_array_to_1d(self, sample_time_index): + """Test that 1D array with matching dimension doesn't change.""" arr_1d = np.array([1, 2, 3, 4, 5]) - xr.testing.assert_equal( - DataConverter.to_dataarray(arr_1d, sample_time_index), - xr.DataArray( - arr_1d, - coords=(sample_time_index,) - ) - ) + result = DataConverter.to_dataarray(arr_1d, sample_time_index) - arr_1d = np.array([1, 2, 3]) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(arr_1d, sample_time_index) + assert np.array_equal(result.values, arr_1d) + assert result.dims == ('time',) + + def test_scalar_dataarray_broadcasting(self, sample_time_index, sample_scenario_index): + """Test broadcasting scalar DataArray.""" + scalar_da = xr.DataArray(42) + + result = DataConverter.to_dataarray(scalar_da, sample_time_index, sample_scenario_index) + + assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + assert np.all(result.values == 42) class TestEdgeCases: @@ -524,38 +443,6 @@ def test_single_scenario(self, sample_time_index): assert result_arr.shape == (5, 1) assert np.array_equal(result_arr.sel(scenario='baseline').values, arr) - # 2D array with single scenario - arr_2d = np.array([[1, 2, 3, 4, 5]]) # Note the extra dimension - result_arr_2d = DataConverter.to_dataarray(arr_2d.T, sample_time_index, single_scenario) - assert result_arr_2d.shape == (5, 1) - assert np.array_equal(result_arr_2d.sel(scenario='baseline').values, arr_2d[0]) - - def test_different_scenario_order(self, sample_time_index): - """Test that scenario order is preserved.""" - # Test with different scenario orders - scenarios1 = pd.Index(['a', 'b', 'c'], name='scenario') - scenarios2 = pd.Index(['c', 'b', 'a'], name='scenario') - - # Create DataArray with first order - data = np.array( - [ - [1, 2, 3, 4, 5], # a - [6, 7, 8, 9, 10], # b - [11, 12, 13, 14, 15], # c - ] - ).T - - result1 = DataConverter.to_dataarray(data, sample_time_index, scenarios1) - assert np.array_equal(result1.sel(scenario='a').values, [1, 2, 3, 4, 5]) - assert np.array_equal(result1.sel(scenario='c').values, [11, 12, 13, 14, 15]) - - # Create DataArray with second order - result2 = DataConverter.to_dataarray(data, sample_time_index, scenarios2) - # First row should match 'c' now - assert np.array_equal(result2.sel(scenario='c').values, [1, 2, 3, 4, 5]) - # Last row should match 'a' now - assert np.array_equal(result2.sel(scenario='a').values, [11, 12, 13, 14, 15]) - def test_all_nan_data(self, sample_time_index, sample_scenario_index): """Test handling of all-NaN data.""" # Create array of all NaNs @@ -568,12 +455,6 @@ def test_all_nan_data(self, sample_time_index, sample_scenario_index): assert result.shape == (len(sample_time_index), len(sample_scenario_index)) assert np.all(np.isnan(result.values)) - # Series of all NaNs - result = DataConverter.to_dataarray( - np.array([np.nan, np.nan, np.nan, np.nan, np.nan]), sample_time_index, sample_scenario_index - ) - assert np.all(np.isnan(result.values)) - def test_mixed_data_types(self, sample_time_index, sample_scenario_index): """Test conversion of mixed integer and float data.""" # Create array with mixed types @@ -590,165 +471,50 @@ def test_mixed_data_types(self, sample_time_index, sample_scenario_index): for scenario in sample_scenario_index: assert np.array_equal(result.sel(scenario=scenario).values, mixed_array) - -class TestFunctionalUseCase: - """Tests for realistic use cases combining multiple features.""" - - def test_large_dataset(self, sample_scenario_index): - """Test with a larger dataset to ensure performance.""" - # Create a larger timestep array (e.g., hourly for a year) - large_timesteps = pd.date_range( - '2024-01-01', - periods=8760, # Hours in a year - freq='H', - name='time', - ) - - # Create large 2D array (3 scenarios × 8760 hours) - large_data = np.random.rand(len(sample_scenario_index), len(large_timesteps)) - - # Convert and check - result = DataConverter.to_dataarray(large_data.T, large_timesteps, sample_scenario_index) - - assert result.shape == (len(large_timesteps), len(sample_scenario_index)) - assert result.dims == ('time', 'scenario') - assert np.array_equal(result.values, large_data.T) - - -class TestMultiScenarioArrayConversion: - """Tests specifically focused on array conversion with scenarios.""" - - def test_1d_array_broadcasting(self, sample_time_index, sample_scenario_index): - """Test that 1D arrays are properly broadcast to all scenarios.""" - arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) - - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) - - # Each scenario should have identical values - for i, scenario in enumerate(sample_scenario_index): - assert np.array_equal(result.sel(scenario=scenario).values, arr_1d) - - # Modify one scenario's values - result.loc[dict(scenario=scenario)] = np.ones(len(sample_time_index)) * i - - # Ensure modifications are isolated to each scenario - for i, scenario in enumerate(sample_scenario_index): - assert np.all(result.sel(scenario=scenario).values == i) - - def test_2d_array_different_shapes(self, sample_time_index): - """Test different scenario shapes with 2D arrays.""" - # Test with 1 scenario - single_scenario = pd.Index(['baseline'], name='scenario') - arr_1_scenario = np.array([[1, 2, 3, 4, 5]]) - - result = DataConverter.to_dataarray(arr_1_scenario.T, sample_time_index, single_scenario) - assert result.shape == (len(sample_time_index), 1) - - # Test with 2 scenarios - two_scenarios = pd.Index(['baseline', 'high_demand'], name='scenario') - arr_2_scenarios = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) - - result = DataConverter.to_dataarray(arr_2_scenarios.T, sample_time_index, two_scenarios) - assert result.shape == (len(sample_time_index), 2) - assert np.array_equal(result.sel(scenario='baseline').values, arr_2_scenarios[0]) - assert np.array_equal(result.sel(scenario='high_demand').values, arr_2_scenarios[1]) - - # Test mismatched scenarios count - three_scenarios = pd.Index(['a', 'b', 'c'], name='scenario') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(arr_2_scenarios, sample_time_index, three_scenarios) - - def test_array_handling_edge_cases(self, sample_time_index, sample_scenario_index): - """Test array edge cases.""" - # Test with boolean array + def test_boolean_data(self, sample_time_index, sample_scenario_index): + """Test handling of boolean data.""" bool_array = np.array([True, False, True, False, True]) result = DataConverter.to_dataarray(bool_array, sample_time_index, sample_scenario_index) assert result.dtype == bool assert result.shape == (len(sample_time_index), len(sample_scenario_index)) - # Test with array containing infinite values - inf_array = np.array([1, np.inf, 3, -np.inf, 5]) - result = DataConverter.to_dataarray(inf_array, sample_time_index, sample_scenario_index) - for scenario in sample_scenario_index: - scenario_data = result.sel(scenario=scenario) - assert np.isinf(scenario_data[1].item()) - assert np.isinf(scenario_data[3].item()) - assert scenario_data[3].item() < 0 # Negative infinity - - -class TestScenarioReindexing: - """Tests for reindexing and coordinate preservation in DataConverter.""" - - def test_preserving_scenario_order(self, sample_time_index): - """Test that scenario order is preserved in converted DataArrays.""" - # Define scenarios in a specific order - scenarios = pd.Index(['scenario3', 'scenario1', 'scenario2'], name='scenario') - - # Create 2D array - data = np.array( - [ - [1, 2, 3, 4, 5], # scenario3 - [6, 7, 8, 9, 10], # scenario1 - [11, 12, 13, 14, 15], # scenario2 - ] - ) - - # Convert to DataArray - result = DataConverter.to_dataarray(data.T, sample_time_index, scenarios) - - # Verify order of scenarios is preserved - assert list(result.coords['scenario'].values) == list(scenarios) - - # Verify data for each scenario - assert np.array_equal(result.sel(scenario='scenario3').values, data[0]) - assert np.array_equal(result.sel(scenario='scenario1').values, data[1]) - assert np.array_equal(result.sel(scenario='scenario2').values, data[2]) +class TestNoIndexConversion: + """Tests for conversion without any indices (scalar results).""" -if __name__ == '__main__': - pytest.main() - - -def test_invalid_inputs(sample_time_index): - # Test invalid input type - with pytest.raises(ConversionError): - DataConverter.to_dataarray('invalid_string', sample_time_index) - - # Test mismatched Series index - mismatched_series = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D')) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(mismatched_series, sample_time_index) - - # Test DataFrame with multiple columns - df_multi_col = pd.DataFrame({'A': [1, 2, 3, 4, 5], 'B': [6, 7, 8, 9, 10]}, index=sample_time_index) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df_multi_col, sample_time_index) - - # Test mismatched array shape - with pytest.raises(ConversionError): - DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length - - # Test multi-dimensional array - with pytest.raises(ConversionError): - DataConverter.to_dataarray(np.array([[1, 2], [3, 4]]), sample_time_index) # 2D array not allowed - - -def test_time_index_validation(): - # Test with unnamed index - unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, unnamed_index) - - # Test with empty index - empty_index = pd.DatetimeIndex([], name='time') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, empty_index) + def test_scalar_no_dimensions(self): + """Test scalar conversion without any dimensions.""" + result = DataConverter.to_dataarray(42) + assert isinstance(result, xr.DataArray) + assert result.shape == () + assert result.dims == () + assert result.item() == 42 + + def test_single_element_array_no_dimensions(self): + """Test single-element array without dimensions.""" + arr = np.array([42]) + result = DataConverter.to_dataarray(arr) + assert result.shape == () + assert result.item() == 42 + + def test_multi_element_array_no_dimensions_fails(self): + """Test that multi-element array fails without dimensions.""" + arr = np.array([1, 2, 3]) + with pytest.raises(ConversionError): + DataConverter.to_dataarray(arr) - # Test with non-DatetimeIndex - wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, wrong_type_index) + def test_series_no_dimensions_fails(self): + """Test that multi-element Series fails without dimensions.""" + series = pd.Series([1, 2, 3]) + with pytest.raises(ConversionError): + DataConverter.to_dataarray(series) + + def test_single_element_series_no_dimensions(self): + """Test single-element Series without dimensions.""" + series = pd.Series([42]) + result = DataConverter.to_dataarray(series) + assert result.shape == () + assert result.item() == 42 if __name__ == '__main__': From a7ec9943866959234fe1db079dfb421350b2ecf7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 20:58:22 +0200 Subject: [PATCH 139/183] Add DataFrame Support back --- flixopt/core.py | 21 ++++++- tests/test_dataconverter.py | 111 ++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 9ab71cab9..687200f21 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -134,9 +134,20 @@ class DataConverter: - xr.DataArray (for broadcasting/checking) """ + @staticmethod + def _convert_dataframe(data: pd.DataFrame, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: + """Convert single-column pandas DataFrame to DataArray by treating it as a Series.""" + # Check that DataFrame has exactly one column + if len(data.columns) != 1: + raise ConversionError(f'Only single-column DataFrames are supported, got {len(data.columns)} columns') + + # Extract the single column as a Series and convert it + series = data.iloc[:, 0] + return DataConverter._convert_series(series, coords, dims) + @staticmethod def to_dataarray( - data: Union[Scalar, np.ndarray, pd.Series, xr.DataArray, TimeSeriesData], + data: Union[Scalar, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, TimeSeriesData], timesteps: Optional[pd.DatetimeIndex] = None, scenarios: Optional[pd.Index] = None, ) -> xr.DataArray: @@ -144,7 +155,7 @@ def to_dataarray( Convert data to xarray.DataArray with specified dimensions. Args: - data: Scalar, 1D array/Series, or existing DataArray + data: Scalar, 1D array/Series, single-column DataFrame, or existing DataArray timesteps: Optional DatetimeIndex for time dimension scenarios: Optional Index for scenario dimension @@ -167,13 +178,17 @@ def to_dataarray( elif isinstance(data, pd.Series): return DataConverter._convert_series(data, coords, dims) + # Handle pandas DataFrames (single column only) + elif isinstance(data, pd.DataFrame): + return DataConverter._convert_dataframe(data, coords, dims) + # Handle existing DataArrays (including TimeSeriesData) elif isinstance(data, xr.DataArray): return DataConverter._handle_dataarray(data, coords, dims) else: raise ConversionError( - f'Unsupported data type: {type(data).__name__}. Only scalars, 1D arrays, Series, and DataArrays are supported.' + f'Unsupported data type: {type(data).__name__}. Only scalars, 1D arrays, Series, single-column DataFrames, and DataArrays are supported.' ) @staticmethod diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index d92077307..5536b8cb2 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -262,6 +262,117 @@ def test_timeseries_data_with_scenarios(self, sample_time_index, sample_scenario assert np.array_equal(result.sel(scenario=scenario).values, [1, 2, 3, 4, 5]) +class TestDataFrameConversion: + """Tests for converting single-column pandas DataFrames to DataArray.""" + + def test_single_column_dataframe_time(self, sample_time_index): + """Test converting a single-column DataFrame with time index.""" + # Create DataFrame with one column + df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + + # Convert and check + result = DataConverter.to_dataarray(df, sample_time_index) + assert isinstance(result, xr.DataArray) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, df['value'].values) + + def test_single_column_dataframe_scenario(self, sample_scenario_index): + """Test converting a single-column DataFrame with scenario index.""" + # Create DataFrame with one column and scenario index + df = pd.DataFrame({'value': [100, 200, 300]}, index=sample_scenario_index) + + result = DataConverter.to_dataarray(df, scenarios=sample_scenario_index) + assert result.shape == (3,) + assert result.dims == ('scenario',) + assert np.array_equal(result.values, df['value'].values) + + def test_dataframe_broadcast_to_scenarios(self, sample_time_index, sample_scenario_index): + """Test broadcasting a time-indexed DataFrame across scenarios.""" + # Create DataFrame with time index + df = pd.DataFrame({'power': [10, 20, 30, 40, 50]}, index=sample_time_index) + + # Convert with scenarios + result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + + # Check broadcasting - each scenario should have the same values + for scenario in sample_scenario_index: + scenario_slice = result.sel(scenario=scenario) + assert np.array_equal(scenario_slice.values, df['power'].values) + + def test_dataframe_broadcast_to_time(self, sample_time_index, sample_scenario_index): + """Test broadcasting a scenario-indexed DataFrame across time.""" + # Create DataFrame with scenario index + df = pd.DataFrame({'cost': [100, 200, 300]}, index=sample_scenario_index) + + # Convert with time + result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + + # Check broadcasting - each time should have the same scenario values + for time in sample_time_index: + time_slice = result.sel(time=time) + assert np.array_equal(time_slice.values, df['cost'].values) + + def test_multi_column_dataframe_fails(self, sample_time_index): + """Test that multi-column DataFrames are rejected.""" + # Create DataFrame with multiple columns + df = pd.DataFrame({ + 'value1': [10, 20, 30, 40, 50], + 'value2': [15, 25, 35, 45, 55] + }, index=sample_time_index) + + # Should raise error + with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + DataConverter.to_dataarray(df, sample_time_index) + + def test_dataframe_mismatched_index(self, sample_time_index): + """Test DataFrame with mismatched index.""" + # Create DataFrame with different time index + different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=different_times) + + # Should raise error for mismatched index + with pytest.raises(ConversionError): + DataConverter.to_dataarray(df, sample_time_index) + + def test_dataframe_copy_behavior(self, sample_time_index): + """Test that DataFrame conversion creates a copy.""" + # Create DataFrame + df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + + # Convert + result = DataConverter.to_dataarray(df, sample_time_index) + + # Modify the result + result[0] = 999 + + # Original DataFrame should be unchanged + assert df.loc[sample_time_index[0], 'value'] == 10 + + def test_empty_dataframe_fails(self, sample_time_index): + """Test that empty DataFrames are rejected.""" + # DataFrame with no columns + df = pd.DataFrame(index=sample_time_index) + + with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + DataConverter.to_dataarray(df, sample_time_index) + + def test_dataframe_with_named_column(self, sample_time_index): + """Test DataFrame with a named column.""" + df = pd.DataFrame(index=sample_time_index) + df['energy_output'] = [100, 150, 200, 175, 125] + + result = DataConverter.to_dataarray(df, sample_time_index) + assert result.shape == (5,) + assert np.array_equal(result.values, [100, 150, 200, 175, 125]) + + class TestInvalidInputs: """Tests for invalid inputs and error handling.""" From 2a75ed3c1c87283ab65d89a8d47cb35e01f8d83e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 20:59:00 +0200 Subject: [PATCH 140/183] Add copy() to DataConverter --- flixopt/core.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 687200f21..9834d8f39 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -240,7 +240,7 @@ def _convert_1d_array(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple raise ConversionError( f'Array length {len(data)} does not match {dim_name} length {len(coords[dim_name])}' ) - return xr.DataArray(data, coords=coords, dims=dims) + return xr.DataArray(data.copy(), coords=coords, dims=dims) elif len(dims) == 2: # Broadcast 1D array to 2D based on which dimension it matches @@ -250,11 +250,11 @@ def _convert_1d_array(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple if len(data) == time_len: # Broadcast across scenarios values = np.repeat(data[:, np.newaxis], scenario_len, axis=1) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) elif len(data) == scenario_len: # Broadcast across time values = np.repeat(data[np.newaxis, :], time_len, axis=0) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) else: raise ConversionError( f'Array length {len(data)} matches neither time ({time_len}) nor scenario ({scenario_len}) dimensions' @@ -275,18 +275,18 @@ def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[st dim_name = dims[0] if not data.index.equals(coords[dim_name]): raise ConversionError(f'Series index does not match {dim_name} coordinates') - return xr.DataArray(data.values, coords=coords, dims=dims) + return xr.DataArray(data.values.copy(), coords=coords, dims=dims) elif len(dims) == 2: # Check which dimension the Series index matches if data.index.equals(coords['time']): # Broadcast across scenarios values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) elif data.index.equals(coords['scenario']): # Broadcast across time values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) else: raise ConversionError('Series index must match either time or scenario coordinates') @@ -337,11 +337,11 @@ def _broadcast_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: if source_dim == 'time': # Broadcast time to include scenarios values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) elif source_dim == 'scenario': # Broadcast scenario to include time values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values, coords=coords, dims=dims) + return xr.DataArray(values.copy(), coords=coords, dims=dims) raise ConversionError(f'Cannot broadcast from {data.dims} to {dims}') From dae9f01baeb9f14ebfa90ae1485f8befb4bdb838 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:06:42 +0200 Subject: [PATCH 141/183] Update fit_to_model_coords to take a list of coords --- flixopt/flow_system.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index b513372bf..6bf7502e5 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -294,7 +294,7 @@ def fit_to_model_coords( self, name: str, data: Optional[Union[TemporalDataUser, NonTemporalDataUser]], - has_time_dim: bool = True, + dimensions: Union[List[str], str] = 'time', # Default to time only ) -> Optional[Union[TemporalData, NonTemporalData]]: """ Fit data to model coordinate system (currently time, but extensible). @@ -310,18 +310,31 @@ def fit_to_model_coords( if data is None: return None + # Build coords from requested dimensions + if isinstance(dimensions, str): + dimensions = [dimensions] + + coords = {} + for dim in dimensions: + if dim == 'time' and self.timesteps is not None: + coords['time'] = self.timesteps + elif dim == 'scenario' and self.scenarios is not None: + coords['scenario'] = self.scenarios + # Future: elif dim == 'region' and self.regions is not None: ... + + # Rest of your method stays the same, just pass coords if isinstance(data, TimeSeriesData): try: data.name = name # Set name of previous object! return TimeSeriesData( - DataConverter.to_dataarray(data, timesteps=self.timesteps, scenarios=self.scenarios), + DataConverter.to_dataarray(data, coords=coords), aggregation_group=data.aggregation_group, aggregation_weight=data.aggregation_weight ).rename(name) except ConversionError as e: logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' f'Take care to use the correct (time) index.') else: - return DataConverter.to_dataarray(data, timesteps=self.timesteps if has_time_dim else None, scenarios=self.scenarios).rename(name) + return DataConverter.to_dataarray(data, coords=coords).rename(name) def fit_effects_to_model_coords( self, From ba195ff45a7dc4f40f87dfe9ceb311765d0e5c88 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:33:01 +0200 Subject: [PATCH 142/183] Make the DataConverter more universal by accepting a list of coords/dims --- flixopt/core.py | 148 ++++--- tests/test_dataconverter.py | 831 +++++++++++++++--------------------- 2 files changed, 437 insertions(+), 542 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 9834d8f39..c11ba3993 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -148,21 +148,23 @@ def _convert_dataframe(data: pd.DataFrame, coords: Dict[str, pd.Index], dims: Tu @staticmethod def to_dataarray( data: Union[Scalar, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, TimeSeriesData], - timesteps: Optional[pd.DatetimeIndex] = None, - scenarios: Optional[pd.Index] = None, + coords: Optional[Dict[str, pd.Index]] = None, ) -> xr.DataArray: """ - Convert data to xarray.DataArray with specified dimensions. + Convert data to xarray.DataArray with specified coordinates. Args: data: Scalar, 1D array/Series, single-column DataFrame, or existing DataArray - timesteps: Optional DatetimeIndex for time dimension - scenarios: Optional Index for scenario dimension + coords: Dictionary mapping dimension names to coordinate indices + e.g., {'time': timesteps, 'scenario': scenarios} Returns: DataArray with the converted data """ - coords, dims = DataConverter._prepare_dimensions(timesteps, scenarios) + if coords is None: + coords = {} + + coords, dims = DataConverter._prepare_dimensions(coords) # Handle scalars if isinstance(data, (int, float, np.integer, np.floating)): @@ -192,30 +194,40 @@ def to_dataarray( ) @staticmethod - def _prepare_dimensions( - timesteps: Optional[pd.DatetimeIndex], scenarios: Optional[pd.Index] - ) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: - """Prepare coordinates and dimensions.""" - coords = {} + def _prepare_dimensions(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: + """ + Prepare and validate coordinates for the DataArray. + + Args: + coords: Dictionary mapping dimension names to coordinate indices + + Returns: + Tuple of (validated coordinates dict, dimensions tuple) + """ + # Check dimension limit + if len(coords) > 2: + raise ConversionError(f'Maximum 2 dimensions currently supported, got {len(coords)}') + + validated_coords = {} dims = [] - if timesteps is not None: - if not isinstance(timesteps, pd.DatetimeIndex) or len(timesteps) == 0: - raise ConversionError('Timesteps must be a non-empty DatetimeIndex') - if timesteps.name != 'time': - timesteps = timesteps.rename('time') - coords['time'] = timesteps - dims.append('time') + for dim_name, coord_index in coords.items(): + # Validate coordinate index + if not isinstance(coord_index, pd.Index) or len(coord_index) == 0: + raise ConversionError(f'{dim_name} coordinates must be a non-empty pandas Index') + + # Ensure coordinate index has the correct name + if coord_index.name != dim_name: + coord_index = coord_index.rename(dim_name) + + # Special validation for time dimension + if dim_name == 'time' and not isinstance(coord_index, pd.DatetimeIndex): + raise ConversionError('time coordinates must be a DatetimeIndex') - if scenarios is not None: - if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: - raise ConversionError('Scenarios must be a non-empty Index') - if scenarios.name != 'scenario': - scenarios = scenarios.rename('scenario') - coords['scenario'] = scenarios - dims.append('scenario') + validated_coords[dim_name] = coord_index + dims.append(dim_name) - return coords, tuple(dims) + return validated_coords, tuple(dims) @staticmethod def _convert_scalar( @@ -244,24 +256,31 @@ def _convert_1d_array(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple elif len(dims) == 2: # Broadcast 1D array to 2D based on which dimension it matches - time_len = len(coords['time']) - scenario_len = len(coords['scenario']) - - if len(data) == time_len: - # Broadcast across scenarios - values = np.repeat(data[:, np.newaxis], scenario_len, axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - elif len(data) == scenario_len: - # Broadcast across time - values = np.repeat(data[np.newaxis, :], time_len, axis=0) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - else: + dim_lengths = {dim: len(coords[dim]) for dim in dims} + + # Find which dimension the array length matches + matching_dims = [dim for dim, length in dim_lengths.items() if len(data) == length] + + if len(matching_dims) == 0: + raise ConversionError(f'Array length {len(data)} matches none of the dimensions: {dim_lengths}') + elif len(matching_dims) > 1: raise ConversionError( - f'Array length {len(data)} matches neither time ({time_len}) nor scenario ({scenario_len}) dimensions' + f'Array length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine broadcasting direction.' ) + # Broadcast along the matching dimension + match_dim = matching_dims[0] + other_dim = [d for d in dims if d != match_dim][0] + + if dims.index(match_dim) == 0: # First dimension + values = np.repeat(data[:, np.newaxis], len(coords[other_dim]), axis=1) + else: # Second dimension + values = np.repeat(data[np.newaxis, :], len(coords[other_dim]), axis=0) + + return xr.DataArray(values.copy(), coords=coords, dims=dims) + else: - raise ConversionError('Maximum 2 dimensions supported') + raise ConversionError(f'Maximum 2 dimensions currently supported, got {len(dims)}') @staticmethod def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: @@ -279,16 +298,23 @@ def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[st elif len(dims) == 2: # Check which dimension the Series index matches - if data.index.equals(coords['time']): - # Broadcast across scenarios - values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - elif data.index.equals(coords['scenario']): - # Broadcast across time - values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - else: - raise ConversionError('Series index must match either time or scenario coordinates') + if 'time' in coords and data.index.equals(coords['time']): + # Broadcast across other dimensions + other_dims = [d for d in dims if d != 'time'] + if len(other_dims) == 1: + other_dim = other_dims[0] + values = np.repeat(data.values[:, np.newaxis], len(coords[other_dim]), axis=1) + return xr.DataArray(values.copy(), coords=coords, dims=dims) + + elif len([d for d in dims if d != 'time']) == 1: + # Check if Series matches the non-time dimension + other_dim = [d for d in dims if d != 'time'][0] + if data.index.equals(coords[other_dim]): + # Broadcast across time + values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) + return xr.DataArray(values.copy(), coords=coords, dims=dims) + + raise ConversionError(f'Series index must match one of the target dimensions: {list(coords.keys())}') else: raise ConversionError('Maximum 2 dimensions supported') @@ -330,18 +356,24 @@ def _broadcast_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: elif len(data.dims) == 1 and len(dims) == 2: source_dim = data.dims[0] + # Check if source dimension exists in target + if source_dim not in coords: + raise ConversionError(f'Source dimension "{source_dim}" not found in target coordinates') + # Check coordinate compatibility if not np.array_equal(data.coords[source_dim].values, coords[source_dim].values): raise ConversionError(f'Source {source_dim} coordinates do not match target coordinates') - if source_dim == 'time': - # Broadcast time to include scenarios - values = np.repeat(data.values[:, np.newaxis], len(coords['scenario']), axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - elif source_dim == 'scenario': - # Broadcast scenario to include time - values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values.copy(), coords=coords, dims=dims) + # Find the other dimension to broadcast to + other_dim = [d for d in dims if d != source_dim][0] + + # Broadcast based on dimension order + if dims.index(source_dim) == 0: # Source is first dimension + values = np.repeat(data.values[:, np.newaxis], len(coords[other_dim]), axis=1) + else: # Source is second dimension + values = np.repeat(data.values[np.newaxis, :], len(coords[other_dim]), axis=0) + + return xr.DataArray(values.copy(), coords=coords, dims=dims) raise ConversionError(f'Cannot broadcast from {data.dims} to {dims}') diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 5536b8cb2..eed6c1283 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -11,621 +11,484 @@ @pytest.fixture -def sample_time_index(): +def time_coords(): return pd.date_range('2024-01-01', periods=5, freq='D', name='time') @pytest.fixture -def sample_scenario_index(): - return pd.Index(['baseline', 'high_demand', 'low_price'], name='scenario') +def scenario_coords(): + return pd.Index(['baseline', 'high', 'low'], name='scenario') -class TestSingleDimensionConversion: - """Tests for converting data without scenarios (1D: time only).""" +@pytest.fixture +def region_coords(): + return pd.Index(['north', 'south', 'east'], name='region') - def test_scalar_conversion(self, sample_time_index): - """Test converting a scalar value.""" - # Test with integer - result = DataConverter.to_dataarray(42, sample_time_index) - assert isinstance(result, xr.DataArray) - assert result.shape == (len(sample_time_index),) - assert result.dims == ('time',) - assert np.all(result.values == 42) - # Test with float - result = DataConverter.to_dataarray(42.5, sample_time_index) - assert np.all(result.values == 42.5) +class TestBasicConversion: + """Test basic data type conversions with different coordinate configurations.""" - # Test with numpy scalar types - result = DataConverter.to_dataarray(np.int64(42), sample_time_index) - assert np.all(result.values == 42) - result = DataConverter.to_dataarray(np.float32(42.5), sample_time_index) - assert np.all(result.values == 42.5) - - def test_ndarray_conversion(self, sample_time_index): - """Test converting a numpy ndarray.""" - # Test with integer 1D array - arr_1d = np.array([1, 2, 3, 4, 5]) - result = DataConverter.to_dataarray(arr_1d, sample_time_index) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, arr_1d) - - # Test with float 1D array - arr_1d = np.array([1.1, 2.2, 3.3, 4.4, 5.5]) - result = DataConverter.to_dataarray(arr_1d, sample_time_index) - assert np.array_equal(result.values, arr_1d) - - # Test with array containing NaN - arr_1d = np.array([1, np.nan, 3, np.nan, 5]) - result = DataConverter.to_dataarray(arr_1d, sample_time_index) - assert np.array_equal(np.isnan(result.values), np.isnan(arr_1d)) - assert np.array_equal(result.values[~np.isnan(result.values)], arr_1d[~np.isnan(arr_1d)]) - - def test_dataarray_conversion(self, sample_time_index): - """Test converting an existing xarray DataArray.""" - # Create original DataArray - original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': sample_time_index}, dims=['time']) - - # Convert and check - result = DataConverter.to_dataarray(original, sample_time_index) + def test_scalar_no_coords(self): + """Scalar without coordinates should create 0D DataArray.""" + result = DataConverter.to_dataarray(42) + assert result.shape == () + assert result.dims == () + assert result.item() == 42 + + def test_scalar_single_coord(self, time_coords): + """Scalar with single coordinate should broadcast.""" + result = DataConverter.to_dataarray(42, coords={'time': time_coords}) assert result.shape == (5,) assert result.dims == ('time',) - assert np.array_equal(result.values, original.values) - - # Ensure it's a copy - result[0] = 999 - assert original[0].item() == 1 # Original should be unchanged - - # Test with different time coordinates but same length - different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') - original = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'time': different_times}, dims=['time']) - - # Should raise an error for mismatched time coordinates - with pytest.raises(ConversionError): - DataConverter.to_dataarray(original, sample_time_index) - - -class TestMultiDimensionConversion: - """Tests for converting data with scenarios (2D: scenario × time).""" - - def test_scalar_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting scalar values with scenario dimension.""" - # Test with integer - result = DataConverter.to_dataarray(42, sample_time_index, sample_scenario_index) + assert np.all(result.values == 42) - assert isinstance(result, xr.DataArray) - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + def test_scalar_multiple_coords(self, time_coords, scenario_coords): + """Scalar with multiple coordinates should broadcast to all.""" + result = DataConverter.to_dataarray(42, coords={'time': time_coords, 'scenario': scenario_coords}) + assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') assert np.all(result.values == 42) - assert set(result.coords['scenario'].values) == set(sample_scenario_index.values) - assert set(result.coords['time'].values) == set(sample_time_index.values) - # Test with float - result = DataConverter.to_dataarray(42.5, sample_time_index, sample_scenario_index) - assert np.all(result.values == 42.5) + def test_numpy_scalars(self, time_coords): + """Test numpy scalar types.""" + for scalar in [np.int32(42), np.int64(42), np.float32(42.5), np.float64(42.5)]: + result = DataConverter.to_dataarray(scalar, coords={'time': time_coords}) + assert result.shape == (5,) + assert np.all(result.values == scalar.item()) - def test_1d_array_with_scenarios_time_broadcast(self, sample_time_index, sample_scenario_index): - """Test converting 1D array matching time dimension (broadcasting across scenarios).""" - # Create 1D array matching timesteps length - arr_1d = np.array([1, 2, 3, 4, 5]) - # Convert with scenarios - result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) +class TestArrayConversion: + """Test numpy array conversions.""" - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) - assert result.dims == ('time', 'scenario') + def test_1d_array_no_coords(self): + """1D array without coords should fail unless single element.""" + # Multi-element fails + with pytest.raises(ConversionError): + DataConverter.to_dataarray(np.array([1, 2, 3])) - # Each scenario should have the same values (broadcasting) - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(scenario_slice.values, arr_1d) + # Single element succeeds + result = DataConverter.to_dataarray(np.array([42])) + assert result.shape == () + assert result.item() == 42 - def test_1d_array_with_scenarios_scenario_broadcast(self, sample_time_index, sample_scenario_index): - """Test converting 1D array matching scenario dimension (broadcasting across time).""" - # Create 1D array matching scenario length - arr_1d = np.array([10, 20, 30]) # 3 scenarios + def test_1d_array_matching_coord(self, time_coords): + """1D array matching coordinate length should work.""" + arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(arr, coords={'time': time_coords}) + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, arr) - # Convert with time and scenarios - result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) + def test_1d_array_mismatched_coord(self, time_coords): + """1D array not matching coordinate length should fail.""" + arr = np.array([10, 20, 30]) # Length 3, time_coords has length 5 + with pytest.raises(ConversionError): + DataConverter.to_dataarray(arr, coords={'time': time_coords}) - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + def test_1d_array_broadcast_to_multiple_coords(self, time_coords, scenario_coords): + """1D array should broadcast to matching dimension.""" + # Array matching time dimension + time_arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(time_arr, coords={'time': time_coords, 'scenario': scenario_coords}) + assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') - # Each time step should have the same scenario values (broadcasting) - for time in sample_time_index: - time_slice = result.sel(time=time) - assert np.array_equal(time_slice.values, arr_1d) - - def test_dataarray_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting an existing DataArray with scenarios.""" - # Create a multi-scenario DataArray with dims in (time, scenario) order - original = xr.DataArray( - data=np.array([[1, 6, 11], [2, 7, 12], [3, 8, 13], [4, 9, 14], [5, 10, 15]]), - coords={'time': sample_time_index, 'scenario': sample_scenario_index}, - dims=['time', 'scenario'], - ) - - # Test conversion - result = DataConverter.to_dataarray(original, sample_time_index, sample_scenario_index) + # Each scenario should have the same time values + for scenario in scenario_coords: + assert np.array_equal(result.sel(scenario=scenario).values, time_arr) + # Array matching scenario dimension + scenario_arr = np.array([100, 200, 300]) + result = DataConverter.to_dataarray(scenario_arr, coords={'time': time_coords, 'scenario': scenario_coords}) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') - assert np.array_equal(result.values, original.values) - # Ensure it's a copy - result.loc[:, 'baseline'] = 999 - assert original.sel(scenario='baseline')[0].item() == 1 # Original should be unchanged + # Each time should have the same scenario values + for time in time_coords: + assert np.array_equal(result.sel(time=time).values, scenario_arr) + + def test_1d_array_ambiguous_length(self): + """Array length matching multiple dimensions should fail.""" + # Both dimensions have length 3 + coords_3x3 = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario') + } + arr = np.array([1, 2, 3]) + + with pytest.raises(ConversionError, match="matches multiple dimensions"): + DataConverter.to_dataarray(arr, coords=coords_3x3) + + def test_multidimensional_array_rejected(self, time_coords): + """Multidimensional arrays should be rejected.""" + arr_2d = np.array([[1, 2, 3], [4, 5, 6]]) + with pytest.raises(ConversionError, match="Only 1D arrays supported"): + DataConverter.to_dataarray(arr_2d, coords={'time': time_coords}) class TestSeriesConversion: - """Tests for converting pandas Series to DataArray.""" + """Test pandas Series conversions.""" - def test_series_single_dimension_time(self, sample_time_index): - """Test converting a pandas Series with time index.""" - # Create a Series with matching time index - series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) + def test_series_no_coords(self): + """Series without coords should fail unless single element.""" + # Multi-element fails + series = pd.Series([1, 2, 3]) + with pytest.raises(ConversionError): + DataConverter.to_dataarray(series) - # Convert and check - result = DataConverter.to_dataarray(series, sample_time_index) - assert isinstance(result, xr.DataArray) + # Single element succeeds + single_series = pd.Series([42]) + result = DataConverter.to_dataarray(single_series) + assert result.shape == () + assert result.item() == 42 + + def test_series_matching_index(self, time_coords, scenario_coords): + """Series with matching index should work.""" + # Time-indexed series + time_series = pd.Series([10, 20, 30, 40, 50], index=time_coords) + result = DataConverter.to_dataarray(time_series, coords={'time': time_coords}) assert result.shape == (5,) assert result.dims == ('time',) - assert np.array_equal(result.values, series.values) - assert np.array_equal(result.coords['time'].values, sample_time_index.values) + assert np.array_equal(result.values, time_series.values) - def test_series_single_dimension_scenario(self, sample_scenario_index): - """Test converting a pandas Series with scenario index.""" - # Create a Series with scenario index - series = pd.Series([100, 200, 300], index=sample_scenario_index) - - result = DataConverter.to_dataarray(series, scenarios=sample_scenario_index) + # Scenario-indexed series + scenario_series = pd.Series([100, 200, 300], index=scenario_coords) + result = DataConverter.to_dataarray(scenario_series, coords={'scenario': scenario_coords}) assert result.shape == (3,) assert result.dims == ('scenario',) - assert np.array_equal(result.values, series.values) - assert np.array_equal(result.coords['scenario'].values, sample_scenario_index.values) + assert np.array_equal(result.values, scenario_series.values) - def test_series_mismatched_index(self, sample_time_index): - """Test converting a Series with mismatched index.""" - # Create Series with different time index - different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') - series = pd.Series([10, 20, 30, 40, 50], index=different_times) + def test_series_mismatched_index(self, time_coords): + """Series with non-matching index should fail.""" + wrong_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + series = pd.Series([10, 20, 30, 40, 50], index=wrong_times) - # Should raise error for mismatched index with pytest.raises(ConversionError): - DataConverter.to_dataarray(series, sample_time_index) + DataConverter.to_dataarray(series, coords={'time': time_coords}) - def test_series_broadcast_to_scenarios(self, sample_time_index, sample_scenario_index): - """Test broadcasting a time-indexed Series across scenarios.""" - # Create a Series with time index - series = pd.Series([10, 20, 30, 40, 50], index=sample_time_index) + def test_series_broadcast_to_multiple_coords(self, time_coords, scenario_coords): + """Series should broadcast to non-matching dimensions.""" + # Time series broadcast to scenarios + time_series = pd.Series([10, 20, 30, 40, 50], index=time_coords) + result = DataConverter.to_dataarray(time_series, coords={'time': time_coords, 'scenario': scenario_coords}) + assert result.shape == (5, 3) - # Convert with scenarios - result = DataConverter.to_dataarray(series, sample_time_index, sample_scenario_index) + for scenario in scenario_coords: + assert np.array_equal(result.sel(scenario=scenario).values, time_series.values) + # Scenario series broadcast to time + scenario_series = pd.Series([100, 200, 300], index=scenario_coords) + result = DataConverter.to_dataarray(scenario_series, coords={'time': time_coords, 'scenario': scenario_coords}) assert result.shape == (5, 3) - assert result.dims == ('time', 'scenario') - # Check broadcasting - each scenario should have the same values - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(scenario_slice.values, series.values) + for time in time_coords: + assert np.array_equal(result.sel(time=time).values, scenario_series.values) - def test_series_broadcast_to_time(self, sample_time_index, sample_scenario_index): - """Test broadcasting a scenario-indexed Series across time.""" - # Create a Series with scenario index - series = pd.Series([100, 200, 300], index=sample_scenario_index) + def test_series_wrong_dimension(self, time_coords, region_coords): + """Series indexed by dimension not in coords should fail.""" + wrong_series = pd.Series([1, 2, 3], index=region_coords) - # Convert with time - result = DataConverter.to_dataarray(series, sample_time_index, sample_scenario_index) + with pytest.raises(ConversionError): + DataConverter.to_dataarray(wrong_series, coords={'time': time_coords}) - assert result.shape == (5, 3) - assert result.dims == ('time', 'scenario') - # Check broadcasting - each time should have the same scenario values - for time in sample_time_index: - time_slice = result.sel(time=time) - assert np.array_equal(time_slice.values, series.values) +class TestDataFrameConversion: + """Test pandas DataFrame conversions.""" + def test_single_column_dataframe(self, time_coords): + """Single-column DataFrame should work like Series.""" + df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=time_coords) + result = DataConverter.to_dataarray(df, coords={'time': time_coords}) -class TestTimeSeriesDataConversion: - """Tests for converting TimeSeriesData objects.""" + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, df['value'].values) - def test_timeseries_data_conversion(self, sample_time_index): - """Test converting TimeSeriesData.""" - # Create TimeSeriesData - data_array = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_time_index}, dims=['time']) - ts_data = TimeSeriesData(data_array, aggregation_group='test_group') + def test_multi_column_dataframe_rejected(self, time_coords): + """Multi-column DataFrame should be rejected.""" + df = pd.DataFrame({ + 'value1': [10, 20, 30, 40, 50], + 'value2': [15, 25, 35, 45, 55] + }, index=time_coords) - # Convert - result = DataConverter.to_dataarray(ts_data, sample_time_index) + with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + DataConverter.to_dataarray(df, coords={'time': time_coords}) - assert isinstance(result, xr.DataArray) - assert result.shape == (5,) - assert result.dims == ('time',) - assert np.array_equal(result.values, [1, 2, 3, 4, 5]) + def test_empty_dataframe_rejected(self, time_coords): + """Empty DataFrame should be rejected.""" + df = pd.DataFrame(index=time_coords) # No columns - def test_timeseries_data_with_scenarios(self, sample_time_index, sample_scenario_index): - """Test converting TimeSeriesData with broadcasting to scenarios.""" - # Create 1D TimeSeriesData - data_array = xr.DataArray([1, 2, 3, 4, 5], coords={'time': sample_time_index}, dims=['time']) - ts_data = TimeSeriesData(data_array) + with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + DataConverter.to_dataarray(df, coords={'time': time_coords}) - # Convert with scenarios (should broadcast) - result = DataConverter.to_dataarray(ts_data, sample_time_index, sample_scenario_index) + def test_dataframe_broadcast(self, time_coords, scenario_coords): + """Single-column DataFrame should broadcast like Series.""" + df = pd.DataFrame({'power': [10, 20, 30, 40, 50]}, index=time_coords) + result = DataConverter.to_dataarray(df, coords={'time': time_coords, 'scenario': scenario_coords}) assert result.shape == (5, 3) - assert result.dims == ('time', 'scenario') + for scenario in scenario_coords: + assert np.array_equal(result.sel(scenario=scenario).values, df['power'].values) - # Each scenario should have the same values - for scenario in sample_scenario_index: - assert np.array_equal(result.sel(scenario=scenario).values, [1, 2, 3, 4, 5]) +class TestDataArrayConversion: + """Test xarray DataArray conversions.""" -class TestDataFrameConversion: - """Tests for converting single-column pandas DataFrames to DataArray.""" - - def test_single_column_dataframe_time(self, sample_time_index): - """Test converting a single-column DataFrame with time index.""" - # Create DataFrame with one column - df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + def test_compatible_dataarray(self, time_coords): + """Compatible DataArray should pass through.""" + original = xr.DataArray([10, 20, 30, 40, 50], coords={'time': time_coords}, dims=['time']) + result = DataConverter.to_dataarray(original, coords={'time': time_coords}) - # Convert and check - result = DataConverter.to_dataarray(df, sample_time_index) - assert isinstance(result, xr.DataArray) assert result.shape == (5,) assert result.dims == ('time',) - assert np.array_equal(result.values, df['value'].values) - - def test_single_column_dataframe_scenario(self, sample_scenario_index): - """Test converting a single-column DataFrame with scenario index.""" - # Create DataFrame with one column and scenario index - df = pd.DataFrame({'value': [100, 200, 300]}, index=sample_scenario_index) - - result = DataConverter.to_dataarray(df, scenarios=sample_scenario_index) - assert result.shape == (3,) - assert result.dims == ('scenario',) - assert np.array_equal(result.values, df['value'].values) + assert np.array_equal(result.values, original.values) - def test_dataframe_broadcast_to_scenarios(self, sample_time_index, sample_scenario_index): - """Test broadcasting a time-indexed DataFrame across scenarios.""" - # Create DataFrame with time index - df = pd.DataFrame({'power': [10, 20, 30, 40, 50]}, index=sample_time_index) + # Should be a copy + result[0] = 999 + assert original[0].item() == 10 - # Convert with scenarios - result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) + def test_incompatible_dataarray_coords(self, time_coords): + """DataArray with wrong coordinates should fail.""" + wrong_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') + original = xr.DataArray([10, 20, 30, 40, 50], coords={'time': wrong_times}, dims=['time']) - assert result.shape == (5, 3) - assert result.dims == ('time', 'scenario') + with pytest.raises(ConversionError): + DataConverter.to_dataarray(original, coords={'time': time_coords}) - # Check broadcasting - each scenario should have the same values - for scenario in sample_scenario_index: - scenario_slice = result.sel(scenario=scenario) - assert np.array_equal(scenario_slice.values, df['power'].values) + def test_incompatible_dataarray_dims(self, time_coords): + """DataArray with wrong dimensions should fail.""" + original = xr.DataArray([10, 20, 30, 40, 50], coords={'wrong_dim': range(5)}, dims=['wrong_dim']) - def test_dataframe_broadcast_to_time(self, sample_time_index, sample_scenario_index): - """Test broadcasting a scenario-indexed DataFrame across time.""" - # Create DataFrame with scenario index - df = pd.DataFrame({'cost': [100, 200, 300]}, index=sample_scenario_index) + with pytest.raises(ConversionError): + DataConverter.to_dataarray(original, coords={'time': time_coords}) - # Convert with time - result = DataConverter.to_dataarray(df, sample_time_index, sample_scenario_index) + def test_dataarray_broadcast(self, time_coords, scenario_coords): + """DataArray should broadcast to additional dimensions.""" + # 1D time DataArray to 2D time+scenario + original = xr.DataArray([10, 20, 30, 40, 50], coords={'time': time_coords}, dims=['time']) + result = DataConverter.to_dataarray(original, coords={'time': time_coords, 'scenario': scenario_coords}) assert result.shape == (5, 3) assert result.dims == ('time', 'scenario') - # Check broadcasting - each time should have the same scenario values - for time in sample_time_index: - time_slice = result.sel(time=time) - assert np.array_equal(time_slice.values, df['cost'].values) + for scenario in scenario_coords: + assert np.array_equal(result.sel(scenario=scenario).values, original.values) - def test_multi_column_dataframe_fails(self, sample_time_index): - """Test that multi-column DataFrames are rejected.""" - # Create DataFrame with multiple columns - df = pd.DataFrame({ - 'value1': [10, 20, 30, 40, 50], - 'value2': [15, 25, 35, 45, 55] - }, index=sample_time_index) + def test_scalar_dataarray_broadcast(self, time_coords, scenario_coords): + """Scalar DataArray should broadcast to all dimensions.""" + scalar_da = xr.DataArray(42) + result = DataConverter.to_dataarray(scalar_da, coords={'time': time_coords, 'scenario': scenario_coords}) - # Should raise error - with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): - DataConverter.to_dataarray(df, sample_time_index) + assert result.shape == (5, 3) + assert np.all(result.values == 42) - def test_dataframe_mismatched_index(self, sample_time_index): - """Test DataFrame with mismatched index.""" - # Create DataFrame with different time index - different_times = pd.date_range('2025-01-01', periods=5, freq='D', name='time') - df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=different_times) - # Should raise error for mismatched index - with pytest.raises(ConversionError): - DataConverter.to_dataarray(df, sample_time_index) +class TestTimeSeriesDataConversion: + """Test TimeSeriesData conversions.""" - def test_dataframe_copy_behavior(self, sample_time_index): - """Test that DataFrame conversion creates a copy.""" - # Create DataFrame - df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=sample_time_index) + def test_timeseries_data_basic(self, time_coords): + """TimeSeriesData should work like DataArray.""" + data_array = xr.DataArray([10, 20, 30, 40, 50], coords={'time': time_coords}, dims=['time']) + ts_data = TimeSeriesData(data_array, aggregation_group='test') - # Convert - result = DataConverter.to_dataarray(df, sample_time_index) + result = DataConverter.to_dataarray(ts_data, coords={'time': time_coords}) - # Modify the result - result[0] = 999 + assert result.shape == (5,) + assert result.dims == ('time',) + assert np.array_equal(result.values, [10, 20, 30, 40, 50]) - # Original DataFrame should be unchanged - assert df.loc[sample_time_index[0], 'value'] == 10 + def test_timeseries_data_broadcast(self, time_coords, scenario_coords): + """TimeSeriesData should broadcast to additional dimensions.""" + data_array = xr.DataArray([10, 20, 30, 40, 50], coords={'time': time_coords}, dims=['time']) + ts_data = TimeSeriesData(data_array) - def test_empty_dataframe_fails(self, sample_time_index): - """Test that empty DataFrames are rejected.""" - # DataFrame with no columns - df = pd.DataFrame(index=sample_time_index) + result = DataConverter.to_dataarray(ts_data, coords={'time': time_coords, 'scenario': scenario_coords}) - with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): - DataConverter.to_dataarray(df, sample_time_index) + assert result.shape == (5, 3) + for scenario in scenario_coords: + assert np.array_equal(result.sel(scenario=scenario).values, [10, 20, 30, 40, 50]) - def test_dataframe_with_named_column(self, sample_time_index): - """Test DataFrame with a named column.""" - df = pd.DataFrame(index=sample_time_index) - df['energy_output'] = [100, 150, 200, 175, 125] - result = DataConverter.to_dataarray(df, sample_time_index) - assert result.shape == (5,) - assert np.array_equal(result.values, [100, 150, 200, 175, 125]) +class TestCustomDimensions: + """Test with custom dimension names beyond time/scenario.""" + def test_custom_single_dimension(self, region_coords): + """Test with custom dimension name.""" + result = DataConverter.to_dataarray(42, coords={'region': region_coords}) + assert result.shape == (3,) + assert result.dims == ('region',) + assert np.all(result.values == 42) -class TestInvalidInputs: - """Tests for invalid inputs and error handling.""" + def test_custom_multiple_dimensions(self): + """Test with multiple custom dimensions.""" + products = pd.Index(['A', 'B'], name='product') + technologies = pd.Index(['solar', 'wind', 'gas'], name='technology') - def test_time_index_validation(self): - """Test validation of time index.""" - # Test with unnamed index - unnamed_index = pd.date_range('2024-01-01', periods=5, freq='D') - # Should automatically rename to 'time' with a warning, not raise error - result = DataConverter.to_dataarray(42, unnamed_index) - assert result.coords['time'].name == 'time' + # Array matching technology dimension + arr = np.array([100, 150, 80]) + result = DataConverter.to_dataarray(arr, coords={'product': products, 'technology': technologies}) - # Test with empty index - empty_index = pd.DatetimeIndex([], name='time') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, empty_index) + assert result.shape == (2, 3) + assert result.dims == ('product', 'technology') - # Test with non-DatetimeIndex - wrong_type_index = pd.Index([1, 2, 3, 4, 5], name='time') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, wrong_type_index) - - def test_scenario_index_validation(self, sample_time_index): - """Test validation of scenario index.""" - # Test with unnamed scenario index - unnamed_index = pd.Index(['baseline', 'high_demand']) - # Should automatically rename to 'scenario' with a warning, not raise error - result = DataConverter.to_dataarray(42, sample_time_index, unnamed_index) - assert result.coords['scenario'].name == 'scenario' - - # Test with empty scenario index - empty_index = pd.Index([], name='scenario') - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, sample_time_index, empty_index) + # Should broadcast across products + for product in products: + assert np.array_equal(result.sel(product=product).values, arr) - # Test with non-Index scenario - with pytest.raises(ConversionError): - DataConverter.to_dataarray(42, sample_time_index, ['baseline', 'high_demand']) + def test_mixed_dimension_types(self): + """Test mixing time dimension with custom dimensions.""" + time_coords = pd.date_range('2024-01-01', periods=3, freq='D', name='time') + regions = pd.Index(['north', 'south'], name='region') - def test_invalid_data_types(self, sample_time_index, sample_scenario_index): - """Test handling of invalid data types.""" - # Test invalid input type (string) - with pytest.raises(ConversionError): - DataConverter.to_dataarray('invalid_string', sample_time_index) + # Time series should broadcast to regions + time_series = pd.Series([10, 20, 30], index=time_coords) + result = DataConverter.to_dataarray(time_series, coords={'time': time_coords, 'region': regions}) - # Test invalid input type with scenarios - with pytest.raises(ConversionError): - DataConverter.to_dataarray('invalid_string', sample_time_index, sample_scenario_index) + assert result.shape == (3, 2) + assert result.dims == ('time', 'region') - # Test unsupported complex object - with pytest.raises(ConversionError): - DataConverter.to_dataarray(object(), sample_time_index) - # Test None value - with pytest.raises(ConversionError): - DataConverter.to_dataarray(None, sample_time_index) +class TestValidation: + """Test coordinate validation.""" - def test_multidimensional_array_rejection(self, sample_time_index, sample_scenario_index): - """Test that multidimensional arrays are rejected.""" - # Test 2D array (not supported in simplified version) - arr_2d = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) - with pytest.raises(ConversionError, match="Only 1D arrays supported"): - DataConverter.to_dataarray(arr_2d, sample_time_index) + def test_empty_coords(self): + """Empty coordinates should work for scalars.""" + result = DataConverter.to_dataarray(42, coords={}) + assert result.shape == () + assert result.item() == 42 - # Test 3D array - arr_3d = np.ones((2, 3, 4)) - with pytest.raises(ConversionError, match="Only 1D arrays supported"): - DataConverter.to_dataarray(arr_3d, sample_time_index, sample_scenario_index) - - def test_mismatched_input_dimensions(self, sample_time_index, sample_scenario_index): - """Test handling of mismatched input dimensions.""" - # Test mismatched Series index - mismatched_series = pd.Series( - [1, 2, 3, 4, 5, 6], index=pd.date_range('2025-01-01', periods=6, freq='D', name='time') - ) + def test_invalid_coord_type(self): + """Non-pandas Index coordinates should fail.""" with pytest.raises(ConversionError): - DataConverter.to_dataarray(mismatched_series, sample_time_index) + DataConverter.to_dataarray(42, coords={'time': [1, 2, 3]}) - # Test mismatched array length for time-only + def test_empty_coord_index(self): + """Empty coordinate index should fail.""" + empty_index = pd.Index([], name='time') with pytest.raises(ConversionError): - DataConverter.to_dataarray(np.array([1, 2, 3]), sample_time_index) # Wrong length + DataConverter.to_dataarray(42, coords={'time': empty_index}) + + def test_time_coord_validation(self): + """Time coordinates must be DatetimeIndex.""" + # Non-datetime index with name 'time' should fail + wrong_time = pd.Index([1, 2, 3], name='time') + with pytest.raises(ConversionError, match="time coordinates must be a DatetimeIndex"): + DataConverter.to_dataarray(42, coords={'time': wrong_time}) + + def test_coord_naming(self, time_coords): + """Coordinates should be auto-renamed to match dimension.""" + # Unnamed time index should be renamed + unnamed_time = time_coords.rename(None) + result = DataConverter.to_dataarray(42, coords={'time': unnamed_time}) + assert result.coords['time'].name == 'time' - # Test array that doesn't match either dimension - wrong_length_array = np.array([1, 2, 3, 4]) # Doesn't match time (5) or scenario (3) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_length_array, sample_time_index, sample_scenario_index) - def test_dataarray_dimension_mismatch(self, sample_time_index, sample_scenario_index): - """Test handling of mismatched DataArray dimensions.""" - # Create DataArray with wrong dimensions - wrong_dims = xr.DataArray(data=np.array([1, 2, 3, 4, 5]), coords={'wrong_dim': range(5)}, dims=['wrong_dim']) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_dims, sample_time_index) - - # Create DataArray with right dims but wrong coordinate values - wrong_coords = xr.DataArray( - data=np.array([1, 2, 3, 4, 5]), - coords={'time': pd.date_range('2025-01-01', periods=5, freq='D', name='time')}, - dims=['time'] - ) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(wrong_coords, sample_time_index) +class TestErrorHandling: + """Test error handling and edge cases.""" + def test_unsupported_data_types(self, time_coords): + """Unsupported data types should fail with clear messages.""" + unsupported = [ + 'string', + object(), + None, + {'dict': 'value'}, + [1, 2, 3] + ] -class TestDataArrayBroadcasting: - """Tests for broadcasting DataArrays.""" + for data in unsupported: + with pytest.raises(ConversionError): + DataConverter.to_dataarray(data, coords={'time': time_coords}) - def test_broadcast_1d_array_to_2d_time(self, sample_time_index, sample_scenario_index): - """Test broadcasting a 1D array (time) to 2D.""" - arr_1d = np.array([1, 2, 3, 4, 5]) + def test_dimension_mismatch_messages(self, time_coords, scenario_coords): + """Error messages should be informative.""" + # Array with wrong length + wrong_arr = np.array([1, 2]) # Length 2, but no dimension has length 2 + with pytest.raises(ConversionError, match="matches none of the dimensions"): + DataConverter.to_dataarray(wrong_arr, coords={'time': time_coords, 'scenario': scenario_coords}) - result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) + def test_maximum_dimensions(self): + """Should handle up to 2 dimensions currently.""" + coords = { + 'dim1': pd.Index(['a', 'b'], name='dim1'), + 'dim2': pd.Index(['x', 'y'], name='dim2'), + 'dim3': pd.Index(['1', '2'], name='dim3') + } - # Should broadcast across scenarios - expected = np.repeat(arr_1d[:, np.newaxis], len(sample_scenario_index), axis=1) - assert np.array_equal(result.values, expected) - assert result.dims == ('time', 'scenario') + with pytest.raises(ConversionError, match="Maximum 2 dimensions currently supported"): + DataConverter.to_dataarray(42, coords=coords) - def test_broadcast_1d_array_to_2d_scenario(self, sample_time_index, sample_scenario_index): - """Test broadcasting a 1D array (scenario) to 2D.""" - arr_1d = np.array([1, 2, 3]) # Matches scenario length - result = DataConverter.to_dataarray(arr_1d, sample_time_index, sample_scenario_index) +class TestDataIntegrity: + """Test data copying and integrity.""" - # Should broadcast across time - expected = np.repeat(arr_1d[np.newaxis, :], len(sample_time_index), axis=0) - assert np.array_equal(result.values, expected) - assert result.dims == ('time', 'scenario') + def test_array_copy_independence(self, time_coords): + """Converted arrays should be independent copies.""" + original_arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(original_arr, coords={'time': time_coords}) - def test_broadcast_1d_array_to_1d(self, sample_time_index): - """Test that 1D array with matching dimension doesn't change.""" - arr_1d = np.array([1, 2, 3, 4, 5]) + # Modify result + result[0] = 999 - result = DataConverter.to_dataarray(arr_1d, sample_time_index) + # Original should be unchanged + assert original_arr[0] == 10 - assert np.array_equal(result.values, arr_1d) - assert result.dims == ('time',) + def test_series_copy_independence(self, time_coords): + """Converted Series should be independent copies.""" + original_series = pd.Series([10, 20, 30, 40, 50], index=time_coords) + result = DataConverter.to_dataarray(original_series, coords={'time': time_coords}) - def test_scalar_dataarray_broadcasting(self, sample_time_index, sample_scenario_index): - """Test broadcasting scalar DataArray.""" - scalar_da = xr.DataArray(42) + # Modify result + result[0] = 999 - result = DataConverter.to_dataarray(scalar_da, sample_time_index, sample_scenario_index) + # Original should be unchanged + assert original_series.iloc[0] == 10 - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) - assert np.all(result.values == 42) + def test_dataframe_copy_independence(self, time_coords): + """Converted DataFrames should be independent copies.""" + original_df = pd.DataFrame({'value': [10, 20, 30, 40, 50]}, index=time_coords) + result = DataConverter.to_dataarray(original_df, coords={'time': time_coords}) + # Modify result + result[0] = 999 -class TestEdgeCases: - """Tests for edge cases and special scenarios.""" + # Original should be unchanged + assert original_df.loc[time_coords[0], 'value'] == 10 - def test_single_timestep(self, sample_scenario_index): - """Test with a single timestep.""" - # Test with only one timestep - single_timestep = pd.DatetimeIndex(['2024-01-01'], name='time') - # Scalar conversion - result = DataConverter.to_dataarray(42, single_timestep) - assert result.shape == (1,) - assert result.dims == ('time',) +class TestSpecialValues: + """Test handling of special numeric values.""" - # With scenarios - result_with_scenarios = DataConverter.to_dataarray(42, single_timestep, sample_scenario_index) - assert result_with_scenarios.shape == (1, len(sample_scenario_index)) - assert result_with_scenarios.dims == ('time', 'scenario') + def test_nan_values(self, time_coords): + """NaN values should be preserved.""" + arr_with_nan = np.array([1, np.nan, 3, np.nan, 5]) + result = DataConverter.to_dataarray(arr_with_nan, coords={'time': time_coords}) - def test_single_scenario(self, sample_time_index): - """Test with a single scenario.""" - # Test with only one scenario - single_scenario = pd.Index(['baseline'], name='scenario') + assert np.array_equal(np.isnan(result.values), np.isnan(arr_with_nan)) + assert np.array_equal(result.values[~np.isnan(result.values)], arr_with_nan[~np.isnan(arr_with_nan)]) - # Scalar conversion with single scenario - result = DataConverter.to_dataarray(42, sample_time_index, single_scenario) - assert result.shape == (len(sample_time_index), 1) - assert result.dims == ('time', 'scenario') + def test_infinite_values(self, time_coords): + """Infinite values should be preserved.""" + arr_with_inf = np.array([1, np.inf, 3, -np.inf, 5]) + result = DataConverter.to_dataarray(arr_with_inf, coords={'time': time_coords}) - # Array conversion with single scenario - arr = np.array([1, 2, 3, 4, 5]) - result_arr = DataConverter.to_dataarray(arr, sample_time_index, single_scenario) - assert result_arr.shape == (5, 1) - assert np.array_equal(result_arr.sel(scenario='baseline').values, arr) - - def test_all_nan_data(self, sample_time_index, sample_scenario_index): - """Test handling of all-NaN data.""" - # Create array of all NaNs - all_nan_array = np.full(5, np.nan) - result = DataConverter.to_dataarray(all_nan_array, sample_time_index) - assert np.all(np.isnan(result.values)) - - # With scenarios - result = DataConverter.to_dataarray(all_nan_array, sample_time_index, sample_scenario_index) - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) - assert np.all(np.isnan(result.values)) - - def test_mixed_data_types(self, sample_time_index, sample_scenario_index): - """Test conversion of mixed integer and float data.""" - # Create array with mixed types - mixed_array = np.array([1, 2.5, 3, 4.5, 5]) - result = DataConverter.to_dataarray(mixed_array, sample_time_index) - - # Result should be float dtype - assert np.issubdtype(result.dtype, np.floating) - assert np.array_equal(result.values, mixed_array) + assert np.array_equal(result.values, arr_with_inf) - # With scenarios - result = DataConverter.to_dataarray(mixed_array, sample_time_index, sample_scenario_index) - assert np.issubdtype(result.dtype, np.floating) - for scenario in sample_scenario_index: - assert np.array_equal(result.sel(scenario=scenario).values, mixed_array) + def test_boolean_values(self, time_coords): + """Boolean values should be preserved.""" + bool_arr = np.array([True, False, True, False, True]) + result = DataConverter.to_dataarray(bool_arr, coords={'time': time_coords}) - def test_boolean_data(self, sample_time_index, sample_scenario_index): - """Test handling of boolean data.""" - bool_array = np.array([True, False, True, False, True]) - result = DataConverter.to_dataarray(bool_array, sample_time_index, sample_scenario_index) assert result.dtype == bool - assert result.shape == (len(sample_time_index), len(sample_scenario_index)) + assert np.array_equal(result.values, bool_arr) + def test_mixed_numeric_types(self, time_coords): + """Mixed integer/float should become float.""" + mixed_arr = np.array([1, 2.5, 3, 4.5, 5]) + result = DataConverter.to_dataarray(mixed_arr, coords={'time': time_coords}) -class TestNoIndexConversion: - """Tests for conversion without any indices (scalar results).""" - - def test_scalar_no_dimensions(self): - """Test scalar conversion without any dimensions.""" - result = DataConverter.to_dataarray(42) - assert isinstance(result, xr.DataArray) - assert result.shape == () - assert result.dims == () - assert result.item() == 42 - - def test_single_element_array_no_dimensions(self): - """Test single-element array without dimensions.""" - arr = np.array([42]) - result = DataConverter.to_dataarray(arr) - assert result.shape == () - assert result.item() == 42 - - def test_multi_element_array_no_dimensions_fails(self): - """Test that multi-element array fails without dimensions.""" - arr = np.array([1, 2, 3]) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(arr) - - def test_series_no_dimensions_fails(self): - """Test that multi-element Series fails without dimensions.""" - series = pd.Series([1, 2, 3]) - with pytest.raises(ConversionError): - DataConverter.to_dataarray(series) - - def test_single_element_series_no_dimensions(self): - """Test single-element Series without dimensions.""" - series = pd.Series([42]) - result = DataConverter.to_dataarray(series) - assert result.shape == () - assert result.item() == 42 + assert np.issubdtype(result.dtype, np.floating) + assert np.array_equal(result.values, mixed_arr) if __name__ == '__main__': From 605f03469ecbc202cdf65841f19937566d6d8e41 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:49:46 +0200 Subject: [PATCH 143/183] Update DataConverter for n-d arrays --- flixopt/core.py | 187 ++++++++++++++++++++++++++++++------ tests/test_dataconverter.py | 98 +++++++++++++++++++ 2 files changed, 255 insertions(+), 30 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index c11ba3993..9278f079c 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -126,24 +126,148 @@ def agg_weight(self): class DataConverter: """ - Converts scalars and 1D data into xarray.DataArray with optional time and scenario dimensions. + Converts data into xarray.DataArray with specified coordinates. - Only handles: - - Scalars (int, float, np.number) - - 1D arrays (np.ndarray, pd.Series) - - xr.DataArray (for broadcasting/checking) + Supports: + - Scalars (broadcast to all dimensions) + - 1D data (np.ndarray, pd.Series, single-column DataFrame) + - xr.DataArray (validated and potentially broadcast) + + Simple 1D data is matched to one dimension and broadcast to others. + DataArrays can have any number of dimensions. """ @staticmethod - def _convert_dataframe(data: pd.DataFrame, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """Convert single-column pandas DataFrame to DataArray by treating it as a Series.""" - # Check that DataFrame has exactly one column - if len(data.columns) != 1: - raise ConversionError(f'Only single-column DataFrames are supported, got {len(data.columns)} columns') + def _convert_1d_data_to_dataarray( + data: Union[np.ndarray, pd.Series], coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Convert 1D data (array or Series) to DataArray by matching to one dimension. + + Args: + data: 1D numpy array or pandas Series + coords: Available coordinates + target_dims: Target dimension names + + Returns: + DataArray with the data matched to appropriate dimension + """ + if len(target_dims) == 0: + # No target dimensions - data must be single element + if len(data) != 1: + raise ConversionError('Cannot convert multi-element data without target dimensions') + return xr.DataArray(data[0] if isinstance(data, np.ndarray) else data.iloc[0]) + + # For Series, try to match index to coordinates + if isinstance(data, pd.Series): + for dim_name in target_dims: + if data.index.equals(coords[dim_name]): + return xr.DataArray(data.values.copy(), coords={dim_name: coords[dim_name]}, dims=[dim_name]) + + # If no index match, fall through to length matching + + # For arrays or unmatched Series, match by length + matching_dims = [] + for dim_name in target_dims: + if len(data) == len(coords[dim_name]): + matching_dims.append(dim_name) + + if len(matching_dims) == 0: + dim_info = {dim: len(coords[dim]) for dim in target_dims} + raise ConversionError(f'Data length {len(data)} matches none of the target dimensions: {dim_info}') + elif len(matching_dims) > 1: + raise ConversionError( + f'Data length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine which dimension to use.' + ) + + # Match to the single matching dimension + match_dim = matching_dims[0] + values = data.values.copy() if isinstance(data, pd.Series) else data.copy() + return xr.DataArray(values, coords={match_dim: coords[match_dim]}, dims=[match_dim]) + + @staticmethod + def _broadcast_to_target_dims( + data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Broadcast DataArray to match target dimensions. + + Args: + data: Source DataArray + coords: Target coordinates + target_dims: Target dimension names + + Returns: + DataArray broadcast to target dimensions + """ + if len(target_dims) == 0: + # Target is scalar + if data.size != 1: + raise ConversionError('Cannot convert multi-element DataArray to scalar') + return xr.DataArray(data.values.item()) + + # If data already matches target, validate coordinates and return + if set(data.dims) == set(target_dims) and len(data.dims) == len(target_dims): + # Check coordinate compatibility + for dim in data.dims: + if dim in coords and not np.array_equal(data.coords[dim].values, coords[dim].values): + raise ConversionError(f'DataArray {dim} coordinates do not match target coordinates') + + # Ensure correct dimension order + if data.dims != target_dims: + data = data.transpose(*target_dims) + return data.copy() + + # Handle scalar data (0D) - broadcast to all dimensions + if data.ndim == 0: + return xr.DataArray(data.item(), coords=coords, dims=target_dims) + + # Handle broadcasting from fewer to more dimensions + if len(data.dims) < len(target_dims): + return DataConverter._broadcast_dataarray_to_more_dims(data, coords, target_dims) + + # Cannot handle more dimensions than target + if len(data.dims) > len(target_dims): + raise ConversionError(f'Cannot reduce DataArray from {len(data.dims)} to {len(target_dims)} dimensions') + + raise ConversionError(f'Cannot convert DataArray with dims {data.dims} to target dims {target_dims}') + + @staticmethod + def _broadcast_dataarray_to_more_dims( + data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """Broadcast DataArray to additional dimensions.""" + # Validate that all source dimensions exist in target + for dim in data.dims: + if dim not in target_dims: + raise ConversionError(f'Source dimension "{dim}" not found in target dimensions {target_dims}') + + # Check coordinate compatibility + if not np.array_equal(data.coords[dim].values, coords[dim].values): + raise ConversionError(f'Source {dim} coordinates do not match target coordinates') + + # Build the full coordinate system + full_coords = {} + for dim in target_dims: + full_coords[dim] = coords[dim] + + # Use xarray's broadcast_to functionality + # Create a template DataArray with target structure + template_data = np.broadcast_to(data.values, [len(coords[dim]) for dim in target_dims]) - # Extract the single column as a Series and convert it - series = data.iloc[:, 0] - return DataConverter._convert_series(series, coords, dims) + # Create mapping for broadcasting + # We need to insert new axes for missing dimensions + expanded_data = data.values + for i, dim in enumerate(target_dims): + if dim not in data.dims: + # Add new axis for this dimension + expanded_data = np.expand_dims(expanded_data, axis=i) + + # Now broadcast to full shape + target_shape = tuple(len(coords[dim]) for dim in target_dims) + broadcasted_data = np.broadcast_to(expanded_data, target_shape) + + return xr.DataArray(broadcasted_data.copy(), coords=full_coords, dims=target_dims) @staticmethod def to_dataarray( @@ -153,10 +277,14 @@ def to_dataarray( """ Convert data to xarray.DataArray with specified coordinates. + Accepts: + - Scalars (broadcast to all dimensions) + - 1D arrays, Series, or single-column DataFrames (matched to one dimension, broadcast to others) + - xr.DataArray (validated and potentially broadcast to additional dimensions) + Args: - data: Scalar, 1D array/Series, single-column DataFrame, or existing DataArray + data: Data to convert coords: Dictionary mapping dimension names to coordinate indices - e.g., {'time': timesteps, 'scenario': scenarios} Returns: DataArray with the converted data @@ -164,35 +292,38 @@ def to_dataarray( if coords is None: coords = {} - coords, dims = DataConverter._prepare_dimensions(coords) + validated_coords, target_dims = DataConverter._prepare_dimensions(coords) - # Handle scalars + # Step 1: Convert to DataArray (with safe 1D/2D logic for simple data) if isinstance(data, (int, float, np.integer, np.floating)): - return DataConverter._convert_scalar(data, coords, dims) + # Scalars: create 0D DataArray, will be broadcast later + intermediate = xr.DataArray(data.item() if hasattr(data, 'item') else data) - # Handle 1D numpy arrays elif isinstance(data, np.ndarray): if data.ndim != 1: raise ConversionError(f'Only 1D arrays supported, got {data.ndim}D array') - return DataConverter._convert_1d_array(data, coords, dims) + intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) - # Handle pandas Series elif isinstance(data, pd.Series): - return DataConverter._convert_series(data, coords, dims) + intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) - # Handle pandas DataFrames (single column only) elif isinstance(data, pd.DataFrame): - return DataConverter._convert_dataframe(data, coords, dims) + if len(data.columns) != 1: + raise ConversionError(f'Only single-column DataFrames are supported, got {len(data.columns)} columns') + series = data.iloc[:, 0] + intermediate = DataConverter._convert_1d_data_to_dataarray(series, validated_coords, target_dims) - # Handle existing DataArrays (including TimeSeriesData) elif isinstance(data, xr.DataArray): - return DataConverter._handle_dataarray(data, coords, dims) + intermediate = data.copy() else: raise ConversionError( f'Unsupported data type: {type(data).__name__}. Only scalars, 1D arrays, Series, single-column DataFrames, and DataArrays are supported.' ) + # Step 2: Broadcast to target dimensions if needed + return DataConverter._broadcast_to_target_dims(intermediate, validated_coords, target_dims) + @staticmethod def _prepare_dimensions(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: """ @@ -204,10 +335,6 @@ def _prepare_dimensions(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index Returns: Tuple of (validated coordinates dict, dimensions tuple) """ - # Check dimension limit - if len(coords) > 2: - raise ConversionError(f'Maximum 2 dimensions currently supported, got {len(coords)}') - validated_coords = {} dims = [] diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index eed6c1283..d8e29014f 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -309,6 +309,104 @@ def test_timeseries_data_broadcast(self, time_coords, scenario_coords): assert np.array_equal(result.sel(scenario=scenario).values, [10, 20, 30, 40, 50]) +class TestMultipleDimensions: + """Test support for more than 2 dimensions.""" + + def test_scalar_many_dimensions(self): + """Scalar should broadcast to any number of dimensions.""" + coords = { + 'time': pd.date_range('2024-01-01', periods=2, freq='D', name='time'), + 'scenario': pd.Index(['A', 'B'], name='scenario'), + 'region': pd.Index(['north', 'south'], name='region'), + 'technology': pd.Index(['solar', 'wind'], name='technology') + } + + result = DataConverter.to_dataarray(42, coords=coords) + assert result.shape == (2, 2, 2, 2) + assert result.dims == ('time', 'scenario', 'region', 'technology') + assert np.all(result.values == 42) + + def test_1d_array_broadcast_to_many_dimensions(self): + """1D array should broadcast to many dimensions.""" + coords = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), + 'scenario': pd.Index(['A', 'B'], name='scenario'), + 'region': pd.Index(['north', 'south'], name='region') + } + + # Array matching time dimension + time_arr = np.array([10, 20, 30]) + result = DataConverter.to_dataarray(time_arr, coords=coords) + + assert result.shape == (3, 2, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting - all scenarios and regions should have same time values + for scenario in coords['scenario']: + for region in coords['region']: + assert np.array_equal( + result.sel(scenario=scenario, region=region).values, + time_arr + ) + + def test_series_broadcast_to_many_dimensions(self): + """Series should broadcast to many dimensions.""" + time_coords = pd.date_range('2024-01-01', periods=3, freq='D', name='time') + coords = { + 'time': time_coords, + 'scenario': pd.Index(['A', 'B'], name='scenario'), + 'region': pd.Index(['north', 'south'], name='region'), + 'product': pd.Index(['X', 'Y', 'Z'], name='product') + } + + # Time-indexed series + time_series = pd.Series([100, 200, 300], index=time_coords) + result = DataConverter.to_dataarray(time_series, coords=coords) + + assert result.shape == (3, 2, 2, 3) + assert result.dims == ('time', 'scenario', 'region', 'product') + + # Check that all non-time dimensions have the same time series values + for scenario in coords['scenario']: + for region in coords['region']: + for product in coords['product']: + assert np.array_equal( + result.sel(scenario=scenario, region=region, product=product).values, + time_series.values + ) + + def test_dataarray_broadcast_to_more_dimensions(self): + """DataArray should broadcast to additional dimensions.""" + time_coords = pd.date_range('2024-01-01', periods=2, freq='D', name='time') + scenario_coords = pd.Index(['A', 'B'], name='scenario') + + # Start with 2D DataArray + original = xr.DataArray( + [[10, 20], [30, 40]], + coords={'time': time_coords, 'scenario': scenario_coords}, + dims=['time', 'scenario'] + ) + + # Broadcast to 3D + coords = { + 'time': time_coords, + 'scenario': scenario_coords, + 'region': pd.Index(['north', 'south'], name='region') + } + + result = DataConverter.to_dataarray(original, coords=coords) + + assert result.shape == (2, 2, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check that all regions have the same time+scenario values + for region in coords['region']: + assert np.array_equal( + result.sel(region=region).values, + original.values + ) + + class TestCustomDimensions: """Test with custom dimension names beyond time/scenario.""" From 656000690d0586c18483013ad9024b286061eeb2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:58:06 +0200 Subject: [PATCH 144/183] Update DataConverter for n-d arrays --- flixopt/core.py | 53 +++++++++++++++++++++---------------- tests/test_dataconverter.py | 13 +-------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 9278f079c..2b841af0b 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -164,7 +164,8 @@ def _convert_1d_data_to_dataarray( if data.index.equals(coords[dim_name]): return xr.DataArray(data.values.copy(), coords={dim_name: coords[dim_name]}, dims=[dim_name]) - # If no index match, fall through to length matching + # If no index matches, raise error + raise ConversionError(f'Data {data} does not match any of the target dimensions: {target_dims}') # For arrays or unmatched Series, match by length matching_dims = [] @@ -246,28 +247,34 @@ def _broadcast_dataarray_to_more_dims( if not np.array_equal(data.coords[dim].values, coords[dim].values): raise ConversionError(f'Source {dim} coordinates do not match target coordinates') - # Build the full coordinate system - full_coords = {} - for dim in target_dims: - full_coords[dim] = coords[dim] - - # Use xarray's broadcast_to functionality - # Create a template DataArray with target structure - template_data = np.broadcast_to(data.values, [len(coords[dim]) for dim in target_dims]) - - # Create mapping for broadcasting - # We need to insert new axes for missing dimensions - expanded_data = data.values - for i, dim in enumerate(target_dims): - if dim not in data.dims: - # Add new axis for this dimension - expanded_data = np.expand_dims(expanded_data, axis=i) - - # Now broadcast to full shape - target_shape = tuple(len(coords[dim]) for dim in target_dims) - broadcasted_data = np.broadcast_to(expanded_data, target_shape) - - return xr.DataArray(broadcasted_data.copy(), coords=full_coords, dims=target_dims) + # Start with the original data + result_data = data.values + result_dims = list(data.dims) + result_coords = {dim: data.coords[dim] for dim in data.dims} + + # Add missing dimensions one by one + for target_dim in target_dims: + if target_dim not in result_dims: + # Add this dimension at the end + result_data = np.expand_dims(result_data, axis=-1) + result_dims.append(target_dim) + result_coords[target_dim] = coords[target_dim] + + # Broadcast along the new dimension + new_shape = list(result_data.shape) + new_shape[-1] = len(coords[target_dim]) + result_data = np.broadcast_to(result_data, new_shape) + + # Reorder dimensions to match target order + if tuple(result_dims) != target_dims: + # Create mapping from current to target order + dim_indices = [result_dims.index(dim) for dim in target_dims] + result_data = np.transpose(result_data, dim_indices) + + # Build final coordinates dict in target order + final_coords = {dim: coords[dim] for dim in target_dims} + + return xr.DataArray(result_data.copy(), coords=final_coords, dims=target_dims) @staticmethod def to_dataarray( diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index d8e29014f..175866c71 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -502,20 +502,9 @@ def test_dimension_mismatch_messages(self, time_coords, scenario_coords): """Error messages should be informative.""" # Array with wrong length wrong_arr = np.array([1, 2]) # Length 2, but no dimension has length 2 - with pytest.raises(ConversionError, match="matches none of the dimensions"): + with pytest.raises(ConversionError, match="matches none of the target dimensions"): DataConverter.to_dataarray(wrong_arr, coords={'time': time_coords, 'scenario': scenario_coords}) - def test_maximum_dimensions(self): - """Should handle up to 2 dimensions currently.""" - coords = { - 'dim1': pd.Index(['a', 'b'], name='dim1'), - 'dim2': pd.Index(['x', 'y'], name='dim2'), - 'dim3': pd.Index(['1', '2'], name='dim3') - } - - with pytest.raises(ConversionError, match="Maximum 2 dimensions currently supported"): - DataConverter.to_dataarray(42, coords=coords) - class TestDataIntegrity: """Test data copying and integrity.""" From 78132ef0e589bd8b627e7f5e0a023482c3b3b422 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sun, 29 Jun 2025 22:04:22 +0200 Subject: [PATCH 145/183] Add extra tests for 3-dims --- flixopt/flow_system.py | 4 +- tests/test_dataconverter.py | 277 ++++++++++++++++++++++++++++++++++++ 2 files changed, 279 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 6bf7502e5..42a287876 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -105,8 +105,8 @@ def _validate_scenarios(scenarios: pd.Index) -> pd.Index: if not isinstance(scenarios, pd.Index) or len(scenarios) == 0: raise ConversionError('Scenarios must be a non-empty Index') - if not scenarios.name == 'scenario': - raise ConversionError(f'Scenarios must be named "scenario", got "{scenarios.name}"') + if scenarios.name != 'scenario': + scenarios = scenarios.rename('scenario') return scenarios diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 175866c71..56f36aabf 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -309,6 +309,283 @@ def test_timeseries_data_broadcast(self, time_coords, scenario_coords): assert np.array_equal(result.sel(scenario=scenario).values, [10, 20, 30, 40, 50]) +class TestThreeDimensionConversion: + """Test conversions with exactly 3 dimensions for all data types.""" + + @pytest.fixture + def three_d_coords(self, time_coords, scenario_coords): + """Standard 3D coordinate system with unique lengths.""" + return { + 'time': time_coords, # length 5 + 'scenario': scenario_coords, # length 3 + 'region': pd.Index(['north', 'south'], name='region') # length 2 - unique! + } + + def test_scalar_three_dimensions(self, three_d_coords): + """Scalar should broadcast to 3 dimensions.""" + result = DataConverter.to_dataarray(42, coords=three_d_coords) + + assert result.shape == (5, 3, 2) # time=5, scenario=3, region=2 + assert result.dims == ('time', 'scenario', 'region') + assert np.all(result.values == 42) + + # Verify all coordinates are correct + assert result.indexes['time'].equals(three_d_coords['time']) + assert result.indexes['scenario'].equals(three_d_coords['scenario']) + assert result.indexes['region'].equals(three_d_coords['region']) + + def test_numpy_scalar_three_dimensions(self, three_d_coords): + """Numpy scalars should broadcast to 3 dimensions.""" + for scalar in [np.int32(100), np.float64(3.14)]: + result = DataConverter.to_dataarray(scalar, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + assert np.all(result.values == scalar.item()) + + def test_1d_array_time_to_three_dimensions(self, three_d_coords): + """1D array matching time should broadcast to 3D.""" + time_arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(time_arr, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting across scenarios and regions + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(slice_data.values, time_arr) + + def test_1d_array_scenario_to_three_dimensions(self, three_d_coords): + """1D array matching scenario should broadcast to 3D.""" + scenario_arr = np.array([100, 200, 300]) + result = DataConverter.to_dataarray(scenario_arr, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting across time and regions + for time in three_d_coords['time']: + for region in three_d_coords['region']: + slice_data = result.sel(time=time, region=region) + assert np.array_equal(slice_data.values, scenario_arr) + + def test_1d_array_region_to_three_dimensions(self, three_d_coords): + """1D array matching region should broadcast to 3D.""" + region_arr = np.array([1000, 2000]) # Length 2 to match region + result = DataConverter.to_dataarray(region_arr, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting across time and scenarios + for time in three_d_coords['time']: + for scenario in three_d_coords['scenario']: + slice_data = result.sel(time=time, scenario=scenario) + assert np.array_equal(slice_data.values, region_arr) + + def test_series_time_to_three_dimensions(self, three_d_coords): + """Time-indexed Series should broadcast to 3D.""" + time_series = pd.Series([15, 25, 35, 45, 55], index=three_d_coords['time']) + result = DataConverter.to_dataarray(time_series, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(slice_data.values, time_series.values) + + def test_series_scenario_to_three_dimensions(self, three_d_coords): + """Scenario-indexed Series should broadcast to 3D.""" + scenario_series = pd.Series([500, 600, 700], index=three_d_coords['scenario']) + result = DataConverter.to_dataarray(scenario_series, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for time in three_d_coords['time']: + for region in three_d_coords['region']: + slice_data = result.sel(time=time, region=region) + assert np.array_equal(slice_data.values, scenario_series.values) + + def test_series_region_to_three_dimensions(self, three_d_coords): + """Region-indexed Series should broadcast to 3D.""" + region_series = pd.Series([5000, 6000], index=three_d_coords['region']) # Length 2 + result = DataConverter.to_dataarray(region_series, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for time in three_d_coords['time']: + for scenario in three_d_coords['scenario']: + slice_data = result.sel(time=time, scenario=scenario) + assert np.array_equal(slice_data.values, region_series.values) + + def test_dataframe_time_to_three_dimensions(self, three_d_coords): + """Time-indexed DataFrame should broadcast to 3D.""" + df = pd.DataFrame({'power': [11, 22, 33, 44, 55]}, index=three_d_coords['time']) + result = DataConverter.to_dataarray(df, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(slice_data.values, df['power'].values) + + def test_dataframe_scenario_to_three_dimensions(self, three_d_coords): + """Scenario-indexed DataFrame should broadcast to 3D.""" + df = pd.DataFrame({'cost': [1100, 1200, 1300]}, index=three_d_coords['scenario']) + result = DataConverter.to_dataarray(df, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for time in three_d_coords['time']: + for region in three_d_coords['region']: + slice_data = result.sel(time=time, region=region) + assert np.array_equal(slice_data.values, df['cost'].values) + + def test_1d_dataarray_time_to_three_dimensions(self, three_d_coords): + """1D time DataArray should broadcast to 3D.""" + original = xr.DataArray([101, 102, 103, 104, 105], + coords={'time': three_d_coords['time']}, + dims=['time']) + result = DataConverter.to_dataarray(original, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(slice_data.values, original.values) + + def test_1d_dataarray_scenario_to_three_dimensions(self, three_d_coords): + """1D scenario DataArray should broadcast to 3D.""" + original = xr.DataArray([2001, 2002, 2003], + coords={'scenario': three_d_coords['scenario']}, + dims=['scenario']) + result = DataConverter.to_dataarray(original, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for time in three_d_coords['time']: + for region in three_d_coords['region']: + slice_data = result.sel(time=time, region=region) + assert np.array_equal(slice_data.values, original.values) + + def test_2d_dataarray_to_three_dimensions(self, three_d_coords): + """2D DataArray should broadcast to 3D.""" + # Create 2D time x scenario DataArray + data_2d = np.random.rand(5, 3) + original = xr.DataArray(data_2d, + coords={'time': three_d_coords['time'], + 'scenario': three_d_coords['scenario']}, + dims=['time', 'scenario']) + + result = DataConverter.to_dataarray(original, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check that all regions have the same time x scenario data + for region in three_d_coords['region']: + slice_data = result.sel(region=region) + assert np.array_equal(slice_data.values, original.values) + + def test_timeseries_data_to_three_dimensions(self, three_d_coords): + """TimeSeriesData should broadcast to 3D.""" + data_array = xr.DataArray([99, 88, 77, 66, 55], + coords={'time': three_d_coords['time']}, + dims=['time']) + ts_data = TimeSeriesData(data_array, aggregation_group='test_3d') + + result = DataConverter.to_dataarray(ts_data, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(slice_data.values, [99, 88, 77, 66, 55]) + + def test_three_d_copy_independence(self, three_d_coords): + """3D results should be independent copies.""" + original_arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(original_arr, coords=three_d_coords) + + # Modify result + result[0, 0, 0] = 999 + + # Original should be unchanged + assert original_arr[0] == 10 + + def test_three_d_special_values(self, three_d_coords): + """3D conversion should preserve special values.""" + # Array with NaN and inf + special_arr = np.array([1, np.nan, np.inf, -np.inf, 5]) + result = DataConverter.to_dataarray(special_arr, coords=three_d_coords) + + assert result.shape == (5, 3, 2) + + # Check that special values are preserved in all broadcasts + for scenario in three_d_coords['scenario']: + for region in three_d_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(np.isnan(slice_data.values), np.isnan(special_arr)) + assert np.array_equal(np.isinf(slice_data.values), np.isinf(special_arr)) + + def test_three_d_ambiguous_length_error(self): + """Should fail when array length matches multiple dimensions in 3D.""" + # All dimensions have length 3 + coords_3x3x3 = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), + 'region': pd.Index(['X', 'Y', 'Z'], name='region') + } + + arr = np.array([1, 2, 3]) # Length 3 - matches all dimensions + + with pytest.raises(ConversionError, match="matches multiple dimensions"): + DataConverter.to_dataarray(arr, coords=coords_3x3x3) + + def test_three_d_custom_dimensions(self): + """3D conversion with custom dimension names.""" + coords = { + 'product': pd.Index(['A', 'B'], name='product'), + 'factory': pd.Index(['F1', 'F2', 'F3'], name='factory'), + 'quarter': pd.Index(['Q1', 'Q2', 'Q3', 'Q4'], name='quarter') + } + + # Array matching factory dimension + factory_arr = np.array([100, 200, 300]) + result = DataConverter.to_dataarray(factory_arr, coords=coords) + + assert result.shape == (2, 3, 4) + assert result.dims == ('product', 'factory', 'quarter') + + # Check broadcasting + for product in coords['product']: + for quarter in coords['quarter']: + slice_data = result.sel(product=product, quarter=quarter) + assert np.array_equal(slice_data.values, factory_arr) + + class TestMultipleDimensions: """Test support for more than 2 dimensions.""" From a53c116c8b69127c1849082a256f7e713d83a9cf Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:53:13 +0200 Subject: [PATCH 146/183] Add FLowSystemDimension Type --- flixopt/core.py | 5 ++++- flixopt/flow_system.py | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 2b841af0b..43b529421 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -5,7 +5,7 @@ import logging import warnings -from typing import Dict, Optional, Union, Tuple +from typing import Dict, Optional, Union, Tuple, Literal import numpy as np import pandas as pd @@ -30,6 +30,9 @@ NonTemporalData = Union[Scalar, xr.DataArray] """Internally used datatypes for non-temporal data. Can be a Scalar or an xr.DataArray.""" +FlowSystemDimensions = Literal['time', 'scenario'] +"""Possible dimensions of a FlowSystem.""" + class PlausibilityError(Exception): """Error for a failing Plausibility check.""" diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 42a287876..c753b8cc8 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,7 +16,7 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser, NonTemporalData +from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser, NonTemporalData, FlowSystemDimensions from .effects import Effect, EffectCollection, NonTemporalEffects, NonTemporalEffectsUser, TemporalEffects, TemporalEffectsUser from .elements import Bus, Component, Flow from .structure import Element, Interface, SystemModel @@ -294,7 +294,7 @@ def fit_to_model_coords( self, name: str, data: Optional[Union[TemporalDataUser, NonTemporalDataUser]], - dimensions: Union[List[str], str] = 'time', # Default to time only + dimensions: Optional[Union[List[FlowSystemDimensions], FlowSystemDimensions]] = None, # Default to time only ) -> Optional[Union[TemporalData, NonTemporalData]]: """ Fit data to model coordinate system (currently time, but extensible). @@ -302,7 +302,7 @@ def fit_to_model_coords( Args: name: Name of the data data: Data to fit to model coordinates - has_time_dim: Whether the data has a time dimension + dimensions: Dimensions to use for the DataArray Returns: xr.DataArray aligned to model coordinate system @@ -316,10 +316,12 @@ def fit_to_model_coords( coords = {} for dim in dimensions: - if dim == 'time' and self.timesteps is not None: + if dim == 'time': coords['time'] = self.timesteps elif dim == 'scenario' and self.scenarios is not None: coords['scenario'] = self.scenarios + else: + raise ValueError(f'Invalid flow system dimension "{dim}"') # Future: elif dim == 'region' and self.regions is not None: ... # Rest of your method stays the same, just pass coords From 2cb551b064a9803fe7c6271561ef3e30111ae060 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:09:51 +0200 Subject: [PATCH 147/183] Revert some logic about the fit_to_model coords --- flixopt/flow_system.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index c753b8cc8..0e6c2cf6f 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -294,7 +294,7 @@ def fit_to_model_coords( self, name: str, data: Optional[Union[TemporalDataUser, NonTemporalDataUser]], - dimensions: Optional[Union[List[FlowSystemDimensions], FlowSystemDimensions]] = None, # Default to time only + has_time_dim: bool = True, ) -> Optional[Union[TemporalData, NonTemporalData]]: """ Fit data to model coordinate system (currently time, but extensible). @@ -302,7 +302,7 @@ def fit_to_model_coords( Args: name: Name of the data data: Data to fit to model coordinates - dimensions: Dimensions to use for the DataArray + has_time_dim: Wether to use the time dimension or not Returns: xr.DataArray aligned to model coordinate system @@ -310,19 +310,10 @@ def fit_to_model_coords( if data is None: return None - # Build coords from requested dimensions - if isinstance(dimensions, str): - dimensions = [dimensions] + coords = self.coords - coords = {} - for dim in dimensions: - if dim == 'time': - coords['time'] = self.timesteps - elif dim == 'scenario' and self.scenarios is not None: - coords['scenario'] = self.scenarios - else: - raise ValueError(f'Invalid flow system dimension "{dim}"') - # Future: elif dim == 'region' and self.regions is not None: ... + if not has_time_dim: + coords.pop('time') # Rest of your method stays the same, just pass coords if isinstance(data, TimeSeriesData): @@ -354,7 +345,11 @@ def fit_effects_to_model_coords( effect_values_dict = self.effects.create_effect_values_dict(effect_values) return { - effect: self.fit_to_model_coords('|'.join(filter(None, [label_prefix, effect, label_suffix])), value, has_time_dim=has_time_dim) + effect: self.fit_to_model_coords( + '|'.join(filter(None, [label_prefix, effect, label_suffix])), + value, + has_time_dim=has_time_dim + ) for effect, value in effect_values_dict.items() } @@ -594,6 +589,13 @@ def flows(self) -> Dict[str, Flow]: def all_elements(self) -> Dict[str, Element]: return {**self.components, **self.effects.effects, **self.flows, **self.buses} + @property + def coords(self) -> Dict[str, pd.Index]: + active_coords = {'time': self.timesteps} + if self.scenarios is not None: + active_coords['scenario'] = self.scenarios + return active_coords + @property def used_in_calculation(self) -> bool: return self._used_in_calculation From d7be7667e27a42f2bf784eab4631883650b3f8a9 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:22:10 +0200 Subject: [PATCH 148/183] Adjust FLowSystem IO for scenarios --- flixopt/flow_system.py | 1 + flixopt/structure.py | 6 +++++- tests/conftest.py | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 0e6c2cf6f..e2a39508d 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -212,6 +212,7 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': # Create FlowSystem instance with constructor parameters flow_system = cls( timesteps=ds.indexes['time'], + scenarios=ds.indexes.get('scenario'), hours_of_last_timestep=reference_structure.get('hours_of_last_timestep'), hours_of_previous_timesteps=reference_structure.get('hours_of_previous_timesteps'), ) diff --git a/flixopt/structure.py b/flixopt/structure.py index d02a8d4e9..681c4dc0c 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -203,12 +203,16 @@ def _create_reference_structure(self) -> Tuple[Dict, Dict[str, xr.DataArray]]: all_extracted_arrays = {} for name in self._cached_init_params: - if name == 'self' or name == 'timesteps': # Skip self and timesteps. Timesteps are directly stored in Datasets + if name == 'self': # Skip self and timesteps. Timesteps are directly stored in Datasets continue value = getattr(self, name, None) + if value is None: continue + if isinstance(value, pd.Index): + logger.debug(f'Skipping {name=} because it is an Index') + continue # Extract arrays and get reference structure processed_value, extracted_arrays = self._extract_dataarrays_recursive(value, name) diff --git a/tests/conftest.py b/tests/conftest.py index 198b9a92b..55f17057d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -170,7 +170,8 @@ def simple_flow_system_scenarios() -> fx.FlowSystem: discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1e4), capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), initial_charge_state=0, - relative_maximum_charge_state=1 / 100 * np.array([80.0, 70.0, 80.0, 80, 80, 80, 80, 80, 80, 80]), + relative_maximum_charge_state=1 / 100 * np.array([80.0, 70.0, 80.0, 80, 80, 80, 80, 80, 80]), + relative_maximum_final_charge_state=0.8, eta_charge=0.9, eta_discharge=1, relative_loss_per_hour=0.08, From e60dd079884d0784e3e3e87e0e15e1ffb1ef449b Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:37:15 +0200 Subject: [PATCH 149/183] BUGFIX: Raise Exception instead of logging --- flixopt/flow_system.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index e2a39508d..1da0184a9 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -325,8 +325,8 @@ def fit_to_model_coords( aggregation_group=data.aggregation_group, aggregation_weight=data.aggregation_weight ).rename(name) except ConversionError as e: - logger.critical(f'Could not convert time series data "{name}" to DataArray: {e}. \n' - f'Take care to use the correct (time) index.') + raise ConversionError( + f'Could not convert time series data "{name}" to DataArray: Original Error: {e}') from e else: return DataConverter.to_dataarray(data, coords=coords).rename(name) From bd2f1b82fd1dbf934a7179726aefb280db2891eb Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 18:40:26 +0200 Subject: [PATCH 150/183] Change usage of TimeSeriesData --- tests/conftest.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 55f17057d..9f247164f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -400,16 +400,17 @@ def flow_system_long(): p_el = data['Strompr.€/MWh'].values gas_price = data['Gaspr.€/MWh'].values + flow_system = fx.FlowSystem(pd.DatetimeIndex(data.index)) + thermal_load_ts, electrical_load_ts = ( - fx.TimeSeriesData(thermal_load), - fx.TimeSeriesData(electrical_load, aggregation_weight=0.7), + fx.TimeSeriesData(thermal_load, coords={'time': flow_system.timesteps}), + fx.TimeSeriesData(electrical_load, aggregation_weight=0.7, coords={'time': flow_system.timesteps}), ) p_feed_in, p_sell = ( - fx.TimeSeriesData(-(p_el - 0.5), aggregation_group='p_el'), - fx.TimeSeriesData(p_el + 0.5, aggregation_group='p_el'), + fx.TimeSeriesData(-(p_el - 0.5), aggregation_group='p_el', coords={'time': flow_system.timesteps}), + fx.TimeSeriesData(p_el + 0.5, aggregation_group='p_el', coords={'time': flow_system.timesteps}), ) - flow_system = fx.FlowSystem(pd.DatetimeIndex(data.index)) flow_system.add_elements( fx.Bus('Strom'), fx.Bus('Fernwärme'), From a7da9d286efd18766ea608d4f8328f54521bf982 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:32:57 +0200 Subject: [PATCH 151/183] Adjust logic to handle non scalars --- flixopt/elements.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/elements.py b/flixopt/elements.py index 9df367eec..1daadeb55 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -195,7 +195,7 @@ def __init__( meta_data: used to store more information about the Element. Is not used internally, but saved in the results. Only use python native types. """ super().__init__(label, meta_data=meta_data) - self.size = size or CONFIG.modeling.BIG # Default size + self.size = size if size is not None else CONFIG.modeling.BIG # Default size self.relative_minimum = relative_minimum self.relative_maximum = relative_maximum self.fixed_relative_profile = fixed_relative_profile From b8f0e226a5f6024489e8e05bb31636f9c788d569 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:33:49 +0200 Subject: [PATCH 152/183] Adjust logic to _resolve_dataarray_reference into separate method --- flixopt/structure.py | 51 ++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 681c4dc0c..84b9c6cba 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -316,6 +316,40 @@ def _extract_dataarrays_recursive(self, obj, context_name: str = '') -> Tuple[An else: return self._serialize_to_basic_types(obj), extracted_arrays + @classmethod + def _resolve_dataarray_reference( + cls, reference: str, arrays_dict: Dict[str, xr.DataArray] + ) -> Union[xr.DataArray, TimeSeriesData]: + """ + Resolve a single DataArray reference (:::name) to actual DataArray or TimeSeriesData. + + Args: + reference: Reference string starting with ":::" + arrays_dict: Dictionary of available DataArrays + + Returns: + Resolved DataArray or TimeSeriesData object + + Raises: + ValueError: If referenced array is not found + """ + array_name = reference[3:] # Remove ":::" prefix + if array_name not in arrays_dict: + raise ValueError(f"Referenced DataArray '{array_name}' not found in dataset") + + array = arrays_dict[array_name] + + # Handle null values with warning + if array.isnull().any(): + logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") + array = array.dropna(dim='time', how='all') + + # Check if this should be restored as TimeSeriesData + if TimeSeriesData.is_timeseries_data(array): + return TimeSeriesData.from_dataarray(array) + + return array + @classmethod def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataArray]): """ @@ -333,22 +367,7 @@ def _resolve_reference_structure(cls, structure, arrays_dict: Dict[str, xr.DataA """ # Handle DataArray references if isinstance(structure, str) and structure.startswith(':::'): - array_name = structure[3:] # Remove ":::" prefix - if array_name not in arrays_dict: - raise ValueError(f"Referenced DataArray '{array_name}' not found in dataset") - - array = arrays_dict[array_name] - - # Handle null values with warning - if array.isnull().any(): - logger.warning(f"DataArray '{array_name}' contains null values. Dropping them.") - array = array.dropna(dim='time', how='all') - - # Check if this should be restored as TimeSeriesData - if TimeSeriesData.is_timeseries_data(array): - return TimeSeriesData.from_dataarray(array) - - return array + return cls._resolve_dataarray_reference(structure, arrays_dict) elif isinstance(structure, list): resolved_list = [] From 7123b6b736cfbd55fbf8fd501bdd48f0ca37470d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 30 Jun 2025 21:34:50 +0200 Subject: [PATCH 153/183] Update IO of FlowSystem --- flixopt/flow_system.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 1da0184a9..656aecd77 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -209,17 +209,20 @@ def from_dataset(cls, ds: xr.Dataset) -> 'FlowSystem': # Get the reference structure from attrs reference_structure = dict(ds.attrs) + # Create arrays dictionary from dataset variables + arrays_dict = {name: array for name, array in ds.data_vars.items()} + # Create FlowSystem instance with constructor parameters flow_system = cls( timesteps=ds.indexes['time'], scenarios=ds.indexes.get('scenario'), + scenario_weights=cls._resolve_dataarray_reference( + reference_structure['scenario_weights'], arrays_dict + ) if 'scenario_weights' in reference_structure else None, hours_of_last_timestep=reference_structure.get('hours_of_last_timestep'), hours_of_previous_timesteps=reference_structure.get('hours_of_previous_timesteps'), ) - # Create arrays dictionary from dataset variables - arrays_dict = {name: array for name, array in ds.data_vars.items()} - # Restore components components_structure = reference_structure.get('components', {}) for comp_label, comp_data in components_structure.items(): From fa5475d89982ee7ae9ba374b4d84ba73b75c9979 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:03:12 +0200 Subject: [PATCH 154/183] Improve get_coords() --- flixopt/structure.py | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 84b9c6cba..7732ffeb4 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -132,26 +132,25 @@ def get_coords( Returns: The coordinates of the model. Might also be None if no scenarios are present and time_dim is False """ - if not scenario_dim and not time_dim: - return None - scenarios = self.flow_system.scenarios - timesteps = ( - self.flow_system.timesteps if not extra_timestep else self.flow_system.timesteps_extra - ) + if extra_timestep and not time_dim: + raise ValueError('extra_timestep=True requires time_dim=True') + + coords = self.flow_system.coords - if scenario_dim and time_dim: - if scenarios is None: - return (timesteps,) - return timesteps, scenarios + if not scenario_dim: + coords.pop('scenario', None) + if not time_dim: + coords.pop('time', None) + if extra_timestep: + coords['time'] = self.flow_system.timesteps_extra + + if not coords: + return None - if scenario_dim and not time_dim: - if scenarios is None: - return None - return (scenarios,) - if time_dim and not scenario_dim: - return (timesteps,) + if len(coords) == 1: + return (coords.popitem()[1],) - raise ValueError(f'Cannot get coordinates with both {scenario_dim=} and {time_dim=}') + return tuple(coords.values()) class Interface: From 80cb1610a5550e97149e5d2601c30c9f268d2ee2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:04:04 +0200 Subject: [PATCH 155/183] Adjust FlowSystem init for correct IO --- flixopt/flow_system.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 656aecd77..ecfca4ed2 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -62,15 +62,24 @@ def __init__( If you use an array, take care that its long enough to cover all previous values! scenario_weights: The weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. """ - # Store timing information directly self.timesteps = self._validate_timesteps(timesteps) + self.timesteps_extra = self._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - self.hours_per_timestep = self.calculate_hours_per_timestep(self.timesteps_extra) - self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps( - timesteps, hours_of_previous_timesteps - ) + self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps(timesteps, hours_of_previous_timesteps) + self.scenarios = None if scenarios is None else self._validate_scenarios(scenarios) - self.scenario_weights = scenario_weights + + hours_per_timestep = self.calculate_hours_per_timestep(self.timesteps_extra) + + self.hours_of_last_timestep = hours_per_timestep[-1].item() + + self.hours_per_timestep = self.fit_to_model_coords('hours_per_timestep', hours_per_timestep) + + self.scenario_weights = self.fit_to_model_coords( + 'scenario_weights', + scenario_weights, + has_time_dim=False, + ) # Element collections self.components: Dict[str, Component] = {} @@ -123,7 +132,7 @@ def _create_timesteps_with_extra( @staticmethod def calculate_hours_per_timestep(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: - """Calculate duration of each timestep.""" + """Calculate duration of each timestep as a 1D DataArray.""" hours_per_step = np.diff(timesteps_extra) / pd.Timedelta(hours=1) return xr.DataArray( hours_per_step, coords={'time': timesteps_extra[:-1]}, dims=['time'], name='hours_per_timestep' From 81ad3baf19bee65853854a20e706bcee19559aa1 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 11:04:02 +0200 Subject: [PATCH 156/183] Add scenario to sel and isel methods, and dont normalize scenario weights --- flixopt/flow_system.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index ecfca4ed2..0d77d046e 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -60,7 +60,7 @@ def __init__( If None, the first time increment of time_series is used. This is needed to calculate previous durations (for example consecutive_on_hours). If you use an array, take care that its long enough to cover all previous values! - scenario_weights: The weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. + scenario_weights: The weights of each scenarios. If None, all scenarios have the same weight (normalized to 1). Its recommended to scale the weights to sum up to 1. """ self.timesteps = self._validate_timesteps(timesteps) @@ -318,7 +318,7 @@ def fit_to_model_coords( has_time_dim: Wether to use the time dimension or not Returns: - xr.DataArray aligned to model coordinate system + xr.DataArray aligned to model coordinate system. If data is None, returns None. """ if data is None: return None @@ -613,12 +613,14 @@ def coords(self) -> Dict[str, pd.Index]: def used_in_calculation(self) -> bool: return self._used_in_calculation - def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp, pd.DatetimeIndex]] = None) -> 'FlowSystem': + def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp, pd.DatetimeIndex]] = None, + scenario: Optional[Union[str, slice, List[str], pd.Index]] = None) -> 'FlowSystem': """ Select a subset of the flowsystem by the time coordinate. Args: time: Time selection (e.g., slice('2023-01-01', '2023-12-31'), '2023-06-15', or list of times) + scenario: Scenario selection (e.g., slice('scenario1', 'scenario2'), or list of scenarios) Returns: FlowSystem: New FlowSystem with selected data @@ -631,18 +633,22 @@ def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp, pd.Datet if time is not None: indexers['time'] = time + if scenario is not None: + indexers['scenario'] = scenario + if not indexers: return self.copy() # Return a copy when no selection selected_dataset = self.to_dataset().sel(**indexers) return self.__class__.from_dataset(selected_dataset) - def isel(self, time: Optional[Union[int, slice, List[int]]] = None) -> 'FlowSystem': + def isel(self, time: Optional[Union[int, slice, List[int]]] = None, scenario: Optional[Union[int, slice, List[int]]] = None) -> 'FlowSystem': """ Select a subset of the flowsystem by integer indices. Args: time: Time selection by integer index (e.g., slice(0, 100), 50, or [0, 5, 10]) + scenario: Scenario selection by integer index (e.g., slice(0, 3), 50, or [0, 5, 10]) Returns: FlowSystem: New FlowSystem with selected data @@ -655,6 +661,9 @@ def isel(self, time: Optional[Union[int, slice, List[int]]] = None) -> 'FlowSyst if time is not None: indexers['time'] = time + if scenario is not None: + indexers['scenario'] = scenario + if not indexers: return self.copy() # Return a copy when no selection From 691a45e40e2ac75b1c0a6403bf51173dd40fafa2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 11:14:15 +0200 Subject: [PATCH 157/183] Improve scenario_weights_handling --- flixopt/flow_system.py | 7 +------ flixopt/structure.py | 25 ++++++++----------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 0d77d046e..fbf65e489 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -68,6 +68,7 @@ def __init__( self.hours_of_previous_timesteps = self._calculate_hours_of_previous_timesteps(timesteps, hours_of_previous_timesteps) self.scenarios = None if scenarios is None else self._validate_scenarios(scenarios) + self.scenario_weights = scenario_weights hours_per_timestep = self.calculate_hours_per_timestep(self.timesteps_extra) @@ -75,12 +76,6 @@ def __init__( self.hours_per_timestep = self.fit_to_model_coords('hours_per_timestep', hours_per_timestep) - self.scenario_weights = self.fit_to_model_coords( - 'scenario_weights', - scenario_weights, - has_time_dim=False, - ) - # Element collections self.components: Dict[str, Component] = {} self.buses: Dict[str, Bus] = {} diff --git a/flixopt/structure.py b/flixopt/structure.py index 7732ffeb4..cbf8af599 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -57,7 +57,6 @@ def __init__(self, flow_system: 'FlowSystem'): super().__init__(force_dim_names=True) self.flow_system = flow_system self.effects: Optional[EffectCollectionModel] = None - self.scenario_weights = self._calculate_scenario_weights(flow_system.scenario_weights) def do_modeling(self): self.effects = self.flow_system.effects.create_model(self) @@ -69,22 +68,6 @@ def do_modeling(self): for bus_model in bus_models: # Buses after Components, because FlowModels are created in ComponentModels bus_model.do_modeling() - def _calculate_scenario_weights(self, weights: Optional[NonTemporalData] = None) -> xr.DataArray: - """Calculates the weights of the scenarios. If None, all scenarios have the same weight. All weights are normalized to 1. - If no scenarios are present, s single weight of 1 is returned. - """ - if weights is not None and not isinstance(weights, xr.DataArray): - raise TypeError(f'Weights must be a xr.DataArray or None, got {type(weights)}') - if self.flow_system.scenarios is None: - return xr.DataArray(1) - if weights is None: - weights = xr.DataArray( - np.ones(len(self.flow_system.scenarios)), - coords={'scenario': self.flow_system.scenarios} - ) - - return weights / weights.sum() - @property def solution(self): solution = super().solution @@ -152,6 +135,14 @@ def get_coords( return tuple(coords.values()) + @property + def scenario_weights(self) -> xr.DataArray: + """Returns the scenario weights of the FlowSystem.""" + if self.flow_system.scenarios is None: + return xr.DataArray(1) + + return self.flow_system.scenario_weights + class Interface: """ From 3931ac52e816d8ddc8b5029dba811009cc70576e Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 11:17:56 +0200 Subject: [PATCH 158/183] Add warning for not scaled weights --- flixopt/flow_system.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index fbf65e489..74492bcd3 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -366,6 +366,10 @@ def connect_and_transform(self): self.scenario_weights = self.fit_to_model_coords( 'scenario_weights', self.scenario_weights, has_time_dim=False ) + if self.scenario_weights is not None and self.scenario_weights.sum() != 1: + logger.warning(f'Scenario weights are not normalized to 1. This is reccomended for a better scaled model. ' + f'Sum of weights={self.scenario_weights.sum().item()}') + if not self._connected_and_transformed: self._connect_network() for element in list(self.components.values()) + list(self.effects.effects.values()) + list(self.buses.values()): From 75a45e1552db8cbc89a5793da9d6a5e32c2d2ca0 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:41:24 +0200 Subject: [PATCH 159/183] Update test_scenarios.py --- tests/test_scenarios.py | 45 ++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 4abdafa5f..cc0be36c6 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -239,8 +239,8 @@ def flow_system_piecewise_conversion_scenarios(flow_system_complex_scenarios) -> { 'P_el': fx.Piecewise( [ - fx.Piece(np.linspace(5, 6, len(flow_system.time_series_collection.timesteps)), 30), - fx.Piece(40, np.linspace(60, 70, len(flow_system.time_series_collection.timesteps))), + fx.Piece(np.linspace(5, 6, len(flow_system.timesteps)), 30), + fx.Piece(40, np.linspace(60, 70, len(flow_system.timesteps))), ] ), 'Q_th': fx.Piecewise([fx.Piece(6, 35), fx.Piece(45, 100)]), @@ -256,7 +256,18 @@ def flow_system_piecewise_conversion_scenarios(flow_system_complex_scenarios) -> def test_scenario_weights(flow_system_piecewise_conversion_scenarios): """Test that scenario weights are correctly used in the model.""" - scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + scenarios = flow_system_piecewise_conversion_scenarios.scenarios + weights = np.linspace(0.5, 1, len(scenarios)) + flow_system_piecewise_conversion_scenarios.scenario_weights = weights + model = create_linopy_model(flow_system_piecewise_conversion_scenarios) + np.testing.assert_allclose(model.scenario_weights.values, weights) + assert_linequal(model.objective.expression, + (model.variables['costs|total'] * weights).sum() + model.variables['Penalty|total']) + assert np.isclose(model.scenario_weights.sum().item(), 2.25) + +def test_scenario_weights_io(flow_system_piecewise_conversion_scenarios): + """Test that scenario weights are correctly used in the model.""" + scenarios = flow_system_piecewise_conversion_scenarios.scenarios weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) flow_system_piecewise_conversion_scenarios.scenario_weights = weights model = create_linopy_model(flow_system_piecewise_conversion_scenarios) @@ -273,7 +284,7 @@ def test_scenario_dimensions_in_variables(flow_system_piecewise_conversion_scena def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): """Test a full optimization with scenarios and verify results.""" - scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + scenarios = flow_system_piecewise_conversion_scenarios.scenarios weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) flow_system_piecewise_conversion_scenarios.scenario_weights = weights calc = create_calculation_and_solve(flow_system_piecewise_conversion_scenarios, @@ -292,7 +303,7 @@ def test_full_scenario_optimization(flow_system_piecewise_conversion_scenarios): @pytest.mark.skip(reason="This test is taking too long with highs and is too big for gurobipy free") def test_io_persistance(flow_system_piecewise_conversion_scenarios): """Test a full optimization with scenarios and verify results.""" - scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + scenarios = flow_system_piecewise_conversion_scenarios.scenarios weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) flow_system_piecewise_conversion_scenarios.scenario_weights = weights calc = create_calculation_and_solve(flow_system_piecewise_conversion_scenarios, @@ -312,21 +323,23 @@ def test_io_persistance(flow_system_piecewise_conversion_scenarios): def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): - flow_system = flow_system_piecewise_conversion_scenarios - scenarios = flow_system_piecewise_conversion_scenarios.time_series_collection.scenarios + flow_system_full = flow_system_piecewise_conversion_scenarios + scenarios = flow_system_full.scenarios weights = np.linspace(0.5, 1, len(scenarios)) / np.sum(np.linspace(0.5, 1, len(scenarios))) - flow_system_piecewise_conversion_scenarios.scenario_weights = weights - calc = fx.FullCalculation(flow_system=flow_system_piecewise_conversion_scenarios, - selected_scenarios=flow_system.time_series_collection.scenarios[0:2], - name='test_full_scenario') + flow_system_full.scenario_weights = weights + flow_system = flow_system_full.sel(scenario=scenarios[0:2]) + + assert flow_system.scenarios.equals(flow_system_full.scenarios[0:2]) + + np.testing.assert_allclose(flow_system.scenario_weights.values, flow_system_full.scenario_weights[0:2]) + + + calc = fx.FullCalculation(flow_system=flow_system, name='test_full_scenario') calc.do_modeling() calc.solve(fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60)) calc.results.to_file() - flow_system_2 = fx.FlowSystem.from_dataset(calc.results.flow_system_data) - - assert calc.results.solution.indexes['scenario'].equals(flow_system.time_series_collection.scenarios[0:2]) - assert flow_system_2.time_series_collection.scenarios.equals(flow_system.time_series_collection.scenarios[0:2]) + xr.testing.assert_allclose(calc.results.objective, calc.results.solution['costs|total'] * flow_system.scenario_weights) - np.testing.assert_allclose(flow_system_2.scenario_weights.selected_data.values, weights[0:2]) + assert calc.results.solution.indexes['scenario'].equals(flow_system_full.scenarios[0:2]) From 2882147805e0ebb3549ab790f435f7b40d097fab Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 15:58:31 +0200 Subject: [PATCH 160/183] Improve util method --- flixopt/calculation.py | 4 ++-- flixopt/utils.py | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 88e509438..b7d17eef6 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -138,7 +138,7 @@ def main_results(self) -> Dict[str, Union[Scalar, Dict]]: ], } - return utils.round_floats(main_results) + return utils.round_nested_floats(main_results) @property def summary(self): @@ -205,7 +205,7 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma logger.info( '\n' + yaml.dump( - utils.round_floats(self.main_results), + utils.round_nested_floats(self.main_results), default_flow_style=False, sort_keys=False, allow_unicode=True, diff --git a/flixopt/utils.py b/flixopt/utils.py index 3e65328a4..1b5ad4497 100644 --- a/flixopt/utils.py +++ b/flixopt/utils.py @@ -11,11 +11,38 @@ logger = logging.getLogger('flixopt') -def round_floats(obj, decimals=2): +def round_nested_floats(obj, decimals=2): + """Recursively round floating point numbers in nested data structures. + + This function traverses nested data structures (dictionaries, lists) and rounds + any floating point numbers to the specified number of decimal places. It handles + various data types including NumPy arrays and xarray DataArrays by converting + them to lists with rounded values. + + Args: + obj: The object to process. Can be a dict, list, float, int, numpy.ndarray, + xarray.DataArray, or any other type. + decimals (int, optional): Number of decimal places to round to. Defaults to 2. + + Returns: + The processed object with the same structure as the input, but with all + floating point numbers rounded to the specified precision. NumPy arrays + and xarray DataArrays are converted to lists. + + Examples: + >>> data = {'a': 3.14159, 'b': [1.234, 2.678]} + >>> round_nested_floats(data, decimals=2) + {'a': 3.14, 'b': [1.23, 2.68]} + + >>> import numpy as np + >>> arr = np.array([1.234, 5.678]) + >>> round_nested_floats(arr, decimals=1) + [1.2, 5.7] + """ if isinstance(obj, dict): - return {k: round_floats(v, decimals) for k, v in obj.items()} + return {k: round_nested_floats(v, decimals) for k, v in obj.items()} elif isinstance(obj, list): - return [round_floats(v, decimals) for v in obj] + return [round_nested_floats(v, decimals) for v in obj] elif isinstance(obj, float): return round(obj, decimals) elif isinstance(obj, int): From 50491ffd3ae770c75ad6bf0f177d962498b41372 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:02:10 +0200 Subject: [PATCH 161/183] Add objective to solution dataset. --- flixopt/results.py | 7 ++++++- flixopt/structure.py | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/flixopt/results.py b/flixopt/results.py index 7a6f7926e..6f522e55c 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -221,7 +221,12 @@ def storages(self) -> List['ComponentResults']: @property def objective(self) -> float: """The objective result of the optimization.""" - return self.summary['Main Results']['Objective'] + # Deprecated. Fallback + if 'objective' not in self.solution: + logger.warning('Objective not found in solution. Fallback to summary (rounded value). This is deprecated') + return self.summary['Main Results']['Objective'] + + return self.solution['objective'].item() @property def variables(self) -> linopy.Variables: diff --git a/flixopt/structure.py b/flixopt/structure.py index cbf8af599..994ff5fc7 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -71,6 +71,7 @@ def do_modeling(self): @property def solution(self): solution = super().solution + solution['objective'] = self.objective.value solution.attrs = { 'Components': { comp.label_full: comp.model.results_structure() From 4f3a7981e01d7d16e7d4f977389bef04b0ee1886 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:02:50 +0200 Subject: [PATCH 162/183] Update handling of scenario_weights update tests --- flixopt/structure.py | 6 +++--- tests/test_scenarios.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flixopt/structure.py b/flixopt/structure.py index 994ff5fc7..b5b458cdb 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -137,10 +137,10 @@ def get_coords( return tuple(coords.values()) @property - def scenario_weights(self) -> xr.DataArray: + def scenario_weights(self) -> Union[int, xr.DataArray]: """Returns the scenario weights of the FlowSystem.""" - if self.flow_system.scenarios is None: - return xr.DataArray(1) + if self.flow_system.scenario_weights is None: + return 1 return self.flow_system.scenario_weights diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index cc0be36c6..cd3de4407 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -340,6 +340,6 @@ def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): calc.results.to_file() - xr.testing.assert_allclose(calc.results.objective, calc.results.solution['costs|total'] * flow_system.scenario_weights) + np.testing.assert_allclose(calc.results.objective, ((calc.results.solution['costs|total'] * flow_system.scenario_weights).sum() + calc.results.solution['Penalty|total']).item()) ## Acount for rounding errors assert calc.results.solution.indexes['scenario'].equals(flow_system_full.scenarios[0:2]) From 314aef9333e63a2898ea74cd9fb249c69080905f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:10:52 +0200 Subject: [PATCH 163/183] Ruff check. Fix type hints --- flixopt/components.py | 2 +- flixopt/core.py | 2 +- flixopt/features.py | 2 +- flixopt/flow_system.py | 20 ++++++++++++++++++-- flixopt/interface.py | 24 ++++++++++++------------ flixopt/results.py | 5 +---- flixopt/structure.py | 2 +- tests/test_component.py | 8 +++++++- 8 files changed, 42 insertions(+), 23 deletions(-) diff --git a/flixopt/components.py b/flixopt/components.py index cf05af0ed..00d0c073f 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -10,7 +10,7 @@ import xarray as xr from . import utils -from .core import PlausibilityError, Scalar, TemporalData, TemporalDataUser, NonTemporalDataUser +from .core import NonTemporalDataUser, PlausibilityError, Scalar, TemporalData, TemporalDataUser from .elements import Component, ComponentModel, Flow from .features import InvestmentModel, OnOffModel, PiecewiseModel from .interface import InvestParameters, OnOffParameters, PiecewiseConversion diff --git a/flixopt/core.py b/flixopt/core.py index 43b529421..7d8db491e 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -5,7 +5,7 @@ import logging import warnings -from typing import Dict, Optional, Union, Tuple, Literal +from typing import Dict, Literal, Optional, Tuple, Union import numpy as np import pandas as pd diff --git a/flixopt/features.py b/flixopt/features.py index 5e0ed0e80..17bb43bcd 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -10,7 +10,7 @@ import numpy as np from .config import CONFIG -from .core import Scalar, TemporalData, NonTemporalData +from .core import NonTemporalData, Scalar, TemporalData from .interface import InvestParameters, OnOffParameters, Piecewise from .structure import Model, SystemModel diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 74492bcd3..2cec86073 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -16,8 +16,24 @@ from rich.pretty import Pretty from . import io as fx_io -from .core import ConversionError, DataConverter, TemporalData, TemporalDataUser, TimeSeriesData, NonTemporalDataUser, NonTemporalData, FlowSystemDimensions -from .effects import Effect, EffectCollection, NonTemporalEffects, NonTemporalEffectsUser, TemporalEffects, TemporalEffectsUser +from .core import ( + ConversionError, + DataConverter, + FlowSystemDimensions, + NonTemporalData, + NonTemporalDataUser, + TemporalData, + TemporalDataUser, + TimeSeriesData, +) +from .effects import ( + Effect, + EffectCollection, + NonTemporalEffects, + NonTemporalEffectsUser, + TemporalEffects, + TemporalEffectsUser, +) from .elements import Bus, Component, Flow from .structure import Element, Interface, SystemModel diff --git a/flixopt/interface.py b/flixopt/interface.py index 81f351ebe..7abf574c6 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -7,11 +7,11 @@ from typing import TYPE_CHECKING, Dict, Iterator, List, Literal, Optional, Union from .config import CONFIG -from .core import Scalar, TemporalDataUser, NonTemporalDataUser, NonTemporalData +from .core import NonTemporalData, NonTemporalDataUser, Scalar, TemporalDataUser from .structure import Interface, register_class_for_io if TYPE_CHECKING: # for type checking and preventing circular imports - from .effects import EffectValuesUserScenario, EffectValuesUserTimestep + from .effects import NonTemporalEffectsUser, TemporalEffectsUser from .flow_system import FlowSystem @@ -150,10 +150,10 @@ def __init__( minimum_size: Optional[NonTemporalDataUser] = None, maximum_size: Optional[NonTemporalDataUser] = None, optional: bool = True, # Investition ist weglassbar - fix_effects: Optional['EffectValuesUserScenario'] = None, - specific_effects: Optional['EffectValuesUserScenario'] = None, # costs per Flow-Unit/Storage-Size/... + fix_effects: Optional[NonTemporalEffectsUser] = None, + specific_effects: Optional[NonTemporalEffectsUser] = None, # costs per Flow-Unit/Storage-Size/... piecewise_effects: Optional[PiecewiseEffects] = None, - divest_effects: Optional['EffectValuesUserScenario'] = None, + divest_effects: Optional[NonTemporalEffectsUser] = None, investment_scenarios: Optional[Union[Literal['individual'], List[Union[int, str]]]] = None, ): """ @@ -173,11 +173,11 @@ def __init__( - List of scenario names: Optimize the size for the passed scenario names (equal size in all). All other scenarios will have the size 0. - None: Equals to a list of all scenarios (default) """ - self.fix_effects: EffectValuesUserScenario = fix_effects if fix_effects is not None else {} - self.divest_effects: EffectValuesUserScenario = divest_effects if divest_effects is not None else {} + self.fix_effects: NonTemporalEffectsUser = fix_effects if fix_effects is not None else {} + self.divest_effects: NonTemporalEffectsUser = divest_effects if divest_effects is not None else {} self.fixed_size = fixed_size self.optional = optional - self.specific_effects: EffectValuesUserScenario = specific_effects if specific_effects is not None else {} + self.specific_effects: NonTemporalEffectsUser = specific_effects if specific_effects is not None else {} self.piecewise_effects = piecewise_effects self.minimum_size = minimum_size if minimum_size is not None else CONFIG.modeling.EPSILON self.maximum_size = maximum_size if maximum_size is not None else CONFIG.modeling.BIG # default maximum @@ -246,8 +246,8 @@ def maximum_or_fixed_size(self) -> NonTemporalData: class OnOffParameters(Interface): def __init__( self, - effects_per_switch_on: Optional['EffectValuesUser'] = None, - effects_per_running_hour: Optional['EffectValuesUser'] = None, + effects_per_switch_on: Optional[NonTemporalEffectsUser] = None, + effects_per_running_hour: Optional[NonTemporalEffectsUser] = None, on_hours_total_min: Optional[int] = None, on_hours_total_max: Optional[int] = None, consecutive_on_hours_min: Optional[TemporalDataUser] = None, @@ -277,8 +277,8 @@ def __init__( switch_on_total_max: max nr of switchOn operations force_switch_on: force creation of switch on variable, even if there is no switch_on_total_max """ - self.effects_per_switch_on: EffectValuesUserTimestep = effects_per_switch_on or {} - self.effects_per_running_hour: EffectValuesUserTimestep = effects_per_running_hour or {} + self.effects_per_switch_on: TemporalEffectsUser = effects_per_switch_on or {} + self.effects_per_running_hour: TemporalEffectsUser = effects_per_running_hour or {} self.on_hours_total_min: Scalar = on_hours_total_min self.on_hours_total_max: Scalar = on_hours_total_max self.consecutive_on_hours_min: TemporalDataUser = consecutive_on_hours_min diff --git a/flixopt/results.py b/flixopt/results.py index 6f522e55c..97dfc136a 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -1161,10 +1161,7 @@ def size(self) -> xr.DataArray: if name in self.solution: return self.solution[name] try: - return DataConverter.as_dataarray( - self._calculation_results.flow_system.flows[self.label].size, - scenarios=self._calculation_results.scenarios - ).rename(name) + return self._calculation_results.flow_system.flows[self.label].size.rename(name) except _FlowSystemRestorationError: logger.critical(f'Size of flow {self.label}.size not availlable. Returning NaN') return xr.DataArray(np.nan).rename(name) diff --git a/flixopt/structure.py b/flixopt/structure.py index b5b458cdb..5edee1bd3 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -19,7 +19,7 @@ from . import io as fx_io from .config import CONFIG -from .core import Scalar, TemporalDataUser, TimeSeriesData, get_dataarray_stats, NonTemporalData +from .core import NonTemporalData, Scalar, TemporalDataUser, TimeSeriesData, get_dataarray_stats if TYPE_CHECKING: # for type checking and preventing circular imports from .effects import EffectCollectionModel diff --git a/tests/test_component.py b/tests/test_component.py index 8a99b5d5b..11b5385c2 100644 --- a/tests/test_component.py +++ b/tests/test_component.py @@ -6,7 +6,13 @@ import flixopt as fx import flixopt.elements -from .conftest import assert_conequal, assert_var_equal, create_linopy_model, create_calculation_and_solve, assert_almost_equal_numeric +from .conftest import ( + assert_almost_equal_numeric, + assert_conequal, + assert_var_equal, + create_calculation_and_solve, + create_linopy_model, +) class TestComponentModel: From 0302947358667715af74531aca19b19330042203 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:30:47 +0200 Subject: [PATCH 164/183] Fix type hints and improve None handling --- flixopt/effects.py | 6 ++++-- flixopt/features.py | 2 +- flixopt/interface.py | 20 ++++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/flixopt/effects.py b/flixopt/effects.py index dc1d7d07f..381b5a3de 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -77,9 +77,11 @@ def __init__( self.is_standard = is_standard self.is_objective = is_objective self.specific_share_to_other_effects_operation: TemporalEffectsUser = ( - specific_share_to_other_effects_operation or {} + specific_share_to_other_effects_operation if specific_share_to_other_effects_operation is not None else {} + ) + self.specific_share_to_other_effects_invest: NonTemporalEffectsUser = ( + specific_share_to_other_effects_invest if specific_share_to_other_effects_invest is not None else {} ) - self.specific_share_to_other_effects_invest: NonTemporalEffectsUser = specific_share_to_other_effects_invest or {} self.minimum_operation = minimum_operation self.maximum_operation = maximum_operation self.minimum_operation_per_hour: TemporalDataUser = minimum_operation_per_hour diff --git a/flixopt/features.py b/flixopt/features.py index 17bb43bcd..4e47ace7f 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -274,7 +274,7 @@ def __init__( self._on_hours_total_min = on_hours_total_min if on_hours_total_min is not None else 0 self._on_hours_total_max = on_hours_total_max if on_hours_total_max is not None else np.inf self._use_off = use_off - self._effects_per_running_hour = effects_per_running_hour or {} + self._effects_per_running_hour = effects_per_running_hour if effects_per_running_hour is not None else {} self.on = None self.total_on_hours: Optional[linopy.Variable] = None diff --git a/flixopt/interface.py b/flixopt/interface.py index 7abf574c6..76e74616b 100644 --- a/flixopt/interface.py +++ b/flixopt/interface.py @@ -150,10 +150,10 @@ def __init__( minimum_size: Optional[NonTemporalDataUser] = None, maximum_size: Optional[NonTemporalDataUser] = None, optional: bool = True, # Investition ist weglassbar - fix_effects: Optional[NonTemporalEffectsUser] = None, - specific_effects: Optional[NonTemporalEffectsUser] = None, # costs per Flow-Unit/Storage-Size/... + fix_effects: Optional['NonTemporalEffectsUser'] = None, + specific_effects: Optional['NonTemporalEffectsUser'] = None, # costs per Flow-Unit/Storage-Size/... piecewise_effects: Optional[PiecewiseEffects] = None, - divest_effects: Optional[NonTemporalEffectsUser] = None, + divest_effects: Optional['NonTemporalEffectsUser'] = None, investment_scenarios: Optional[Union[Literal['individual'], List[Union[int, str]]]] = None, ): """ @@ -173,11 +173,11 @@ def __init__( - List of scenario names: Optimize the size for the passed scenario names (equal size in all). All other scenarios will have the size 0. - None: Equals to a list of all scenarios (default) """ - self.fix_effects: NonTemporalEffectsUser = fix_effects if fix_effects is not None else {} - self.divest_effects: NonTemporalEffectsUser = divest_effects if divest_effects is not None else {} + self.fix_effects: 'NonTemporalEffectsUser' = fix_effects if fix_effects is not None else {} + self.divest_effects: 'NonTemporalEffectsUser' = divest_effects if divest_effects is not None else {} self.fixed_size = fixed_size self.optional = optional - self.specific_effects: NonTemporalEffectsUser = specific_effects if specific_effects is not None else {} + self.specific_effects: 'NonTemporalEffectsUser' = specific_effects if specific_effects is not None else {} self.piecewise_effects = piecewise_effects self.minimum_size = minimum_size if minimum_size is not None else CONFIG.modeling.EPSILON self.maximum_size = maximum_size if maximum_size is not None else CONFIG.modeling.BIG # default maximum @@ -246,8 +246,8 @@ def maximum_or_fixed_size(self) -> NonTemporalData: class OnOffParameters(Interface): def __init__( self, - effects_per_switch_on: Optional[NonTemporalEffectsUser] = None, - effects_per_running_hour: Optional[NonTemporalEffectsUser] = None, + effects_per_switch_on: Optional['NonTemporalEffectsUser'] = None, + effects_per_running_hour: Optional['NonTemporalEffectsUser'] = None, on_hours_total_min: Optional[int] = None, on_hours_total_max: Optional[int] = None, consecutive_on_hours_min: Optional[TemporalDataUser] = None, @@ -277,8 +277,8 @@ def __init__( switch_on_total_max: max nr of switchOn operations force_switch_on: force creation of switch on variable, even if there is no switch_on_total_max """ - self.effects_per_switch_on: TemporalEffectsUser = effects_per_switch_on or {} - self.effects_per_running_hour: TemporalEffectsUser = effects_per_running_hour or {} + self.effects_per_switch_on: 'TemporalEffectsUser' = effects_per_switch_on if effects_per_switch_on is not None else {} + self.effects_per_running_hour: 'TemporalEffectsUser' = effects_per_running_hour if effects_per_running_hour is not None else {} self.on_hours_total_min: Scalar = on_hours_total_min self.on_hours_total_max: Scalar = on_hours_total_max self.consecutive_on_hours_min: TemporalDataUser = consecutive_on_hours_min From 40c437a533fc74710f4e5f075a9f1c7a5f9989d8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 3 Jul 2025 10:52:48 +0200 Subject: [PATCH 165/183] Fix coords in AggregatedCalculation --- flixopt/calculation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index b7d17eef6..e035aaa13 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -323,7 +323,7 @@ def _perform_aggregation(self): if self.aggregation_parameters.aggregate_data_and_fix_non_binary_vars: ds = self.flow_system.to_dataset() for name, series in self.aggregation.aggregated_data.items(): - da = DataConverter.to_dataarray(series, timesteps=self.flow_system.timesteps).rename(name).assign_attrs(ds[name].attrs) + da = DataConverter.to_dataarray(series, self.flow_system.coords).rename(name).assign_attrs(ds[name].attrs) if TimeSeriesData.is_timeseries_data(da): da = TimeSeriesData.from_dataarray(da) From 8d2d208d4bce947c2487ceebb6c33cad0d210fe2 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 3 Jul 2025 11:23:14 +0200 Subject: [PATCH 166/183] Improve Error Messages of DataConversion --- flixopt/flow_system.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 2cec86073..0568f3b3a 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -349,9 +349,13 @@ def fit_to_model_coords( ).rename(name) except ConversionError as e: raise ConversionError( - f'Could not convert time series data "{name}" to DataArray: Original Error: {e}') from e + f'Could not convert time series data "{name}" to DataArray:\n{data}\nOriginal Error: {e}') from e else: - return DataConverter.to_dataarray(data, coords=coords).rename(name) + try: + return DataConverter.to_dataarray(data, coords=coords).rename(name) + except ConversionError as e: + raise ConversionError( + f'Could not convert data "{name}" to DataArray:\n{data}\nOriginal Error: {e}') from e def fit_effects_to_model_coords( self, From 9c0c95fe86b51acc719c258265fd6fd38e18accc Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 3 Jul 2025 12:59:44 +0200 Subject: [PATCH 167/183] Allow multi dim data conversion and broadcasting by length --- flixopt/core.py | 89 ++++++++-- tests/test_dataconverter.py | 330 +++++++++++++++++++++++++++++++++++- 2 files changed, 407 insertions(+), 12 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 7d8db491e..ad5b011ba 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -279,6 +279,63 @@ def _broadcast_dataarray_to_more_dims( return xr.DataArray(result_data.copy(), coords=final_coords, dims=target_dims) + @staticmethod + def _convert_multid_array_to_dataarray( + data: np.ndarray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Convert multi-dimensional numpy array to DataArray by matching dimensions by length. + + Args: + data: Multi-dimensional numpy array + coords: Available coordinates + target_dims: Target dimension names + + Returns: + DataArray with dimensions matched by length + + Raises: + ConversionError: If array dimensions cannot be uniquely matched to coordinates + """ + if len(target_dims) == 0: + if data.size != 1: + raise ConversionError('Cannot convert multi-element array without target dimensions') + return xr.DataArray(data.item()) + + if data.ndim != len(target_dims): + raise ConversionError(f'Array has {data.ndim} dimensions but {len(target_dims)} target dimensions provided') + + # Get lengths of each dimension + array_shape = data.shape + coord_lengths = {dim: len(coords[dim]) for dim in target_dims} + + # Try to find a unique mapping from array dimensions to coordinate dimensions + possible_mappings = [] + + # Generate all possible permutations of target dimensions + from itertools import permutations + + for dim_order in permutations(target_dims): + # Check if this permutation matches the array shape + if all(array_shape[i] == coord_lengths[dim_order[i]] for i in range(len(dim_order))): + possible_mappings.append(dim_order) + + if len(possible_mappings) == 0: + shape_info = f'Array shape: {array_shape}, Coordinate lengths: {coord_lengths}' + raise ConversionError(f'Array dimensions do not match any coordinate lengths. {shape_info}') + + if len(possible_mappings) > 1: + raise ConversionError( + f'Array shape {array_shape} matches multiple dimension orders: {possible_mappings}. ' + 'Cannot uniquely determine dimension mapping.' + ) + + # Use the unique mapping found + matched_dims = possible_mappings[0] + matched_coords = {dim: coords[dim] for dim in matched_dims} + + return xr.DataArray(data.copy(), coords=matched_coords, dims=matched_dims) + @staticmethod def to_dataarray( data: Union[Scalar, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, TimeSeriesData], @@ -289,7 +346,8 @@ def to_dataarray( Accepts: - Scalars (broadcast to all dimensions) - - 1D arrays, Series, or single-column DataFrames (matched to one dimension, broadcast to others) + - 1D arrays or Series (matched to one dimension, broadcast to others) + - Multi-D arrays or DataFrames (dimensions matched by length) - xr.DataArray (validated and potentially broadcast to additional dimensions) Args: @@ -304,31 +362,42 @@ def to_dataarray( validated_coords, target_dims = DataConverter._prepare_dimensions(coords) - # Step 1: Convert to DataArray (with safe 1D/2D logic for simple data) + # Step 1: Convert to DataArray if isinstance(data, (int, float, np.integer, np.floating)): # Scalars: create 0D DataArray, will be broadcast later intermediate = xr.DataArray(data.item() if hasattr(data, 'item') else data) elif isinstance(data, np.ndarray): - if data.ndim != 1: - raise ConversionError(f'Only 1D arrays supported, got {data.ndim}D array') - intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) + if data.ndim == 1: + intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) + else: + # Handle multi-dimensional arrays + intermediate = DataConverter._convert_multid_array_to_dataarray(data, validated_coords, target_dims) elif isinstance(data, pd.Series): + if isinstance(data.index, pd.MultiIndex): + raise ConversionError('Series index must be a single level Index. Multi-index Series are not supported.') intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) elif isinstance(data, pd.DataFrame): - if len(data.columns) != 1: - raise ConversionError(f'Only single-column DataFrames are supported, got {len(data.columns)} columns') - series = data.iloc[:, 0] - intermediate = DataConverter._convert_1d_data_to_dataarray(series, validated_coords, target_dims) + if isinstance(data.index, pd.MultiIndex): + raise ConversionError('DataFrame index must be a single level Index. Multi-index DataFrames are not supported.') + if len(data.columns) == 0 or data.empty: + raise ConversionError('DataFrame must have at least one column.') + + if len(data.columns) == 1: + intermediate = DataConverter._convert_1d_data_to_dataarray(data.iloc[:, 0], validated_coords, target_dims) + else: + # Handle multi-column DataFrames + logger.warning(f'Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') + intermediate = DataConverter._convert_multid_array_to_dataarray(data.to_numpy(), validated_coords, target_dims) elif isinstance(data, xr.DataArray): intermediate = data.copy() else: raise ConversionError( - f'Unsupported data type: {type(data).__name__}. Only scalars, 1D arrays, Series, single-column DataFrames, and DataArrays are supported.' + f'Unsupported data type: {type(data).__name__}. Only scalars, arrays, Series, DataFrames, and DataArrays are supported.' ) # Step 2: Broadcast to target dimensions if needed diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 56f36aabf..659528c4d 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -211,14 +211,14 @@ def test_multi_column_dataframe_rejected(self, time_coords): 'value2': [15, 25, 35, 45, 55] }, index=time_coords) - with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + with pytest.raises(ConversionError, match="Array has 2 dimensions but 1 target "): DataConverter.to_dataarray(df, coords={'time': time_coords}) def test_empty_dataframe_rejected(self, time_coords): """Empty DataFrame should be rejected.""" df = pd.DataFrame(index=time_coords) # No columns - with pytest.raises(ConversionError, match="Only single-column DataFrames are supported"): + with pytest.raises(ConversionError, match="DataFrame must have at least one"): DataConverter.to_dataarray(df, coords={'time': time_coords}) def test_dataframe_broadcast(self, time_coords, scenario_coords): @@ -855,5 +855,331 @@ def test_mixed_numeric_types(self, time_coords): assert np.array_equal(result.values, mixed_arr) +class TestMultiDimensionalArrayConversion: + """Test multi-dimensional numpy array conversions.""" + + @pytest.fixture + def standard_coords(self): + """Standard coordinates with unique lengths for easy testing.""" + return { + 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south'], name='region') # length 2 + } + + def test_2d_array_unique_dimensions(self, standard_coords): + """2D array with unique dimension lengths should work.""" + # 5x3 array should map to time x scenario + data_2d = np.random.rand(5, 3) + result = DataConverter.to_dataarray(data_2d, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, data_2d) + + # 3x5 array should map to scenario x time + data_2d_flipped = np.random.rand(3, 5) + result_flipped = DataConverter.to_dataarray(data_2d_flipped, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_flipped.shape == (3, 5) + assert result_flipped.dims == ('scenario', 'time') + assert np.array_equal(result_flipped.values, data_2d_flipped) + + def test_3d_array_unique_dimensions(self, standard_coords): + """3D array with unique dimension lengths should work.""" + # 5x3x2 array should map to time x scenario x region + data_3d = np.random.rand(5, 3, 2) + result = DataConverter.to_dataarray(data_3d, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + assert np.array_equal(result.values, data_3d) + + def test_3d_array_different_permutation(self, standard_coords): + """3D array with different dimension order should work.""" + # 2x5x3 array should map to region x time x scenario + data_3d = np.random.rand(2, 5, 3) + result = DataConverter.to_dataarray(data_3d, coords=standard_coords) + + assert result.shape == (2, 5, 3) + assert result.dims == ('region', 'time', 'scenario') + assert np.array_equal(result.values, data_3d) + + def test_4d_array_unique_dimensions(self): + """4D array with unique dimension lengths should work.""" + coords = { + 'time': pd.date_range('2024-01-01', periods=2, freq='D', name='time'), # length 2 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east', 'west'], name='region'), # length 4 + 'technology': pd.Index(['solar', 'wind', 'gas', 'coal', 'hydro'], name='technology') # length 5 + } + + # 3x5x2x4 array should map to scenario x technology x time x region + data_4d = np.random.rand(3, 5, 2, 4) + result = DataConverter.to_dataarray(data_4d, coords=coords) + + assert result.shape == (3, 5, 2, 4) + assert result.dims == ('scenario', 'technology', 'time', 'region') + assert np.array_equal(result.values, data_4d) + + def test_2d_array_ambiguous_dimensions_error(self): + """2D array with ambiguous dimension lengths should fail.""" + # Both dimensions have length 3 + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east'], name='region') # length 3 + } + + data_2d = np.random.rand(3, 3) + with pytest.raises(ConversionError, match="matches multiple dimension orders"): + DataConverter.to_dataarray(data_2d, coords=coords_ambiguous) + + def test_3d_array_ambiguous_dimensions_error(self): + """3D array with ambiguous dimension lengths should fail.""" + # All dimensions have length 2 + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B'], name='scenario'), # length 2 + 'region': pd.Index(['north', 'south'], name='region'), # length 2 + 'technology': pd.Index(['solar', 'wind'], name='technology') # length 2 + } + + data_3d = np.random.rand(2, 2, 2) + with pytest.raises(ConversionError, match="matches multiple dimension orders"): + DataConverter.to_dataarray(data_3d, coords=coords_ambiguous) + + def test_array_dimension_count_mismatch_error(self, standard_coords): + """Array with wrong number of dimensions should fail.""" + # 2D array with 3D coordinates + data_2d = np.random.rand(5, 3) + with pytest.raises(ConversionError, match="Array has 2 dimensions but 3 target dimensions provided"): + DataConverter.to_dataarray(data_2d, coords=standard_coords) + + # 4D array with 3D coordinates + data_4d = np.random.rand(5, 3, 2, 4) + with pytest.raises(ConversionError, match="Array has 4 dimensions but 3 target dimensions provided"): + DataConverter.to_dataarray(data_4d, coords=standard_coords) + + def test_array_no_matching_dimensions_error(self, standard_coords): + """Array with no matching dimension lengths should fail.""" + # 7x8 array - no dimension has length 7 or 8 + data_2d = np.random.rand(7, 8) + coords_2d = { + 'time': standard_coords['time'], # length 5 + 'scenario': standard_coords['scenario'] # length 3 + } + + with pytest.raises(ConversionError, match="Array dimensions do not match any coordinate lengths"): + DataConverter.to_dataarray(data_2d, coords=coords_2d) + + def test_2d_array_custom_dimensions(self): + """2D array with custom dimension names should work.""" + coords = { + 'product': pd.Index(['A', 'B', 'C', 'D'], name='product'), # length 4 + 'factory': pd.Index(['F1', 'F2', 'F3'], name='factory') # length 3 + } + + # 4x3 array should map to product x factory + data_2d = np.array([[10, 11, 12], + [20, 21, 22], + [30, 31, 32], + [40, 41, 42]]) + + result = DataConverter.to_dataarray(data_2d, coords=coords) + + assert result.shape == (4, 3) + assert result.dims == ('product', 'factory') + assert np.array_equal(result.values, data_2d) + + # Verify coordinates are correct + assert result.indexes['product'].equals(coords['product']) + assert result.indexes['factory'].equals(coords['factory']) + + def test_multid_array_copy_independence(self, standard_coords): + """Multi-D arrays should be independent copies.""" + original_data = np.random.rand(5, 3) + result = DataConverter.to_dataarray(original_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + # Modify result + result[0, 0] = 999 + + # Original should be unchanged + assert original_data[0, 0] != 999 + + def test_multid_array_special_values(self, standard_coords): + """Multi-D arrays should preserve special values.""" + # Create 2D array with special values + data_2d = np.array([[1.0, np.nan, 3.0], + [np.inf, 5.0, -np.inf], + [7.0, 8.0, 9.0], + [10.0, np.nan, 12.0], + [13.0, 14.0, np.inf]]) + + result = DataConverter.to_dataarray(data_2d, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result.shape == (5, 3) + assert np.array_equal(np.isnan(result.values), np.isnan(data_2d)) + assert np.array_equal(np.isinf(result.values), np.isinf(data_2d)) + + def test_multid_array_with_time_dimension(self): + """Multi-D arrays should work with time dimension.""" + time_coords = pd.date_range('2024-01-01', periods=4, freq='H', name='time') + scenario_coords = pd.Index(['base', 'high', 'low'], name='scenario') + + # 4x3 time series data + data_2d = np.array([[100, 110, 120], + [200, 210, 220], + [300, 310, 320], + [400, 410, 420]]) + + result = DataConverter.to_dataarray(data_2d, coords={ + 'time': time_coords, + 'scenario': scenario_coords + }) + + assert result.shape == (4, 3) + assert result.dims == ('time', 'scenario') + assert isinstance(result.indexes['time'], pd.DatetimeIndex) + assert np.array_equal(result.values, data_2d) + + def test_multid_array_dtype_preservation(self, standard_coords): + """Multi-D arrays should preserve data types.""" + # Integer array + int_data = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10, 11, 12], + [13, 14, 15]], dtype=np.int32) + + result_int = DataConverter.to_dataarray(int_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_int.dtype == np.int32 + assert np.array_equal(result_int.values, int_data) + + # Float array + float_data = np.array([[1.1, 2.2, 3.3], + [4.4, 5.5, 6.6], + [7.7, 8.8, 9.9], + [10.1, 11.1, 12.1], + [13.1, 14.1, 15.1]], dtype=np.float64) + + result_float = DataConverter.to_dataarray(float_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_float.dtype == np.float64 + assert np.array_equal(result_float.values, float_data) + + # Boolean array + bool_data = np.array([[True, False, True], + [False, True, False], + [True, True, False], + [False, False, True], + [True, False, True]]) + + result_bool = DataConverter.to_dataarray(bool_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_bool.dtype == bool + assert np.array_equal(result_bool.values, bool_data) + + def test_multid_array_no_coords(self): + """Multi-D arrays without coords should fail unless scalar.""" + # Multi-element fails + data_2d = np.random.rand(2, 3) + with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): + DataConverter.to_dataarray(data_2d) + + # Single element succeeds + single_element = np.array([[42]]) + result = DataConverter.to_dataarray(single_element) + assert result.shape == () + assert result.item() == 42 + + def test_multid_array_empty_coords(self, standard_coords): + """Multi-D arrays with empty coords should fail.""" + data_2d = np.random.rand(5, 3) + with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): + DataConverter.to_dataarray(data_2d, coords={}) + + def test_multid_array_coordinate_validation(self): + """Multi-D arrays should validate coordinates properly.""" + # Test with time coordinate that's not DatetimeIndex + wrong_time = pd.Index([1, 2, 3, 4, 5], name='time') + scenario_coords = pd.Index(['A', 'B', 'C'], name='scenario') + + data_2d = np.random.rand(5, 3) + with pytest.raises(ConversionError, match="time coordinates must be a DatetimeIndex"): + DataConverter.to_dataarray(data_2d, coords={ + 'time': wrong_time, + 'scenario': scenario_coords + }) + + def test_multid_array_complex_scenario(self): + """Complex real-world scenario with multi-D array.""" + # Energy system data: time x technology x region + coords = { + 'time': pd.date_range('2024-01-01', periods=8760, freq='H', name='time'), # 1 year hourly + 'technology': pd.Index(['solar', 'wind', 'gas', 'coal'], name='technology'), # 4 technologies + 'region': pd.Index(['north', 'south', 'east'], name='region') # 3 regions + } + + # Capacity factors: 8760 x 4 x 3 + capacity_factors = np.random.rand(8760, 4, 3) + + result = DataConverter.to_dataarray(capacity_factors, coords=coords) + + assert result.shape == (8760, 4, 3) + assert result.dims == ('time', 'technology', 'region') + assert isinstance(result.indexes['time'], pd.DatetimeIndex) + assert len(result.indexes['time']) == 8760 + assert len(result.indexes['technology']) == 4 + assert len(result.indexes['region']) == 3 + + def test_multid_array_edge_cases(self): + """Test edge cases for multi-D arrays.""" + # Single dimension with multi-D array should fail + coords_1d = {'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time')} + data_2d = np.random.rand(5, 3) + + with pytest.raises(ConversionError, match="Array has 2 dimensions but 1 target dimensions provided"): + DataConverter.to_dataarray(data_2d, coords=coords_1d) + + # Zero dimensions with multi-D array should fail + data_1d = np.array([1, 2, 3]) + with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): + DataConverter.to_dataarray(data_1d, coords={}) + + def test_multid_array_partial_dimension_match(self): + """Array where only some dimensions match should fail.""" + coords = { + 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario') # length 3 + } + + # 5x7 array - first dimension matches time (5) but second doesn't match scenario (3) + data_2d = np.random.rand(5, 7) + with pytest.raises(ConversionError, match="Array dimensions do not match any coordinate lengths"): + DataConverter.to_dataarray(data_2d, coords=coords) + + + if __name__ == '__main__': pytest.main() From d568ad605659c81f1dd3de40625be774129a94d0 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Thu, 3 Jul 2025 14:13:49 +0200 Subject: [PATCH 168/183] Improve DataConverter to handle multi-dim arrays --- flixopt/core.py | 47 +- tests/test_dataconverter.py | 1139 +++++++++++++---------------------- 2 files changed, 451 insertions(+), 735 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index ad5b011ba..97e91c274 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -285,6 +285,7 @@ def _convert_multid_array_to_dataarray( ) -> xr.DataArray: """ Convert multi-dimensional numpy array to DataArray by matching dimensions by length. + Returns a DataArray that may need further broadcasting to target dimensions. Args: data: Multi-dimensional numpy array @@ -292,7 +293,7 @@ def _convert_multid_array_to_dataarray( target_dims: Target dimension names Returns: - DataArray with dimensions matched by length + DataArray with dimensions matched by length (may be subset of target_dims) Raises: ConversionError: If array dimensions cannot be uniquely matched to coordinates @@ -302,23 +303,19 @@ def _convert_multid_array_to_dataarray( raise ConversionError('Cannot convert multi-element array without target dimensions') return xr.DataArray(data.item()) - if data.ndim != len(target_dims): - raise ConversionError(f'Array has {data.ndim} dimensions but {len(target_dims)} target dimensions provided') - # Get lengths of each dimension array_shape = data.shape coord_lengths = {dim: len(coords[dim]) for dim in target_dims} - # Try to find a unique mapping from array dimensions to coordinate dimensions - possible_mappings = [] - - # Generate all possible permutations of target dimensions + # Find all possible ways to match array dimensions to available coordinates from itertools import permutations - for dim_order in permutations(target_dims): + # Try all permutations of target_dims that match the array's number of dimensions + possible_mappings = [] + for dim_subset in permutations(target_dims, data.ndim): # Check if this permutation matches the array shape - if all(array_shape[i] == coord_lengths[dim_order[i]] for i in range(len(dim_order))): - possible_mappings.append(dim_order) + if all(array_shape[i] == coord_lengths[dim_subset[i]] for i in range(len(dim_subset))): + possible_mappings.append(dim_subset) if len(possible_mappings) == 0: shape_info = f'Array shape: {array_shape}, Coordinate lengths: {coord_lengths}' @@ -334,6 +331,7 @@ def _convert_multid_array_to_dataarray( matched_dims = possible_mappings[0] matched_coords = {dim: coords[dim] for dim in matched_dims} + # Return DataArray with matched dimensions - broadcasting will happen later if needed return xr.DataArray(data.copy(), coords=matched_coords, dims=matched_dims) @staticmethod @@ -347,7 +345,7 @@ def to_dataarray( Accepts: - Scalars (broadcast to all dimensions) - 1D arrays or Series (matched to one dimension, broadcast to others) - - Multi-D arrays or DataFrames (dimensions matched by length) + - Multi-D arrays or DataFrames (dimensions matched by length, broadcast to remaining) - xr.DataArray (validated and potentially broadcast to additional dimensions) Args: @@ -362,7 +360,7 @@ def to_dataarray( validated_coords, target_dims = DataConverter._prepare_dimensions(coords) - # Step 1: Convert to DataArray + # Step 1: Convert to DataArray (may have fewer dimensions than target) if isinstance(data, (int, float, np.integer, np.floating)): # Scalars: create 0D DataArray, will be broadcast later intermediate = xr.DataArray(data.item() if hasattr(data, 'item') else data) @@ -371,26 +369,34 @@ def to_dataarray( if data.ndim == 1: intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) else: - # Handle multi-dimensional arrays + # Handle multi-dimensional arrays - this now allows partial matching intermediate = DataConverter._convert_multid_array_to_dataarray(data, validated_coords, target_dims) elif isinstance(data, pd.Series): if isinstance(data.index, pd.MultiIndex): - raise ConversionError('Series index must be a single level Index. Multi-index Series are not supported.') + raise ConversionError( + 'Series index must be a single level Index. Multi-index Series are not supported.' + ) intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) elif isinstance(data, pd.DataFrame): if isinstance(data.index, pd.MultiIndex): - raise ConversionError('DataFrame index must be a single level Index. Multi-index DataFrames are not supported.') + raise ConversionError( + 'DataFrame index must be a single level Index. Multi-index DataFrames are not supported.' + ) if len(data.columns) == 0 or data.empty: raise ConversionError('DataFrame must have at least one column.') if len(data.columns) == 1: - intermediate = DataConverter._convert_1d_data_to_dataarray(data.iloc[:, 0], validated_coords, target_dims) + intermediate = DataConverter._convert_1d_data_to_dataarray( + data.iloc[:, 0], validated_coords, target_dims + ) else: - # Handle multi-column DataFrames - logger.warning(f'Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') - intermediate = DataConverter._convert_multid_array_to_dataarray(data.to_numpy(), validated_coords, target_dims) + # Handle multi-column DataFrames - this now allows partial matching + logger.warning('Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') + intermediate = DataConverter._convert_multid_array_to_dataarray( + data.to_numpy(), validated_coords, target_dims + ) elif isinstance(data, xr.DataArray): intermediate = data.copy() @@ -401,6 +407,7 @@ def to_dataarray( ) # Step 2: Broadcast to target dimensions if needed + # This now handles cases where intermediate has some but not all target dimensions return DataConverter._broadcast_to_target_dims(intermediate, validated_coords, target_dims) @staticmethod diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index 659528c4d..c174aebe8 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -25,8 +25,18 @@ def region_coords(): return pd.Index(['north', 'south', 'east'], name='region') -class TestBasicConversion: - """Test basic data type conversions with different coordinate configurations.""" +@pytest.fixture +def standard_coords(): + """Standard coordinates with unique lengths for easy testing.""" + return { + 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south'], name='region') # length 2 + } + + +class TestScalarConversion: + """Test scalar data conversions with different coordinate configurations.""" def test_scalar_no_coords(self): """Scalar without coordinates should create 0D DataArray.""" @@ -56,9 +66,21 @@ def test_numpy_scalars(self, time_coords): assert result.shape == (5,) assert np.all(result.values == scalar.item()) + def test_scalar_many_dimensions(self, standard_coords): + """Scalar should broadcast to any number of dimensions.""" + coords = { + **standard_coords, + 'technology': pd.Index(['solar', 'wind'], name='technology') + } + + result = DataConverter.to_dataarray(42, coords=coords) + assert result.shape == (5, 3, 2, 2) + assert result.dims == ('time', 'scenario', 'region', 'technology') + assert np.all(result.values == 42) + -class TestArrayConversion: - """Test numpy array conversions.""" +class TestOneDimensionalArrayConversion: + """Test 1D numpy array and pandas Series conversions.""" def test_1d_array_no_coords(self): """1D array without coords should fail unless single element.""" @@ -119,11 +141,22 @@ def test_1d_array_ambiguous_length(self): with pytest.raises(ConversionError, match="matches multiple dimensions"): DataConverter.to_dataarray(arr, coords=coords_3x3) - def test_multidimensional_array_rejected(self, time_coords): - """Multidimensional arrays should be rejected.""" - arr_2d = np.array([[1, 2, 3], [4, 5, 6]]) - with pytest.raises(ConversionError, match="Only 1D arrays supported"): - DataConverter.to_dataarray(arr_2d, coords={'time': time_coords}) + def test_1d_array_broadcast_to_many_dimensions(self, standard_coords): + """1D array should broadcast to many dimensions.""" + # Array matching time dimension + time_arr = np.array([10, 20, 30, 40, 50]) + result = DataConverter.to_dataarray(time_arr, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check broadcasting - all scenarios and regions should have same time values + for scenario in standard_coords['scenario']: + for region in standard_coords['region']: + assert np.array_equal( + result.sel(scenario=scenario, region=region).values, + time_arr + ) class TestSeriesConversion: @@ -176,14 +209,6 @@ def test_series_broadcast_to_multiple_coords(self, time_coords, scenario_coords) for scenario in scenario_coords: assert np.array_equal(result.sel(scenario=scenario).values, time_series.values) - # Scenario series broadcast to time - scenario_series = pd.Series([100, 200, 300], index=scenario_coords) - result = DataConverter.to_dataarray(scenario_series, coords={'time': time_coords, 'scenario': scenario_coords}) - assert result.shape == (5, 3) - - for time in time_coords: - assert np.array_equal(result.sel(time=time).values, scenario_series.values) - def test_series_wrong_dimension(self, time_coords, region_coords): """Series indexed by dimension not in coords should fail.""" wrong_series = pd.Series([1, 2, 3], index=region_coords) @@ -191,6 +216,22 @@ def test_series_wrong_dimension(self, time_coords, region_coords): with pytest.raises(ConversionError): DataConverter.to_dataarray(wrong_series, coords={'time': time_coords}) + def test_series_broadcast_to_many_dimensions(self, standard_coords): + """Series should broadcast to many dimensions.""" + time_series = pd.Series([100, 200, 300, 400, 500], index=standard_coords['time']) + result = DataConverter.to_dataarray(time_series, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check that all non-time dimensions have the same time series values + for scenario in standard_coords['scenario']: + for region in standard_coords['region']: + assert np.array_equal( + result.sel(scenario=scenario, region=region).values, + time_series.values + ) + class TestDataFrameConversion: """Test pandas DataFrame conversions.""" @@ -204,21 +245,26 @@ def test_single_column_dataframe(self, time_coords): assert result.dims == ('time',) assert np.array_equal(result.values, df['value'].values) - def test_multi_column_dataframe_rejected(self, time_coords): - """Multi-column DataFrame should be rejected.""" + def test_multi_column_dataframe_accepted(self, time_coords, scenario_coords): + """Multi-column DataFrame should now be accepted and converted via numpy array path.""" df = pd.DataFrame({ 'value1': [10, 20, 30, 40, 50], - 'value2': [15, 25, 35, 45, 55] + 'value2': [15, 25, 35, 45, 55], + 'value3': [12, 22, 32, 42, 52] }, index=time_coords) - with pytest.raises(ConversionError, match="Array has 2 dimensions but 1 target "): - DataConverter.to_dataarray(df, coords={'time': time_coords}) + # Should work by converting to numpy array (5x3) and matching to time x scenario + result = DataConverter.to_dataarray(df, coords={'time': time_coords, 'scenario': scenario_coords}) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, df.to_numpy()) def test_empty_dataframe_rejected(self, time_coords): """Empty DataFrame should be rejected.""" df = pd.DataFrame(index=time_coords) # No columns - with pytest.raises(ConversionError, match="DataFrame must have at least one"): + with pytest.raises(ConversionError, match="DataFrame must have at least one column"): DataConverter.to_dataarray(df, coords={'time': time_coords}) def test_dataframe_broadcast(self, time_coords, scenario_coords): @@ -231,6 +277,171 @@ def test_dataframe_broadcast(self, time_coords, scenario_coords): assert np.array_equal(result.sel(scenario=scenario).values, df['power'].values) +class TestMultiDimensionalArrayConversion: + """Test multi-dimensional numpy array conversions.""" + + def test_2d_array_unique_dimensions(self, standard_coords): + """2D array with unique dimension lengths should work.""" + # 5x3 array should map to time x scenario + data_2d = np.random.rand(5, 3) + result = DataConverter.to_dataarray(data_2d, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result.shape == (5, 3) + assert result.dims == ('time', 'scenario') + assert np.array_equal(result.values, data_2d) + + # 3x5 array should map to scenario x time + data_2d_flipped = np.random.rand(3, 5) + result_flipped = DataConverter.to_dataarray(data_2d_flipped, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_flipped.shape == (5, 3) + assert result_flipped.dims == ('time', 'scenario') + assert np.array_equal(result_flipped.values.transpose(), data_2d_flipped) + + def test_2d_array_broadcast_to_3d(self, standard_coords): + """2D array should broadcast to additional dimensions when using partial matching.""" + # With improved integration, 2D array (5x3) should match time×scenario and broadcast to region + data_2d = np.random.rand(5, 3) + result = DataConverter.to_dataarray(data_2d, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check that all regions have the same time x scenario data + for region in standard_coords['region']: + assert np.array_equal(result.sel(region=region).values, data_2d) + + def test_3d_array_unique_dimensions(self, standard_coords): + """3D array with unique dimension lengths should work.""" + # 5x3x2 array should map to time x scenario x region + data_3d = np.random.rand(5, 3, 2) + result = DataConverter.to_dataarray(data_3d, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + assert np.array_equal(result.values, data_3d) + + def test_3d_array_different_permutation(self, standard_coords): + """3D array with different dimension order should work.""" + # 2x5x3 array should map to region x time x scenario + data_3d = np.random.rand(2, 5, 3) + result = DataConverter.to_dataarray(data_3d, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + assert np.array_equal(result.transpose('region', 'time', 'scenario').values, data_3d) + + def test_4d_array_unique_dimensions(self): + """4D array with unique dimension lengths should work.""" + coords = { + 'time': pd.date_range('2024-01-01', periods=2, freq='D', name='time'), # length 2 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east', 'west'], name='region'), # length 4 + 'technology': pd.Index(['solar', 'wind', 'gas', 'coal', 'hydro'], name='technology') # length 5 + } + + # 3x5x2x4 array should map to scenario x technology x time x region + data_4d = np.random.rand(3, 5, 2, 4) + result = DataConverter.to_dataarray(data_4d, coords=coords) + + assert result.shape == (2,3,4,5) + assert result.dims == ('time', 'scenario', 'region', 'technology') + assert np.array_equal(result.transpose('scenario', 'technology', 'time', 'region').values, data_4d) + + def test_2d_array_ambiguous_dimensions_error(self): + """2D array with ambiguous dimension lengths should fail.""" + # Both dimensions have length 3 + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east'], name='region') # length 3 + } + + data_2d = np.random.rand(3, 3) + with pytest.raises(ConversionError, match="matches multiple dimension orders"): + DataConverter.to_dataarray(data_2d, coords=coords_ambiguous) + + def test_multid_array_no_coords(self): + """Multi-D arrays without coords should fail unless scalar.""" + # Multi-element fails + data_2d = np.random.rand(2, 3) + with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): + DataConverter.to_dataarray(data_2d) + + # Single element succeeds + single_element = np.array([[42]]) + result = DataConverter.to_dataarray(single_element) + assert result.shape == () + assert result.item() == 42 + + def test_array_no_matching_dimensions_error(self, standard_coords): + """Array with no matching dimension lengths should fail.""" + # 7x8 array - no dimension has length 7 or 8 + data_2d = np.random.rand(7, 8) + coords_2d = { + 'time': standard_coords['time'], # length 5 + 'scenario': standard_coords['scenario'] # length 3 + } + + with pytest.raises(ConversionError, match="Array dimensions do not match any coordinate lengths"): + DataConverter.to_dataarray(data_2d, coords=coords_2d) + + def test_multid_array_special_values(self, standard_coords): + """Multi-D arrays should preserve special values.""" + # Create 2D array with special values + data_2d = np.array([[1.0, np.nan, 3.0], + [np.inf, 5.0, -np.inf], + [7.0, 8.0, 9.0], + [10.0, np.nan, 12.0], + [13.0, 14.0, np.inf]]) + + result = DataConverter.to_dataarray(data_2d, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result.shape == (5, 3) + assert np.array_equal(np.isnan(result.values), np.isnan(data_2d)) + assert np.array_equal(np.isinf(result.values), np.isinf(data_2d)) + + def test_multid_array_dtype_preservation(self, standard_coords): + """Multi-D arrays should preserve data types.""" + # Integer array + int_data = np.array([[1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10, 11, 12], + [13, 14, 15]], dtype=np.int32) + + result_int = DataConverter.to_dataarray(int_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_int.dtype == np.int32 + assert np.array_equal(result_int.values, int_data) + + # Boolean array + bool_data = np.array([[True, False, True], + [False, True, False], + [True, True, False], + [False, False, True], + [True, False, True]]) + + result_bool = DataConverter.to_dataarray(bool_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + assert result_bool.dtype == bool + assert np.array_equal(result_bool.values, bool_data) + + class TestDataArrayConversion: """Test xarray DataArray conversions.""" @@ -282,6 +493,28 @@ def test_scalar_dataarray_broadcast(self, time_coords, scenario_coords): assert result.shape == (5, 3) assert np.all(result.values == 42) + def test_2d_dataarray_broadcast_to_more_dimensions(self, standard_coords): + """DataArray should broadcast to additional dimensions.""" + # Start with 2D DataArray + original = xr.DataArray( + [[10, 20, 30], [40, 50, 60], [70, 80, 90], [100, 110, 120], [130, 140, 150]], + coords={'time': standard_coords['time'], 'scenario': standard_coords['scenario']}, + dims=['time', 'scenario'] + ) + + # Broadcast to 3D + result = DataConverter.to_dataarray(original, coords=standard_coords) + + assert result.shape == (5, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # Check that all regions have the same time+scenario values + for region in standard_coords['region']: + assert np.array_equal( + result.sel(region=region).values, + original.values + ) + class TestTimeSeriesDataConversion: """Test TimeSeriesData conversions.""" @@ -309,275 +542,58 @@ def test_timeseries_data_broadcast(self, time_coords, scenario_coords): assert np.array_equal(result.sel(scenario=scenario).values, [10, 20, 30, 40, 50]) -class TestThreeDimensionConversion: - """Test conversions with exactly 3 dimensions for all data types.""" - - @pytest.fixture - def three_d_coords(self, time_coords, scenario_coords): - """Standard 3D coordinate system with unique lengths.""" - return { - 'time': time_coords, # length 5 - 'scenario': scenario_coords, # length 3 - 'region': pd.Index(['north', 'south'], name='region') # length 2 - unique! - } - - def test_scalar_three_dimensions(self, three_d_coords): - """Scalar should broadcast to 3 dimensions.""" - result = DataConverter.to_dataarray(42, coords=three_d_coords) +class TestCustomDimensions: + """Test with custom dimension names beyond time/scenario.""" - assert result.shape == (5, 3, 2) # time=5, scenario=3, region=2 - assert result.dims == ('time', 'scenario', 'region') + def test_custom_single_dimension(self, region_coords): + """Test with custom dimension name.""" + result = DataConverter.to_dataarray(42, coords={'region': region_coords}) + assert result.shape == (3,) + assert result.dims == ('region',) assert np.all(result.values == 42) - # Verify all coordinates are correct - assert result.indexes['time'].equals(three_d_coords['time']) - assert result.indexes['scenario'].equals(three_d_coords['scenario']) - assert result.indexes['region'].equals(three_d_coords['region']) - - def test_numpy_scalar_three_dimensions(self, three_d_coords): - """Numpy scalars should broadcast to 3 dimensions.""" - for scalar in [np.int32(100), np.float64(3.14)]: - result = DataConverter.to_dataarray(scalar, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - assert np.all(result.values == scalar.item()) + def test_custom_multiple_dimensions(self): + """Test with multiple custom dimensions.""" + products = pd.Index(['A', 'B'], name='product') + technologies = pd.Index(['solar', 'wind', 'gas'], name='technology') - def test_1d_array_time_to_three_dimensions(self, three_d_coords): - """1D array matching time should broadcast to 3D.""" - time_arr = np.array([10, 20, 30, 40, 50]) - result = DataConverter.to_dataarray(time_arr, coords=three_d_coords) + # Array matching technology dimension + arr = np.array([100, 150, 80]) + result = DataConverter.to_dataarray(arr, coords={'product': products, 'technology': technologies}) - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') + assert result.shape == (2, 3) + assert result.dims == ('product', 'technology') - # Check broadcasting across scenarios and regions - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(slice_data.values, time_arr) + # Should broadcast across products + for product in products: + assert np.array_equal(result.sel(product=product).values, arr) - def test_1d_array_scenario_to_three_dimensions(self, three_d_coords): - """1D array matching scenario should broadcast to 3D.""" - scenario_arr = np.array([100, 200, 300]) - result = DataConverter.to_dataarray(scenario_arr, coords=three_d_coords) + def test_mixed_dimension_types(self): + """Test mixing time dimension with custom dimensions.""" + time_coords = pd.date_range('2024-01-01', periods=3, freq='D', name='time') + regions = pd.Index(['north', 'south'], name='region') - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') + # Time series should broadcast to regions + time_series = pd.Series([10, 20, 30], index=time_coords) + result = DataConverter.to_dataarray(time_series, coords={'time': time_coords, 'region': regions}) - # Check broadcasting across time and regions - for time in three_d_coords['time']: - for region in three_d_coords['region']: - slice_data = result.sel(time=time, region=region) - assert np.array_equal(slice_data.values, scenario_arr) + assert result.shape == (3, 2) + assert result.dims == ('time', 'region') - def test_1d_array_region_to_three_dimensions(self, three_d_coords): - """1D array matching region should broadcast to 3D.""" - region_arr = np.array([1000, 2000]) # Length 2 to match region - result = DataConverter.to_dataarray(region_arr, coords=three_d_coords) + def test_custom_dimensions_complex(self): + """Test complex scenario with custom dimensions.""" + coords = { + 'product': pd.Index(['A', 'B'], name='product'), + 'factory': pd.Index(['F1', 'F2', 'F3'], name='factory'), + 'quarter': pd.Index(['Q1', 'Q2', 'Q3', 'Q4'], name='quarter') + } - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') + # Array matching factory dimension + factory_arr = np.array([100, 200, 300]) + result = DataConverter.to_dataarray(factory_arr, coords=coords) - # Check broadcasting across time and scenarios - for time in three_d_coords['time']: - for scenario in three_d_coords['scenario']: - slice_data = result.sel(time=time, scenario=scenario) - assert np.array_equal(slice_data.values, region_arr) - - def test_series_time_to_three_dimensions(self, three_d_coords): - """Time-indexed Series should broadcast to 3D.""" - time_series = pd.Series([15, 25, 35, 45, 55], index=three_d_coords['time']) - result = DataConverter.to_dataarray(time_series, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(slice_data.values, time_series.values) - - def test_series_scenario_to_three_dimensions(self, three_d_coords): - """Scenario-indexed Series should broadcast to 3D.""" - scenario_series = pd.Series([500, 600, 700], index=three_d_coords['scenario']) - result = DataConverter.to_dataarray(scenario_series, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for time in three_d_coords['time']: - for region in three_d_coords['region']: - slice_data = result.sel(time=time, region=region) - assert np.array_equal(slice_data.values, scenario_series.values) - - def test_series_region_to_three_dimensions(self, three_d_coords): - """Region-indexed Series should broadcast to 3D.""" - region_series = pd.Series([5000, 6000], index=three_d_coords['region']) # Length 2 - result = DataConverter.to_dataarray(region_series, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for time in three_d_coords['time']: - for scenario in three_d_coords['scenario']: - slice_data = result.sel(time=time, scenario=scenario) - assert np.array_equal(slice_data.values, region_series.values) - - def test_dataframe_time_to_three_dimensions(self, three_d_coords): - """Time-indexed DataFrame should broadcast to 3D.""" - df = pd.DataFrame({'power': [11, 22, 33, 44, 55]}, index=three_d_coords['time']) - result = DataConverter.to_dataarray(df, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(slice_data.values, df['power'].values) - - def test_dataframe_scenario_to_three_dimensions(self, three_d_coords): - """Scenario-indexed DataFrame should broadcast to 3D.""" - df = pd.DataFrame({'cost': [1100, 1200, 1300]}, index=three_d_coords['scenario']) - result = DataConverter.to_dataarray(df, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for time in three_d_coords['time']: - for region in three_d_coords['region']: - slice_data = result.sel(time=time, region=region) - assert np.array_equal(slice_data.values, df['cost'].values) - - def test_1d_dataarray_time_to_three_dimensions(self, three_d_coords): - """1D time DataArray should broadcast to 3D.""" - original = xr.DataArray([101, 102, 103, 104, 105], - coords={'time': three_d_coords['time']}, - dims=['time']) - result = DataConverter.to_dataarray(original, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(slice_data.values, original.values) - - def test_1d_dataarray_scenario_to_three_dimensions(self, three_d_coords): - """1D scenario DataArray should broadcast to 3D.""" - original = xr.DataArray([2001, 2002, 2003], - coords={'scenario': three_d_coords['scenario']}, - dims=['scenario']) - result = DataConverter.to_dataarray(original, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for time in three_d_coords['time']: - for region in three_d_coords['region']: - slice_data = result.sel(time=time, region=region) - assert np.array_equal(slice_data.values, original.values) - - def test_2d_dataarray_to_three_dimensions(self, three_d_coords): - """2D DataArray should broadcast to 3D.""" - # Create 2D time x scenario DataArray - data_2d = np.random.rand(5, 3) - original = xr.DataArray(data_2d, - coords={'time': three_d_coords['time'], - 'scenario': three_d_coords['scenario']}, - dims=['time', 'scenario']) - - result = DataConverter.to_dataarray(original, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check that all regions have the same time x scenario data - for region in three_d_coords['region']: - slice_data = result.sel(region=region) - assert np.array_equal(slice_data.values, original.values) - - def test_timeseries_data_to_three_dimensions(self, three_d_coords): - """TimeSeriesData should broadcast to 3D.""" - data_array = xr.DataArray([99, 88, 77, 66, 55], - coords={'time': three_d_coords['time']}, - dims=['time']) - ts_data = TimeSeriesData(data_array, aggregation_group='test_3d') - - result = DataConverter.to_dataarray(ts_data, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(slice_data.values, [99, 88, 77, 66, 55]) - - def test_three_d_copy_independence(self, three_d_coords): - """3D results should be independent copies.""" - original_arr = np.array([10, 20, 30, 40, 50]) - result = DataConverter.to_dataarray(original_arr, coords=three_d_coords) - - # Modify result - result[0, 0, 0] = 999 - - # Original should be unchanged - assert original_arr[0] == 10 - - def test_three_d_special_values(self, three_d_coords): - """3D conversion should preserve special values.""" - # Array with NaN and inf - special_arr = np.array([1, np.nan, np.inf, -np.inf, 5]) - result = DataConverter.to_dataarray(special_arr, coords=three_d_coords) - - assert result.shape == (5, 3, 2) - - # Check that special values are preserved in all broadcasts - for scenario in three_d_coords['scenario']: - for region in three_d_coords['region']: - slice_data = result.sel(scenario=scenario, region=region) - assert np.array_equal(np.isnan(slice_data.values), np.isnan(special_arr)) - assert np.array_equal(np.isinf(slice_data.values), np.isinf(special_arr)) - - def test_three_d_ambiguous_length_error(self): - """Should fail when array length matches multiple dimensions in 3D.""" - # All dimensions have length 3 - coords_3x3x3 = { - 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), - 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), - 'region': pd.Index(['X', 'Y', 'Z'], name='region') - } - - arr = np.array([1, 2, 3]) # Length 3 - matches all dimensions - - with pytest.raises(ConversionError, match="matches multiple dimensions"): - DataConverter.to_dataarray(arr, coords=coords_3x3x3) - - def test_three_d_custom_dimensions(self): - """3D conversion with custom dimension names.""" - coords = { - 'product': pd.Index(['A', 'B'], name='product'), - 'factory': pd.Index(['F1', 'F2', 'F3'], name='factory'), - 'quarter': pd.Index(['Q1', 'Q2', 'Q3', 'Q4'], name='quarter') - } - - # Array matching factory dimension - factory_arr = np.array([100, 200, 300]) - result = DataConverter.to_dataarray(factory_arr, coords=coords) - - assert result.shape == (2, 3, 4) - assert result.dims == ('product', 'factory', 'quarter') + assert result.shape == (2, 3, 4) + assert result.dims == ('product', 'factory', 'quarter') # Check broadcasting for product in coords['product']: @@ -586,143 +602,6 @@ def test_three_d_custom_dimensions(self): assert np.array_equal(slice_data.values, factory_arr) -class TestMultipleDimensions: - """Test support for more than 2 dimensions.""" - - def test_scalar_many_dimensions(self): - """Scalar should broadcast to any number of dimensions.""" - coords = { - 'time': pd.date_range('2024-01-01', periods=2, freq='D', name='time'), - 'scenario': pd.Index(['A', 'B'], name='scenario'), - 'region': pd.Index(['north', 'south'], name='region'), - 'technology': pd.Index(['solar', 'wind'], name='technology') - } - - result = DataConverter.to_dataarray(42, coords=coords) - assert result.shape == (2, 2, 2, 2) - assert result.dims == ('time', 'scenario', 'region', 'technology') - assert np.all(result.values == 42) - - def test_1d_array_broadcast_to_many_dimensions(self): - """1D array should broadcast to many dimensions.""" - coords = { - 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), - 'scenario': pd.Index(['A', 'B'], name='scenario'), - 'region': pd.Index(['north', 'south'], name='region') - } - - # Array matching time dimension - time_arr = np.array([10, 20, 30]) - result = DataConverter.to_dataarray(time_arr, coords=coords) - - assert result.shape == (3, 2, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check broadcasting - all scenarios and regions should have same time values - for scenario in coords['scenario']: - for region in coords['region']: - assert np.array_equal( - result.sel(scenario=scenario, region=region).values, - time_arr - ) - - def test_series_broadcast_to_many_dimensions(self): - """Series should broadcast to many dimensions.""" - time_coords = pd.date_range('2024-01-01', periods=3, freq='D', name='time') - coords = { - 'time': time_coords, - 'scenario': pd.Index(['A', 'B'], name='scenario'), - 'region': pd.Index(['north', 'south'], name='region'), - 'product': pd.Index(['X', 'Y', 'Z'], name='product') - } - - # Time-indexed series - time_series = pd.Series([100, 200, 300], index=time_coords) - result = DataConverter.to_dataarray(time_series, coords=coords) - - assert result.shape == (3, 2, 2, 3) - assert result.dims == ('time', 'scenario', 'region', 'product') - - # Check that all non-time dimensions have the same time series values - for scenario in coords['scenario']: - for region in coords['region']: - for product in coords['product']: - assert np.array_equal( - result.sel(scenario=scenario, region=region, product=product).values, - time_series.values - ) - - def test_dataarray_broadcast_to_more_dimensions(self): - """DataArray should broadcast to additional dimensions.""" - time_coords = pd.date_range('2024-01-01', periods=2, freq='D', name='time') - scenario_coords = pd.Index(['A', 'B'], name='scenario') - - # Start with 2D DataArray - original = xr.DataArray( - [[10, 20], [30, 40]], - coords={'time': time_coords, 'scenario': scenario_coords}, - dims=['time', 'scenario'] - ) - - # Broadcast to 3D - coords = { - 'time': time_coords, - 'scenario': scenario_coords, - 'region': pd.Index(['north', 'south'], name='region') - } - - result = DataConverter.to_dataarray(original, coords=coords) - - assert result.shape == (2, 2, 2) - assert result.dims == ('time', 'scenario', 'region') - - # Check that all regions have the same time+scenario values - for region in coords['region']: - assert np.array_equal( - result.sel(region=region).values, - original.values - ) - - -class TestCustomDimensions: - """Test with custom dimension names beyond time/scenario.""" - - def test_custom_single_dimension(self, region_coords): - """Test with custom dimension name.""" - result = DataConverter.to_dataarray(42, coords={'region': region_coords}) - assert result.shape == (3,) - assert result.dims == ('region',) - assert np.all(result.values == 42) - - def test_custom_multiple_dimensions(self): - """Test with multiple custom dimensions.""" - products = pd.Index(['A', 'B'], name='product') - technologies = pd.Index(['solar', 'wind', 'gas'], name='technology') - - # Array matching technology dimension - arr = np.array([100, 150, 80]) - result = DataConverter.to_dataarray(arr, coords={'product': products, 'technology': technologies}) - - assert result.shape == (2, 3) - assert result.dims == ('product', 'technology') - - # Should broadcast across products - for product in products: - assert np.array_equal(result.sel(product=product).values, arr) - - def test_mixed_dimension_types(self): - """Test mixing time dimension with custom dimensions.""" - time_coords = pd.date_range('2024-01-01', periods=3, freq='D', name='time') - regions = pd.Index(['north', 'south'], name='region') - - # Time series should broadcast to regions - time_series = pd.Series([10, 20, 30], index=time_coords) - result = DataConverter.to_dataarray(time_series, coords={'time': time_coords, 'region': regions}) - - assert result.shape == (3, 2) - assert result.dims == ('time', 'region') - - class TestValidation: """Test coordinate validation.""" @@ -782,6 +661,30 @@ def test_dimension_mismatch_messages(self, time_coords, scenario_coords): with pytest.raises(ConversionError, match="matches none of the target dimensions"): DataConverter.to_dataarray(wrong_arr, coords={'time': time_coords, 'scenario': scenario_coords}) + def test_multidimensional_array_dimension_count_mismatch(self, standard_coords): + """Array with wrong number of dimensions should fail with clear error.""" + # 4D array with 3D coordinates + data_4d = np.random.rand(5, 3, 2, 4) + with pytest.raises(ConversionError, match="matches multiple dimension orders|Array dimensions do not match"): + DataConverter.to_dataarray(data_4d, coords=standard_coords) + + def test_error_message_quality(self, standard_coords): + """Error messages should include helpful information.""" + # Wrong shape array + data_2d = np.random.rand(7, 8) + coords_2d = { + 'time': standard_coords['time'], # length 5 + 'scenario': standard_coords['scenario'] # length 3 + } + + try: + DataConverter.to_dataarray(data_2d, coords=coords_2d) + assert False, "Should have raised ConversionError" + except ConversionError as e: + error_msg = str(e) + assert "Array shape: (7, 8)" in error_msg + assert "Coordinate lengths:" in error_msg + class TestDataIntegrity: """Test data copying and integrity.""" @@ -819,6 +722,20 @@ def test_dataframe_copy_independence(self, time_coords): # Original should be unchanged assert original_df.loc[time_coords[0], 'value'] == 10 + def test_multid_array_copy_independence(self, standard_coords): + """Multi-D arrays should be independent copies.""" + original_data = np.random.rand(5, 3) + result = DataConverter.to_dataarray(original_data, coords={ + 'time': standard_coords['time'], + 'scenario': standard_coords['scenario'] + }) + + # Modify result + result[0, 0] = 999 + + # Original should be unchanged + assert original_data[0, 0] != 999 + class TestSpecialValues: """Test handling of special numeric values.""" @@ -854,331 +771,123 @@ def test_mixed_numeric_types(self, time_coords): assert np.issubdtype(result.dtype, np.floating) assert np.array_equal(result.values, mixed_arr) + def test_special_values_in_multid_arrays(self, standard_coords): + """Special values should be preserved in multi-D arrays and broadcasting.""" + # Array with NaN and inf + special_arr = np.array([1, np.nan, np.inf, -np.inf, 5]) + result = DataConverter.to_dataarray(special_arr, coords=standard_coords) -class TestMultiDimensionalArrayConversion: - """Test multi-dimensional numpy array conversions.""" - - @pytest.fixture - def standard_coords(self): - """Standard coordinates with unique lengths for easy testing.""" - return { - 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 - 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 - 'region': pd.Index(['north', 'south'], name='region') # length 2 - } - - def test_2d_array_unique_dimensions(self, standard_coords): - """2D array with unique dimension lengths should work.""" - # 5x3 array should map to time x scenario - data_2d = np.random.rand(5, 3) - result = DataConverter.to_dataarray(data_2d, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) + assert result.shape == (5, 3, 2) - assert result.shape == (5, 3) - assert result.dims == ('time', 'scenario') - assert np.array_equal(result.values, data_2d) + # Check that special values are preserved in all broadcasts + for scenario in standard_coords['scenario']: + for region in standard_coords['region']: + slice_data = result.sel(scenario=scenario, region=region) + assert np.array_equal(np.isnan(slice_data.values), np.isnan(special_arr)) + assert np.array_equal(np.isinf(slice_data.values), np.isinf(special_arr)) - # 3x5 array should map to scenario x time - data_2d_flipped = np.random.rand(3, 5) - result_flipped = DataConverter.to_dataarray(data_2d_flipped, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - assert result_flipped.shape == (3, 5) - assert result_flipped.dims == ('scenario', 'time') - assert np.array_equal(result_flipped.values, data_2d_flipped) +class TestAdvancedBroadcasting: + """Test advanced broadcasting scenarios and edge cases.""" - def test_3d_array_unique_dimensions(self, standard_coords): - """3D array with unique dimension lengths should work.""" - # 5x3x2 array should map to time x scenario x region - data_3d = np.random.rand(5, 3, 2) - result = DataConverter.to_dataarray(data_3d, coords=standard_coords) + def test_partial_dimension_matching_with_broadcasting(self, standard_coords): + """Test that partial dimension matching works with the improved integration.""" + # 1D array matching one dimension should broadcast to all target dimensions + time_arr = np.array([10, 20, 30, 40, 50]) # matches time (length 5) + result = DataConverter.to_dataarray(time_arr, coords=standard_coords) assert result.shape == (5, 3, 2) assert result.dims == ('time', 'scenario', 'region') - assert np.array_equal(result.values, data_3d) - - def test_3d_array_different_permutation(self, standard_coords): - """3D array with different dimension order should work.""" - # 2x5x3 array should map to region x time x scenario - data_3d = np.random.rand(2, 5, 3) - result = DataConverter.to_dataarray(data_3d, coords=standard_coords) - - assert result.shape == (2, 5, 3) - assert result.dims == ('region', 'time', 'scenario') - assert np.array_equal(result.values, data_3d) - - def test_4d_array_unique_dimensions(self): - """4D array with unique dimension lengths should work.""" - coords = { - 'time': pd.date_range('2024-01-01', periods=2, freq='D', name='time'), # length 2 - 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 - 'region': pd.Index(['north', 'south', 'east', 'west'], name='region'), # length 4 - 'technology': pd.Index(['solar', 'wind', 'gas', 'coal', 'hydro'], name='technology') # length 5 - } - - # 3x5x2x4 array should map to scenario x technology x time x region - data_4d = np.random.rand(3, 5, 2, 4) - result = DataConverter.to_dataarray(data_4d, coords=coords) - - assert result.shape == (3, 5, 2, 4) - assert result.dims == ('scenario', 'technology', 'time', 'region') - assert np.array_equal(result.values, data_4d) - - def test_2d_array_ambiguous_dimensions_error(self): - """2D array with ambiguous dimension lengths should fail.""" - # Both dimensions have length 3 - coords_ambiguous = { - 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 - 'region': pd.Index(['north', 'south', 'east'], name='region') # length 3 - } - - data_2d = np.random.rand(3, 3) - with pytest.raises(ConversionError, match="matches multiple dimension orders"): - DataConverter.to_dataarray(data_2d, coords=coords_ambiguous) - - def test_3d_array_ambiguous_dimensions_error(self): - """3D array with ambiguous dimension lengths should fail.""" - # All dimensions have length 2 - coords_ambiguous = { - 'scenario': pd.Index(['A', 'B'], name='scenario'), # length 2 - 'region': pd.Index(['north', 'south'], name='region'), # length 2 - 'technology': pd.Index(['solar', 'wind'], name='technology') # length 2 - } - - data_3d = np.random.rand(2, 2, 2) - with pytest.raises(ConversionError, match="matches multiple dimension orders"): - DataConverter.to_dataarray(data_3d, coords=coords_ambiguous) - - def test_array_dimension_count_mismatch_error(self, standard_coords): - """Array with wrong number of dimensions should fail.""" - # 2D array with 3D coordinates - data_2d = np.random.rand(5, 3) - with pytest.raises(ConversionError, match="Array has 2 dimensions but 3 target dimensions provided"): - DataConverter.to_dataarray(data_2d, coords=standard_coords) - - # 4D array with 3D coordinates - data_4d = np.random.rand(5, 3, 2, 4) - with pytest.raises(ConversionError, match="Array has 4 dimensions but 3 target dimensions provided"): - DataConverter.to_dataarray(data_4d, coords=standard_coords) - - def test_array_no_matching_dimensions_error(self, standard_coords): - """Array with no matching dimension lengths should fail.""" - # 7x8 array - no dimension has length 7 or 8 - data_2d = np.random.rand(7, 8) - coords_2d = { - 'time': standard_coords['time'], # length 5 - 'scenario': standard_coords['scenario'] # length 3 - } - - with pytest.raises(ConversionError, match="Array dimensions do not match any coordinate lengths"): - DataConverter.to_dataarray(data_2d, coords=coords_2d) - - def test_2d_array_custom_dimensions(self): - """2D array with custom dimension names should work.""" - coords = { - 'product': pd.Index(['A', 'B', 'C', 'D'], name='product'), # length 4 - 'factory': pd.Index(['F1', 'F2', 'F3'], name='factory') # length 3 - } - - # 4x3 array should map to product x factory - data_2d = np.array([[10, 11, 12], - [20, 21, 22], - [30, 31, 32], - [40, 41, 42]]) - - result = DataConverter.to_dataarray(data_2d, coords=coords) - - assert result.shape == (4, 3) - assert result.dims == ('product', 'factory') - assert np.array_equal(result.values, data_2d) - - # Verify coordinates are correct - assert result.indexes['product'].equals(coords['product']) - assert result.indexes['factory'].equals(coords['factory']) - - def test_multid_array_copy_independence(self, standard_coords): - """Multi-D arrays should be independent copies.""" - original_data = np.random.rand(5, 3) - result = DataConverter.to_dataarray(original_data, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - - # Modify result - result[0, 0] = 999 - - # Original should be unchanged - assert original_data[0, 0] != 999 - - def test_multid_array_special_values(self, standard_coords): - """Multi-D arrays should preserve special values.""" - # Create 2D array with special values - data_2d = np.array([[1.0, np.nan, 3.0], - [np.inf, 5.0, -np.inf], - [7.0, 8.0, 9.0], - [10.0, np.nan, 12.0], - [13.0, 14.0, np.inf]]) - - result = DataConverter.to_dataarray(data_2d, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - - assert result.shape == (5, 3) - assert np.array_equal(np.isnan(result.values), np.isnan(data_2d)) - assert np.array_equal(np.isinf(result.values), np.isinf(data_2d)) - - def test_multid_array_with_time_dimension(self): - """Multi-D arrays should work with time dimension.""" - time_coords = pd.date_range('2024-01-01', periods=4, freq='H', name='time') - scenario_coords = pd.Index(['base', 'high', 'low'], name='scenario') - - # 4x3 time series data - data_2d = np.array([[100, 110, 120], - [200, 210, 220], - [300, 310, 320], - [400, 410, 420]]) - - result = DataConverter.to_dataarray(data_2d, coords={ - 'time': time_coords, - 'scenario': scenario_coords - }) - - assert result.shape == (4, 3) - assert result.dims == ('time', 'scenario') - assert isinstance(result.indexes['time'], pd.DatetimeIndex) - assert np.array_equal(result.values, data_2d) - - def test_multid_array_dtype_preservation(self, standard_coords): - """Multi-D arrays should preserve data types.""" - # Integer array - int_data = np.array([[1, 2, 3], - [4, 5, 6], - [7, 8, 9], - [10, 11, 12], - [13, 14, 15]], dtype=np.int32) - - result_int = DataConverter.to_dataarray(int_data, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - - assert result_int.dtype == np.int32 - assert np.array_equal(result_int.values, int_data) - - # Float array - float_data = np.array([[1.1, 2.2, 3.3], - [4.4, 5.5, 6.6], - [7.7, 8.8, 9.9], - [10.1, 11.1, 12.1], - [13.1, 14.1, 15.1]], dtype=np.float64) - - result_float = DataConverter.to_dataarray(float_data, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - - assert result_float.dtype == np.float64 - assert np.array_equal(result_float.values, float_data) - # Boolean array - bool_data = np.array([[True, False, True], - [False, True, False], - [True, True, False], - [False, False, True], - [True, False, True]]) - - result_bool = DataConverter.to_dataarray(bool_data, coords={ - 'time': standard_coords['time'], - 'scenario': standard_coords['scenario'] - }) - - assert result_bool.dtype == bool - assert np.array_equal(result_bool.values, bool_data) - - def test_multid_array_no_coords(self): - """Multi-D arrays without coords should fail unless scalar.""" - # Multi-element fails - data_2d = np.random.rand(2, 3) - with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): - DataConverter.to_dataarray(data_2d) - - # Single element succeeds - single_element = np.array([[42]]) - result = DataConverter.to_dataarray(single_element) - assert result.shape == () - assert result.item() == 42 + # Verify broadcasting + for scenario in standard_coords['scenario']: + for region in standard_coords['region']: + assert np.array_equal(result.sel(scenario=scenario, region=region).values, time_arr) - def test_multid_array_empty_coords(self, standard_coords): - """Multi-D arrays with empty coords should fail.""" - data_2d = np.random.rand(5, 3) - with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): - DataConverter.to_dataarray(data_2d, coords={}) - - def test_multid_array_coordinate_validation(self): - """Multi-D arrays should validate coordinates properly.""" - # Test with time coordinate that's not DatetimeIndex - wrong_time = pd.Index([1, 2, 3, 4, 5], name='time') - scenario_coords = pd.Index(['A', 'B', 'C'], name='scenario') - - data_2d = np.random.rand(5, 3) - with pytest.raises(ConversionError, match="time coordinates must be a DatetimeIndex"): - DataConverter.to_dataarray(data_2d, coords={ - 'time': wrong_time, - 'scenario': scenario_coords - }) - - def test_multid_array_complex_scenario(self): - """Complex real-world scenario with multi-D array.""" - # Energy system data: time x technology x region + def test_complex_multid_scenario(self): + """Complex real-world scenario with multi-D array and broadcasting.""" + # Energy system data: time x technology, broadcast to regions coords = { - 'time': pd.date_range('2024-01-01', periods=8760, freq='H', name='time'), # 1 year hourly + 'time': pd.date_range('2024-01-01', periods=24, freq='H', name='time'), # 24 hours 'technology': pd.Index(['solar', 'wind', 'gas', 'coal'], name='technology'), # 4 technologies 'region': pd.Index(['north', 'south', 'east'], name='region') # 3 regions } - # Capacity factors: 8760 x 4 x 3 - capacity_factors = np.random.rand(8760, 4, 3) + # Capacity factors: 24 x 4 (will broadcast to 24 x 4 x 3) + capacity_factors = np.random.rand(24, 4) result = DataConverter.to_dataarray(capacity_factors, coords=coords) - assert result.shape == (8760, 4, 3) + assert result.shape == (24, 4, 3) assert result.dims == ('time', 'technology', 'region') assert isinstance(result.indexes['time'], pd.DatetimeIndex) - assert len(result.indexes['time']) == 8760 - assert len(result.indexes['technology']) == 4 - assert len(result.indexes['region']) == 3 - - def test_multid_array_edge_cases(self): - """Test edge cases for multi-D arrays.""" - # Single dimension with multi-D array should fail - coords_1d = {'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time')} - data_2d = np.random.rand(5, 3) - with pytest.raises(ConversionError, match="Array has 2 dimensions but 1 target dimensions provided"): - DataConverter.to_dataarray(data_2d, coords=coords_1d) + # Verify broadcasting: all regions should have same time×technology data + for region in coords['region']: + assert np.array_equal(result.sel(region=region).values, capacity_factors) - # Zero dimensions with multi-D array should fail - data_1d = np.array([1, 2, 3]) - with pytest.raises(ConversionError, match="Cannot convert multi-element array without target dimensions"): - DataConverter.to_dataarray(data_1d, coords={}) + def test_ambiguous_length_handling(self): + """Test handling of ambiguous length scenarios across different data types.""" + # All dimensions have length 3 + coords_3x3x3 = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), + 'region': pd.Index(['X', 'Y', 'Z'], name='region') + } + + # 1D array - should fail + arr_1d = np.array([1, 2, 3]) + with pytest.raises(ConversionError, match="matches multiple dimensions"): + DataConverter.to_dataarray(arr_1d, coords=coords_3x3x3) + + # 2D array - should fail + arr_2d = np.random.rand(3, 3) + with pytest.raises(ConversionError, match="matches multiple dimension orders"): + DataConverter.to_dataarray(arr_2d, coords=coords_3x3x3) - def test_multid_array_partial_dimension_match(self): - """Array where only some dimensions match should fail.""" + # 3D array - should fail + arr_3d = np.random.rand(3, 3, 3) + with pytest.raises(ConversionError, match="matches multiple dimension orders"): + DataConverter.to_dataarray(arr_3d, coords=coords_3x3x3) + + def test_mixed_broadcasting_scenarios(self): + """Test various broadcasting scenarios with different input types.""" coords = { - 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 - 'scenario': pd.Index(['A', 'B', 'C'], name='scenario') # length 3 + 'time': pd.date_range('2024-01-01', periods=4, freq='D', name='time'), # length 4 + 'scenario': pd.Index(['A', 'B'], name='scenario'), # length 2 + 'region': pd.Index(['north', 'south', 'east'], name='region'), # length 3 + 'product': pd.Index(['X', 'Y', 'Z', 'W', 'V'], name='product') # length 5 } - # 5x7 array - first dimension matches time (5) but second doesn't match scenario (3) - data_2d = np.random.rand(5, 7) - with pytest.raises(ConversionError, match="Array dimensions do not match any coordinate lengths"): - DataConverter.to_dataarray(data_2d, coords=coords) + # Scalar to 4D + scalar_result = DataConverter.to_dataarray(42, coords=coords) + assert scalar_result.shape == (4, 2, 3, 5) + assert np.all(scalar_result.values == 42) + # 1D array (length 4, matches time) to 4D + arr_1d = np.array([10, 20, 30, 40]) + arr_result = DataConverter.to_dataarray(arr_1d, coords=coords) + assert arr_result.shape == (4, 2, 3, 5) + # Verify broadcasting + for scenario in coords['scenario']: + for region in coords['region']: + for product in coords['product']: + assert np.array_equal( + arr_result.sel(scenario=scenario, region=region, product=product).values, + arr_1d + ) + + # 2D array (4x2, matches time×scenario) to 4D + arr_2d = np.random.rand(4, 2) + arr_2d_result = DataConverter.to_dataarray(arr_2d, coords=coords) + assert arr_2d_result.shape == (4, 2, 3, 5) + # Verify broadcasting + for region in coords['region']: + for product in coords['product']: + assert np.array_equal( + arr_2d_result.sel(region=region, product=product).values, + arr_2d + ) if __name__ == '__main__': From ffc196a4f70efbe3a7165bcf7ac98777c43bb25d Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Jul 2025 08:36:49 +0200 Subject: [PATCH 169/183] Rename methods and remove unused code --- flixopt/core.py | 192 ++++++------------------------------------------ 1 file changed, 22 insertions(+), 170 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 97e91c274..d4f83f5a1 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -141,11 +141,11 @@ class DataConverter: """ @staticmethod - def _convert_1d_data_to_dataarray( + def _convert_1d_with_index_matching( data: Union[np.ndarray, pd.Series], coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert 1D data (array or Series) to DataArray by matching to one dimension. + Convert 1D data to DataArray, trying index matching for Series first, then length matching. Args: data: 1D numpy array or pandas Series @@ -161,16 +161,16 @@ def _convert_1d_data_to_dataarray( raise ConversionError('Cannot convert multi-element data without target dimensions') return xr.DataArray(data[0] if isinstance(data, np.ndarray) else data.iloc[0]) - # For Series, try to match index to coordinates + # For Series, try to match index to coordinates first if isinstance(data, pd.Series): for dim_name in target_dims: if data.index.equals(coords[dim_name]): return xr.DataArray(data.values.copy(), coords={dim_name: coords[dim_name]}, dims=[dim_name]) - # If no index matches, raise error - raise ConversionError(f'Data {data} does not match any of the target dimensions: {target_dims}') + # If no index matches, raise error for Series (they should match by index) + raise ConversionError(f'Series index does not match any target dimension coordinates: {target_dims}') - # For arrays or unmatched Series, match by length + # For arrays, match by length matching_dims = [] for dim_name in target_dims: if len(data) == len(coords[dim_name]): @@ -178,10 +178,10 @@ def _convert_1d_data_to_dataarray( if len(matching_dims) == 0: dim_info = {dim: len(coords[dim]) for dim in target_dims} - raise ConversionError(f'Data length {len(data)} matches none of the target dimensions: {dim_info}') + raise ConversionError(f'Array length {len(data)} matches none of the target dimensions: {dim_info}') elif len(matching_dims) > 1: raise ConversionError( - f'Data length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine which dimension to use.' + f'Array length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine which dimension to use.' ) # Match to the single matching dimension @@ -228,7 +228,7 @@ def _broadcast_to_target_dims( # Handle broadcasting from fewer to more dimensions if len(data.dims) < len(target_dims): - return DataConverter._broadcast_dataarray_to_more_dims(data, coords, target_dims) + return DataConverter._expand_to_more_dims(data, coords, target_dims) # Cannot handle more dimensions than target if len(data.dims) > len(target_dims): @@ -237,10 +237,10 @@ def _broadcast_to_target_dims( raise ConversionError(f'Cannot convert DataArray with dims {data.dims} to target dims {target_dims}') @staticmethod - def _broadcast_dataarray_to_more_dims( + def _expand_to_more_dims( data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: - """Broadcast DataArray to additional dimensions.""" + """Expand DataArray to additional dimensions by broadcasting.""" # Validate that all source dimensions exist in target for dim in data.dims: if dim not in target_dims: @@ -280,11 +280,11 @@ def _broadcast_dataarray_to_more_dims( return xr.DataArray(result_data.copy(), coords=final_coords, dims=target_dims) @staticmethod - def _convert_multid_array_to_dataarray( + def _convert_multid_array_by_shape( data: np.ndarray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert multi-dimensional numpy array to DataArray by matching dimensions by length. + Convert multi-dimensional numpy array to DataArray by matching dimensions by shape. Returns a DataArray that may need further broadcasting to target dimensions. Args: @@ -293,7 +293,7 @@ def _convert_multid_array_to_dataarray( target_dims: Target dimension names Returns: - DataArray with dimensions matched by length (may be subset of target_dims) + DataArray with dimensions matched by shape (may be subset of target_dims) Raises: ConversionError: If array dimensions cannot be uniquely matched to coordinates @@ -358,7 +358,7 @@ def to_dataarray( if coords is None: coords = {} - validated_coords, target_dims = DataConverter._prepare_dimensions(coords) + validated_coords, target_dims = DataConverter._validate_and_prepare_coords(coords) # Step 1: Convert to DataArray (may have fewer dimensions than target) if isinstance(data, (int, float, np.integer, np.floating)): @@ -367,17 +367,17 @@ def to_dataarray( elif isinstance(data, np.ndarray): if data.ndim == 1: - intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) + intermediate = DataConverter._convert_1d_with_index_matching(data, validated_coords, target_dims) else: # Handle multi-dimensional arrays - this now allows partial matching - intermediate = DataConverter._convert_multid_array_to_dataarray(data, validated_coords, target_dims) + intermediate = DataConverter._convert_multid_array_by_shape(data, validated_coords, target_dims) elif isinstance(data, pd.Series): if isinstance(data.index, pd.MultiIndex): raise ConversionError( 'Series index must be a single level Index. Multi-index Series are not supported.' ) - intermediate = DataConverter._convert_1d_data_to_dataarray(data, validated_coords, target_dims) + intermediate = DataConverter._convert_1d_with_index_matching(data, validated_coords, target_dims) elif isinstance(data, pd.DataFrame): if isinstance(data.index, pd.MultiIndex): @@ -388,13 +388,13 @@ def to_dataarray( raise ConversionError('DataFrame must have at least one column.') if len(data.columns) == 1: - intermediate = DataConverter._convert_1d_data_to_dataarray( + intermediate = DataConverter._convert_1d_with_index_matching( data.iloc[:, 0], validated_coords, target_dims ) else: # Handle multi-column DataFrames - this now allows partial matching logger.warning('Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') - intermediate = DataConverter._convert_multid_array_to_dataarray( + intermediate = DataConverter._convert_multid_array_by_shape( data.to_numpy(), validated_coords, target_dims ) @@ -411,9 +411,9 @@ def to_dataarray( return DataConverter._broadcast_to_target_dims(intermediate, validated_coords, target_dims) @staticmethod - def _prepare_dimensions(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: + def _validate_and_prepare_coords(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: """ - Prepare and validate coordinates for the DataArray. + Validate and prepare coordinates for the DataArray. Args: coords: Dictionary mapping dimension names to coordinate indices @@ -442,154 +442,6 @@ def _prepare_dimensions(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index return validated_coords, tuple(dims) - @staticmethod - def _convert_scalar( - data: Union[int, float, np.integer, np.floating], coords: Dict[str, pd.Index], dims: Tuple[str, ...] - ) -> xr.DataArray: - """Convert scalar to DataArray, broadcasting to all dimensions.""" - if isinstance(data, (np.integer, np.floating)): - data = data.item() - return xr.DataArray(data, coords=coords, dims=dims) - - @staticmethod - def _convert_1d_array(data: np.ndarray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """Convert 1D array to DataArray.""" - if len(dims) == 0: - if len(data) != 1: - raise ConversionError('Cannot convert multi-element array without dimensions') - return xr.DataArray(data[0]) - - elif len(dims) == 1: - dim_name = dims[0] - if len(data) != len(coords[dim_name]): - raise ConversionError( - f'Array length {len(data)} does not match {dim_name} length {len(coords[dim_name])}' - ) - return xr.DataArray(data.copy(), coords=coords, dims=dims) - - elif len(dims) == 2: - # Broadcast 1D array to 2D based on which dimension it matches - dim_lengths = {dim: len(coords[dim]) for dim in dims} - - # Find which dimension the array length matches - matching_dims = [dim for dim, length in dim_lengths.items() if len(data) == length] - - if len(matching_dims) == 0: - raise ConversionError(f'Array length {len(data)} matches none of the dimensions: {dim_lengths}') - elif len(matching_dims) > 1: - raise ConversionError( - f'Array length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine broadcasting direction.' - ) - - # Broadcast along the matching dimension - match_dim = matching_dims[0] - other_dim = [d for d in dims if d != match_dim][0] - - if dims.index(match_dim) == 0: # First dimension - values = np.repeat(data[:, np.newaxis], len(coords[other_dim]), axis=1) - else: # Second dimension - values = np.repeat(data[np.newaxis, :], len(coords[other_dim]), axis=0) - - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - else: - raise ConversionError(f'Maximum 2 dimensions currently supported, got {len(dims)}') - - @staticmethod - def _convert_series(data: pd.Series, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """Convert pandas Series to DataArray.""" - if len(dims) == 0: - if len(data) != 1: - raise ConversionError('Cannot convert multi-element Series without dimensions') - return xr.DataArray(data.iloc[0]) - - elif len(dims) == 1: - dim_name = dims[0] - if not data.index.equals(coords[dim_name]): - raise ConversionError(f'Series index does not match {dim_name} coordinates') - return xr.DataArray(data.values.copy(), coords=coords, dims=dims) - - elif len(dims) == 2: - # Check which dimension the Series index matches - if 'time' in coords and data.index.equals(coords['time']): - # Broadcast across other dimensions - other_dims = [d for d in dims if d != 'time'] - if len(other_dims) == 1: - other_dim = other_dims[0] - values = np.repeat(data.values[:, np.newaxis], len(coords[other_dim]), axis=1) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - elif len([d for d in dims if d != 'time']) == 1: - # Check if Series matches the non-time dimension - other_dim = [d for d in dims if d != 'time'][0] - if data.index.equals(coords[other_dim]): - # Broadcast across time - values = np.repeat(data.values[np.newaxis, :], len(coords['time']), axis=0) - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - raise ConversionError(f'Series index must match one of the target dimensions: {list(coords.keys())}') - - else: - raise ConversionError('Maximum 2 dimensions supported') - - @staticmethod - def _handle_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """Handle existing DataArray - check compatibility or broadcast.""" - # If no target dimensions, data must be scalar - if len(dims) == 0: - if data.size != 1: - raise ConversionError('DataArray must be scalar when no dimensions specified') - return xr.DataArray(data.values.item()) - - # Check if already compatible - if data.dims == dims: - # Check if coordinates match - compatible = True - for dim in dims: - if not np.array_equal(data.coords[dim].values, coords[dim].values): - compatible = False - break - if compatible: - return data.copy() - - # Handle broadcasting from smaller to larger dimensions - if len(data.dims) < len(dims): - return DataConverter._broadcast_dataarray(data, coords, dims) - - # If dimensions don't match and can't broadcast, raise error - raise ConversionError(f'Cannot convert DataArray with dims {data.dims} to target dims {dims}') - - @staticmethod - def _broadcast_dataarray(data: xr.DataArray, coords: Dict[str, pd.Index], dims: Tuple[str, ...]) -> xr.DataArray: - """Broadcast DataArray to target dimensions.""" - if len(data.dims) == 0: - # Scalar DataArray - broadcast to all dimensions - return xr.DataArray(data.values.item(), coords=coords, dims=dims) - - elif len(data.dims) == 1 and len(dims) == 2: - source_dim = data.dims[0] - - # Check if source dimension exists in target - if source_dim not in coords: - raise ConversionError(f'Source dimension "{source_dim}" not found in target coordinates') - - # Check coordinate compatibility - if not np.array_equal(data.coords[source_dim].values, coords[source_dim].values): - raise ConversionError(f'Source {source_dim} coordinates do not match target coordinates') - - # Find the other dimension to broadcast to - other_dim = [d for d in dims if d != source_dim][0] - - # Broadcast based on dimension order - if dims.index(source_dim) == 0: # Source is first dimension - values = np.repeat(data.values[:, np.newaxis], len(coords[other_dim]), axis=1) - else: # Source is second dimension - values = np.repeat(data.values[np.newaxis, :], len(coords[other_dim]), axis=0) - - return xr.DataArray(values.copy(), coords=coords, dims=dims) - - raise ConversionError(f'Cannot broadcast from {data.dims} to {dims}') - def get_dataarray_stats(arr: xr.DataArray) -> Dict: """Generate statistical summary of a DataArray.""" From 6142f44ca44df5aec97f87533db68d97a539d246 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Jul 2025 08:47:32 +0200 Subject: [PATCH 170/183] Improve DataConverter by better splitting handling per datatype. Series only matches index (for one dim). Numpy matches shape --- flixopt/core.py | 80 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index d4f83f5a1..d1e61b4c5 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -141,36 +141,60 @@ class DataConverter: """ @staticmethod - def _convert_1d_with_index_matching( - data: Union[np.ndarray, pd.Series], coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + def _convert_series_by_index( + data: pd.Series, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert 1D data to DataArray, trying index matching for Series first, then length matching. + Convert pandas Series to DataArray by matching index to coordinates. Args: - data: 1D numpy array or pandas Series + data: pandas Series coords: Available coordinates target_dims: Target dimension names Returns: - DataArray with the data matched to appropriate dimension + DataArray with the Series matched to appropriate dimension by index + + Raises: + ConversionError: If Series index doesn't match any target dimension coordinates """ if len(target_dims) == 0: - # No target dimensions - data must be single element if len(data) != 1: - raise ConversionError('Cannot convert multi-element data without target dimensions') - return xr.DataArray(data[0] if isinstance(data, np.ndarray) else data.iloc[0]) + raise ConversionError('Cannot convert multi-element Series without target dimensions') + return xr.DataArray(data.iloc[0]) + + # Try to match Series index to coordinates + for dim_name in target_dims: + if data.index.equals(coords[dim_name]): + return xr.DataArray(data.values.copy(), coords={dim_name: coords[dim_name]}, dims=[dim_name]) + + # If no index matches, raise error + raise ConversionError(f'Series index does not match any target dimension coordinates: {target_dims}') + + @staticmethod + def _convert_1d_array_by_length( + data: np.ndarray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Convert 1D numpy array to DataArray by matching length to coordinates. + + Args: + data: 1D numpy array + coords: Available coordinates + target_dims: Target dimension names - # For Series, try to match index to coordinates first - if isinstance(data, pd.Series): - for dim_name in target_dims: - if data.index.equals(coords[dim_name]): - return xr.DataArray(data.values.copy(), coords={dim_name: coords[dim_name]}, dims=[dim_name]) + Returns: + DataArray with the array matched to appropriate dimension by length - # If no index matches, raise error for Series (they should match by index) - raise ConversionError(f'Series index does not match any target dimension coordinates: {target_dims}') + Raises: + ConversionError: If array length doesn't uniquely match a target dimension + """ + if len(target_dims) == 0: + if len(data) != 1: + raise ConversionError('Cannot convert multi-element array without target dimensions') + return xr.DataArray(data[0]) - # For arrays, match by length + # Match by length matching_dims = [] for dim_name in target_dims: if len(data) == len(coords[dim_name]): @@ -181,13 +205,14 @@ def _convert_1d_with_index_matching( raise ConversionError(f'Array length {len(data)} matches none of the target dimensions: {dim_info}') elif len(matching_dims) > 1: raise ConversionError( - f'Array length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine which dimension to use.' + f'Array length {len(data)} matches multiple dimensions: {matching_dims}. Cannot determine which ' + f'dimension to use. To avoid this error, convert the array to a DataArray with the correct dimensions ' + f'yourself.' ) # Match to the single matching dimension match_dim = matching_dims[0] - values = data.values.copy() if isinstance(data, pd.Series) else data.copy() - return xr.DataArray(values, coords={match_dim: coords[match_dim]}, dims=[match_dim]) + return xr.DataArray(data.copy(), coords={match_dim: coords[match_dim]}, dims=[match_dim]) @staticmethod def _broadcast_to_target_dims( @@ -334,8 +359,9 @@ def _convert_multid_array_by_shape( # Return DataArray with matched dimensions - broadcasting will happen later if needed return xr.DataArray(data.copy(), coords=matched_coords, dims=matched_dims) - @staticmethod + @classmethod def to_dataarray( + cls, data: Union[Scalar, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, TimeSeriesData], coords: Optional[Dict[str, pd.Index]] = None, ) -> xr.DataArray: @@ -358,7 +384,7 @@ def to_dataarray( if coords is None: coords = {} - validated_coords, target_dims = DataConverter._validate_and_prepare_coords(coords) + validated_coords, target_dims = cls._validate_and_prepare_coords(coords) # Step 1: Convert to DataArray (may have fewer dimensions than target) if isinstance(data, (int, float, np.integer, np.floating)): @@ -367,17 +393,17 @@ def to_dataarray( elif isinstance(data, np.ndarray): if data.ndim == 1: - intermediate = DataConverter._convert_1d_with_index_matching(data, validated_coords, target_dims) + intermediate = cls._convert_1d_array_by_length(data, validated_coords, target_dims) else: # Handle multi-dimensional arrays - this now allows partial matching - intermediate = DataConverter._convert_multid_array_by_shape(data, validated_coords, target_dims) + intermediate = cls._convert_multid_array_by_shape(data, validated_coords, target_dims) elif isinstance(data, pd.Series): if isinstance(data.index, pd.MultiIndex): raise ConversionError( 'Series index must be a single level Index. Multi-index Series are not supported.' ) - intermediate = DataConverter._convert_1d_with_index_matching(data, validated_coords, target_dims) + intermediate = cls._convert_series_by_index(data, validated_coords, target_dims) elif isinstance(data, pd.DataFrame): if isinstance(data.index, pd.MultiIndex): @@ -388,13 +414,13 @@ def to_dataarray( raise ConversionError('DataFrame must have at least one column.') if len(data.columns) == 1: - intermediate = DataConverter._convert_1d_with_index_matching( + intermediate = cls._convert_series_by_index( data.iloc[:, 0], validated_coords, target_dims ) else: # Handle multi-column DataFrames - this now allows partial matching logger.warning('Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') - intermediate = DataConverter._convert_multid_array_by_shape( + intermediate = cls._convert_multid_array_by_shape( data.to_numpy(), validated_coords, target_dims ) @@ -408,7 +434,7 @@ def to_dataarray( # Step 2: Broadcast to target dimensions if needed # This now handles cases where intermediate has some but not all target dimensions - return DataConverter._broadcast_to_target_dims(intermediate, validated_coords, target_dims) + return cls._broadcast_to_target_dims(intermediate, validated_coords, target_dims) @staticmethod def _validate_and_prepare_coords(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: From cec7367687f16b08cc12973cc7bca943457f9e4f Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Jul 2025 08:53:23 +0200 Subject: [PATCH 171/183] Add test for error handling --- tests/test_dataconverter.py | 271 +++++++++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 1 deletion(-) diff --git a/tests/test_dataconverter.py b/tests/test_dataconverter.py index c174aebe8..2fbad4a13 100644 --- a/tests/test_dataconverter.py +++ b/tests/test_dataconverter.py @@ -679,7 +679,7 @@ def test_error_message_quality(self, standard_coords): try: DataConverter.to_dataarray(data_2d, coords=coords_2d) - assert False, "Should have raised ConversionError" + raise AssertionError('Should have raised ConversionError') except ConversionError as e: error_msg = str(e) assert "Array shape: (7, 8)" in error_msg @@ -889,6 +889,275 @@ def test_mixed_broadcasting_scenarios(self): arr_2d ) +class TestAmbiguousDimensionLengthHandling: + """Test that DataConverter correctly raises errors when multiple dimensions have the same length.""" + + def test_1d_array_ambiguous_dimensions_simple(self): + """Test 1D array with two dimensions of same length should fail.""" + # Both dimensions have length 3 + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east'], name='region'), # length 3 + } + + arr_1d = np.array([1, 2, 3]) # length 3 - matches both dimensions + + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(arr_1d, coords=coords_ambiguous) + + def test_1d_array_ambiguous_dimensions_complex(self): + """Test 1D array with multiple dimensions of same length.""" + # Three dimensions have length 4 + coords_4x4x4 = { + 'time': pd.date_range('2024-01-01', periods=4, freq='D', name='time'), # length 4 + 'scenario': pd.Index(['A', 'B', 'C', 'D'], name='scenario'), # length 4 + 'region': pd.Index(['north', 'south', 'east', 'west'], name='region'), # length 4 + 'product': pd.Index(['X', 'Y'], name='product'), # length 2 - unique + } + + # Array matching the ambiguous length + arr_1d = np.array([10, 20, 30, 40]) # length 4 - matches time, scenario, region + + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(arr_1d, coords=coords_4x4x4) + + # Array matching the unique length should work + arr_1d_unique = np.array([100, 200]) # length 2 - matches only product + result = DataConverter.to_dataarray(arr_1d_unique, coords=coords_4x4x4) + assert result.shape == (4, 4, 4, 2) # broadcast to all dimensions + assert result.dims == ('time', 'scenario', 'region', 'product') + + def test_2d_array_ambiguous_dimensions_both_same(self): + """Test 2D array where both dimensions have the same ambiguous length.""" + # All dimensions have length 3 + coords_3x3x3 = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), # length 3 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 + } + + # 3x3 array - could be any combination of the three dimensions + arr_2d = np.random.rand(3, 3) + + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_2d, coords=coords_3x3x3) + + def test_2d_array_one_dimension_ambiguous(self): + """Test 2D array where only one dimension length is ambiguous.""" + coords_mixed = { + 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 - unique + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 - same as scenario + 'product': pd.Index(['P1', 'P2'], name='product'), # length 2 - unique + } + + # 5x3 array - first dimension clearly maps to time (unique length 5) + # but second dimension could be scenario or region (both length 3) + arr_5x3 = np.random.rand(5, 3) + + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_5x3, coords=coords_mixed) + + # 5x2 array should work - dimensions are unambiguous + arr_5x2 = np.random.rand(5, 2) + result = DataConverter.to_dataarray( + arr_5x2, coords={'time': coords_mixed['time'], 'product': coords_mixed['product']} + ) + assert result.shape == (5, 2) + assert result.dims == ('time', 'product') + + def test_3d_array_all_dimensions_ambiguous(self): + """Test 3D array where all dimension lengths are ambiguous.""" + # All dimensions have length 2 + coords_2x2x2x2 = { + 'scenario': pd.Index(['A', 'B'], name='scenario'), # length 2 + 'region': pd.Index(['north', 'south'], name='region'), # length 2 + 'technology': pd.Index(['solar', 'wind'], name='technology'), # length 2 + 'product': pd.Index(['X', 'Y'], name='product'), # length 2 + } + + # 2x2x2 array - could be any combination of 3 dimensions from the 4 available + arr_3d = np.random.rand(2, 2, 2) + + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_3d, coords=coords_2x2x2x2) + + def test_3d_array_partial_ambiguity(self): + """Test 3D array with partial dimension ambiguity.""" + coords_partial = { + 'time': pd.date_range('2024-01-01', periods=4, freq='D', name='time'), # length 4 - unique + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 - same as scenario + 'technology': pd.Index(['solar', 'wind'], name='technology'), # length 2 - unique + } + + # 4x3x2 array - first and third dimensions are unique, middle is ambiguous + # This should still fail because middle dimension (length 3) could be scenario or region + arr_4x3x2 = np.random.rand(4, 3, 2) + + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_4x3x2, coords=coords_partial) + + def test_pandas_series_ambiguous_dimensions(self): + """Test pandas Series with ambiguous dimension lengths.""" + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['north', 'south', 'east'], name='region'), # length 3 + } + + # Series with length 3 but index that doesn't match either coordinate exactly + generic_series = pd.Series([10, 20, 30], index=[0, 1, 2]) + + # Should fail because length matches multiple dimensions and index doesn't match any + with pytest.raises(ConversionError, match='index does not match any target dimension'): + DataConverter.to_dataarray(generic_series, coords=coords_ambiguous) + + # Series with index that matches one of the ambiguous coordinates should work + scenario_series = pd.Series([10, 20, 30], index=coords_ambiguous['scenario']) + result = DataConverter.to_dataarray(scenario_series, coords=coords_ambiguous) + assert result.shape == (3, 3) # should broadcast to both dimensions + assert result.dims == ('scenario', 'region') + + def test_edge_case_many_same_lengths(self): + """Test edge case with many dimensions having the same length.""" + # Five dimensions all have length 2 + coords_many = { + 'dim1': pd.Index(['A', 'B'], name='dim1'), + 'dim2': pd.Index(['X', 'Y'], name='dim2'), + 'dim3': pd.Index(['P', 'Q'], name='dim3'), + 'dim4': pd.Index(['M', 'N'], name='dim4'), + 'dim5': pd.Index(['U', 'V'], name='dim5'), + } + + # 1D array + arr_1d = np.array([1, 2]) + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(arr_1d, coords=coords_many) + + # 2D array + arr_2d = np.random.rand(2, 2) + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_2d, coords=coords_many) + + # 3D array + arr_3d = np.random.rand(2, 2, 2) + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_3d, coords=coords_many) + + def test_mixed_lengths_with_duplicates(self): + """Test mixed scenario with some duplicate and some unique lengths.""" + coords_mixed = { + 'time': pd.date_range('2024-01-01', periods=8, freq='D', name='time'), # length 8 - unique + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 - same as scenario + 'technology': pd.Index(['solar'], name='technology'), # length 1 - unique + 'product': pd.Index(['P1', 'P2', 'P3', 'P4', 'P5'], name='product'), # length 5 - unique + } + + # Arrays with unique lengths should work + arr_8 = np.arange(8) + result_8 = DataConverter.to_dataarray(arr_8, coords=coords_mixed) + assert result_8.dims == ('time', 'scenario', 'region', 'technology', 'product') + + arr_1 = np.array([42]) + result_1 = DataConverter.to_dataarray(arr_1, coords={'technology': coords_mixed['technology']}) + assert result_1.shape == (1,) + + arr_5 = np.arange(5) + result_5 = DataConverter.to_dataarray(arr_5, coords={'product': coords_mixed['product']}) + assert result_5.shape == (5,) + + # Arrays with ambiguous length should fail + arr_3 = np.array([1, 2, 3]) # matches both scenario and region + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(arr_3, coords=coords_mixed) + + def test_dataframe_with_ambiguous_dimensions(self): + """Test DataFrame handling with ambiguous dimensions.""" + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 + } + + # Multi-column DataFrame with ambiguous dimensions + df = pd.DataFrame({'col1': [1, 2, 3], 'col2': [4, 5, 6], 'col3': [7, 8, 9]}) # 3x3 DataFrame + + # Should fail due to ambiguous dimensions + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(df, coords=coords_ambiguous) + + def test_error_message_quality_for_ambiguous_dimensions(self): + """Test that error messages for ambiguous dimensions are helpful.""" + coords_ambiguous = { + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), + 'region': pd.Index(['north', 'south', 'east'], name='region'), + 'technology': pd.Index(['solar', 'wind', 'gas'], name='technology'), + } + + # 1D array case + arr_1d = np.array([1, 2, 3]) + try: + DataConverter.to_dataarray(arr_1d, coords=coords_ambiguous) + raise AssertionError('Should have raised ConversionError') + except ConversionError as e: + error_msg = str(e) + assert 'matches multiple dimensions' in error_msg + assert 'scenario' in error_msg + assert 'region' in error_msg + assert 'technology' in error_msg + + # 2D array case + arr_2d = np.random.rand(3, 3) + try: + DataConverter.to_dataarray(arr_2d, coords=coords_ambiguous) + raise AssertionError('Should have raised ConversionError') + except ConversionError as e: + error_msg = str(e) + assert 'matches multiple dimension orders' in error_msg + assert '(3, 3)' in error_msg + + def test_ambiguous_with_broadcasting_target(self): + """Test ambiguous dimensions when target includes broadcasting.""" + coords_ambiguous_plus = { + 'time': pd.date_range('2024-01-01', periods=5, freq='D', name='time'), # length 5 + 'scenario': pd.Index(['A', 'B', 'C'], name='scenario'), # length 3 + 'region': pd.Index(['X', 'Y', 'Z'], name='region'), # length 3 - same as scenario + 'technology': pd.Index(['solar', 'wind'], name='technology'), # length 2 + } + + # 1D array with ambiguous length, but targeting broadcast scenario + arr_3 = np.array([10, 20, 30]) # length 3, matches scenario and region + + # Should fail even though it would broadcast to other dimensions + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(arr_3, coords=coords_ambiguous_plus) + + # 2D array with one ambiguous dimension + arr_5x3 = np.random.rand(5, 3) # 5 is unique (time), 3 is ambiguous (scenario/region) + + with pytest.raises(ConversionError, match='matches multiple dimension orders'): + DataConverter.to_dataarray(arr_5x3, coords=coords_ambiguous_plus) + + def test_time_dimension_ambiguity(self): + """Test ambiguity specifically involving time dimension.""" + # Create scenario where time has same length as another dimension + coords_time_ambiguous = { + 'time': pd.date_range('2024-01-01', periods=3, freq='D', name='time'), # length 3 + 'scenario': pd.Index(['base', 'high', 'low'], name='scenario'), # length 3 - same as time + 'region': pd.Index(['north', 'south'], name='region'), # length 2 - unique + } + + # Time-indexed series should work even with ambiguous lengths (index matching takes precedence) + time_series = pd.Series([100, 200, 300], index=coords_time_ambiguous['time']) + result = DataConverter.to_dataarray(time_series, coords=coords_time_ambiguous) + assert result.shape == (3, 3, 2) + assert result.dims == ('time', 'scenario', 'region') + + # But generic array with length 3 should still fail + generic_array = np.array([100, 200, 300]) + with pytest.raises(ConversionError, match='matches multiple dimensions'): + DataConverter.to_dataarray(generic_array, coords=coords_time_ambiguous) + if __name__ == '__main__': pytest.main() From eba1ec436ad4156051582b001f6f7ef6c290c737 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Sat, 5 Jul 2025 08:53:36 +0200 Subject: [PATCH 172/183] Update scenario example --- examples/04_Scenarios/scenario_example.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/04_Scenarios/scenario_example.py b/examples/04_Scenarios/scenario_example.py index b9932a016..ae53cc1ff 100644 --- a/examples/04_Scenarios/scenario_example.py +++ b/examples/04_Scenarios/scenario_example.py @@ -70,7 +70,8 @@ discharging=fx.Flow('Q_th_unload', bus='Fernwärme', size=1000), capacity_in_flow_hours=fx.InvestParameters(fix_effects=20, fixed_size=30, optional=False), initial_charge_state=0, # Initial storage state: empty - relative_maximum_charge_state=np.array([80, 70, 80, 80, 80, 80, 80, 80, 80, 80]) * 0.01, + relative_maximum_charge_state=np.array([80, 70, 80, 80, 80, 80, 80, 80, 80]) * 0.01, + relative_maximum_final_charge_state=0.8, eta_charge=0.9, eta_discharge=1, # Efficiency factors for charging/discharging relative_loss_per_hour=0.08, # 8% loss per hour. Absolute loss depends on current charge state From 5f97bf3cab08c1efa628b96430f791e8fbbad478 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:10:16 +0200 Subject: [PATCH 173/183] Fix Handling of TimeSeriesData --- flixopt/calculation.py | 6 ++++-- flixopt/core.py | 28 ++++++++++++++++++++++++++-- flixopt/flow_system.py | 5 +---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index e035aaa13..b3eec2d06 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -61,8 +61,10 @@ def __init__( """ self.name = name if flow_system.used_in_calculation: - logging.warning(f'FlowSystem {flow_system} is already used in a calculation. ' - f'Creating a copy for Calculation "{self.name}".') + logger.warning( + f'FlowSystem {flow_system} is already used in a calculation. ' + f'Creating a copy of the FlowSystem for Calculation "{self.name}".' + ) flow_system = flow_system.copy() if active_timesteps is not None: diff --git a/flixopt/core.py b/flixopt/core.py index d1e61b4c5..39490f536 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -51,8 +51,15 @@ class TimeSeriesData(xr.DataArray): __slots__ = () # No additional instance attributes - everything goes in attrs - def __init__(self, *args, aggregation_group: Optional[str] = None, aggregation_weight: Optional[float] = None, - agg_group: Optional[str] = None, agg_weight: Optional[float] = None, **kwargs): + def __init__( + self, + *args, + aggregation_group: Optional[str] = None, + aggregation_weight: Optional[float] = None, + agg_group: Optional[str] = None, + agg_weight: Optional[float] = None, + **kwargs + ): """ Args: *args: Arguments passed to DataArray @@ -84,6 +91,23 @@ def __init__(self, *args, aggregation_group: Optional[str] = None, aggregation_w # Always mark as TimeSeriesData self.attrs['__timeseries_data__'] = True + def fit_to_coords( + self, + coords: Dict[str, pd.Index], + name: Optional[str] = None, + ) -> 'TimeSeriesData': + """Fit the data to the given coordinates. Returns a new TimeSeriesData object if the current coords are different.""" + if self.coords.equals(xr.Coordinates(coords)): + return self + + da = DataConverter.to_dataarray(self.data, coords=coords) + return self.__class__( + da, + aggregation_group=self.aggregation_group, + aggregation_weight=self.aggregation_weight, + name=name if name is not None else self.name + ) + @property def aggregation_group(self) -> Optional[str]: return self.attrs.get('aggregation_group') diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 0568f3b3a..3fa920b84 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -343,10 +343,7 @@ def fit_to_model_coords( if isinstance(data, TimeSeriesData): try: data.name = name # Set name of previous object! - return TimeSeriesData( - DataConverter.to_dataarray(data, coords=coords), - aggregation_group=data.aggregation_group, aggregation_weight=data.aggregation_weight - ).rename(name) + return data.fit_to_coords(coords) except ConversionError as e: raise ConversionError( f'Could not convert time series data "{name}" to DataArray:\n{data}\nOriginal Error: {e}') from e From 9351083c65bb893863d040f96852e517f918f048 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Mon, 7 Jul 2025 14:31:05 +0200 Subject: [PATCH 174/183] Improve DataConverter --- flixopt/core.py | 207 +++++++++++++++--------------------------------- 1 file changed, 66 insertions(+), 141 deletions(-) diff --git a/flixopt/core.py b/flixopt/core.py index 39490f536..ee0ef0540 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -158,6 +158,7 @@ class DataConverter: Supports: - Scalars (broadcast to all dimensions) - 1D data (np.ndarray, pd.Series, single-column DataFrame) + - Multi-dimensional arrays - xr.DataArray (validated and potentially broadcast) Simple 1D data is matched to one dimension and broadcast to others. @@ -165,11 +166,11 @@ class DataConverter: """ @staticmethod - def _convert_series_by_index( + def _match_series_to_dimension( data: pd.Series, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert pandas Series to DataArray by matching index to coordinates. + Match pandas Series to a dimension by comparing its index to coordinates. Args: data: pandas Series @@ -196,11 +197,11 @@ def _convert_series_by_index( raise ConversionError(f'Series index does not match any target dimension coordinates: {target_dims}') @staticmethod - def _convert_1d_array_by_length( + def _match_array_to_dimension( data: np.ndarray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert 1D numpy array to DataArray by matching length to coordinates. + Match 1D numpy array to a dimension by comparing its length to coordinate lengths. Args: data: 1D numpy array @@ -218,7 +219,7 @@ def _convert_1d_array_by_length( raise ConversionError('Cannot convert multi-element array without target dimensions') return xr.DataArray(data[0]) - # Match by length + # Find dimensions with matching lengths matching_dims = [] for dim_name in target_dims: if len(data) == len(coords[dim_name]): @@ -239,102 +240,11 @@ def _convert_1d_array_by_length( return xr.DataArray(data.copy(), coords={match_dim: coords[match_dim]}, dims=[match_dim]) @staticmethod - def _broadcast_to_target_dims( - data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] - ) -> xr.DataArray: - """ - Broadcast DataArray to match target dimensions. - - Args: - data: Source DataArray - coords: Target coordinates - target_dims: Target dimension names - - Returns: - DataArray broadcast to target dimensions - """ - if len(target_dims) == 0: - # Target is scalar - if data.size != 1: - raise ConversionError('Cannot convert multi-element DataArray to scalar') - return xr.DataArray(data.values.item()) - - # If data already matches target, validate coordinates and return - if set(data.dims) == set(target_dims) and len(data.dims) == len(target_dims): - # Check coordinate compatibility - for dim in data.dims: - if dim in coords and not np.array_equal(data.coords[dim].values, coords[dim].values): - raise ConversionError(f'DataArray {dim} coordinates do not match target coordinates') - - # Ensure correct dimension order - if data.dims != target_dims: - data = data.transpose(*target_dims) - return data.copy() - - # Handle scalar data (0D) - broadcast to all dimensions - if data.ndim == 0: - return xr.DataArray(data.item(), coords=coords, dims=target_dims) - - # Handle broadcasting from fewer to more dimensions - if len(data.dims) < len(target_dims): - return DataConverter._expand_to_more_dims(data, coords, target_dims) - - # Cannot handle more dimensions than target - if len(data.dims) > len(target_dims): - raise ConversionError(f'Cannot reduce DataArray from {len(data.dims)} to {len(target_dims)} dimensions') - - raise ConversionError(f'Cannot convert DataArray with dims {data.dims} to target dims {target_dims}') - - @staticmethod - def _expand_to_more_dims( - data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] - ) -> xr.DataArray: - """Expand DataArray to additional dimensions by broadcasting.""" - # Validate that all source dimensions exist in target - for dim in data.dims: - if dim not in target_dims: - raise ConversionError(f'Source dimension "{dim}" not found in target dimensions {target_dims}') - - # Check coordinate compatibility - if not np.array_equal(data.coords[dim].values, coords[dim].values): - raise ConversionError(f'Source {dim} coordinates do not match target coordinates') - - # Start with the original data - result_data = data.values - result_dims = list(data.dims) - result_coords = {dim: data.coords[dim] for dim in data.dims} - - # Add missing dimensions one by one - for target_dim in target_dims: - if target_dim not in result_dims: - # Add this dimension at the end - result_data = np.expand_dims(result_data, axis=-1) - result_dims.append(target_dim) - result_coords[target_dim] = coords[target_dim] - - # Broadcast along the new dimension - new_shape = list(result_data.shape) - new_shape[-1] = len(coords[target_dim]) - result_data = np.broadcast_to(result_data, new_shape) - - # Reorder dimensions to match target order - if tuple(result_dims) != target_dims: - # Create mapping from current to target order - dim_indices = [result_dims.index(dim) for dim in target_dims] - result_data = np.transpose(result_data, dim_indices) - - # Build final coordinates dict in target order - final_coords = {dim: coords[dim] for dim in target_dims} - - return xr.DataArray(result_data.copy(), coords=final_coords, dims=target_dims) - - @staticmethod - def _convert_multid_array_by_shape( + def _match_multidim_array_to_dimensions( data: np.ndarray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] ) -> xr.DataArray: """ - Convert multi-dimensional numpy array to DataArray by matching dimensions by shape. - Returns a DataArray that may need further broadcasting to target dimensions. + Match multi-dimensional numpy array to dimensions by finding the correct shape permutation. Args: data: Multi-dimensional numpy array @@ -342,7 +252,7 @@ def _convert_multid_array_by_shape( target_dims: Target dimension names Returns: - DataArray with dimensions matched by shape (may be subset of target_dims) + DataArray with dimensions matched by shape Raises: ConversionError: If array dimensions cannot be uniquely matched to coordinates @@ -352,17 +262,14 @@ def _convert_multid_array_by_shape( raise ConversionError('Cannot convert multi-element array without target dimensions') return xr.DataArray(data.item()) - # Get lengths of each dimension + from itertools import permutations + array_shape = data.shape coord_lengths = {dim: len(coords[dim]) for dim in target_dims} - # Find all possible ways to match array dimensions to available coordinates - from itertools import permutations - - # Try all permutations of target_dims that match the array's number of dimensions + # Find all possible dimension mappings possible_mappings = [] for dim_subset in permutations(target_dims, data.ndim): - # Check if this permutation matches the array shape if all(array_shape[i] == coord_lengths[dim_subset[i]] for i in range(len(dim_subset))): possible_mappings.append(dim_subset) @@ -376,58 +283,80 @@ def _convert_multid_array_by_shape( 'Cannot uniquely determine dimension mapping.' ) - # Use the unique mapping found matched_dims = possible_mappings[0] matched_coords = {dim: coords[dim] for dim in matched_dims} - # Return DataArray with matched dimensions - broadcasting will happen later if needed return xr.DataArray(data.copy(), coords=matched_coords, dims=matched_dims) + @staticmethod + def _broadcast_to_target( + data: xr.DataArray, coords: Dict[str, pd.Index], target_dims: Tuple[str, ...] + ) -> xr.DataArray: + """ + Broadcast DataArray to target dimensions with validation. + + Handles all cases: scalar expansion, dimension validation, coordinate matching, + and broadcasting to additional dimensions using xarray's capabilities. + """ + # Cannot reduce dimensions of data + if len(data.dims) > len(target_dims): + raise ConversionError(f'Cannot reduce DataArray from {len(data.dims)} to {len(target_dims)} dimensions') + + # Validate coordinate compatibility + for dim in data.dims: + if dim not in target_dims: + raise ConversionError(f'Source dimension "{dim}" not found in target dimensions {target_dims}') + + if not np.array_equal(data.coords[dim].values, coords[dim].values): + raise ConversionError(f'DataArray {dim} coordinates do not match target coordinates') + + # Use xarray's broadcast_like for efficient expansion and broadcasting + target_template = xr.DataArray( + np.empty([len(coords[dim]) for dim in target_dims]), coords=coords, dims=target_dims + ) + return data.broadcast_like(target_template).transpose(*target_dims) + @classmethod def to_dataarray( cls, - data: Union[Scalar, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray, TimeSeriesData], + data: Union[float, int, np.ndarray, pd.Series, pd.DataFrame, xr.DataArray], coords: Optional[Dict[str, pd.Index]] = None, ) -> xr.DataArray: """ - Convert data to xarray.DataArray with specified coordinates. - - Accepts: - - Scalars (broadcast to all dimensions) - - 1D arrays or Series (matched to one dimension, broadcast to others) - - Multi-D arrays or DataFrames (dimensions matched by length, broadcast to remaining) - - xr.DataArray (validated and potentially broadcast to additional dimensions) + Convert various data types to xarray.DataArray with specified coordinates. Args: - data: Data to convert + data: Data to convert (scalar, array, Series, DataFrame, or DataArray) coords: Dictionary mapping dimension names to coordinate indices Returns: - DataArray with the converted data + DataArray with the converted data broadcast to target dimensions + + Raises: + ConversionError: If data cannot be converted or dimensions are ambiguous """ if coords is None: coords = {} - validated_coords, target_dims = cls._validate_and_prepare_coords(coords) + validated_coords, target_dims = cls._prepare_coordinates(coords) - # Step 1: Convert to DataArray (may have fewer dimensions than target) + # Step 1: Convert input data to initial DataArray if isinstance(data, (int, float, np.integer, np.floating)): - # Scalars: create 0D DataArray, will be broadcast later + # Scalar values intermediate = xr.DataArray(data.item() if hasattr(data, 'item') else data) elif isinstance(data, np.ndarray): if data.ndim == 1: - intermediate = cls._convert_1d_array_by_length(data, validated_coords, target_dims) + intermediate = cls._match_array_to_dimension(data, validated_coords, target_dims) else: - # Handle multi-dimensional arrays - this now allows partial matching - intermediate = cls._convert_multid_array_by_shape(data, validated_coords, target_dims) + intermediate = cls._match_multidim_array_to_dimensions(data, validated_coords, target_dims) elif isinstance(data, pd.Series): if isinstance(data.index, pd.MultiIndex): raise ConversionError( 'Series index must be a single level Index. Multi-index Series are not supported.' ) - intermediate = cls._convert_series_by_index(data, validated_coords, target_dims) + intermediate = cls._match_series_to_dimension(data, validated_coords, target_dims) elif isinstance(data, pd.DataFrame): if isinstance(data.index, pd.MultiIndex): @@ -438,44 +367,40 @@ def to_dataarray( raise ConversionError('DataFrame must have at least one column.') if len(data.columns) == 1: - intermediate = cls._convert_series_by_index( - data.iloc[:, 0], validated_coords, target_dims - ) + # Single-column DataFrame - treat as Series + intermediate = cls._match_series_to_dimension(data.iloc[:, 0], validated_coords, target_dims) else: - # Handle multi-column DataFrames - this now allows partial matching - logger.warning('Converting multi-column DataFrame to xr.DataArray. We advise to do this manually.') - intermediate = cls._convert_multid_array_by_shape( - data.to_numpy(), validated_coords, target_dims - ) + # Multi-column DataFrame - treat as multi-dimensional array + intermediate = cls._match_multidim_array_to_dimensions(data.to_numpy(), validated_coords, target_dims) elif isinstance(data, xr.DataArray): intermediate = data.copy() else: - raise ConversionError( - f'Unsupported data type: {type(data).__name__}. Only scalars, arrays, Series, DataFrames, and DataArrays are supported.' - ) + raise ConversionError(f'Unsupported data type: {type(data).__name__}.') - # Step 2: Broadcast to target dimensions if needed - # This now handles cases where intermediate has some but not all target dimensions - return cls._broadcast_to_target_dims(intermediate, validated_coords, target_dims) + # Step 2: Broadcast to target dimensions + return cls._broadcast_to_target(intermediate, validated_coords, target_dims) @staticmethod - def _validate_and_prepare_coords(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: + def _prepare_coordinates(coords: Dict[str, pd.Index]) -> Tuple[Dict[str, pd.Index], Tuple[str, ...]]: """ - Validate and prepare coordinates for the DataArray. + Validate coordinates and prepare them for DataArray creation. Args: coords: Dictionary mapping dimension names to coordinate indices Returns: Tuple of (validated coordinates dict, dimensions tuple) + + Raises: + ConversionError: If coordinates are invalid """ validated_coords = {} dims = [] for dim_name, coord_index in coords.items(): - # Validate coordinate index + # Basic validation if not isinstance(coord_index, pd.Index) or len(coord_index) == 0: raise ConversionError(f'{dim_name} coordinates must be a non-empty pandas Index') From 99e6b1956f88f16e95818111286c86b2b606c87c Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:19:32 +0200 Subject: [PATCH 175/183] Fix resampling of the FlowSystem --- flixopt/flow_system.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 3fa920b84..7001ca9e3 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -694,6 +694,7 @@ def resample( ) -> 'FlowSystem': """ Create a resampled FlowSystem by resampling data along the time dimension (like xr.Dataset.resample()). + Only resamples data variables that have a time dimension. Args: time: Resampling frequency (e.g., '3h', '2D', '1M') @@ -707,12 +708,32 @@ def resample( self.connect_and_transform() dataset = self.to_dataset() - resampler = dataset.resample(time=time, **kwargs) + + # Separate variables with and without time dimension + time_vars = {} + non_time_vars = {} + + for var_name, var in dataset.data_vars.items(): + if 'time' in var.dims: + time_vars[var_name] = var + else: + non_time_vars[var_name] = var + + # Only resample variables that have time dimension + time_dataset = dataset[list(time_vars.keys())] + resampler = time_dataset.resample(time=time, **kwargs) if hasattr(resampler, method): - resampled_dataset = getattr(resampler, method)() + resampled_time_data = getattr(resampler, method)() else: available_methods = ['mean', 'sum', 'max', 'min', 'first', 'last', 'std', 'var', 'median', 'count'] raise ValueError(f'Unsupported resampling method: {method}. Available: {available_methods}') + # Combine resampled time variables with non-time variables + if non_time_vars: + non_time_dataset = dataset[list(non_time_vars.keys())] + resampled_dataset = xr.merge([resampled_time_data, non_time_dataset]) + else: + resampled_dataset = resampled_time_data + return self.__class__.from_dataset(resampled_dataset) From 4981a9c87222b6103e8a293a9fb596d6455b3236 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:35:30 +0200 Subject: [PATCH 176/183] Improve Warning Message --- flixopt/calculation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index b3eec2d06..0fb735bef 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -62,7 +62,7 @@ def __init__( self.name = name if flow_system.used_in_calculation: logger.warning( - f'FlowSystem {flow_system} is already used in a calculation. ' + f'This FlowSystem is already used in a calculation:\n{flow_system}\n' f'Creating a copy of the FlowSystem for Calculation "{self.name}".' ) flow_system = flow_system.copy() From 516f45b4b356b0f7db04670707e5506bab4540da Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:39:25 +0200 Subject: [PATCH 177/183] Add example that leverages resampling --- .../Zeitreihen2020.csv | 35137 ++++++++++++++++ .../two_stage_optimization.py | 148 + 2 files changed, 35285 insertions(+) create mode 100644 examples/05_Two-stage-optimization/Zeitreihen2020.csv create mode 100644 examples/05_Two-stage-optimization/two_stage_optimization.py diff --git a/examples/05_Two-stage-optimization/Zeitreihen2020.csv b/examples/05_Two-stage-optimization/Zeitreihen2020.csv new file mode 100644 index 000000000..9b660ef9c --- /dev/null +++ b/examples/05_Two-stage-optimization/Zeitreihen2020.csv @@ -0,0 +1,35137 @@ +Zeit,P_Netz/MW,Q_Netz/MW,Strompr.€/MWh,Gaspr.€/MWh +2020-01-01 00:00:00,58.39,127.059,7.461,32.459 +2020-01-01 00:15:00,58.36,122.156,7.461,32.459 +2020-01-01 00:30:00,58.11,124.412,7.461,32.459 +2020-01-01 00:45:00,57.71,127.713,7.461,32.459 +2020-01-01 01:00:00,55.53,130.69899999999998,2.65,32.459 +2020-01-01 01:15:00,56.24,132.166,2.65,32.459 +2020-01-01 01:30:00,55.17,132.394,2.65,32.459 +2020-01-01 01:45:00,54.5,132.431,2.65,32.459 +2020-01-01 02:00:00,52.95,134.403,-2.949,32.459 +2020-01-01 02:15:00,51.75,134.755,-2.949,32.459 +2020-01-01 02:30:00,50.7,135.631,-2.949,32.459 +2020-01-01 02:45:00,50.33,138.345,-2.949,32.459 +2020-01-01 03:00:00,47.11,141.071,-3.2680000000000002,32.459 +2020-01-01 03:15:00,49.35,141.319,-3.2680000000000002,32.459 +2020-01-01 03:30:00,48.33,143.024,-3.2680000000000002,32.459 +2020-01-01 03:45:00,48.73,144.697,-3.2680000000000002,32.459 +2020-01-01 04:00:00,46.6,152.569,-3.2680000000000002,32.459 +2020-01-01 04:15:00,47.33,160.688,-3.2680000000000002,32.459 +2020-01-01 04:30:00,47.5,161.673,-3.2680000000000002,32.459 +2020-01-01 04:45:00,48.62,163.165,-3.2680000000000002,32.459 +2020-01-01 05:00:00,49.18,176.398,-3.2680000000000002,32.459 +2020-01-01 05:15:00,50.0,184.233,-3.2680000000000002,32.459 +2020-01-01 05:30:00,50.1,181.12,-3.2680000000000002,32.459 +2020-01-01 05:45:00,50.36,179.642,-3.2680000000000002,32.459 +2020-01-01 06:00:00,50.32,196.364,-3.2680000000000002,32.459 +2020-01-01 06:15:00,50.22,215.918,-3.2680000000000002,32.459 +2020-01-01 06:30:00,52.17,211.0,-3.2680000000000002,32.459 +2020-01-01 06:45:00,54.2,206.111,-3.2680000000000002,32.459 +2020-01-01 07:00:00,55.6,202.635,-3.2680000000000002,32.459 +2020-01-01 07:15:00,55.35,207.093,-3.2680000000000002,32.459 +2020-01-01 07:30:00,55.44,211.667,-3.2680000000000002,32.459 +2020-01-01 07:45:00,55.15,215.882,-3.2680000000000002,32.459 +2020-01-01 08:00:00,56.21,219.793,-2.146,32.459 +2020-01-01 08:15:00,55.94,223.388,-2.146,32.459 +2020-01-01 08:30:00,58.2,226.081,-2.146,32.459 +2020-01-01 08:45:00,56.11,227.016,-2.146,32.459 +2020-01-01 09:00:00,61.62,222.30700000000002,1.7519999999999998,32.459 +2020-01-01 09:15:00,63.5,221.018,1.7519999999999998,32.459 +2020-01-01 09:30:00,64.24,219.09,1.7519999999999998,32.459 +2020-01-01 09:45:00,64.02,215.80700000000002,1.7519999999999998,32.459 +2020-01-01 10:00:00,61.38,212.231,4.19,32.459 +2020-01-01 10:15:00,62.05,209.50099999999998,4.19,32.459 +2020-01-01 10:30:00,61.84,206.963,4.19,32.459 +2020-01-01 10:45:00,65.9,204.111,4.19,32.459 +2020-01-01 11:00:00,67.33,203.37599999999998,5.517,32.459 +2020-01-01 11:15:00,68.34,200.575,5.517,32.459 +2020-01-01 11:30:00,67.43,198.755,5.517,32.459 +2020-01-01 11:45:00,68.73,197.22099999999998,5.517,32.459 +2020-01-01 12:00:00,74.2,191.555,4.27,32.459 +2020-01-01 12:15:00,69.91,191.37,4.27,32.459 +2020-01-01 12:30:00,68.83,190.28799999999998,4.27,32.459 +2020-01-01 12:45:00,70.01,189.851,4.27,32.459 +2020-01-01 13:00:00,69.02,188.09599999999998,3.484,32.459 +2020-01-01 13:15:00,68.68,189.604,3.484,32.459 +2020-01-01 13:30:00,68.45,188.61,3.484,32.459 +2020-01-01 13:45:00,68.18,188.303,3.484,32.459 +2020-01-01 14:00:00,68.94,187.555,2.523,32.459 +2020-01-01 14:15:00,66.1,188.747,2.523,32.459 +2020-01-01 14:30:00,70.61,189.53099999999998,2.523,32.459 +2020-01-01 14:45:00,70.58,189.93,2.523,32.459 +2020-01-01 15:00:00,73.81,189.581,5.667999999999999,32.459 +2020-01-01 15:15:00,69.6,191.454,5.667999999999999,32.459 +2020-01-01 15:30:00,68.66,194.55599999999998,5.667999999999999,32.459 +2020-01-01 15:45:00,71.32,197.227,5.667999999999999,32.459 +2020-01-01 16:00:00,73.3,197.49900000000002,12.109000000000002,32.459 +2020-01-01 16:15:00,77.24,199.38099999999997,12.109000000000002,32.459 +2020-01-01 16:30:00,83.2,202.511,12.109000000000002,32.459 +2020-01-01 16:45:00,86.91,205.047,12.109000000000002,32.459 +2020-01-01 17:00:00,89.85,207.06099999999998,22.824,32.459 +2020-01-01 17:15:00,90.61,208.69799999999998,22.824,32.459 +2020-01-01 17:30:00,92.03,209.25099999999998,22.824,32.459 +2020-01-01 17:45:00,93.49,210.672,22.824,32.459 +2020-01-01 18:00:00,95.17,211.632,21.656,32.459 +2020-01-01 18:15:00,94.51,211.645,21.656,32.459 +2020-01-01 18:30:00,93.87,209.94,21.656,32.459 +2020-01-01 18:45:00,93.48,208.489,21.656,32.459 +2020-01-01 19:00:00,92.14,209.826,19.749000000000002,32.459 +2020-01-01 19:15:00,90.85,207.487,19.749000000000002,32.459 +2020-01-01 19:30:00,89.81,204.959,19.749000000000002,32.459 +2020-01-01 19:45:00,88.14,202.29,19.749000000000002,32.459 +2020-01-01 20:00:00,84.73,200.835,24.274,32.459 +2020-01-01 20:15:00,83.37,197.709,24.274,32.459 +2020-01-01 20:30:00,82.23,194.57299999999998,24.274,32.459 +2020-01-01 20:45:00,80.34,191.52200000000002,24.274,32.459 +2020-01-01 21:00:00,78.43,188.843,23.044,32.459 +2020-01-01 21:15:00,77.95,186.56099999999998,23.044,32.459 +2020-01-01 21:30:00,77.04,186.183,23.044,32.459 +2020-01-01 21:45:00,76.57,184.824,23.044,32.459 +2020-01-01 22:00:00,72.56,178.928,25.155,32.459 +2020-01-01 22:15:00,72.48,174.81799999999998,25.155,32.459 +2020-01-01 22:30:00,70.04,170.76,25.155,32.459 +2020-01-01 22:45:00,67.68,167.32,25.155,32.459 +2020-01-01 23:00:00,59.3,159.845,20.101,32.459 +2020-01-01 23:15:00,63.51,156.17700000000002,20.101,32.459 +2020-01-01 23:30:00,61.08,153.764,20.101,32.459 +2020-01-01 23:45:00,60.81,150.845,20.101,32.459 +2020-01-02 00:00:00,72.56,131.099,38.399,32.641 +2020-01-02 00:15:00,75.51,130.822,38.399,32.641 +2020-01-02 00:30:00,76.2,132.15,38.399,32.641 +2020-01-02 00:45:00,79.94,133.753,38.399,32.641 +2020-01-02 01:00:00,76.22,136.549,36.94,32.641 +2020-01-02 01:15:00,75.52,136.971,36.94,32.641 +2020-01-02 01:30:00,69.71,137.433,36.94,32.641 +2020-01-02 01:45:00,72.15,137.96200000000002,36.94,32.641 +2020-01-02 02:00:00,68.46,139.933,35.275,32.641 +2020-01-02 02:15:00,68.93,141.73,35.275,32.641 +2020-01-02 02:30:00,69.13,142.344,35.275,32.641 +2020-01-02 02:45:00,71.05,144.38,35.275,32.641 +2020-01-02 03:00:00,77.24,147.17700000000002,35.329,32.641 +2020-01-02 03:15:00,78.88,148.17700000000002,35.329,32.641 +2020-01-02 03:30:00,79.6,150.07,35.329,32.641 +2020-01-02 03:45:00,73.47,151.487,35.329,32.641 +2020-01-02 04:00:00,71.66,163.75,36.275,32.641 +2020-01-02 04:15:00,72.6,175.77599999999998,36.275,32.641 +2020-01-02 04:30:00,74.43,178.856,36.275,32.641 +2020-01-02 04:45:00,76.31,181.783,36.275,32.641 +2020-01-02 05:00:00,80.9,217.042,42.193999999999996,32.641 +2020-01-02 05:15:00,83.91,245.78799999999998,42.193999999999996,32.641 +2020-01-02 05:30:00,88.55,241.4,42.193999999999996,32.641 +2020-01-02 05:45:00,92.98,234.083,42.193999999999996,32.641 +2020-01-02 06:00:00,101.92,230.607,56.422,32.641 +2020-01-02 06:15:00,106.02,236.44099999999997,56.422,32.641 +2020-01-02 06:30:00,110.67,239.31400000000002,56.422,32.641 +2020-01-02 06:45:00,115.36,243.33,56.422,32.641 +2020-01-02 07:00:00,121.21,242.197,72.569,32.641 +2020-01-02 07:15:00,125.44,247.68200000000002,72.569,32.641 +2020-01-02 07:30:00,127.04,250.858,72.569,32.641 +2020-01-02 07:45:00,129.3,252.55900000000003,72.569,32.641 +2020-01-02 08:00:00,131.83,251.364,67.704,32.641 +2020-01-02 08:15:00,129.92,251.618,67.704,32.641 +2020-01-02 08:30:00,130.03,249.708,67.704,32.641 +2020-01-02 08:45:00,129.65,246.764,67.704,32.641 +2020-01-02 09:00:00,130.4,240.053,63.434,32.641 +2020-01-02 09:15:00,132.18,236.87900000000002,63.434,32.641 +2020-01-02 09:30:00,133.68,234.63099999999997,63.434,32.641 +2020-01-02 09:45:00,133.99,231.472,63.434,32.641 +2020-01-02 10:00:00,133.44,226.187,61.88399999999999,32.641 +2020-01-02 10:15:00,135.64,221.955,61.88399999999999,32.641 +2020-01-02 10:30:00,134.92,218.68400000000003,61.88399999999999,32.641 +2020-01-02 10:45:00,136.65,216.98,61.88399999999999,32.641 +2020-01-02 11:00:00,136.01,214.97299999999998,61.481,32.641 +2020-01-02 11:15:00,135.6,213.702,61.481,32.641 +2020-01-02 11:30:00,135.49,212.15200000000002,61.481,32.641 +2020-01-02 11:45:00,136.56,210.945,61.481,32.641 +2020-01-02 12:00:00,136.24,205.8,59.527,32.641 +2020-01-02 12:15:00,135.89,205.143,59.527,32.641 +2020-01-02 12:30:00,133.92,205.12400000000002,59.527,32.641 +2020-01-02 12:45:00,134.1,205.96099999999998,59.527,32.641 +2020-01-02 13:00:00,130.94,204.35,58.794,32.641 +2020-01-02 13:15:00,129.98,203.88099999999997,58.794,32.641 +2020-01-02 13:30:00,129.31,203.575,58.794,32.641 +2020-01-02 13:45:00,127.59,203.519,58.794,32.641 +2020-01-02 14:00:00,126.5,202.428,60.32,32.641 +2020-01-02 14:15:00,129.81,203.06900000000002,60.32,32.641 +2020-01-02 14:30:00,128.98,203.95,60.32,32.641 +2020-01-02 14:45:00,127.66,204.167,60.32,32.641 +2020-01-02 15:00:00,131.69,205.303,62.52,32.641 +2020-01-02 15:15:00,134.08,205.93900000000002,62.52,32.641 +2020-01-02 15:30:00,130.49,208.357,62.52,32.641 +2020-01-02 15:45:00,132.71,210.109,62.52,32.641 +2020-01-02 16:00:00,134.12,210.895,64.199,32.641 +2020-01-02 16:15:00,134.79,212.476,64.199,32.641 +2020-01-02 16:30:00,137.68,215.345,64.199,32.641 +2020-01-02 16:45:00,138.3,216.87599999999998,64.199,32.641 +2020-01-02 17:00:00,141.23,219.28900000000002,68.19800000000001,32.641 +2020-01-02 17:15:00,140.55,219.918,68.19800000000001,32.641 +2020-01-02 17:30:00,141.63,220.68200000000002,68.19800000000001,32.641 +2020-01-02 17:45:00,141.55,220.424,68.19800000000001,32.641 +2020-01-02 18:00:00,140.02,221.84400000000002,67.899,32.641 +2020-01-02 18:15:00,138.28,218.955,67.899,32.641 +2020-01-02 18:30:00,137.19,217.683,67.899,32.641 +2020-01-02 18:45:00,137.6,217.707,67.899,32.641 +2020-01-02 19:00:00,134.26,217.453,64.72399999999999,32.641 +2020-01-02 19:15:00,133.22,213.481,64.72399999999999,32.641 +2020-01-02 19:30:00,134.64,210.797,64.72399999999999,32.641 +2020-01-02 19:45:00,136.88,207.28599999999997,64.72399999999999,32.641 +2020-01-02 20:00:00,130.24,203.518,64.062,32.641 +2020-01-02 20:15:00,119.33,197.062,64.062,32.641 +2020-01-02 20:30:00,118.31,192.979,64.062,32.641 +2020-01-02 20:45:00,112.7,191.03099999999998,64.062,32.641 +2020-01-02 21:00:00,107.44,188.12900000000002,57.971000000000004,32.641 +2020-01-02 21:15:00,112.86,185.61900000000003,57.971000000000004,32.641 +2020-01-02 21:30:00,111.93,183.535,57.971000000000004,32.641 +2020-01-02 21:45:00,108.29,181.894,57.971000000000004,32.641 +2020-01-02 22:00:00,98.52,174.86,53.715,32.641 +2020-01-02 22:15:00,94.85,168.98,53.715,32.641 +2020-01-02 22:30:00,98.1,155.042,53.715,32.641 +2020-01-02 22:45:00,96.62,146.733,53.715,32.641 +2020-01-02 23:00:00,92.22,140.148,47.8,32.641 +2020-01-02 23:15:00,84.12,138.344,47.8,32.641 +2020-01-02 23:30:00,79.31,138.518,47.8,32.641 +2020-01-02 23:45:00,83.54,137.971,47.8,32.641 +2020-01-03 00:00:00,83.23,130.233,43.656000000000006,32.641 +2020-01-03 00:15:00,82.94,130.13299999999998,43.656000000000006,32.641 +2020-01-03 00:30:00,78.25,131.279,43.656000000000006,32.641 +2020-01-03 00:45:00,76.9,132.954,43.656000000000006,32.641 +2020-01-03 01:00:00,71.11,135.453,41.263000000000005,32.641 +2020-01-03 01:15:00,78.36,136.91899999999998,41.263000000000005,32.641 +2020-01-03 01:30:00,78.99,137.079,41.263000000000005,32.641 +2020-01-03 01:45:00,77.55,137.732,41.263000000000005,32.641 +2020-01-03 02:00:00,71.19,139.738,40.799,32.641 +2020-01-03 02:15:00,72.17,141.41299999999998,40.799,32.641 +2020-01-03 02:30:00,74.79,142.542,40.799,32.641 +2020-01-03 02:45:00,78.06,144.681,40.799,32.641 +2020-01-03 03:00:00,76.51,146.311,41.398,32.641 +2020-01-03 03:15:00,74.89,148.47,41.398,32.641 +2020-01-03 03:30:00,79.0,150.364,41.398,32.641 +2020-01-03 03:45:00,77.85,152.072,41.398,32.641 +2020-01-03 04:00:00,73.23,164.549,42.38,32.641 +2020-01-03 04:15:00,74.31,176.43200000000002,42.38,32.641 +2020-01-03 04:30:00,74.97,179.67700000000002,42.38,32.641 +2020-01-03 04:45:00,78.19,181.39700000000002,42.38,32.641 +2020-01-03 05:00:00,82.49,215.25400000000002,46.181000000000004,32.641 +2020-01-03 05:15:00,85.31,245.535,46.181000000000004,32.641 +2020-01-03 05:30:00,88.01,242.33599999999998,46.181000000000004,32.641 +2020-01-03 05:45:00,93.72,235.011,46.181000000000004,32.641 +2020-01-03 06:00:00,101.82,232.016,59.33,32.641 +2020-01-03 06:15:00,106.19,236.21900000000002,59.33,32.641 +2020-01-03 06:30:00,110.85,238.166,59.33,32.641 +2020-01-03 06:45:00,115.45,244.024,59.33,32.641 +2020-01-03 07:00:00,122.71,241.924,72.454,32.641 +2020-01-03 07:15:00,123.17,248.451,72.454,32.641 +2020-01-03 07:30:00,125.55,251.585,72.454,32.641 +2020-01-03 07:45:00,128.9,252.31099999999998,72.454,32.641 +2020-01-03 08:00:00,130.12,249.83599999999998,67.175,32.641 +2020-01-03 08:15:00,128.89,249.597,67.175,32.641 +2020-01-03 08:30:00,128.21,248.743,67.175,32.641 +2020-01-03 08:45:00,128.6,244.05900000000003,67.175,32.641 +2020-01-03 09:00:00,128.71,238.011,65.365,32.641 +2020-01-03 09:15:00,131.57,235.324,65.365,32.641 +2020-01-03 09:30:00,133.77,232.672,65.365,32.641 +2020-01-03 09:45:00,136.47,229.359,65.365,32.641 +2020-01-03 10:00:00,135.77,222.843,63.95,32.641 +2020-01-03 10:15:00,137.43,219.39,63.95,32.641 +2020-01-03 10:30:00,136.81,215.986,63.95,32.641 +2020-01-03 10:45:00,137.07,213.808,63.95,32.641 +2020-01-03 11:00:00,136.69,211.75,63.92100000000001,32.641 +2020-01-03 11:15:00,137.82,209.576,63.92100000000001,32.641 +2020-01-03 11:30:00,137.25,209.956,63.92100000000001,32.641 +2020-01-03 11:45:00,136.75,208.86900000000003,63.92100000000001,32.641 +2020-01-03 12:00:00,135.72,204.88400000000001,60.79600000000001,32.641 +2020-01-03 12:15:00,135.16,202.011,60.79600000000001,32.641 +2020-01-03 12:30:00,133.9,202.153,60.79600000000001,32.641 +2020-01-03 12:45:00,134.58,203.607,60.79600000000001,32.641 +2020-01-03 13:00:00,130.87,202.96200000000002,59.393,32.641 +2020-01-03 13:15:00,131.78,203.36,59.393,32.641 +2020-01-03 13:30:00,130.35,203.00799999999998,59.393,32.641 +2020-01-03 13:45:00,131.11,202.855,59.393,32.641 +2020-01-03 14:00:00,130.67,200.58700000000002,57.943999999999996,32.641 +2020-01-03 14:15:00,130.14,201.0,57.943999999999996,32.641 +2020-01-03 14:30:00,129.02,202.34799999999998,57.943999999999996,32.641 +2020-01-03 14:45:00,129.81,202.954,57.943999999999996,32.641 +2020-01-03 15:00:00,130.32,203.59099999999998,60.153999999999996,32.641 +2020-01-03 15:15:00,129.09,203.75099999999998,60.153999999999996,32.641 +2020-01-03 15:30:00,128.33,204.56099999999998,60.153999999999996,32.641 +2020-01-03 15:45:00,128.17,206.41299999999998,60.153999999999996,32.641 +2020-01-03 16:00:00,131.49,205.998,62.933,32.641 +2020-01-03 16:15:00,133.11,207.87400000000002,62.933,32.641 +2020-01-03 16:30:00,134.7,210.864,62.933,32.641 +2020-01-03 16:45:00,133.81,212.332,62.933,32.641 +2020-01-03 17:00:00,137.76,214.84900000000002,68.657,32.641 +2020-01-03 17:15:00,136.16,215.076,68.657,32.641 +2020-01-03 17:30:00,140.54,215.513,68.657,32.641 +2020-01-03 17:45:00,137.57,215.02900000000002,68.657,32.641 +2020-01-03 18:00:00,136.81,217.225,67.111,32.641 +2020-01-03 18:15:00,136.99,213.96900000000002,67.111,32.641 +2020-01-03 18:30:00,135.65,213.13,67.111,32.641 +2020-01-03 18:45:00,136.2,213.142,67.111,32.641 +2020-01-03 19:00:00,132.87,213.791,62.434,32.641 +2020-01-03 19:15:00,131.87,211.248,62.434,32.641 +2020-01-03 19:30:00,132.37,208.11900000000003,62.434,32.641 +2020-01-03 19:45:00,134.77,204.162,62.434,32.641 +2020-01-03 20:00:00,128.17,200.44400000000002,61.763000000000005,32.641 +2020-01-03 20:15:00,120.91,193.95,61.763000000000005,32.641 +2020-01-03 20:30:00,117.84,189.834,61.763000000000005,32.641 +2020-01-03 20:45:00,113.28,188.551,61.763000000000005,32.641 +2020-01-03 21:00:00,110.73,186.10299999999998,56.785,32.641 +2020-01-03 21:15:00,113.2,183.95,56.785,32.641 +2020-01-03 21:30:00,110.05,181.924,56.785,32.641 +2020-01-03 21:45:00,104.45,180.86900000000003,56.785,32.641 +2020-01-03 22:00:00,98.56,174.898,52.693000000000005,32.641 +2020-01-03 22:15:00,102.19,168.90400000000002,52.693000000000005,32.641 +2020-01-03 22:30:00,100.03,161.57,52.693000000000005,32.641 +2020-01-03 22:45:00,96.9,157.013,52.693000000000005,32.641 +2020-01-03 23:00:00,89.83,149.83100000000002,45.443999999999996,32.641 +2020-01-03 23:15:00,91.91,146.02700000000002,45.443999999999996,32.641 +2020-01-03 23:30:00,88.18,144.751,45.443999999999996,32.641 +2020-01-03 23:45:00,85.54,143.495,45.443999999999996,32.641 +2020-01-04 00:00:00,82.22,127.199,44.738,32.459 +2020-01-04 00:15:00,83.18,122.48899999999999,44.738,32.459 +2020-01-04 00:30:00,81.48,125.102,44.738,32.459 +2020-01-04 00:45:00,73.34,127.589,44.738,32.459 +2020-01-04 01:00:00,72.21,130.76,40.303000000000004,32.459 +2020-01-04 01:15:00,77.36,131.101,40.303000000000004,32.459 +2020-01-04 01:30:00,78.27,130.754,40.303000000000004,32.459 +2020-01-04 01:45:00,75.98,131.06799999999998,40.303000000000004,32.459 +2020-01-04 02:00:00,69.25,133.903,38.61,32.459 +2020-01-04 02:15:00,74.64,135.233,38.61,32.459 +2020-01-04 02:30:00,74.85,135.217,38.61,32.459 +2020-01-04 02:45:00,73.61,137.407,38.61,32.459 +2020-01-04 03:00:00,68.1,139.812,37.554,32.459 +2020-01-04 03:15:00,73.13,140.717,37.554,32.459 +2020-01-04 03:30:00,75.4,140.84799999999998,37.554,32.459 +2020-01-04 03:45:00,70.56,142.566,37.554,32.459 +2020-01-04 04:00:00,67.07,150.611,37.176,32.459 +2020-01-04 04:15:00,67.97,159.77100000000002,37.176,32.459 +2020-01-04 04:30:00,67.71,160.769,37.176,32.459 +2020-01-04 04:45:00,68.5,161.921,37.176,32.459 +2020-01-04 05:00:00,67.87,178.856,36.893,32.459 +2020-01-04 05:15:00,67.75,189.139,36.893,32.459 +2020-01-04 05:30:00,66.1,186.215,36.893,32.459 +2020-01-04 05:45:00,67.33,184.52,36.893,32.459 +2020-01-04 06:00:00,69.91,201.26,37.803000000000004,32.459 +2020-01-04 06:15:00,69.0,222.77200000000002,37.803000000000004,32.459 +2020-01-04 06:30:00,69.5,219.078,37.803000000000004,32.459 +2020-01-04 06:45:00,71.88,215.377,37.803000000000004,32.459 +2020-01-04 07:00:00,76.68,209.287,41.086999999999996,32.459 +2020-01-04 07:15:00,77.71,214.53599999999997,41.086999999999996,32.459 +2020-01-04 07:30:00,80.15,220.502,41.086999999999996,32.459 +2020-01-04 07:45:00,83.14,225.497,41.086999999999996,32.459 +2020-01-04 08:00:00,85.43,227.446,48.222,32.459 +2020-01-04 08:15:00,85.69,231.175,48.222,32.459 +2020-01-04 08:30:00,89.46,232.06099999999998,48.222,32.459 +2020-01-04 08:45:00,91.91,230.729,48.222,32.459 +2020-01-04 09:00:00,92.65,226.387,52.791000000000004,32.459 +2020-01-04 09:15:00,93.59,224.49200000000002,52.791000000000004,32.459 +2020-01-04 09:30:00,94.68,222.80200000000002,52.791000000000004,32.459 +2020-01-04 09:45:00,96.53,219.69,52.791000000000004,32.459 +2020-01-04 10:00:00,98.05,213.424,54.341,32.459 +2020-01-04 10:15:00,95.0,210.122,54.341,32.459 +2020-01-04 10:30:00,94.99,206.91099999999997,54.341,32.459 +2020-01-04 10:45:00,92.74,206.176,54.341,32.459 +2020-01-04 11:00:00,96.61,204.363,51.94,32.459 +2020-01-04 11:15:00,100.28,201.382,51.94,32.459 +2020-01-04 11:30:00,99.55,200.535,51.94,32.459 +2020-01-04 11:45:00,98.58,198.36700000000002,51.94,32.459 +2020-01-04 12:00:00,96.09,193.4,50.973,32.459 +2020-01-04 12:15:00,98.67,191.18099999999998,50.973,32.459 +2020-01-04 12:30:00,96.52,191.675,50.973,32.459 +2020-01-04 12:45:00,95.72,192.248,50.973,32.459 +2020-01-04 13:00:00,93.57,191.208,48.06399999999999,32.459 +2020-01-04 13:15:00,92.81,189.408,48.06399999999999,32.459 +2020-01-04 13:30:00,91.55,188.543,48.06399999999999,32.459 +2020-01-04 13:45:00,89.25,188.99,48.06399999999999,32.459 +2020-01-04 14:00:00,89.5,188.077,45.707,32.459 +2020-01-04 14:15:00,89.52,187.99,45.707,32.459 +2020-01-04 14:30:00,90.0,187.408,45.707,32.459 +2020-01-04 14:45:00,91.09,188.234,45.707,32.459 +2020-01-04 15:00:00,91.5,189.60299999999998,47.567,32.459 +2020-01-04 15:15:00,91.94,190.57,47.567,32.459 +2020-01-04 15:30:00,92.53,193.02900000000002,47.567,32.459 +2020-01-04 15:45:00,94.28,194.947,47.567,32.459 +2020-01-04 16:00:00,96.74,193.149,52.031000000000006,32.459 +2020-01-04 16:15:00,95.42,196.053,52.031000000000006,32.459 +2020-01-04 16:30:00,100.94,198.982,52.031000000000006,32.459 +2020-01-04 16:45:00,102.46,201.418,52.031000000000006,32.459 +2020-01-04 17:00:00,105.49,203.416,58.218999999999994,32.459 +2020-01-04 17:15:00,104.81,205.575,58.218999999999994,32.459 +2020-01-04 17:30:00,107.13,205.94299999999998,58.218999999999994,32.459 +2020-01-04 17:45:00,108.26,205.013,58.218999999999994,32.459 +2020-01-04 18:00:00,110.16,206.662,57.65,32.459 +2020-01-04 18:15:00,109.91,205.239,57.65,32.459 +2020-01-04 18:30:00,109.66,205.75799999999998,57.65,32.459 +2020-01-04 18:45:00,109.05,202.394,57.65,32.459 +2020-01-04 19:00:00,107.78,204.105,51.261,32.459 +2020-01-04 19:15:00,103.06,201.082,51.261,32.459 +2020-01-04 19:30:00,101.89,198.71599999999998,51.261,32.459 +2020-01-04 19:45:00,102.31,194.47799999999998,51.261,32.459 +2020-01-04 20:00:00,94.91,193.0,44.068000000000005,32.459 +2020-01-04 20:15:00,93.02,188.81400000000002,44.068000000000005,32.459 +2020-01-04 20:30:00,88.29,184.359,44.068000000000005,32.459 +2020-01-04 20:45:00,87.9,182.563,44.068000000000005,32.459 +2020-01-04 21:00:00,84.51,182.58599999999998,38.861,32.459 +2020-01-04 21:15:00,83.28,180.90400000000002,38.861,32.459 +2020-01-04 21:30:00,82.28,180.18200000000002,38.861,32.459 +2020-01-04 21:45:00,81.25,178.737,38.861,32.459 +2020-01-04 22:00:00,77.7,174.207,39.485,32.459 +2020-01-04 22:15:00,77.25,170.87,39.485,32.459 +2020-01-04 22:30:00,74.24,170.22299999999998,39.485,32.459 +2020-01-04 22:45:00,73.18,167.65200000000002,39.485,32.459 +2020-01-04 23:00:00,68.36,163.07399999999998,32.027,32.459 +2020-01-04 23:15:00,68.86,157.509,32.027,32.459 +2020-01-04 23:30:00,66.13,154.256,32.027,32.459 +2020-01-04 23:45:00,65.23,150.387,32.027,32.459 +2020-01-05 00:00:00,61.83,127.708,26.96,32.459 +2020-01-05 00:15:00,59.65,122.706,26.96,32.459 +2020-01-05 00:30:00,56.73,124.90899999999999,26.96,32.459 +2020-01-05 00:45:00,58.04,128.141,26.96,32.459 +2020-01-05 01:00:00,54.64,131.15200000000002,24.295,32.459 +2020-01-05 01:15:00,53.12,132.619,24.295,32.459 +2020-01-05 01:30:00,54.89,132.84799999999998,24.295,32.459 +2020-01-05 01:45:00,55.39,132.83700000000002,24.295,32.459 +2020-01-05 02:00:00,52.81,134.87,24.268,32.459 +2020-01-05 02:15:00,53.85,135.224,24.268,32.459 +2020-01-05 02:30:00,53.36,136.131,24.268,32.459 +2020-01-05 02:45:00,53.22,138.842,24.268,32.459 +2020-01-05 03:00:00,51.24,141.54,23.373,32.459 +2020-01-05 03:15:00,52.77,141.878,23.373,32.459 +2020-01-05 03:30:00,53.33,143.584,23.373,32.459 +2020-01-05 03:45:00,52.95,145.284,23.373,32.459 +2020-01-05 04:00:00,53.02,153.043,23.874000000000002,32.459 +2020-01-05 04:15:00,54.36,161.123,23.874000000000002,32.459 +2020-01-05 04:30:00,55.19,162.089,23.874000000000002,32.459 +2020-01-05 04:45:00,54.83,163.567,23.874000000000002,32.459 +2020-01-05 05:00:00,56.2,176.618,24.871,32.459 +2020-01-05 05:15:00,57.61,184.28099999999998,24.871,32.459 +2020-01-05 05:30:00,58.05,181.218,24.871,32.459 +2020-01-05 05:45:00,58.68,179.833,24.871,32.459 +2020-01-05 06:00:00,58.97,196.65900000000002,23.84,32.459 +2020-01-05 06:15:00,60.37,216.24099999999999,23.84,32.459 +2020-01-05 06:30:00,60.21,211.415,23.84,32.459 +2020-01-05 06:45:00,60.83,206.671,23.84,32.459 +2020-01-05 07:00:00,63.06,203.24599999999998,27.430999999999997,32.459 +2020-01-05 07:15:00,64.35,207.676,27.430999999999997,32.459 +2020-01-05 07:30:00,66.38,212.19400000000002,27.430999999999997,32.459 +2020-01-05 07:45:00,68.85,216.345,27.430999999999997,32.459 +2020-01-05 08:00:00,71.87,220.25099999999998,33.891999999999996,32.459 +2020-01-05 08:15:00,72.96,223.791,33.891999999999996,32.459 +2020-01-05 08:30:00,75.42,226.391,33.891999999999996,32.459 +2020-01-05 08:45:00,77.7,227.232,33.891999999999996,32.459 +2020-01-05 09:00:00,79.34,222.44799999999998,37.571,32.459 +2020-01-05 09:15:00,79.42,221.187,37.571,32.459 +2020-01-05 09:30:00,80.81,219.317,37.571,32.459 +2020-01-05 09:45:00,82.3,216.00799999999998,37.571,32.459 +2020-01-05 10:00:00,83.72,212.428,40.594,32.459 +2020-01-05 10:15:00,84.02,209.692,40.594,32.459 +2020-01-05 10:30:00,86.92,207.107,40.594,32.459 +2020-01-05 10:45:00,89.06,204.26,40.594,32.459 +2020-01-05 11:00:00,91.79,203.445,44.133,32.459 +2020-01-05 11:15:00,98.14,200.63099999999997,44.133,32.459 +2020-01-05 11:30:00,99.24,198.81799999999998,44.133,32.459 +2020-01-05 11:45:00,100.24,197.28900000000002,44.133,32.459 +2020-01-05 12:00:00,99.8,191.658,41.198,32.459 +2020-01-05 12:15:00,96.2,191.544,41.198,32.459 +2020-01-05 12:30:00,92.34,190.454,41.198,32.459 +2020-01-05 12:45:00,91.8,190.02599999999998,41.198,32.459 +2020-01-05 13:00:00,86.98,188.218,37.014,32.459 +2020-01-05 13:15:00,86.22,189.688,37.014,32.459 +2020-01-05 13:30:00,85.32,188.65200000000002,37.014,32.459 +2020-01-05 13:45:00,85.43,188.31799999999998,37.014,32.459 +2020-01-05 14:00:00,83.74,187.613,34.934,32.459 +2020-01-05 14:15:00,83.86,188.78400000000002,34.934,32.459 +2020-01-05 14:30:00,84.5,189.609,34.934,32.459 +2020-01-05 14:45:00,84.63,190.06799999999998,34.934,32.459 +2020-01-05 15:00:00,85.54,189.8,34.588,32.459 +2020-01-05 15:15:00,85.23,191.607,34.588,32.459 +2020-01-05 15:30:00,84.85,194.704,34.588,32.459 +2020-01-05 15:45:00,85.8,197.34099999999998,34.588,32.459 +2020-01-05 16:00:00,87.47,197.61700000000002,37.874,32.459 +2020-01-05 16:15:00,90.49,199.547,37.874,32.459 +2020-01-05 16:30:00,92.21,202.707,37.874,32.459 +2020-01-05 16:45:00,93.29,205.295,37.874,32.459 +2020-01-05 17:00:00,98.04,207.237,47.303999999999995,32.459 +2020-01-05 17:15:00,97.7,208.99900000000002,47.303999999999995,32.459 +2020-01-05 17:30:00,99.31,209.662,47.303999999999995,32.459 +2020-01-05 17:45:00,100.34,211.15,47.303999999999995,32.459 +2020-01-05 18:00:00,102.25,212.196,48.879,32.459 +2020-01-05 18:15:00,100.54,212.206,48.879,32.459 +2020-01-05 18:30:00,100.22,210.523,48.879,32.459 +2020-01-05 18:45:00,99.47,209.142,48.879,32.459 +2020-01-05 19:00:00,99.14,210.358,44.826,32.459 +2020-01-05 19:15:00,98.01,208.015,44.826,32.459 +2020-01-05 19:30:00,96.07,205.486,44.826,32.459 +2020-01-05 19:45:00,94.14,202.801,44.826,32.459 +2020-01-05 20:00:00,93.2,201.287,40.154,32.459 +2020-01-05 20:15:00,91.82,198.157,40.154,32.459 +2020-01-05 20:30:00,90.16,194.97099999999998,40.154,32.459 +2020-01-05 20:45:00,88.47,191.995,40.154,32.459 +2020-01-05 21:00:00,84.27,189.24099999999999,36.549,32.459 +2020-01-05 21:15:00,84.17,186.889,36.549,32.459 +2020-01-05 21:30:00,84.43,186.51,36.549,32.459 +2020-01-05 21:45:00,85.26,185.207,36.549,32.459 +2020-01-05 22:00:00,83.7,179.30900000000003,37.663000000000004,32.459 +2020-01-05 22:15:00,82.23,175.25599999999997,37.663000000000004,32.459 +2020-01-05 22:30:00,80.43,171.27900000000002,37.663000000000004,32.459 +2020-01-05 22:45:00,79.58,167.87099999999998,37.663000000000004,32.459 +2020-01-05 23:00:00,75.67,160.305,31.945,32.459 +2020-01-05 23:15:00,77.15,156.664,31.945,32.459 +2020-01-05 23:30:00,74.59,154.317,31.945,32.459 +2020-01-05 23:45:00,73.41,151.388,31.945,32.459 +2020-01-06 00:00:00,70.86,132.295,31.533,32.641 +2020-01-06 00:15:00,69.7,130.435,31.533,32.641 +2020-01-06 00:30:00,69.11,132.78799999999998,31.533,32.641 +2020-01-06 00:45:00,68.33,135.447,31.533,32.641 +2020-01-06 01:00:00,67.32,138.469,30.56,32.641 +2020-01-06 01:15:00,67.69,139.35399999999998,30.56,32.641 +2020-01-06 01:30:00,67.44,139.616,30.56,32.641 +2020-01-06 01:45:00,67.1,139.725,30.56,32.641 +2020-01-06 02:00:00,67.52,141.722,29.55,32.641 +2020-01-06 02:15:00,67.29,143.738,29.55,32.641 +2020-01-06 02:30:00,67.05,145.003,29.55,32.641 +2020-01-06 02:45:00,67.2,147.05200000000002,29.55,32.641 +2020-01-06 03:00:00,68.35,151.1,27.059,32.641 +2020-01-06 03:15:00,68.78,153.201,27.059,32.641 +2020-01-06 03:30:00,69.82,154.562,27.059,32.641 +2020-01-06 03:45:00,69.92,155.701,27.059,32.641 +2020-01-06 04:00:00,67.73,167.963,28.384,32.641 +2020-01-06 04:15:00,71.53,180.334,28.384,32.641 +2020-01-06 04:30:00,73.1,183.72799999999998,28.384,32.641 +2020-01-06 04:45:00,76.51,185.351,28.384,32.641 +2020-01-06 05:00:00,81.52,215.055,35.915,32.641 +2020-01-06 05:15:00,84.58,243.92,35.915,32.641 +2020-01-06 05:30:00,89.42,241.291,35.915,32.641 +2020-01-06 05:45:00,94.89,233.987,35.915,32.641 +2020-01-06 06:00:00,103.99,232.019,56.18,32.641 +2020-01-06 06:15:00,110.77,236.107,56.18,32.641 +2020-01-06 06:30:00,116.07,239.75,56.18,32.641 +2020-01-06 06:45:00,119.86,244.24,56.18,32.641 +2020-01-06 07:00:00,126.05,243.37599999999998,70.877,32.641 +2020-01-06 07:15:00,129.26,248.982,70.877,32.641 +2020-01-06 07:30:00,131.09,252.704,70.877,32.641 +2020-01-06 07:45:00,132.63,254.012,70.877,32.641 +2020-01-06 08:00:00,136.79,252.68599999999998,65.65,32.641 +2020-01-06 08:15:00,137.86,253.967,65.65,32.641 +2020-01-06 08:30:00,137.45,252.168,65.65,32.641 +2020-01-06 08:45:00,136.78,249.345,65.65,32.641 +2020-01-06 09:00:00,137.29,243.503,62.037,32.641 +2020-01-06 09:15:00,138.96,238.595,62.037,32.641 +2020-01-06 09:30:00,139.49,235.72099999999998,62.037,32.641 +2020-01-06 09:45:00,139.62,232.888,62.037,32.641 +2020-01-06 10:00:00,140.37,228.12599999999998,60.409,32.641 +2020-01-06 10:15:00,140.63,225.046,60.409,32.641 +2020-01-06 10:30:00,138.89,221.533,60.409,32.641 +2020-01-06 10:45:00,138.54,219.613,60.409,32.641 +2020-01-06 11:00:00,137.59,215.89700000000002,60.211999999999996,32.641 +2020-01-06 11:15:00,138.33,215.011,60.211999999999996,32.641 +2020-01-06 11:30:00,139.52,214.644,60.211999999999996,32.641 +2020-01-06 11:45:00,136.44,212.643,60.211999999999996,32.641 +2020-01-06 12:00:00,137.83,209.015,57.733000000000004,32.641 +2020-01-06 12:15:00,134.41,208.912,57.733000000000004,32.641 +2020-01-06 12:30:00,133.37,208.162,57.733000000000004,32.641 +2020-01-06 12:45:00,131.24,209.385,57.733000000000004,32.641 +2020-01-06 13:00:00,130.49,208.141,58.695,32.641 +2020-01-06 13:15:00,127.09,208.16099999999997,58.695,32.641 +2020-01-06 13:30:00,123.67,206.52900000000002,58.695,32.641 +2020-01-06 13:45:00,127.05,206.16299999999998,58.695,32.641 +2020-01-06 14:00:00,129.68,204.84099999999998,59.505,32.641 +2020-01-06 14:15:00,129.73,205.275,59.505,32.641 +2020-01-06 14:30:00,130.81,205.53400000000002,59.505,32.641 +2020-01-06 14:45:00,131.3,205.83599999999998,59.505,32.641 +2020-01-06 15:00:00,131.31,207.50599999999997,59.946000000000005,32.641 +2020-01-06 15:15:00,131.43,207.78400000000002,59.946000000000005,32.641 +2020-01-06 15:30:00,130.17,209.935,59.946000000000005,32.641 +2020-01-06 15:45:00,131.03,212.113,59.946000000000005,32.641 +2020-01-06 16:00:00,134.41,212.476,61.766999999999996,32.641 +2020-01-06 16:15:00,134.55,213.59400000000002,61.766999999999996,32.641 +2020-01-06 16:30:00,140.59,215.763,61.766999999999996,32.641 +2020-01-06 16:45:00,140.87,217.083,61.766999999999996,32.641 +2020-01-06 17:00:00,140.83,218.85299999999998,67.85600000000001,32.641 +2020-01-06 17:15:00,140.84,219.60299999999998,67.85600000000001,32.641 +2020-01-06 17:30:00,142.16,219.729,67.85600000000001,32.641 +2020-01-06 17:45:00,140.41,219.657,67.85600000000001,32.641 +2020-01-06 18:00:00,137.78,221.21599999999998,64.564,32.641 +2020-01-06 18:15:00,136.67,218.96900000000002,64.564,32.641 +2020-01-06 18:30:00,133.62,218.032,64.564,32.641 +2020-01-06 18:45:00,134.52,217.257,64.564,32.641 +2020-01-06 19:00:00,131.97,216.72299999999998,58.536,32.641 +2020-01-06 19:15:00,129.61,213.06,58.536,32.641 +2020-01-06 19:30:00,131.14,211.083,58.536,32.641 +2020-01-06 19:45:00,133.72,207.54,58.536,32.641 +2020-01-06 20:00:00,128.55,203.563,59.888999999999996,32.641 +2020-01-06 20:15:00,117.39,197.7,59.888999999999996,32.641 +2020-01-06 20:30:00,115.28,192.479,59.888999999999996,32.641 +2020-01-06 20:45:00,111.64,191.25099999999998,59.888999999999996,32.641 +2020-01-06 21:00:00,108.63,189.085,52.652,32.641 +2020-01-06 21:15:00,110.16,185.43099999999998,52.652,32.641 +2020-01-06 21:30:00,111.85,184.145,52.652,32.641 +2020-01-06 21:45:00,110.02,182.327,52.652,32.641 +2020-01-06 22:00:00,103.97,173.459,46.17,32.641 +2020-01-06 22:15:00,101.93,167.895,46.17,32.641 +2020-01-06 22:30:00,100.68,153.954,46.17,32.641 +2020-01-06 22:45:00,97.59,145.379,46.17,32.641 +2020-01-06 23:00:00,90.0,138.627,36.281,32.641 +2020-01-06 23:15:00,87.29,137.92700000000002,36.281,32.641 +2020-01-06 23:30:00,83.89,138.554,36.281,32.641 +2020-01-06 23:45:00,88.74,138.484,36.281,32.641 +2020-01-07 00:00:00,86.26,131.85,38.821999999999996,32.641 +2020-01-07 00:15:00,81.91,131.452,38.821999999999996,32.641 +2020-01-07 00:30:00,81.41,132.71200000000002,38.821999999999996,32.641 +2020-01-07 00:45:00,83.42,134.23,38.821999999999996,32.641 +2020-01-07 01:00:00,82.73,137.08700000000002,36.936,32.641 +2020-01-07 01:15:00,80.08,137.469,36.936,32.641 +2020-01-07 01:30:00,78.9,137.929,36.936,32.641 +2020-01-07 01:45:00,81.69,138.401,36.936,32.641 +2020-01-07 02:00:00,81.57,140.446,34.42,32.641 +2020-01-07 02:15:00,80.71,142.245,34.42,32.641 +2020-01-07 02:30:00,78.77,142.898,34.42,32.641 +2020-01-07 02:45:00,81.99,144.93200000000002,34.42,32.641 +2020-01-07 03:00:00,82.0,147.695,33.585,32.641 +2020-01-07 03:15:00,79.21,148.804,33.585,32.641 +2020-01-07 03:30:00,82.09,150.697,33.585,32.641 +2020-01-07 03:45:00,82.69,152.15,33.585,32.641 +2020-01-07 04:00:00,77.46,164.275,35.622,32.641 +2020-01-07 04:15:00,77.37,176.252,35.622,32.641 +2020-01-07 04:30:00,77.18,179.312,35.622,32.641 +2020-01-07 04:45:00,80.05,182.22,35.622,32.641 +2020-01-07 05:00:00,84.68,217.25599999999997,40.599000000000004,32.641 +2020-01-07 05:15:00,88.21,245.796,40.599000000000004,32.641 +2020-01-07 05:30:00,91.8,241.465,40.599000000000004,32.641 +2020-01-07 05:45:00,96.21,234.25900000000001,40.599000000000004,32.641 +2020-01-07 06:00:00,104.74,230.912,55.203,32.641 +2020-01-07 06:15:00,111.62,236.783,55.203,32.641 +2020-01-07 06:30:00,115.53,239.761,55.203,32.641 +2020-01-07 06:45:00,119.58,243.951,55.203,32.641 +2020-01-07 07:00:00,121.42,242.885,69.029,32.641 +2020-01-07 07:15:00,129.39,248.329,69.029,32.641 +2020-01-07 07:30:00,132.48,251.43200000000002,69.029,32.641 +2020-01-07 07:45:00,134.56,253.047,69.029,32.641 +2020-01-07 08:00:00,136.12,251.842,65.85300000000001,32.641 +2020-01-07 08:15:00,135.08,252.024,65.85300000000001,32.641 +2020-01-07 08:30:00,135.3,249.989,65.85300000000001,32.641 +2020-01-07 08:45:00,134.8,246.93400000000003,65.85300000000001,32.641 +2020-01-07 09:00:00,135.59,240.132,61.566,32.641 +2020-01-07 09:15:00,137.31,236.993,61.566,32.641 +2020-01-07 09:30:00,139.13,234.81900000000002,61.566,32.641 +2020-01-07 09:45:00,138.88,231.628,61.566,32.641 +2020-01-07 10:00:00,138.47,226.34,61.244,32.641 +2020-01-07 10:15:00,136.8,222.109,61.244,32.641 +2020-01-07 10:30:00,135.87,218.78,61.244,32.641 +2020-01-07 10:45:00,136.63,217.085,61.244,32.641 +2020-01-07 11:00:00,136.36,214.982,61.16,32.641 +2020-01-07 11:15:00,139.62,213.697,61.16,32.641 +2020-01-07 11:30:00,138.15,212.15400000000002,61.16,32.641 +2020-01-07 11:45:00,134.33,210.957,61.16,32.641 +2020-01-07 12:00:00,132.11,205.859,59.09,32.641 +2020-01-07 12:15:00,133.48,205.292,59.09,32.641 +2020-01-07 12:30:00,132.6,205.255,59.09,32.641 +2020-01-07 12:45:00,132.01,206.10299999999998,59.09,32.641 +2020-01-07 13:00:00,132.8,204.433,60.21,32.641 +2020-01-07 13:15:00,132.97,203.91299999999998,60.21,32.641 +2020-01-07 13:30:00,131.24,203.553,60.21,32.641 +2020-01-07 13:45:00,130.77,203.46599999999998,60.21,32.641 +2020-01-07 14:00:00,129.75,202.437,60.673,32.641 +2020-01-07 14:15:00,129.53,203.049,60.673,32.641 +2020-01-07 14:30:00,124.81,203.976,60.673,32.641 +2020-01-07 14:45:00,126.69,204.27,60.673,32.641 +2020-01-07 15:00:00,127.02,205.505,62.232,32.641 +2020-01-07 15:15:00,126.96,206.055,62.232,32.641 +2020-01-07 15:30:00,127.74,208.46,62.232,32.641 +2020-01-07 15:45:00,128.19,210.165,62.232,32.641 +2020-01-07 16:00:00,130.28,210.956,63.611999999999995,32.641 +2020-01-07 16:15:00,132.81,212.595,63.611999999999995,32.641 +2020-01-07 16:30:00,135.09,215.49900000000002,63.611999999999995,32.641 +2020-01-07 16:45:00,135.68,217.08900000000003,63.611999999999995,32.641 +2020-01-07 17:00:00,136.74,219.416,70.658,32.641 +2020-01-07 17:15:00,137.73,220.201,70.658,32.641 +2020-01-07 17:30:00,140.3,221.10299999999998,70.658,32.641 +2020-01-07 17:45:00,142.14,220.933,70.658,32.641 +2020-01-07 18:00:00,139.97,222.459,68.361,32.641 +2020-01-07 18:15:00,138.96,219.579,68.361,32.641 +2020-01-07 18:30:00,138.24,218.333,68.361,32.641 +2020-01-07 18:45:00,139.74,218.446,68.361,32.641 +2020-01-07 19:00:00,132.95,218.03900000000002,62.922,32.641 +2020-01-07 19:15:00,131.59,214.065,62.922,32.641 +2020-01-07 19:30:00,137.99,211.38400000000001,62.922,32.641 +2020-01-07 19:45:00,136.02,207.86,62.922,32.641 +2020-01-07 20:00:00,126.68,204.014,63.251999999999995,32.641 +2020-01-07 20:15:00,117.77,197.558,63.251999999999995,32.641 +2020-01-07 20:30:00,114.85,193.41400000000002,63.251999999999995,32.641 +2020-01-07 20:45:00,111.71,191.56,63.251999999999995,32.641 +2020-01-07 21:00:00,108.54,188.565,54.47,32.641 +2020-01-07 21:15:00,108.54,185.96599999999998,54.47,32.641 +2020-01-07 21:30:00,111.64,183.882,54.47,32.641 +2020-01-07 21:45:00,109.59,182.312,54.47,32.641 +2020-01-07 22:00:00,99.32,175.27200000000002,51.12,32.641 +2020-01-07 22:15:00,98.89,169.467,51.12,32.641 +2020-01-07 22:30:00,93.04,155.618,51.12,32.641 +2020-01-07 22:45:00,92.29,147.349,51.12,32.641 +2020-01-07 23:00:00,94.19,140.65200000000002,42.156000000000006,32.641 +2020-01-07 23:15:00,93.18,138.885,42.156000000000006,32.641 +2020-01-07 23:30:00,88.85,139.142,42.156000000000006,32.641 +2020-01-07 23:45:00,80.72,138.58700000000002,42.156000000000006,32.641 +2020-01-08 00:00:00,80.57,131.975,37.192,32.641 +2020-01-08 00:15:00,77.7,131.555,37.192,32.641 +2020-01-08 00:30:00,82.22,132.80200000000002,37.192,32.641 +2020-01-08 00:45:00,83.18,134.30200000000002,37.192,32.641 +2020-01-08 01:00:00,80.31,137.168,32.24,32.641 +2020-01-08 01:15:00,80.35,137.54,32.24,32.641 +2020-01-08 01:30:00,81.14,138.001,32.24,32.641 +2020-01-08 01:45:00,83.23,138.461,32.24,32.641 +2020-01-08 02:00:00,82.4,140.52,30.34,32.641 +2020-01-08 02:15:00,76.87,142.319,30.34,32.641 +2020-01-08 02:30:00,81.52,142.981,30.34,32.641 +2020-01-08 02:45:00,82.44,145.014,30.34,32.641 +2020-01-08 03:00:00,82.38,147.77100000000002,29.129,32.641 +2020-01-08 03:15:00,76.92,148.901,29.129,32.641 +2020-01-08 03:30:00,80.45,150.79399999999998,29.129,32.641 +2020-01-08 03:45:00,83.8,152.255,29.129,32.641 +2020-01-08 04:00:00,82.13,164.354,30.075,32.641 +2020-01-08 04:15:00,80.51,176.32,30.075,32.641 +2020-01-08 04:30:00,79.95,179.37900000000002,30.075,32.641 +2020-01-08 04:45:00,82.2,182.28,30.075,32.641 +2020-01-08 05:00:00,86.19,217.273,35.684,32.641 +2020-01-08 05:15:00,86.69,245.778,35.684,32.641 +2020-01-08 05:30:00,92.91,241.455,35.684,32.641 +2020-01-08 05:45:00,97.02,234.27,35.684,32.641 +2020-01-08 06:00:00,105.5,230.947,51.49,32.641 +2020-01-08 06:15:00,111.76,236.826,51.49,32.641 +2020-01-08 06:30:00,116.68,239.821,51.49,32.641 +2020-01-08 06:45:00,121.17,244.044,51.49,32.641 +2020-01-08 07:00:00,125.64,242.99099999999999,68.242,32.641 +2020-01-08 07:15:00,129.08,248.426,68.242,32.641 +2020-01-08 07:30:00,132.63,251.513,68.242,32.641 +2020-01-08 07:45:00,135.49,253.108,68.242,32.641 +2020-01-08 08:00:00,134.07,251.9,63.619,32.641 +2020-01-08 08:15:00,136.8,252.067,63.619,32.641 +2020-01-08 08:30:00,136.39,250.003,63.619,32.641 +2020-01-08 08:45:00,135.84,246.926,63.619,32.641 +2020-01-08 09:00:00,136.47,240.108,61.333,32.641 +2020-01-08 09:15:00,137.33,236.976,61.333,32.641 +2020-01-08 09:30:00,137.5,234.81799999999998,61.333,32.641 +2020-01-08 09:45:00,137.48,231.62099999999998,61.333,32.641 +2020-01-08 10:00:00,134.72,226.333,59.663000000000004,32.641 +2020-01-08 10:15:00,132.18,222.105,59.663000000000004,32.641 +2020-01-08 10:30:00,130.36,218.767,59.663000000000004,32.641 +2020-01-08 10:45:00,128.21,217.075,59.663000000000004,32.641 +2020-01-08 11:00:00,129.5,214.952,59.771,32.641 +2020-01-08 11:15:00,129.67,213.665,59.771,32.641 +2020-01-08 11:30:00,130.99,212.125,59.771,32.641 +2020-01-08 11:45:00,126.7,210.93099999999998,59.771,32.641 +2020-01-08 12:00:00,126.02,205.842,58.723,32.641 +2020-01-08 12:15:00,124.09,205.294,58.723,32.641 +2020-01-08 12:30:00,124.12,205.25099999999998,58.723,32.641 +2020-01-08 12:45:00,123.16,206.101,58.723,32.641 +2020-01-08 13:00:00,121.39,204.422,58.727,32.641 +2020-01-08 13:15:00,121.87,203.891,58.727,32.641 +2020-01-08 13:30:00,120.34,203.52,58.727,32.641 +2020-01-08 13:45:00,120.5,203.426,58.727,32.641 +2020-01-08 14:00:00,121.17,202.41299999999998,59.803999999999995,32.641 +2020-01-08 14:15:00,121.88,203.019,59.803999999999995,32.641 +2020-01-08 14:30:00,122.69,203.952,59.803999999999995,32.641 +2020-01-08 14:45:00,123.96,204.262,59.803999999999995,32.641 +2020-01-08 15:00:00,124.01,205.516,61.05,32.641 +2020-01-08 15:15:00,124.57,206.048,61.05,32.641 +2020-01-08 15:30:00,121.73,208.44799999999998,61.05,32.641 +2020-01-08 15:45:00,124.55,210.141,61.05,32.641 +2020-01-08 16:00:00,126.01,210.935,64.012,32.641 +2020-01-08 16:15:00,128.33,212.583,64.012,32.641 +2020-01-08 16:30:00,131.93,215.49400000000003,64.012,32.641 +2020-01-08 16:45:00,136.83,217.09400000000002,64.012,32.641 +2020-01-08 17:00:00,138.72,219.403,66.751,32.641 +2020-01-08 17:15:00,141.55,220.22,66.751,32.641 +2020-01-08 17:30:00,141.31,221.15200000000002,66.751,32.641 +2020-01-08 17:45:00,141.4,220.99900000000002,66.751,32.641 +2020-01-08 18:00:00,140.41,222.546,65.91199999999999,32.641 +2020-01-08 18:15:00,138.77,219.672,65.91199999999999,32.641 +2020-01-08 18:30:00,137.47,218.43200000000002,65.91199999999999,32.641 +2020-01-08 18:45:00,137.79,218.563,65.91199999999999,32.641 +2020-01-08 19:00:00,135.13,218.12400000000002,63.324,32.641 +2020-01-08 19:15:00,133.38,214.15,63.324,32.641 +2020-01-08 19:30:00,137.98,211.472,63.324,32.641 +2020-01-08 19:45:00,138.21,207.94799999999998,63.324,32.641 +2020-01-08 20:00:00,130.16,204.08599999999998,63.573,32.641 +2020-01-08 20:15:00,121.28,197.63099999999997,63.573,32.641 +2020-01-08 20:30:00,114.19,193.47799999999998,63.573,32.641 +2020-01-08 20:45:00,114.46,191.641,63.573,32.641 +2020-01-08 21:00:00,111.07,188.627,55.073,32.641 +2020-01-08 21:15:00,113.94,186.01,55.073,32.641 +2020-01-08 21:30:00,114.23,183.926,55.073,32.641 +2020-01-08 21:45:00,109.84,182.37,55.073,32.641 +2020-01-08 22:00:00,103.51,175.329,51.321999999999996,32.641 +2020-01-08 22:15:00,99.13,169.53799999999998,51.321999999999996,32.641 +2020-01-08 22:30:00,95.71,155.703,51.321999999999996,32.641 +2020-01-08 22:45:00,101.09,147.44299999999998,51.321999999999996,32.641 +2020-01-08 23:00:00,97.81,140.725,42.09,32.641 +2020-01-08 23:15:00,93.34,138.966,42.09,32.641 +2020-01-08 23:30:00,90.01,139.239,42.09,32.641 +2020-01-08 23:45:00,90.33,138.685,42.09,32.641 +2020-01-09 00:00:00,89.51,132.093,38.399,32.641 +2020-01-09 00:15:00,88.2,131.65,38.399,32.641 +2020-01-09 00:30:00,82.53,132.88299999999998,38.399,32.641 +2020-01-09 00:45:00,87.05,134.366,38.399,32.641 +2020-01-09 01:00:00,85.85,137.24,36.94,32.641 +2020-01-09 01:15:00,83.8,137.60399999999998,36.94,32.641 +2020-01-09 01:30:00,79.51,138.062,36.94,32.641 +2020-01-09 01:45:00,77.14,138.512,36.94,32.641 +2020-01-09 02:00:00,82.48,140.584,35.275,32.641 +2020-01-09 02:15:00,82.45,142.384,35.275,32.641 +2020-01-09 02:30:00,84.42,143.055,35.275,32.641 +2020-01-09 02:45:00,79.29,145.088,35.275,32.641 +2020-01-09 03:00:00,83.67,147.839,35.329,32.641 +2020-01-09 03:15:00,85.27,148.987,35.329,32.641 +2020-01-09 03:30:00,86.25,150.881,35.329,32.641 +2020-01-09 03:45:00,82.05,152.349,35.329,32.641 +2020-01-09 04:00:00,85.8,164.422,36.275,32.641 +2020-01-09 04:15:00,85.66,176.38,36.275,32.641 +2020-01-09 04:30:00,84.93,179.43599999999998,36.275,32.641 +2020-01-09 04:45:00,85.52,182.334,36.275,32.641 +2020-01-09 05:00:00,88.07,217.282,42.193999999999996,32.641 +2020-01-09 05:15:00,91.08,245.75099999999998,42.193999999999996,32.641 +2020-01-09 05:30:00,94.82,241.43599999999998,42.193999999999996,32.641 +2020-01-09 05:45:00,99.1,234.27200000000002,42.193999999999996,32.641 +2020-01-09 06:00:00,105.02,230.97299999999998,56.422,32.641 +2020-01-09 06:15:00,114.36,236.86,56.422,32.641 +2020-01-09 06:30:00,118.47,239.872,56.422,32.641 +2020-01-09 06:45:00,123.49,244.12599999999998,56.422,32.641 +2020-01-09 07:00:00,128.98,243.08900000000003,72.569,32.641 +2020-01-09 07:15:00,133.18,248.513,72.569,32.641 +2020-01-09 07:30:00,135.84,251.582,72.569,32.641 +2020-01-09 07:45:00,136.0,253.157,72.569,32.641 +2020-01-09 08:00:00,136.35,251.945,67.704,32.641 +2020-01-09 08:15:00,136.41,252.09599999999998,67.704,32.641 +2020-01-09 08:30:00,136.61,250.002,67.704,32.641 +2020-01-09 08:45:00,135.57,246.90599999999998,67.704,32.641 +2020-01-09 09:00:00,134.91,240.072,63.434,32.641 +2020-01-09 09:15:00,135.13,236.947,63.434,32.641 +2020-01-09 09:30:00,132.82,234.805,63.434,32.641 +2020-01-09 09:45:00,134.04,231.602,63.434,32.641 +2020-01-09 10:00:00,133.79,226.315,61.88399999999999,32.641 +2020-01-09 10:15:00,132.44,222.09,61.88399999999999,32.641 +2020-01-09 10:30:00,131.18,218.74200000000002,61.88399999999999,32.641 +2020-01-09 10:45:00,129.86,217.054,61.88399999999999,32.641 +2020-01-09 11:00:00,129.31,214.91099999999997,61.481,32.641 +2020-01-09 11:15:00,129.73,213.62400000000002,61.481,32.641 +2020-01-09 11:30:00,129.62,212.08599999999998,61.481,32.641 +2020-01-09 11:45:00,128.23,210.895,61.481,32.641 +2020-01-09 12:00:00,128.19,205.817,59.527,32.641 +2020-01-09 12:15:00,126.99,205.28599999999997,59.527,32.641 +2020-01-09 12:30:00,126.29,205.238,59.527,32.641 +2020-01-09 12:45:00,125.88,206.08900000000003,59.527,32.641 +2020-01-09 13:00:00,125.21,204.40099999999998,58.794,32.641 +2020-01-09 13:15:00,125.26,203.859,58.794,32.641 +2020-01-09 13:30:00,120.6,203.476,58.794,32.641 +2020-01-09 13:45:00,123.97,203.37599999999998,58.794,32.641 +2020-01-09 14:00:00,127.41,202.38099999999997,60.32,32.641 +2020-01-09 14:15:00,128.59,202.979,60.32,32.641 +2020-01-09 14:30:00,127.7,203.919,60.32,32.641 +2020-01-09 14:45:00,128.7,204.245,60.32,32.641 +2020-01-09 15:00:00,129.88,205.519,62.52,32.641 +2020-01-09 15:15:00,129.65,206.03,62.52,32.641 +2020-01-09 15:30:00,128.81,208.423,62.52,32.641 +2020-01-09 15:45:00,129.3,210.108,62.52,32.641 +2020-01-09 16:00:00,130.58,210.90200000000002,64.199,32.641 +2020-01-09 16:15:00,132.89,212.55900000000003,64.199,32.641 +2020-01-09 16:30:00,135.51,215.477,64.199,32.641 +2020-01-09 16:45:00,138.94,217.085,64.199,32.641 +2020-01-09 17:00:00,140.54,219.38,68.19800000000001,32.641 +2020-01-09 17:15:00,141.52,220.227,68.19800000000001,32.641 +2020-01-09 17:30:00,142.06,221.187,68.19800000000001,32.641 +2020-01-09 17:45:00,142.3,221.053,68.19800000000001,32.641 +2020-01-09 18:00:00,140.27,222.622,67.899,32.641 +2020-01-09 18:15:00,139.4,219.75400000000002,67.899,32.641 +2020-01-09 18:30:00,137.51,218.52,67.899,32.641 +2020-01-09 18:45:00,137.88,218.67,67.899,32.641 +2020-01-09 19:00:00,135.66,218.199,64.72399999999999,32.641 +2020-01-09 19:15:00,133.63,214.226,64.72399999999999,32.641 +2020-01-09 19:30:00,136.63,211.551,64.72399999999999,32.641 +2020-01-09 19:45:00,136.33,208.028,64.72399999999999,32.641 +2020-01-09 20:00:00,130.0,204.149,64.062,32.641 +2020-01-09 20:15:00,120.43,197.695,64.062,32.641 +2020-01-09 20:30:00,115.58,193.532,64.062,32.641 +2020-01-09 20:45:00,114.76,191.713,64.062,32.641 +2020-01-09 21:00:00,111.28,188.68,57.971000000000004,32.641 +2020-01-09 21:15:00,114.3,186.046,57.971000000000004,32.641 +2020-01-09 21:30:00,112.74,183.96200000000002,57.971000000000004,32.641 +2020-01-09 21:45:00,107.12,182.422,57.971000000000004,32.641 +2020-01-09 22:00:00,103.37,175.37599999999998,53.715,32.641 +2020-01-09 22:15:00,97.81,169.60299999999998,53.715,32.641 +2020-01-09 22:30:00,96.27,155.78,53.715,32.641 +2020-01-09 22:45:00,99.06,147.52700000000002,53.715,32.641 +2020-01-09 23:00:00,96.54,140.78799999999998,47.8,32.641 +2020-01-09 23:15:00,90.59,139.037,47.8,32.641 +2020-01-09 23:30:00,83.45,139.326,47.8,32.641 +2020-01-09 23:45:00,85.64,138.773,47.8,32.641 +2020-01-10 00:00:00,84.68,131.172,43.656000000000006,32.641 +2020-01-10 00:15:00,87.29,130.907,43.656000000000006,32.641 +2020-01-10 00:30:00,86.37,131.95600000000002,43.656000000000006,32.641 +2020-01-10 00:45:00,81.92,133.513,43.656000000000006,32.641 +2020-01-10 01:00:00,80.49,136.08100000000002,41.263000000000005,32.641 +2020-01-10 01:15:00,84.15,137.489,41.263000000000005,32.641 +2020-01-10 01:30:00,82.86,137.643,41.263000000000005,32.641 +2020-01-10 01:45:00,80.26,138.219,41.263000000000005,32.641 +2020-01-10 02:00:00,82.41,140.32299999999998,40.799,32.641 +2020-01-10 02:15:00,82.9,142.0,40.799,32.641 +2020-01-10 02:30:00,82.52,143.188,40.799,32.641 +2020-01-10 02:45:00,78.57,145.32399999999998,40.799,32.641 +2020-01-10 03:00:00,82.79,146.91,41.398,32.641 +2020-01-10 03:15:00,83.34,149.214,41.398,32.641 +2020-01-10 03:30:00,81.33,151.107,41.398,32.641 +2020-01-10 03:45:00,83.38,152.868,41.398,32.641 +2020-01-10 04:00:00,84.86,165.15900000000002,42.38,32.641 +2020-01-10 04:15:00,82.09,176.97400000000002,42.38,32.641 +2020-01-10 04:30:00,80.74,180.197,42.38,32.641 +2020-01-10 04:45:00,81.91,181.887,42.38,32.641 +2020-01-10 05:00:00,85.77,215.43599999999998,46.181000000000004,32.641 +2020-01-10 05:15:00,88.51,245.451,46.181000000000004,32.641 +2020-01-10 05:30:00,92.4,242.31599999999997,46.181000000000004,32.641 +2020-01-10 05:45:00,97.86,235.141,46.181000000000004,32.641 +2020-01-10 06:00:00,105.68,232.324,59.33,32.641 +2020-01-10 06:15:00,110.88,236.578,59.33,32.641 +2020-01-10 06:30:00,114.95,238.657,59.33,32.641 +2020-01-10 06:45:00,120.02,244.748,59.33,32.641 +2020-01-10 07:00:00,125.32,242.745,72.454,32.641 +2020-01-10 07:15:00,129.26,249.206,72.454,32.641 +2020-01-10 07:30:00,133.08,252.229,72.454,32.641 +2020-01-10 07:45:00,137.34,252.824,72.454,32.641 +2020-01-10 08:00:00,138.92,250.329,67.175,32.641 +2020-01-10 08:15:00,139.19,249.984,67.175,32.641 +2020-01-10 08:30:00,142.05,248.93900000000002,67.175,32.641 +2020-01-10 08:45:00,139.98,244.104,67.175,32.641 +2020-01-10 09:00:00,139.84,237.938,65.365,32.641 +2020-01-10 09:15:00,142.47,235.299,65.365,32.641 +2020-01-10 09:30:00,143.87,232.75599999999997,65.365,32.641 +2020-01-10 09:45:00,144.34,229.4,65.365,32.641 +2020-01-10 10:00:00,143.86,222.885,63.95,32.641 +2020-01-10 10:15:00,145.24,219.442,63.95,32.641 +2020-01-10 10:30:00,146.08,215.968,63.95,32.641 +2020-01-10 10:45:00,145.52,213.80700000000002,63.95,32.641 +2020-01-10 11:00:00,143.53,211.615,63.92100000000001,32.641 +2020-01-10 11:15:00,144.41,209.428,63.92100000000001,32.641 +2020-01-10 11:30:00,142.84,209.82,63.92100000000001,32.641 +2020-01-10 11:45:00,140.57,208.75099999999998,63.92100000000001,32.641 +2020-01-10 12:00:00,139.98,204.835,60.79600000000001,32.641 +2020-01-10 12:15:00,139.61,202.088,60.79600000000001,32.641 +2020-01-10 12:30:00,138.38,202.197,60.79600000000001,32.641 +2020-01-10 12:45:00,137.77,203.66400000000002,60.79600000000001,32.641 +2020-01-10 13:00:00,135.76,202.949,59.393,32.641 +2020-01-10 13:15:00,137.41,203.271,59.393,32.641 +2020-01-10 13:30:00,133.35,202.84099999999998,59.393,32.641 +2020-01-10 13:45:00,131.39,202.644,59.393,32.641 +2020-01-10 14:00:00,131.97,200.481,57.943999999999996,32.641 +2020-01-10 14:15:00,132.24,200.84900000000002,57.943999999999996,32.641 +2020-01-10 14:30:00,130.8,202.25,57.943999999999996,32.641 +2020-01-10 14:45:00,131.39,202.96599999999998,57.943999999999996,32.641 +2020-01-10 15:00:00,131.3,203.739,60.153999999999996,32.641 +2020-01-10 15:15:00,130.95,203.773,60.153999999999996,32.641 +2020-01-10 15:30:00,130.15,204.55,60.153999999999996,32.641 +2020-01-10 15:45:00,129.69,206.333,60.153999999999996,32.641 +2020-01-10 16:00:00,130.75,205.926,62.933,32.641 +2020-01-10 16:15:00,132.79,207.87400000000002,62.933,32.641 +2020-01-10 16:30:00,135.68,210.912,62.933,32.641 +2020-01-10 16:45:00,137.72,212.451,62.933,32.641 +2020-01-10 17:00:00,139.5,214.852,68.657,32.641 +2020-01-10 17:15:00,139.88,215.297,68.657,32.641 +2020-01-10 17:30:00,142.57,215.93400000000003,68.657,32.641 +2020-01-10 17:45:00,139.22,215.576,68.657,32.641 +2020-01-10 18:00:00,136.73,217.919,67.111,32.641 +2020-01-10 18:15:00,136.11,214.695,67.111,32.641 +2020-01-10 18:30:00,134.73,213.893,67.111,32.641 +2020-01-10 18:45:00,135.6,214.03400000000002,67.111,32.641 +2020-01-10 19:00:00,132.42,214.46200000000002,62.434,32.641 +2020-01-10 19:15:00,130.68,211.921,62.434,32.641 +2020-01-10 19:30:00,135.53,208.804,62.434,32.641 +2020-01-10 19:45:00,135.12,204.845,62.434,32.641 +2020-01-10 20:00:00,127.21,201.00900000000001,61.763000000000005,32.641 +2020-01-10 20:15:00,118.7,194.521,61.763000000000005,32.641 +2020-01-10 20:30:00,112.79,190.331,61.763000000000005,32.641 +2020-01-10 20:45:00,111.77,189.175,61.763000000000005,32.641 +2020-01-10 21:00:00,108.32,186.59400000000002,56.785,32.641 +2020-01-10 21:15:00,110.88,184.317,56.785,32.641 +2020-01-10 21:30:00,102.73,182.29,56.785,32.641 +2020-01-10 21:45:00,100.86,181.33900000000003,56.785,32.641 +2020-01-10 22:00:00,96.64,175.354,52.693000000000005,32.641 +2020-01-10 22:15:00,93.07,169.468,52.693000000000005,32.641 +2020-01-10 22:30:00,92.88,162.24,52.693000000000005,32.641 +2020-01-10 22:45:00,93.82,157.739,52.693000000000005,32.641 +2020-01-10 23:00:00,88.99,150.405,45.443999999999996,32.641 +2020-01-10 23:15:00,89.2,146.657,45.443999999999996,32.641 +2020-01-10 23:30:00,85.72,145.495,45.443999999999996,32.641 +2020-01-10 23:45:00,80.79,144.237,45.443999999999996,32.641 +2020-01-11 00:00:00,75.56,128.079,44.738,32.459 +2020-01-11 00:15:00,80.09,123.209,44.738,32.459 +2020-01-11 00:30:00,80.07,125.723,44.738,32.459 +2020-01-11 00:45:00,77.68,128.094,44.738,32.459 +2020-01-11 01:00:00,71.96,131.326,40.303000000000004,32.459 +2020-01-11 01:15:00,70.77,131.606,40.303000000000004,32.459 +2020-01-11 01:30:00,68.37,131.253,40.303000000000004,32.459 +2020-01-11 01:45:00,68.2,131.491,40.303000000000004,32.459 +2020-01-11 02:00:00,69.18,134.41899999999998,38.61,32.459 +2020-01-11 02:15:00,74.74,135.753,38.61,32.459 +2020-01-11 02:30:00,74.65,135.797,38.61,32.459 +2020-01-11 02:45:00,74.36,137.984,38.61,32.459 +2020-01-11 03:00:00,67.49,140.34799999999998,37.554,32.459 +2020-01-11 03:15:00,72.14,141.393,37.554,32.459 +2020-01-11 03:30:00,74.34,141.52200000000002,37.554,32.459 +2020-01-11 03:45:00,74.02,143.29399999999998,37.554,32.459 +2020-01-11 04:00:00,69.83,151.159,37.176,32.459 +2020-01-11 04:15:00,66.48,160.25,37.176,32.459 +2020-01-11 04:30:00,66.65,161.23,37.176,32.459 +2020-01-11 04:45:00,68.43,162.349,37.176,32.459 +2020-01-11 05:00:00,69.3,178.98,36.893,32.459 +2020-01-11 05:15:00,68.52,189.00599999999997,36.893,32.459 +2020-01-11 05:30:00,68.35,186.139,36.893,32.459 +2020-01-11 05:45:00,69.45,184.59400000000002,36.893,32.459 +2020-01-11 06:00:00,71.01,201.507,37.803000000000004,32.459 +2020-01-11 06:15:00,71.8,223.074,37.803000000000004,32.459 +2020-01-11 06:30:00,72.53,219.50099999999998,37.803000000000004,32.459 +2020-01-11 06:45:00,74.42,216.02700000000002,37.803000000000004,32.459 +2020-01-11 07:00:00,77.16,210.03799999999998,41.086999999999996,32.459 +2020-01-11 07:15:00,79.89,215.21599999999998,41.086999999999996,32.459 +2020-01-11 07:30:00,82.88,221.06599999999997,41.086999999999996,32.459 +2020-01-11 07:45:00,87.25,225.924,41.086999999999996,32.459 +2020-01-11 08:00:00,89.61,227.84799999999998,48.222,32.459 +2020-01-11 08:15:00,91.87,231.47099999999998,48.222,32.459 +2020-01-11 08:30:00,94.54,232.15900000000002,48.222,32.459 +2020-01-11 08:45:00,97.83,230.68099999999998,48.222,32.459 +2020-01-11 09:00:00,99.9,226.22400000000002,52.791000000000004,32.459 +2020-01-11 09:15:00,102.11,224.375,52.791000000000004,32.459 +2020-01-11 09:30:00,103.03,222.796,52.791000000000004,32.459 +2020-01-11 09:45:00,104.11,219.641,52.791000000000004,32.459 +2020-01-11 10:00:00,105.67,213.37900000000002,54.341,32.459 +2020-01-11 10:15:00,106.52,210.09400000000002,54.341,32.459 +2020-01-11 10:30:00,107.3,206.815,54.341,32.459 +2020-01-11 10:45:00,107.94,206.1,54.341,32.459 +2020-01-11 11:00:00,109.65,204.15400000000002,51.94,32.459 +2020-01-11 11:15:00,111.25,201.162,51.94,32.459 +2020-01-11 11:30:00,111.53,200.33,51.94,32.459 +2020-01-11 11:45:00,111.53,198.183,51.94,32.459 +2020-01-11 12:00:00,110.54,193.285,50.973,32.459 +2020-01-11 12:15:00,109.01,191.19299999999998,50.973,32.459 +2020-01-11 12:30:00,105.95,191.648,50.973,32.459 +2020-01-11 12:45:00,104.43,192.233,50.973,32.459 +2020-01-11 13:00:00,101.49,191.13,48.06399999999999,32.459 +2020-01-11 13:15:00,99.78,189.25,48.06399999999999,32.459 +2020-01-11 13:30:00,98.12,188.30700000000002,48.06399999999999,32.459 +2020-01-11 13:45:00,96.61,188.71200000000002,48.06399999999999,32.459 +2020-01-11 14:00:00,95.14,187.91299999999998,45.707,32.459 +2020-01-11 14:15:00,94.37,187.77599999999998,45.707,32.459 +2020-01-11 14:30:00,94.65,187.243,45.707,32.459 +2020-01-11 14:45:00,94.99,188.178,45.707,32.459 +2020-01-11 15:00:00,94.68,189.683,47.567,32.459 +2020-01-11 15:15:00,94.55,190.521,47.567,32.459 +2020-01-11 15:30:00,93.53,192.94,47.567,32.459 +2020-01-11 15:45:00,96.02,194.787,47.567,32.459 +2020-01-11 16:00:00,97.55,192.99599999999998,52.031000000000006,32.459 +2020-01-11 16:15:00,98.75,195.96900000000002,52.031000000000006,32.459 +2020-01-11 16:30:00,102.68,198.946,52.031000000000006,32.459 +2020-01-11 16:45:00,104.37,201.447,52.031000000000006,32.459 +2020-01-11 17:00:00,106.29,203.333,58.218999999999994,32.459 +2020-01-11 17:15:00,107.17,205.708,58.218999999999994,32.459 +2020-01-11 17:30:00,108.36,206.278,58.218999999999994,32.459 +2020-01-11 17:45:00,109.4,205.47799999999998,58.218999999999994,32.459 +2020-01-11 18:00:00,108.57,207.27200000000002,57.65,32.459 +2020-01-11 18:15:00,108.91,205.892,57.65,32.459 +2020-01-11 18:30:00,108.15,206.44799999999998,57.65,32.459 +2020-01-11 18:45:00,107.83,203.21400000000003,57.65,32.459 +2020-01-11 19:00:00,105.71,204.699,51.261,32.459 +2020-01-11 19:15:00,103.93,201.683,51.261,32.459 +2020-01-11 19:30:00,102.6,199.333,51.261,32.459 +2020-01-11 19:45:00,101.86,195.09900000000002,51.261,32.459 +2020-01-11 20:00:00,97.56,193.503,44.068000000000005,32.459 +2020-01-11 20:15:00,92.87,189.32299999999998,44.068000000000005,32.459 +2020-01-11 20:30:00,90.31,184.799,44.068000000000005,32.459 +2020-01-11 20:45:00,88.48,183.128,44.068000000000005,32.459 +2020-01-11 21:00:00,86.43,183.017,38.861,32.459 +2020-01-11 21:15:00,84.3,181.21200000000002,38.861,32.459 +2020-01-11 21:30:00,83.24,180.489,38.861,32.459 +2020-01-11 21:45:00,82.41,179.149,38.861,32.459 +2020-01-11 22:00:00,79.49,174.601,39.485,32.459 +2020-01-11 22:15:00,77.77,171.377,39.485,32.459 +2020-01-11 22:30:00,76.13,170.824,39.485,32.459 +2020-01-11 22:45:00,75.32,168.31,39.485,32.459 +2020-01-11 23:00:00,72.71,163.583,32.027,32.459 +2020-01-11 23:15:00,71.28,158.07399999999998,32.027,32.459 +2020-01-11 23:30:00,69.11,154.937,32.027,32.459 +2020-01-11 23:45:00,66.62,151.07,32.027,32.459 +2020-01-12 00:00:00,64.99,128.533,26.96,32.459 +2020-01-12 00:15:00,63.46,123.37200000000001,26.96,32.459 +2020-01-12 00:30:00,61.72,125.475,26.96,32.459 +2020-01-12 00:45:00,60.63,128.591,26.96,32.459 +2020-01-12 01:00:00,59.84,131.655,24.295,32.459 +2020-01-12 01:15:00,59.13,133.06,24.295,32.459 +2020-01-12 01:30:00,58.42,133.279,24.295,32.459 +2020-01-12 01:45:00,57.74,133.195,24.295,32.459 +2020-01-12 02:00:00,57.23,135.319,24.268,32.459 +2020-01-12 02:15:00,56.86,135.67700000000002,24.268,32.459 +2020-01-12 02:30:00,56.65,136.64600000000002,24.268,32.459 +2020-01-12 02:45:00,56.47,139.35399999999998,24.268,32.459 +2020-01-12 03:00:00,56.4,142.011,23.373,32.459 +2020-01-12 03:15:00,56.47,142.487,23.373,32.459 +2020-01-12 03:30:00,56.48,144.191,23.373,32.459 +2020-01-12 03:45:00,56.69,145.944,23.373,32.459 +2020-01-12 04:00:00,57.05,153.52700000000002,23.874000000000002,32.459 +2020-01-12 04:15:00,56.86,161.537,23.874000000000002,32.459 +2020-01-12 04:30:00,57.57,162.489,23.874000000000002,32.459 +2020-01-12 04:45:00,58.1,163.93400000000003,23.874000000000002,32.459 +2020-01-12 05:00:00,58.61,176.683,24.871,32.459 +2020-01-12 05:15:00,59.09,184.1,24.871,32.459 +2020-01-12 05:30:00,58.93,181.088,24.871,32.459 +2020-01-12 05:45:00,59.64,179.84799999999998,24.871,32.459 +2020-01-12 06:00:00,60.42,196.84599999999998,23.84,32.459 +2020-01-12 06:15:00,60.79,216.484,23.84,32.459 +2020-01-12 06:30:00,60.98,211.77,23.84,32.459 +2020-01-12 06:45:00,62.52,207.247,23.84,32.459 +2020-01-12 07:00:00,64.57,203.924,27.430999999999997,32.459 +2020-01-12 07:15:00,66.17,208.28099999999998,27.430999999999997,32.459 +2020-01-12 07:30:00,67.93,212.678,27.430999999999997,32.459 +2020-01-12 07:45:00,69.79,216.687,27.430999999999997,32.459 +2020-01-12 08:00:00,71.93,220.56599999999997,33.891999999999996,32.459 +2020-01-12 08:15:00,74.46,223.995,33.891999999999996,32.459 +2020-01-12 08:30:00,76.78,226.389,33.891999999999996,32.459 +2020-01-12 08:45:00,78.97,227.08900000000003,33.891999999999996,32.459 +2020-01-12 09:00:00,80.51,222.19299999999998,37.571,32.459 +2020-01-12 09:15:00,81.13,220.979,37.571,32.459 +2020-01-12 09:30:00,81.84,219.22099999999998,37.571,32.459 +2020-01-12 09:45:00,82.37,215.87099999999998,37.571,32.459 +2020-01-12 10:00:00,81.27,212.296,40.594,32.459 +2020-01-12 10:15:00,79.61,209.584,40.594,32.459 +2020-01-12 10:30:00,79.77,206.93400000000003,40.594,32.459 +2020-01-12 10:45:00,85.58,204.109,40.594,32.459 +2020-01-12 11:00:00,86.51,203.16299999999998,44.133,32.459 +2020-01-12 11:15:00,88.46,200.34099999999998,44.133,32.459 +2020-01-12 11:30:00,91.87,198.543,44.133,32.459 +2020-01-12 11:45:00,93.46,197.037,44.133,32.459 +2020-01-12 12:00:00,91.63,191.477,41.198,32.459 +2020-01-12 12:15:00,90.3,191.49099999999999,41.198,32.459 +2020-01-12 12:30:00,88.06,190.357,41.198,32.459 +2020-01-12 12:45:00,86.68,189.94,41.198,32.459 +2020-01-12 13:00:00,85.19,188.076,37.014,32.459 +2020-01-12 13:15:00,83.88,189.463,37.014,32.459 +2020-01-12 13:30:00,82.6,188.347,37.014,32.459 +2020-01-12 13:45:00,82.08,187.97099999999998,37.014,32.459 +2020-01-12 14:00:00,82.67,187.389,34.934,32.459 +2020-01-12 14:15:00,82.79,188.51,34.934,32.459 +2020-01-12 14:30:00,82.64,189.378,34.934,32.459 +2020-01-12 14:45:00,85.76,189.947,34.934,32.459 +2020-01-12 15:00:00,83.46,189.813,34.588,32.459 +2020-01-12 15:15:00,83.64,191.487,34.588,32.459 +2020-01-12 15:30:00,84.7,194.537,34.588,32.459 +2020-01-12 15:45:00,85.25,197.101,34.588,32.459 +2020-01-12 16:00:00,86.79,197.385,37.874,32.459 +2020-01-12 16:15:00,88.0,199.38,37.874,32.459 +2020-01-12 16:30:00,92.21,202.588,37.874,32.459 +2020-01-12 16:45:00,94.95,205.236,37.874,32.459 +2020-01-12 17:00:00,97.3,207.06599999999997,47.303999999999995,32.459 +2020-01-12 17:15:00,99.15,209.045,47.303999999999995,32.459 +2020-01-12 17:30:00,100.86,209.912,47.303999999999995,32.459 +2020-01-12 17:45:00,102.12,211.532,47.303999999999995,32.459 +2020-01-12 18:00:00,102.63,212.722,48.879,32.459 +2020-01-12 18:15:00,102.78,212.78599999999997,48.879,32.459 +2020-01-12 18:30:00,101.93,211.138,48.879,32.459 +2020-01-12 18:45:00,100.6,209.889,48.879,32.459 +2020-01-12 19:00:00,99.22,210.878,44.826,32.459 +2020-01-12 19:15:00,96.93,208.544,44.826,32.459 +2020-01-12 19:30:00,98.09,206.03599999999997,44.826,32.459 +2020-01-12 19:45:00,94.04,203.36,44.826,32.459 +2020-01-12 20:00:00,92.48,201.725,40.154,32.459 +2020-01-12 20:15:00,90.52,198.604,40.154,32.459 +2020-01-12 20:30:00,88.61,195.35299999999998,40.154,32.459 +2020-01-12 20:45:00,86.91,192.50099999999998,40.154,32.459 +2020-01-12 21:00:00,86.27,189.613,36.549,32.459 +2020-01-12 21:15:00,85.53,187.137,36.549,32.459 +2020-01-12 21:30:00,85.67,186.76,36.549,32.459 +2020-01-12 21:45:00,86.26,185.56099999999998,36.549,32.459 +2020-01-12 22:00:00,84.68,179.642,37.663000000000004,32.459 +2020-01-12 22:15:00,84.59,175.704,37.663000000000004,32.459 +2020-01-12 22:30:00,81.91,171.81099999999998,37.663000000000004,32.459 +2020-01-12 22:45:00,80.26,168.46,37.663000000000004,32.459 +2020-01-12 23:00:00,77.38,160.747,31.945,32.459 +2020-01-12 23:15:00,76.18,157.165,31.945,32.459 +2020-01-12 23:30:00,75.03,154.933,31.945,32.459 +2020-01-12 23:45:00,75.75,152.011,31.945,32.459 +2020-01-13 00:00:00,70.95,133.061,31.533,32.641 +2020-01-13 00:15:00,70.9,131.047,31.533,32.641 +2020-01-13 00:30:00,70.47,133.298,31.533,32.641 +2020-01-13 00:45:00,69.99,135.843,31.533,32.641 +2020-01-13 01:00:00,67.64,138.91,30.56,32.641 +2020-01-13 01:15:00,66.93,139.731,30.56,32.641 +2020-01-13 01:30:00,67.33,139.983,30.56,32.641 +2020-01-13 01:45:00,67.11,140.019,30.56,32.641 +2020-01-13 02:00:00,66.76,142.105,29.55,32.641 +2020-01-13 02:15:00,67.45,144.125,29.55,32.641 +2020-01-13 02:30:00,67.68,145.453,29.55,32.641 +2020-01-13 02:45:00,67.43,147.497,29.55,32.641 +2020-01-13 03:00:00,67.66,151.507,27.059,32.641 +2020-01-13 03:15:00,68.77,153.741,27.059,32.641 +2020-01-13 03:30:00,68.92,155.1,27.059,32.641 +2020-01-13 03:45:00,68.84,156.29399999999998,27.059,32.641 +2020-01-13 04:00:00,70.83,168.38299999999998,28.384,32.641 +2020-01-13 04:15:00,73.73,180.68599999999998,28.384,32.641 +2020-01-13 04:30:00,73.75,184.06799999999998,28.384,32.641 +2020-01-13 04:45:00,77.08,185.655,28.384,32.641 +2020-01-13 05:00:00,81.53,215.06400000000002,35.915,32.641 +2020-01-13 05:15:00,84.06,243.69099999999997,35.915,32.641 +2020-01-13 05:30:00,88.5,241.104,35.915,32.641 +2020-01-13 05:45:00,94.62,233.94400000000002,35.915,32.641 +2020-01-13 06:00:00,104.44,232.146,56.18,32.641 +2020-01-13 06:15:00,112.23,236.292,56.18,32.641 +2020-01-13 06:30:00,114.44,240.037,56.18,32.641 +2020-01-13 06:45:00,119.14,244.743,56.18,32.641 +2020-01-13 07:00:00,126.22,243.983,70.877,32.641 +2020-01-13 07:15:00,129.2,249.511,70.877,32.641 +2020-01-13 07:30:00,131.57,253.109,70.877,32.641 +2020-01-13 07:45:00,131.68,254.268,70.877,32.641 +2020-01-13 08:00:00,135.22,252.912,65.65,32.641 +2020-01-13 08:15:00,134.08,254.079,65.65,32.641 +2020-01-13 08:30:00,135.2,252.06799999999998,65.65,32.641 +2020-01-13 08:45:00,132.28,249.106,65.65,32.641 +2020-01-13 09:00:00,133.04,243.158,62.037,32.641 +2020-01-13 09:15:00,133.98,238.296,62.037,32.641 +2020-01-13 09:30:00,132.22,235.535,62.037,32.641 +2020-01-13 09:45:00,130.3,232.66299999999998,62.037,32.641 +2020-01-13 10:00:00,129.71,227.908,60.409,32.641 +2020-01-13 10:15:00,129.27,224.858,60.409,32.641 +2020-01-13 10:30:00,127.57,221.285,60.409,32.641 +2020-01-13 10:45:00,130.5,219.388,60.409,32.641 +2020-01-13 11:00:00,125.92,215.54,60.211999999999996,32.641 +2020-01-13 11:15:00,126.17,214.65,60.211999999999996,32.641 +2020-01-13 11:30:00,125.52,214.301,60.211999999999996,32.641 +2020-01-13 11:45:00,127.51,212.32299999999998,60.211999999999996,32.641 +2020-01-13 12:00:00,124.16,208.769,57.733000000000004,32.641 +2020-01-13 12:15:00,123.73,208.793,57.733000000000004,32.641 +2020-01-13 12:30:00,123.65,207.995,57.733000000000004,32.641 +2020-01-13 12:45:00,119.92,209.227,57.733000000000004,32.641 +2020-01-13 13:00:00,118.02,207.935,58.695,32.641 +2020-01-13 13:15:00,118.69,207.868,58.695,32.641 +2020-01-13 13:30:00,116.48,206.155,58.695,32.641 +2020-01-13 13:45:00,117.81,205.75,58.695,32.641 +2020-01-13 14:00:00,124.13,204.56,59.505,32.641 +2020-01-13 14:15:00,122.13,204.937,59.505,32.641 +2020-01-13 14:30:00,125.07,205.237,59.505,32.641 +2020-01-13 14:45:00,121.35,205.65,59.505,32.641 +2020-01-13 15:00:00,123.98,207.451,59.946000000000005,32.641 +2020-01-13 15:15:00,125.58,207.59400000000002,59.946000000000005,32.641 +2020-01-13 15:30:00,124.7,209.69,59.946000000000005,32.641 +2020-01-13 15:45:00,124.42,211.794,59.946000000000005,32.641 +2020-01-13 16:00:00,126.95,212.16400000000002,61.766999999999996,32.641 +2020-01-13 16:15:00,128.1,213.343,61.766999999999996,32.641 +2020-01-13 16:30:00,133.84,215.56,61.766999999999996,32.641 +2020-01-13 16:45:00,134.97,216.93400000000003,61.766999999999996,32.641 +2020-01-13 17:00:00,137.72,218.595,67.85600000000001,32.641 +2020-01-13 17:15:00,139.22,219.562,67.85600000000001,32.641 +2020-01-13 17:30:00,140.31,219.893,67.85600000000001,32.641 +2020-01-13 17:45:00,138.79,219.953,67.85600000000001,32.641 +2020-01-13 18:00:00,138.41,221.658,64.564,32.641 +2020-01-13 18:15:00,136.09,219.475,64.564,32.641 +2020-01-13 18:30:00,135.55,218.57299999999998,64.564,32.641 +2020-01-13 18:45:00,135.23,217.93200000000002,64.564,32.641 +2020-01-13 19:00:00,132.57,217.167,58.536,32.641 +2020-01-13 19:15:00,131.58,213.516,58.536,32.641 +2020-01-13 19:30:00,129.12,211.56400000000002,58.536,32.641 +2020-01-13 19:45:00,134.32,208.04,58.536,32.641 +2020-01-13 20:00:00,128.64,203.938,59.888999999999996,32.641 +2020-01-13 20:15:00,125.25,198.085,59.888999999999996,32.641 +2020-01-13 20:30:00,116.68,192.804,59.888999999999996,32.641 +2020-01-13 20:45:00,117.43,191.696,59.888999999999996,32.641 +2020-01-13 21:00:00,109.83,189.396,52.652,32.641 +2020-01-13 21:15:00,114.34,185.62,52.652,32.641 +2020-01-13 21:30:00,113.42,184.334,52.652,32.641 +2020-01-13 21:45:00,107.41,182.62400000000002,52.652,32.641 +2020-01-13 22:00:00,100.72,173.732,46.17,32.641 +2020-01-13 22:15:00,98.52,168.285,46.17,32.641 +2020-01-13 22:30:00,100.74,154.417,46.17,32.641 +2020-01-13 22:45:00,100.33,145.899,46.17,32.641 +2020-01-13 23:00:00,94.57,139.002,36.281,32.641 +2020-01-13 23:15:00,89.14,138.363,36.281,32.641 +2020-01-13 23:30:00,91.26,139.106,36.281,32.641 +2020-01-13 23:45:00,91.53,139.046,36.281,32.641 +2020-01-14 00:00:00,87.82,132.558,38.821999999999996,32.641 +2020-01-14 00:15:00,81.82,132.01,38.821999999999996,32.641 +2020-01-14 00:30:00,84.32,133.167,38.821999999999996,32.641 +2020-01-14 00:45:00,87.32,134.571,38.821999999999996,32.641 +2020-01-14 01:00:00,84.42,137.464,36.936,32.641 +2020-01-14 01:15:00,79.89,137.782,36.936,32.641 +2020-01-14 01:30:00,77.82,138.22899999999998,36.936,32.641 +2020-01-14 01:45:00,79.93,138.631,36.936,32.641 +2020-01-14 02:00:00,82.47,140.761,34.42,32.641 +2020-01-14 02:15:00,81.18,142.563,34.42,32.641 +2020-01-14 02:30:00,78.27,143.282,34.42,32.641 +2020-01-14 02:45:00,83.03,145.312,34.42,32.641 +2020-01-14 03:00:00,82.63,148.03799999999998,33.585,32.641 +2020-01-14 03:15:00,80.68,149.27700000000002,33.585,32.641 +2020-01-14 03:30:00,74.84,151.167,33.585,32.641 +2020-01-14 03:45:00,75.84,152.67600000000002,33.585,32.641 +2020-01-14 04:00:00,76.53,164.63299999999998,35.622,32.641 +2020-01-14 04:15:00,77.34,176.54,35.622,32.641 +2020-01-14 04:30:00,78.06,179.59400000000002,35.622,32.641 +2020-01-14 04:45:00,80.59,182.463,35.622,32.641 +2020-01-14 05:00:00,83.73,217.205,40.599000000000004,32.641 +2020-01-14 05:15:00,86.8,245.519,40.599000000000004,32.641 +2020-01-14 05:30:00,90.08,241.22400000000002,40.599000000000004,32.641 +2020-01-14 05:45:00,95.43,234.15900000000002,40.599000000000004,32.641 +2020-01-14 06:00:00,104.29,230.98,55.203,32.641 +2020-01-14 06:15:00,109.76,236.90900000000002,55.203,32.641 +2020-01-14 06:30:00,114.01,239.98,55.203,32.641 +2020-01-14 06:45:00,118.59,244.38,55.203,32.641 +2020-01-14 07:00:00,125.25,243.42,69.029,32.641 +2020-01-14 07:15:00,128.89,248.783,69.029,32.641 +2020-01-14 07:30:00,129.58,251.75599999999997,69.029,32.641 +2020-01-14 07:45:00,135.04,253.217,69.029,32.641 +2020-01-14 08:00:00,137.03,251.979,65.85300000000001,32.641 +2020-01-14 08:15:00,136.03,252.045,65.85300000000001,32.641 +2020-01-14 08:30:00,135.16,249.79,65.85300000000001,32.641 +2020-01-14 08:45:00,132.47,246.59900000000002,65.85300000000001,32.641 +2020-01-14 09:00:00,135.18,239.695,61.566,32.641 +2020-01-14 09:15:00,137.97,236.602,61.566,32.641 +2020-01-14 09:30:00,137.36,234.543,61.566,32.641 +2020-01-14 09:45:00,138.26,231.315,61.566,32.641 +2020-01-14 10:00:00,140.95,226.03599999999997,61.244,32.641 +2020-01-14 10:15:00,144.05,221.83900000000003,61.244,32.641 +2020-01-14 10:30:00,146.07,218.455,61.244,32.641 +2020-01-14 10:45:00,144.01,216.787,61.244,32.641 +2020-01-14 11:00:00,132.46,214.55200000000002,61.16,32.641 +2020-01-14 11:15:00,135.5,213.266,61.16,32.641 +2020-01-14 11:30:00,136.58,211.74099999999999,61.16,32.641 +2020-01-14 11:45:00,138.0,210.571,61.16,32.641 +2020-01-14 12:00:00,137.63,205.547,59.09,32.641 +2020-01-14 12:15:00,135.35,205.107,59.09,32.641 +2020-01-14 12:30:00,134.93,205.018,59.09,32.641 +2020-01-14 12:45:00,134.94,205.87400000000002,59.09,32.641 +2020-01-14 13:00:00,133.76,204.162,60.21,32.641 +2020-01-14 13:15:00,131.09,203.554,60.21,32.641 +2020-01-14 13:30:00,129.61,203.111,60.21,32.641 +2020-01-14 13:45:00,134.96,202.985,60.21,32.641 +2020-01-14 14:00:00,133.08,202.09599999999998,60.673,32.641 +2020-01-14 14:15:00,132.64,202.65099999999998,60.673,32.641 +2020-01-14 14:30:00,131.69,203.612,60.673,32.641 +2020-01-14 14:45:00,133.24,204.018,60.673,32.641 +2020-01-14 15:00:00,134.24,205.382,62.232,32.641 +2020-01-14 15:15:00,133.79,205.793,62.232,32.641 +2020-01-14 15:30:00,132.79,208.138,62.232,32.641 +2020-01-14 15:45:00,132.99,209.766,62.232,32.641 +2020-01-14 16:00:00,134.62,210.56599999999997,63.611999999999995,32.641 +2020-01-14 16:15:00,133.49,212.261,63.611999999999995,32.641 +2020-01-14 16:30:00,136.5,215.21200000000002,63.611999999999995,32.641 +2020-01-14 16:45:00,140.39,216.851,63.611999999999995,32.641 +2020-01-14 17:00:00,141.25,219.071,70.658,32.641 +2020-01-14 17:15:00,141.82,220.072,70.658,32.641 +2020-01-14 17:30:00,142.91,221.18200000000002,70.658,32.641 +2020-01-14 17:45:00,141.94,221.146,70.658,32.641 +2020-01-14 18:00:00,140.95,222.817,68.361,32.641 +2020-01-14 18:15:00,139.33,220.011,68.361,32.641 +2020-01-14 18:30:00,138.51,218.8,68.361,32.641 +2020-01-14 18:45:00,138.78,219.049,68.361,32.641 +2020-01-14 19:00:00,134.6,218.407,62.922,32.641 +2020-01-14 19:15:00,133.06,214.447,62.922,32.641 +2020-01-14 19:30:00,130.38,211.797,62.922,32.641 +2020-01-14 19:45:00,135.52,208.298,62.922,32.641 +2020-01-14 20:00:00,131.04,204.324,63.251999999999995,32.641 +2020-01-14 20:15:00,124.9,197.88099999999997,63.251999999999995,32.641 +2020-01-14 20:30:00,119.8,193.68200000000002,63.251999999999995,32.641 +2020-01-14 20:45:00,113.77,191.946,63.251999999999995,32.641 +2020-01-14 21:00:00,111.04,188.81599999999997,54.47,32.641 +2020-01-14 21:15:00,115.36,186.09599999999998,54.47,32.641 +2020-01-14 21:30:00,114.69,184.012,54.47,32.641 +2020-01-14 21:45:00,110.23,182.55,54.47,32.641 +2020-01-14 22:00:00,103.08,175.484,51.12,32.641 +2020-01-14 22:15:00,98.43,169.798,51.12,32.641 +2020-01-14 22:30:00,97.92,156.011,51.12,32.641 +2020-01-14 22:45:00,100.32,147.8,51.12,32.641 +2020-01-14 23:00:00,96.47,140.96200000000002,42.156000000000006,32.641 +2020-01-14 23:15:00,92.03,139.256,42.156000000000006,32.641 +2020-01-14 23:30:00,86.85,139.628,42.156000000000006,32.641 +2020-01-14 23:45:00,87.06,139.088,42.156000000000006,32.641 +2020-01-15 00:00:00,86.86,132.626,37.192,32.641 +2020-01-15 00:15:00,87.23,132.058,37.192,32.641 +2020-01-15 00:30:00,85.9,133.19799999999998,37.192,32.641 +2020-01-15 00:45:00,82.51,134.589,37.192,32.641 +2020-01-15 01:00:00,83.73,137.483,32.24,32.641 +2020-01-15 01:15:00,83.66,137.791,32.24,32.641 +2020-01-15 01:30:00,80.75,138.233,32.24,32.641 +2020-01-15 01:45:00,76.71,138.627,32.24,32.641 +2020-01-15 02:00:00,83.36,140.768,30.34,32.641 +2020-01-15 02:15:00,84.01,142.57,30.34,32.641 +2020-01-15 02:30:00,81.99,143.299,30.34,32.641 +2020-01-15 02:45:00,77.46,145.328,30.34,32.641 +2020-01-15 03:00:00,75.23,148.05,29.129,32.641 +2020-01-15 03:15:00,76.54,149.305,29.129,32.641 +2020-01-15 03:30:00,77.67,151.195,29.129,32.641 +2020-01-15 03:45:00,77.78,152.713,29.129,32.641 +2020-01-15 04:00:00,83.87,164.64700000000002,30.075,32.641 +2020-01-15 04:15:00,85.62,176.545,30.075,32.641 +2020-01-15 04:30:00,87.03,179.59900000000002,30.075,32.641 +2020-01-15 04:45:00,87.26,182.463,30.075,32.641 +2020-01-15 05:00:00,92.08,217.16400000000002,35.684,32.641 +2020-01-15 05:15:00,95.05,245.451,35.684,32.641 +2020-01-15 05:30:00,93.94,241.158,35.684,32.641 +2020-01-15 05:45:00,97.28,234.112,35.684,32.641 +2020-01-15 06:00:00,105.8,230.955,51.49,32.641 +2020-01-15 06:15:00,110.68,236.893,51.49,32.641 +2020-01-15 06:30:00,116.49,239.972,51.49,32.641 +2020-01-15 06:45:00,120.13,244.398,51.49,32.641 +2020-01-15 07:00:00,127.25,243.456,68.242,32.641 +2020-01-15 07:15:00,130.04,248.804,68.242,32.641 +2020-01-15 07:30:00,133.29,251.75599999999997,68.242,32.641 +2020-01-15 07:45:00,136.04,253.19299999999998,68.242,32.641 +2020-01-15 08:00:00,138.64,251.947,63.619,32.641 +2020-01-15 08:15:00,135.35,251.99599999999998,63.619,32.641 +2020-01-15 08:30:00,135.07,249.706,63.619,32.641 +2020-01-15 08:45:00,135.36,246.498,63.619,32.641 +2020-01-15 09:00:00,138.48,239.581,61.333,32.641 +2020-01-15 09:15:00,137.35,236.493,61.333,32.641 +2020-01-15 09:30:00,139.08,234.452,61.333,32.641 +2020-01-15 09:45:00,139.42,231.21900000000002,61.333,32.641 +2020-01-15 10:00:00,139.42,225.94299999999998,59.663000000000004,32.641 +2020-01-15 10:15:00,139.9,221.75400000000002,59.663000000000004,32.641 +2020-01-15 10:30:00,142.04,218.365,59.663000000000004,32.641 +2020-01-15 10:45:00,143.07,216.702,59.663000000000004,32.641 +2020-01-15 11:00:00,139.95,214.44799999999998,59.771,32.641 +2020-01-15 11:15:00,139.24,213.165,59.771,32.641 +2020-01-15 11:30:00,137.7,211.642,59.771,32.641 +2020-01-15 11:45:00,137.77,210.477,59.771,32.641 +2020-01-15 12:00:00,137.15,205.465,58.723,32.641 +2020-01-15 12:15:00,135.27,205.044,58.723,32.641 +2020-01-15 12:30:00,135.67,204.94400000000002,58.723,32.641 +2020-01-15 12:45:00,137.77,205.801,58.723,32.641 +2020-01-15 13:00:00,136.15,204.08700000000002,58.727,32.641 +2020-01-15 13:15:00,136.78,203.465,58.727,32.641 +2020-01-15 13:30:00,136.5,203.01,58.727,32.641 +2020-01-15 13:45:00,136.87,202.877,58.727,32.641 +2020-01-15 14:00:00,136.85,202.014,59.803999999999995,32.641 +2020-01-15 14:15:00,134.91,202.55900000000003,59.803999999999995,32.641 +2020-01-15 14:30:00,134.1,203.52200000000002,59.803999999999995,32.641 +2020-01-15 14:45:00,135.12,203.94400000000002,59.803999999999995,32.641 +2020-01-15 15:00:00,135.73,205.327,61.05,32.641 +2020-01-15 15:15:00,134.79,205.715,61.05,32.641 +2020-01-15 15:30:00,132.55,208.048,61.05,32.641 +2020-01-15 15:45:00,131.89,209.665,61.05,32.641 +2020-01-15 16:00:00,132.28,210.465,64.012,32.641 +2020-01-15 16:15:00,134.26,212.166,64.012,32.641 +2020-01-15 16:30:00,136.16,215.123,64.012,32.641 +2020-01-15 16:45:00,138.74,216.766,64.012,32.641 +2020-01-15 17:00:00,141.87,218.972,66.751,32.641 +2020-01-15 17:15:00,142.23,220.003,66.751,32.641 +2020-01-15 17:30:00,143.82,221.144,66.751,32.641 +2020-01-15 17:45:00,143.42,221.12900000000002,66.751,32.641 +2020-01-15 18:00:00,142.04,222.82,65.91199999999999,32.641 +2020-01-15 18:15:00,140.16,220.03099999999998,65.91199999999999,32.641 +2020-01-15 18:30:00,139.4,218.824,65.91199999999999,32.641 +2020-01-15 18:45:00,140.52,219.09400000000002,65.91199999999999,32.641 +2020-01-15 19:00:00,137.94,218.416,63.324,32.641 +2020-01-15 19:15:00,133.64,214.46099999999998,63.324,32.641 +2020-01-15 19:30:00,130.94,211.817,63.324,32.641 +2020-01-15 19:45:00,129.29,208.326,63.324,32.641 +2020-01-15 20:00:00,123.9,204.331,63.573,32.641 +2020-01-15 20:15:00,121.7,197.892,63.573,32.641 +2020-01-15 20:30:00,116.5,193.687,63.573,32.641 +2020-01-15 20:45:00,115.14,191.967,63.573,32.641 +2020-01-15 21:00:00,110.98,188.81799999999998,55.073,32.641 +2020-01-15 21:15:00,115.16,186.081,55.073,32.641 +2020-01-15 21:30:00,114.07,183.99599999999998,55.073,32.641 +2020-01-15 21:45:00,110.47,182.551,55.073,32.641 +2020-01-15 22:00:00,101.67,175.48,51.321999999999996,32.641 +2020-01-15 22:15:00,100.33,169.81099999999998,51.321999999999996,32.641 +2020-01-15 22:30:00,96.77,156.029,51.321999999999996,32.641 +2020-01-15 22:45:00,95.32,147.825,51.321999999999996,32.641 +2020-01-15 23:00:00,97.65,140.968,42.09,32.641 +2020-01-15 23:15:00,96.42,139.27200000000002,42.09,32.641 +2020-01-15 23:30:00,91.99,139.661,42.09,32.641 +2020-01-15 23:45:00,86.16,139.125,42.09,32.641 +2020-01-16 00:00:00,87.33,139.56,38.399,32.641 +2020-01-16 00:15:00,88.49,139.33,38.399,32.641 +2020-01-16 00:30:00,88.26,140.88299999999998,38.399,32.641 +2020-01-16 00:45:00,83.0,142.724,38.399,32.641 +2020-01-16 01:00:00,83.99,145.79399999999998,36.94,32.641 +2020-01-16 01:15:00,85.07,145.787,36.94,32.641 +2020-01-16 01:30:00,84.84,146.149,36.94,32.641 +2020-01-16 01:45:00,79.18,146.644,36.94,32.641 +2020-01-16 02:00:00,80.14,148.93200000000002,35.275,32.641 +2020-01-16 02:15:00,85.15,151.02200000000002,35.275,32.641 +2020-01-16 02:30:00,84.32,152.084,35.275,32.641 +2020-01-16 02:45:00,82.42,154.27100000000002,35.275,32.641 +2020-01-16 03:00:00,77.71,157.27,35.329,32.641 +2020-01-16 03:15:00,84.16,158.311,35.329,32.641 +2020-01-16 03:30:00,85.53,160.137,35.329,32.641 +2020-01-16 03:45:00,85.45,162.055,35.329,32.641 +2020-01-16 04:00:00,78.58,173.658,36.275,32.641 +2020-01-16 04:15:00,79.47,185.22099999999998,36.275,32.641 +2020-01-16 04:30:00,81.13,188.989,36.275,32.641 +2020-01-16 04:45:00,82.77,192.137,36.275,32.641 +2020-01-16 05:00:00,86.6,227.294,42.193999999999996,32.641 +2020-01-16 05:15:00,89.37,255.28799999999998,42.193999999999996,32.641 +2020-01-16 05:30:00,93.2,250.99099999999999,42.193999999999996,32.641 +2020-01-16 05:45:00,98.25,244.456,42.193999999999996,32.641 +2020-01-16 06:00:00,106.36,241.44099999999997,56.422,32.641 +2020-01-16 06:15:00,111.91,247.579,56.422,32.641 +2020-01-16 06:30:00,115.81,250.795,56.422,32.641 +2020-01-16 06:45:00,121.56,255.86900000000003,56.422,32.641 +2020-01-16 07:00:00,125.43,254.107,72.569,32.641 +2020-01-16 07:15:00,128.69,259.99,72.569,32.641 +2020-01-16 07:30:00,132.2,263.454,72.569,32.641 +2020-01-16 07:45:00,136.04,265.41200000000003,72.569,32.641 +2020-01-16 08:00:00,137.28,264.069,67.704,32.641 +2020-01-16 08:15:00,136.74,264.55400000000003,67.704,32.641 +2020-01-16 08:30:00,137.81,262.16900000000004,67.704,32.641 +2020-01-16 08:45:00,135.89,259.421,67.704,32.641 +2020-01-16 09:00:00,137.97,252.798,63.434,32.641 +2020-01-16 09:15:00,140.1,249.953,63.434,32.641 +2020-01-16 09:30:00,141.78,248.072,63.434,32.641 +2020-01-16 09:45:00,141.49,244.673,63.434,32.641 +2020-01-16 10:00:00,142.24,238.75599999999997,61.88399999999999,32.641 +2020-01-16 10:15:00,140.48,234.90400000000002,61.88399999999999,32.641 +2020-01-16 10:30:00,140.15,230.903,61.88399999999999,32.641 +2020-01-16 10:45:00,140.59,229.00799999999998,61.88399999999999,32.641 +2020-01-16 11:00:00,140.53,225.988,61.481,32.641 +2020-01-16 11:15:00,141.45,224.646,61.481,32.641 +2020-01-16 11:30:00,141.71,222.722,61.481,32.641 +2020-01-16 11:45:00,139.21,222.058,61.481,32.641 +2020-01-16 12:00:00,136.2,218.021,59.527,32.641 +2020-01-16 12:15:00,135.53,217.612,59.527,32.641 +2020-01-16 12:30:00,135.7,217.34900000000002,59.527,32.641 +2020-01-16 12:45:00,136.9,217.868,59.527,32.641 +2020-01-16 13:00:00,135.4,215.40400000000002,58.794,32.641 +2020-01-16 13:15:00,134.78,214.74099999999999,58.794,32.641 +2020-01-16 13:30:00,132.47,213.917,58.794,32.641 +2020-01-16 13:45:00,132.22,214.22,58.794,32.641 +2020-01-16 14:00:00,128.13,213.833,60.32,32.641 +2020-01-16 14:15:00,126.7,214.40200000000002,60.32,32.641 +2020-01-16 14:30:00,124.84,215.28599999999997,60.32,32.641 +2020-01-16 14:45:00,124.2,215.984,60.32,32.641 +2020-01-16 15:00:00,125.48,216.856,62.52,32.641 +2020-01-16 15:15:00,125.05,217.75799999999998,62.52,32.641 +2020-01-16 15:30:00,126.77,219.547,62.52,32.641 +2020-01-16 15:45:00,129.95,220.50099999999998,62.52,32.641 +2020-01-16 16:00:00,132.47,222.225,64.199,32.641 +2020-01-16 16:15:00,131.37,224.09099999999998,64.199,32.641 +2020-01-16 16:30:00,133.46,226.852,64.199,32.641 +2020-01-16 16:45:00,136.14,228.108,64.199,32.641 +2020-01-16 17:00:00,140.04,230.606,68.19800000000001,32.641 +2020-01-16 17:15:00,139.73,231.262,68.19800000000001,32.641 +2020-01-16 17:30:00,140.43,232.16,68.19800000000001,32.641 +2020-01-16 17:45:00,140.42,231.972,68.19800000000001,32.641 +2020-01-16 18:00:00,138.81,233.65400000000002,67.899,32.641 +2020-01-16 18:15:00,136.53,230.218,67.899,32.641 +2020-01-16 18:30:00,137.16,229.18400000000003,67.899,32.641 +2020-01-16 18:45:00,136.92,229.485,67.899,32.641 +2020-01-16 19:00:00,133.32,227.865,64.72399999999999,32.641 +2020-01-16 19:15:00,131.23,223.705,64.72399999999999,32.641 +2020-01-16 19:30:00,133.3,220.62099999999998,64.72399999999999,32.641 +2020-01-16 19:45:00,135.03,217.56900000000002,64.72399999999999,32.641 +2020-01-16 20:00:00,127.62,213.46200000000002,64.062,32.641 +2020-01-16 20:15:00,119.26,206.58700000000002,64.062,32.641 +2020-01-16 20:30:00,116.31,202.333,64.062,32.641 +2020-01-16 20:45:00,111.77,201.076,64.062,32.641 +2020-01-16 21:00:00,108.51,197.449,57.971000000000004,32.641 +2020-01-16 21:15:00,112.3,194.61,57.971000000000004,32.641 +2020-01-16 21:30:00,110.27,192.56900000000002,57.971000000000004,32.641 +2020-01-16 21:45:00,106.27,190.99400000000003,57.971000000000004,32.641 +2020-01-16 22:00:00,97.28,183.658,53.715,32.641 +2020-01-16 22:15:00,96.36,177.688,53.715,32.641 +2020-01-16 22:30:00,94.99,163.819,53.715,32.641 +2020-01-16 22:45:00,98.2,155.327,53.715,32.641 +2020-01-16 23:00:00,94.28,147.961,47.8,32.641 +2020-01-16 23:15:00,91.98,146.525,47.8,32.641 +2020-01-16 23:30:00,84.84,146.689,47.8,32.641 +2020-01-16 23:45:00,82.59,146.202,47.8,32.641 +2020-01-17 00:00:00,85.95,138.651,43.656000000000006,32.641 +2020-01-17 00:15:00,86.53,138.59799999999998,43.656000000000006,32.641 +2020-01-17 00:30:00,85.43,139.931,43.656000000000006,32.641 +2020-01-17 00:45:00,79.36,141.822,43.656000000000006,32.641 +2020-01-17 01:00:00,80.97,144.585,41.263000000000005,32.641 +2020-01-17 01:15:00,83.5,145.747,41.263000000000005,32.641 +2020-01-17 01:30:00,83.45,145.722,41.263000000000005,32.641 +2020-01-17 01:45:00,80.4,146.374,41.263000000000005,32.641 +2020-01-17 02:00:00,78.23,148.619,40.799,32.641 +2020-01-17 02:15:00,74.91,150.584,40.799,32.641 +2020-01-17 02:30:00,73.54,152.134,40.799,32.641 +2020-01-17 02:45:00,74.37,154.489,40.799,32.641 +2020-01-17 03:00:00,79.04,156.16899999999998,41.398,32.641 +2020-01-17 03:15:00,83.49,158.55200000000002,41.398,32.641 +2020-01-17 03:30:00,84.6,160.399,41.398,32.641 +2020-01-17 03:45:00,82.01,162.561,41.398,32.641 +2020-01-17 04:00:00,77.58,174.391,42.38,32.641 +2020-01-17 04:15:00,78.35,185.93099999999998,42.38,32.641 +2020-01-17 04:30:00,79.29,189.803,42.38,32.641 +2020-01-17 04:45:00,82.16,191.699,42.38,32.641 +2020-01-17 05:00:00,85.97,225.387,46.181000000000004,32.641 +2020-01-17 05:15:00,87.85,254.97799999999998,46.181000000000004,32.641 +2020-01-17 05:30:00,94.06,251.938,46.181000000000004,32.641 +2020-01-17 05:45:00,97.27,245.42700000000002,46.181000000000004,32.641 +2020-01-17 06:00:00,106.38,242.907,59.33,32.641 +2020-01-17 06:15:00,111.42,247.27,59.33,32.641 +2020-01-17 06:30:00,115.82,249.47,59.33,32.641 +2020-01-17 06:45:00,119.88,256.536,59.33,32.641 +2020-01-17 07:00:00,127.7,253.679,72.454,32.641 +2020-01-17 07:15:00,130.26,260.601,72.454,32.641 +2020-01-17 07:30:00,132.73,264.178,72.454,32.641 +2020-01-17 07:45:00,134.99,265.091,72.454,32.641 +2020-01-17 08:00:00,136.8,262.293,67.175,32.641 +2020-01-17 08:15:00,135.31,262.188,67.175,32.641 +2020-01-17 08:30:00,136.28,260.932,67.175,32.641 +2020-01-17 08:45:00,136.61,256.327,67.175,32.641 +2020-01-17 09:00:00,136.32,250.61700000000002,65.365,32.641 +2020-01-17 09:15:00,137.58,248.14,65.365,32.641 +2020-01-17 09:30:00,139.47,245.88400000000001,65.365,32.641 +2020-01-17 09:45:00,139.06,242.29,65.365,32.641 +2020-01-17 10:00:00,137.15,235.06400000000002,63.95,32.641 +2020-01-17 10:15:00,136.31,232.081,63.95,32.641 +2020-01-17 10:30:00,137.15,227.899,63.95,32.641 +2020-01-17 10:45:00,138.46,225.50599999999997,63.95,32.641 +2020-01-17 11:00:00,138.45,222.419,63.92100000000001,32.641 +2020-01-17 11:15:00,140.66,220.19799999999998,63.92100000000001,32.641 +2020-01-17 11:30:00,142.55,220.382,63.92100000000001,32.641 +2020-01-17 11:45:00,141.43,219.925,63.92100000000001,32.641 +2020-01-17 12:00:00,140.64,217.108,60.79600000000001,32.641 +2020-01-17 12:15:00,141.7,214.36599999999999,60.79600000000001,32.641 +2020-01-17 12:30:00,140.79,214.257,60.79600000000001,32.641 +2020-01-17 12:45:00,140.1,215.50099999999998,60.79600000000001,32.641 +2020-01-17 13:00:00,138.62,214.043,59.393,32.641 +2020-01-17 13:15:00,137.23,214.293,59.393,32.641 +2020-01-17 13:30:00,134.36,213.351,59.393,32.641 +2020-01-17 13:45:00,134.11,213.528,59.393,32.641 +2020-01-17 14:00:00,133.51,211.956,57.943999999999996,32.641 +2020-01-17 14:15:00,133.72,212.239,57.943999999999996,32.641 +2020-01-17 14:30:00,131.64,213.493,57.943999999999996,32.641 +2020-01-17 14:45:00,132.21,214.667,57.943999999999996,32.641 +2020-01-17 15:00:00,131.29,215.00400000000002,60.153999999999996,32.641 +2020-01-17 15:15:00,129.71,215.408,60.153999999999996,32.641 +2020-01-17 15:30:00,128.64,215.49900000000002,60.153999999999996,32.641 +2020-01-17 15:45:00,127.96,216.497,60.153999999999996,32.641 +2020-01-17 16:00:00,129.48,217.007,62.933,32.641 +2020-01-17 16:15:00,129.9,219.145,62.933,32.641 +2020-01-17 16:30:00,132.34,222.05,62.933,32.641 +2020-01-17 16:45:00,137.1,223.28400000000002,62.933,32.641 +2020-01-17 17:00:00,139.8,225.78599999999997,68.657,32.641 +2020-01-17 17:15:00,138.22,226.025,68.657,32.641 +2020-01-17 17:30:00,139.12,226.56900000000002,68.657,32.641 +2020-01-17 17:45:00,139.16,226.158,68.657,32.641 +2020-01-17 18:00:00,138.22,228.674,67.111,32.641 +2020-01-17 18:15:00,136.69,224.929,67.111,32.641 +2020-01-17 18:30:00,135.95,224.361,67.111,32.641 +2020-01-17 18:45:00,135.85,224.62599999999998,67.111,32.641 +2020-01-17 19:00:00,132.67,223.908,62.434,32.641 +2020-01-17 19:15:00,129.94,221.23,62.434,32.641 +2020-01-17 19:30:00,127.9,217.68,62.434,32.641 +2020-01-17 19:45:00,126.36,214.235,62.434,32.641 +2020-01-17 20:00:00,119.39,210.176,61.763000000000005,32.641 +2020-01-17 20:15:00,116.31,203.215,61.763000000000005,32.641 +2020-01-17 20:30:00,112.3,198.968,61.763000000000005,32.641 +2020-01-17 20:45:00,109.84,198.465,61.763000000000005,32.641 +2020-01-17 21:00:00,104.58,195.234,56.785,32.641 +2020-01-17 21:15:00,101.37,192.667,56.785,32.641 +2020-01-17 21:30:00,99.26,190.696,56.785,32.641 +2020-01-17 21:45:00,97.84,189.733,56.785,32.641 +2020-01-17 22:00:00,96.2,183.52700000000002,52.693000000000005,32.641 +2020-01-17 22:15:00,91.12,177.456,52.693000000000005,32.641 +2020-01-17 22:30:00,88.41,170.262,52.693000000000005,32.641 +2020-01-17 22:45:00,87.24,165.695,52.693000000000005,32.641 +2020-01-17 23:00:00,83.19,157.593,45.443999999999996,32.641 +2020-01-17 23:15:00,82.01,154.141,45.443999999999996,32.641 +2020-01-17 23:30:00,80.42,152.884,45.443999999999996,32.641 +2020-01-17 23:45:00,79.42,151.662,45.443999999999996,32.641 +2020-01-18 00:00:00,75.81,135.17700000000002,44.738,32.459 +2020-01-18 00:15:00,73.43,130.239,44.738,32.459 +2020-01-18 00:30:00,72.12,133.179,44.738,32.459 +2020-01-18 00:45:00,70.8,136.001,44.738,32.459 +2020-01-18 01:00:00,68.46,139.438,40.303000000000004,32.459 +2020-01-18 01:15:00,68.68,139.34,40.303000000000004,32.459 +2020-01-18 01:30:00,68.0,138.83100000000002,40.303000000000004,32.459 +2020-01-18 01:45:00,67.64,139.023,40.303000000000004,32.459 +2020-01-18 02:00:00,66.26,142.22299999999998,38.61,32.459 +2020-01-18 02:15:00,66.19,143.881,38.61,32.459 +2020-01-18 02:30:00,65.47,144.265,38.61,32.459 +2020-01-18 02:45:00,65.76,146.61,38.61,32.459 +2020-01-18 03:00:00,65.61,149.232,37.554,32.459 +2020-01-18 03:15:00,65.84,150.313,37.554,32.459 +2020-01-18 03:30:00,64.93,150.23,37.554,32.459 +2020-01-18 03:45:00,66.07,152.283,37.554,32.459 +2020-01-18 04:00:00,65.87,159.47899999999998,37.176,32.459 +2020-01-18 04:15:00,66.39,168.15,37.176,32.459 +2020-01-18 04:30:00,65.9,169.74400000000003,37.176,32.459 +2020-01-18 04:45:00,68.46,170.998,37.176,32.459 +2020-01-18 05:00:00,67.62,187.06400000000002,36.893,32.459 +2020-01-18 05:15:00,67.44,195.977,36.893,32.459 +2020-01-18 05:30:00,68.22,193.09,36.893,32.459 +2020-01-18 05:45:00,68.87,192.283,36.893,32.459 +2020-01-18 06:00:00,70.41,210.16299999999998,37.803000000000004,32.459 +2020-01-18 06:15:00,71.3,232.584,37.803000000000004,32.459 +2020-01-18 06:30:00,73.03,228.908,37.803000000000004,32.459 +2020-01-18 06:45:00,75.0,225.896,37.803000000000004,32.459 +2020-01-18 07:00:00,78.98,218.862,41.086999999999996,32.459 +2020-01-18 07:15:00,80.53,224.50599999999997,41.086999999999996,32.459 +2020-01-18 07:30:00,83.81,231.007,41.086999999999996,32.459 +2020-01-18 07:45:00,85.94,236.44799999999998,41.086999999999996,32.459 +2020-01-18 08:00:00,88.7,238.415,48.222,32.459 +2020-01-18 08:15:00,89.91,242.604,48.222,32.459 +2020-01-18 08:30:00,92.41,243.21,48.222,32.459 +2020-01-18 08:45:00,95.69,242.15099999999998,48.222,32.459 +2020-01-18 09:00:00,98.41,238.05700000000002,52.791000000000004,32.459 +2020-01-18 09:15:00,98.48,236.391,52.791000000000004,32.459 +2020-01-18 09:30:00,102.21,235.132,52.791000000000004,32.459 +2020-01-18 09:45:00,100.36,231.793,52.791000000000004,32.459 +2020-01-18 10:00:00,100.12,224.771,54.341,32.459 +2020-01-18 10:15:00,100.75,221.933,54.341,32.459 +2020-01-18 10:30:00,101.87,217.99599999999998,54.341,32.459 +2020-01-18 10:45:00,102.85,217.19299999999998,54.341,32.459 +2020-01-18 11:00:00,101.48,214.399,51.94,32.459 +2020-01-18 11:15:00,103.12,211.248,51.94,32.459 +2020-01-18 11:30:00,103.04,210.08900000000003,51.94,32.459 +2020-01-18 11:45:00,100.05,208.422,51.94,32.459 +2020-01-18 12:00:00,97.04,204.50599999999997,50.973,32.459 +2020-01-18 12:15:00,95.62,202.4,50.973,32.459 +2020-01-18 12:30:00,92.58,202.668,50.973,32.459 +2020-01-18 12:45:00,92.09,202.90200000000002,50.973,32.459 +2020-01-18 13:00:00,90.24,201.095,48.06399999999999,32.459 +2020-01-18 13:15:00,87.19,199.011,48.06399999999999,32.459 +2020-01-18 13:30:00,86.44,197.50099999999998,48.06399999999999,32.459 +2020-01-18 13:45:00,89.07,198.437,48.06399999999999,32.459 +2020-01-18 14:00:00,85.42,198.355,45.707,32.459 +2020-01-18 14:15:00,85.02,198.19299999999998,45.707,32.459 +2020-01-18 14:30:00,88.14,197.393,45.707,32.459 +2020-01-18 14:45:00,88.52,198.765,45.707,32.459 +2020-01-18 15:00:00,88.89,199.88299999999998,47.567,32.459 +2020-01-18 15:15:00,89.04,201.095,47.567,32.459 +2020-01-18 15:30:00,91.84,202.926,47.567,32.459 +2020-01-18 15:45:00,89.9,204.063,47.567,32.459 +2020-01-18 16:00:00,91.8,202.982,52.031000000000006,32.459 +2020-01-18 16:15:00,92.59,206.278,52.031000000000006,32.459 +2020-01-18 16:30:00,94.37,209.09900000000002,52.031000000000006,32.459 +2020-01-18 16:45:00,98.68,211.359,52.031000000000006,32.459 +2020-01-18 17:00:00,104.16,213.451,58.218999999999994,32.459 +2020-01-18 17:15:00,105.49,215.894,58.218999999999994,32.459 +2020-01-18 17:30:00,106.99,216.37900000000002,58.218999999999994,32.459 +2020-01-18 17:45:00,107.98,215.463,58.218999999999994,32.459 +2020-01-18 18:00:00,108.94,217.34,57.65,32.459 +2020-01-18 18:15:00,110.75,215.455,57.65,32.459 +2020-01-18 18:30:00,108.32,216.24099999999999,57.65,32.459 +2020-01-18 18:45:00,107.67,213.148,57.65,32.459 +2020-01-18 19:00:00,105.41,213.674,51.261,32.459 +2020-01-18 19:15:00,104.23,210.55700000000002,51.261,32.459 +2020-01-18 19:30:00,103.1,207.775,51.261,32.459 +2020-01-18 19:45:00,104.08,203.93900000000002,51.261,32.459 +2020-01-18 20:00:00,96.04,202.172,44.068000000000005,32.459 +2020-01-18 20:15:00,93.37,197.706,44.068000000000005,32.459 +2020-01-18 20:30:00,90.26,193.16400000000002,44.068000000000005,32.459 +2020-01-18 20:45:00,88.26,191.99400000000003,44.068000000000005,32.459 +2020-01-18 21:00:00,85.63,191.48,38.861,32.459 +2020-01-18 21:15:00,83.97,189.435,38.861,32.459 +2020-01-18 21:30:00,82.78,188.83599999999998,38.861,32.459 +2020-01-18 21:45:00,81.38,187.49400000000003,38.861,32.459 +2020-01-18 22:00:00,78.87,182.81099999999998,39.485,32.459 +2020-01-18 22:15:00,77.45,179.549,39.485,32.459 +2020-01-18 22:30:00,74.7,179.446,39.485,32.459 +2020-01-18 22:45:00,73.83,176.96,39.485,32.459 +2020-01-18 23:00:00,70.86,171.66400000000002,32.027,32.459 +2020-01-18 23:15:00,70.25,166.322,32.027,32.459 +2020-01-18 23:30:00,67.49,162.817,32.027,32.459 +2020-01-18 23:45:00,66.15,158.811,32.027,32.459 +2020-01-19 00:00:00,63.53,135.497,26.96,32.459 +2020-01-19 00:15:00,61.94,130.341,26.96,32.459 +2020-01-19 00:30:00,60.49,132.85,26.96,32.459 +2020-01-19 00:45:00,59.53,136.483,26.96,32.459 +2020-01-19 01:00:00,58.12,139.71,24.295,32.459 +2020-01-19 01:15:00,58.27,140.829,24.295,32.459 +2020-01-19 01:30:00,57.64,140.945,24.295,32.459 +2020-01-19 01:45:00,57.35,140.821,24.295,32.459 +2020-01-19 02:00:00,56.61,143.149,24.268,32.459 +2020-01-19 02:15:00,56.08,143.701,24.268,32.459 +2020-01-19 02:30:00,55.8,145.066,24.268,32.459 +2020-01-19 02:45:00,56.32,147.996,24.268,32.459 +2020-01-19 03:00:00,55.66,150.884,23.373,32.459 +2020-01-19 03:15:00,55.71,151.326,23.373,32.459 +2020-01-19 03:30:00,55.81,153.001,23.373,32.459 +2020-01-19 03:45:00,56.11,155.10299999999998,23.373,32.459 +2020-01-19 04:00:00,55.85,162.005,23.874000000000002,32.459 +2020-01-19 04:15:00,56.49,169.545,23.874000000000002,32.459 +2020-01-19 04:30:00,57.02,170.997,23.874000000000002,32.459 +2020-01-19 04:45:00,57.49,172.642,23.874000000000002,32.459 +2020-01-19 05:00:00,58.49,184.49599999999998,24.871,32.459 +2020-01-19 05:15:00,58.83,190.658,24.871,32.459 +2020-01-19 05:30:00,58.88,187.644,24.871,32.459 +2020-01-19 05:45:00,59.64,187.18900000000002,24.871,32.459 +2020-01-19 06:00:00,59.96,205.362,23.84,32.459 +2020-01-19 06:15:00,60.41,225.62599999999998,23.84,32.459 +2020-01-19 06:30:00,60.71,220.766,23.84,32.459 +2020-01-19 06:45:00,64.2,216.699,23.84,32.459 +2020-01-19 07:00:00,64.77,212.528,27.430999999999997,32.459 +2020-01-19 07:15:00,65.87,217.419,27.430999999999997,32.459 +2020-01-19 07:30:00,68.27,222.238,27.430999999999997,32.459 +2020-01-19 07:45:00,70.01,226.76,27.430999999999997,32.459 +2020-01-19 08:00:00,72.98,230.78099999999998,33.891999999999996,32.459 +2020-01-19 08:15:00,74.51,234.66099999999997,33.891999999999996,32.459 +2020-01-19 08:30:00,77.33,237.02900000000002,33.891999999999996,32.459 +2020-01-19 08:45:00,79.56,238.33,33.891999999999996,32.459 +2020-01-19 09:00:00,81.57,233.78099999999998,37.571,32.459 +2020-01-19 09:15:00,83.04,232.83599999999998,37.571,32.459 +2020-01-19 09:30:00,86.55,231.352,37.571,32.459 +2020-01-19 09:45:00,86.23,227.72299999999998,37.571,32.459 +2020-01-19 10:00:00,87.7,223.55200000000002,40.594,32.459 +2020-01-19 10:15:00,89.59,221.326,40.594,32.459 +2020-01-19 10:30:00,90.48,218.054,40.594,32.459 +2020-01-19 10:45:00,92.41,214.893,40.594,32.459 +2020-01-19 11:00:00,94.56,213.213,44.133,32.459 +2020-01-19 11:15:00,98.06,210.283,44.133,32.459 +2020-01-19 11:30:00,99.96,208.044,44.133,32.459 +2020-01-19 11:45:00,100.24,207.048,44.133,32.459 +2020-01-19 12:00:00,97.6,202.331,41.198,32.459 +2020-01-19 12:15:00,96.12,202.545,41.198,32.459 +2020-01-19 12:30:00,92.64,201.079,41.198,32.459 +2020-01-19 12:45:00,92.83,200.282,41.198,32.459 +2020-01-19 13:00:00,87.94,197.68400000000003,37.014,32.459 +2020-01-19 13:15:00,87.17,199.167,37.014,32.459 +2020-01-19 13:30:00,85.79,197.56099999999998,37.014,32.459 +2020-01-19 13:45:00,85.29,197.56599999999997,37.014,32.459 +2020-01-19 14:00:00,84.54,197.63099999999997,34.934,32.459 +2020-01-19 14:15:00,83.97,198.785,34.934,32.459 +2020-01-19 14:30:00,84.35,199.588,34.934,32.459 +2020-01-19 14:45:00,85.14,200.653,34.934,32.459 +2020-01-19 15:00:00,84.99,199.99,34.588,32.459 +2020-01-19 15:15:00,84.82,202.16,34.588,32.459 +2020-01-19 15:30:00,85.3,204.67700000000002,34.588,32.459 +2020-01-19 15:45:00,84.88,206.55900000000003,34.588,32.459 +2020-01-19 16:00:00,86.12,207.805,37.874,32.459 +2020-01-19 16:15:00,86.01,210.03400000000002,37.874,32.459 +2020-01-19 16:30:00,87.08,213.02700000000002,37.874,32.459 +2020-01-19 16:45:00,89.57,215.435,37.874,32.459 +2020-01-19 17:00:00,97.87,217.421,47.303999999999995,32.459 +2020-01-19 17:15:00,102.85,219.338,47.303999999999995,32.459 +2020-01-19 17:30:00,105.44,220.085,47.303999999999995,32.459 +2020-01-19 17:45:00,106.76,221.72400000000002,47.303999999999995,32.459 +2020-01-19 18:00:00,106.95,222.90200000000002,48.879,32.459 +2020-01-19 18:15:00,108.0,222.58700000000002,48.879,32.459 +2020-01-19 18:30:00,104.37,221.048,48.879,32.459 +2020-01-19 18:45:00,103.14,220.05900000000003,48.879,32.459 +2020-01-19 19:00:00,101.77,219.907,44.826,32.459 +2020-01-19 19:15:00,99.54,217.583,44.826,32.459 +2020-01-19 19:30:00,98.11,214.65400000000002,44.826,32.459 +2020-01-19 19:45:00,96.83,212.50400000000002,44.826,32.459 +2020-01-19 20:00:00,98.04,210.7,40.154,32.459 +2020-01-19 20:15:00,101.48,207.36900000000003,40.154,32.459 +2020-01-19 20:30:00,97.9,204.132,40.154,32.459 +2020-01-19 20:45:00,89.29,201.812,40.154,32.459 +2020-01-19 21:00:00,89.58,198.354,36.549,32.459 +2020-01-19 21:15:00,86.78,195.61900000000003,36.549,32.459 +2020-01-19 21:30:00,86.94,195.425,36.549,32.459 +2020-01-19 21:45:00,87.39,194.21200000000002,36.549,32.459 +2020-01-19 22:00:00,87.92,187.94099999999997,37.663000000000004,32.459 +2020-01-19 22:15:00,85.91,184.03,37.663000000000004,32.459 +2020-01-19 22:30:00,86.09,180.403,37.663000000000004,32.459 +2020-01-19 22:45:00,90.89,177.109,37.663000000000004,32.459 +2020-01-19 23:00:00,87.77,168.668,31.945,32.459 +2020-01-19 23:15:00,86.14,165.292,31.945,32.459 +2020-01-19 23:30:00,80.64,162.781,31.945,32.459 +2020-01-19 23:45:00,82.46,159.77200000000002,31.945,32.459 +2020-01-20 00:00:00,84.43,140.184,31.533,32.641 +2020-01-20 00:15:00,82.35,138.34799999999998,31.533,32.641 +2020-01-20 00:30:00,79.55,141.045,31.533,32.641 +2020-01-20 00:45:00,74.22,144.09799999999998,31.533,32.641 +2020-01-20 01:00:00,68.93,147.299,30.56,32.641 +2020-01-20 01:15:00,74.43,147.789,30.56,32.641 +2020-01-20 01:30:00,78.16,147.909,30.56,32.641 +2020-01-20 01:45:00,77.86,147.925,30.56,32.641 +2020-01-20 02:00:00,74.11,150.179,29.55,32.641 +2020-01-20 02:15:00,78.18,152.602,29.55,32.641 +2020-01-20 02:30:00,78.02,154.341,29.55,32.641 +2020-01-20 02:45:00,78.43,156.56799999999998,29.55,32.641 +2020-01-20 03:00:00,75.4,160.888,27.059,32.641 +2020-01-20 03:15:00,72.87,163.166,27.059,32.641 +2020-01-20 03:30:00,73.43,164.412,27.059,32.641 +2020-01-20 03:45:00,74.76,165.95,27.059,32.641 +2020-01-20 04:00:00,81.45,177.48,28.384,32.641 +2020-01-20 04:15:00,80.59,189.41,28.384,32.641 +2020-01-20 04:30:00,78.24,193.53599999999997,28.384,32.641 +2020-01-20 04:45:00,80.69,195.301,28.384,32.641 +2020-01-20 05:00:00,85.34,224.606,35.915,32.641 +2020-01-20 05:15:00,87.03,252.854,35.915,32.641 +2020-01-20 05:30:00,92.23,250.43200000000002,35.915,32.641 +2020-01-20 05:45:00,96.51,243.827,35.915,32.641 +2020-01-20 06:00:00,105.11,242.514,56.18,32.641 +2020-01-20 06:15:00,109.59,246.791,56.18,32.641 +2020-01-20 06:30:00,116.86,250.736,56.18,32.641 +2020-01-20 06:45:00,120.28,256.20099999999996,56.18,32.641 +2020-01-20 07:00:00,128.01,254.76,70.877,32.641 +2020-01-20 07:15:00,133.06,260.723,70.877,32.641 +2020-01-20 07:30:00,134.78,264.745,70.877,32.641 +2020-01-20 07:45:00,134.45,266.157,70.877,32.641 +2020-01-20 08:00:00,137.24,264.639,65.65,32.641 +2020-01-20 08:15:00,136.02,266.177,65.65,32.641 +2020-01-20 08:30:00,137.76,263.861,65.65,32.641 +2020-01-20 08:45:00,135.54,261.198,65.65,32.641 +2020-01-20 09:00:00,137.3,255.574,62.037,32.641 +2020-01-20 09:15:00,140.43,250.84400000000002,62.037,32.641 +2020-01-20 09:30:00,141.76,248.355,62.037,32.641 +2020-01-20 09:45:00,141.7,245.425,62.037,32.641 +2020-01-20 10:00:00,140.32,239.98,60.409,32.641 +2020-01-20 10:15:00,143.66,237.407,60.409,32.641 +2020-01-20 10:30:00,143.46,233.18,60.409,32.641 +2020-01-20 10:45:00,142.34,231.155,60.409,32.641 +2020-01-20 11:00:00,141.51,226.299,60.211999999999996,32.641 +2020-01-20 11:15:00,141.96,225.43,60.211999999999996,32.641 +2020-01-20 11:30:00,141.04,224.695,60.211999999999996,32.641 +2020-01-20 11:45:00,140.21,223.153,60.211999999999996,32.641 +2020-01-20 12:00:00,137.28,220.753,57.733000000000004,32.641 +2020-01-20 12:15:00,135.08,220.96900000000002,57.733000000000004,32.641 +2020-01-20 12:30:00,134.0,219.947,57.733000000000004,32.641 +2020-01-20 12:45:00,133.78,220.92700000000002,57.733000000000004,32.641 +2020-01-20 13:00:00,132.27,218.886,58.695,32.641 +2020-01-20 13:15:00,132.8,218.87,58.695,32.641 +2020-01-20 13:30:00,131.49,216.606,58.695,32.641 +2020-01-20 13:45:00,135.03,216.507,58.695,32.641 +2020-01-20 14:00:00,134.0,215.979,59.505,32.641 +2020-01-20 14:15:00,133.27,216.283,59.505,32.641 +2020-01-20 14:30:00,132.16,216.48,59.505,32.641 +2020-01-20 14:45:00,133.29,217.21,59.505,32.641 +2020-01-20 15:00:00,134.34,218.625,59.946000000000005,32.641 +2020-01-20 15:15:00,131.87,219.19099999999997,59.946000000000005,32.641 +2020-01-20 15:30:00,131.59,220.61599999999999,59.946000000000005,32.641 +2020-01-20 15:45:00,130.93,222.04,59.946000000000005,32.641 +2020-01-20 16:00:00,132.11,223.24900000000002,61.766999999999996,32.641 +2020-01-20 16:15:00,130.14,224.595,61.766999999999996,32.641 +2020-01-20 16:30:00,131.7,226.576,61.766999999999996,32.641 +2020-01-20 16:45:00,135.3,227.615,61.766999999999996,32.641 +2020-01-20 17:00:00,141.09,229.46099999999998,67.85600000000001,32.641 +2020-01-20 17:15:00,139.31,230.275,67.85600000000001,32.641 +2020-01-20 17:30:00,138.86,230.47799999999998,67.85600000000001,32.641 +2020-01-20 17:45:00,138.41,230.486,67.85600000000001,32.641 +2020-01-20 18:00:00,138.69,232.252,64.564,32.641 +2020-01-20 18:15:00,137.29,229.68,64.564,32.641 +2020-01-20 18:30:00,136.61,228.98,64.564,32.641 +2020-01-20 18:45:00,137.05,228.43400000000003,64.564,32.641 +2020-01-20 19:00:00,133.91,226.423,58.536,32.641 +2020-01-20 19:15:00,132.7,222.625,58.536,32.641 +2020-01-20 19:30:00,128.93,220.31099999999998,58.536,32.641 +2020-01-20 19:45:00,128.61,217.30700000000002,58.536,32.641 +2020-01-20 20:00:00,121.04,212.972,59.888999999999996,32.641 +2020-01-20 20:15:00,117.83,206.653,59.888999999999996,32.641 +2020-01-20 20:30:00,115.43,201.231,59.888999999999996,32.641 +2020-01-20 20:45:00,114.45,200.747,59.888999999999996,32.641 +2020-01-20 21:00:00,108.29,197.956,52.652,32.641 +2020-01-20 21:15:00,112.06,193.812,52.652,32.641 +2020-01-20 21:30:00,112.01,192.627,52.652,32.641 +2020-01-20 21:45:00,109.57,190.88400000000001,52.652,32.641 +2020-01-20 22:00:00,100.81,181.58599999999998,46.17,32.641 +2020-01-20 22:15:00,98.31,175.95,46.17,32.641 +2020-01-20 22:30:00,96.61,162.003,46.17,32.641 +2020-01-20 22:45:00,99.56,153.227,46.17,32.641 +2020-01-20 23:00:00,96.3,145.66,36.281,32.641 +2020-01-20 23:15:00,94.34,145.513,36.281,32.641 +2020-01-20 23:30:00,84.13,146.162,36.281,32.641 +2020-01-20 23:45:00,85.68,146.227,36.281,32.641 +2020-01-21 00:00:00,88.44,139.76,38.821999999999996,32.641 +2020-01-21 00:15:00,87.49,139.435,38.821999999999996,32.641 +2020-01-21 00:30:00,86.84,140.901,38.821999999999996,32.641 +2020-01-21 00:45:00,82.6,142.671,38.821999999999996,32.641 +2020-01-21 01:00:00,84.63,145.717,36.936,32.641 +2020-01-21 01:15:00,85.35,145.653,36.936,32.641 +2020-01-21 01:30:00,83.29,145.993,36.936,32.641 +2020-01-21 01:45:00,81.04,146.444,36.936,32.641 +2020-01-21 02:00:00,83.81,148.781,34.42,32.641 +2020-01-21 02:15:00,86.37,150.878,34.42,32.641 +2020-01-21 02:30:00,82.71,151.991,34.42,32.641 +2020-01-21 02:45:00,82.96,154.17600000000002,34.42,32.641 +2020-01-21 03:00:00,82.05,157.155,33.585,32.641 +2020-01-21 03:15:00,84.36,158.278,33.585,32.641 +2020-01-21 03:30:00,78.36,160.099,33.585,32.641 +2020-01-21 03:45:00,85.53,162.072,33.585,32.641 +2020-01-21 04:00:00,85.62,173.56400000000002,35.622,32.641 +2020-01-21 04:15:00,85.41,185.06599999999997,35.622,32.641 +2020-01-21 04:30:00,84.33,188.84900000000002,35.622,32.641 +2020-01-21 04:45:00,89.36,191.958,35.622,32.641 +2020-01-21 05:00:00,95.38,226.903,40.599000000000004,32.641 +2020-01-21 05:15:00,91.27,254.767,40.599000000000004,32.641 +2020-01-21 05:30:00,93.24,250.47400000000002,40.599000000000004,32.641 +2020-01-21 05:45:00,98.12,244.033,40.599000000000004,32.641 +2020-01-21 06:00:00,108.67,241.128,55.203,32.641 +2020-01-21 06:15:00,112.52,247.321,55.203,32.641 +2020-01-21 06:30:00,114.67,250.555,55.203,32.641 +2020-01-21 06:45:00,119.03,255.76,55.203,32.641 +2020-01-21 07:00:00,126.8,254.093,69.029,32.641 +2020-01-21 07:15:00,129.56,259.89599999999996,69.029,32.641 +2020-01-21 07:30:00,135.6,263.238,69.029,32.641 +2020-01-21 07:45:00,132.37,265.056,69.029,32.641 +2020-01-21 08:00:00,134.51,263.666,65.85300000000001,32.641 +2020-01-21 08:15:00,134.53,264.052,65.85300000000001,32.641 +2020-01-21 08:30:00,135.3,261.45099999999996,65.85300000000001,32.641 +2020-01-21 08:45:00,136.72,258.621,65.85300000000001,32.641 +2020-01-21 09:00:00,137.69,251.945,61.566,32.641 +2020-01-21 09:15:00,138.99,249.12900000000002,61.566,32.641 +2020-01-21 09:30:00,140.22,247.34099999999998,61.566,32.641 +2020-01-21 09:45:00,141.41,243.92,61.566,32.641 +2020-01-21 10:00:00,138.32,238.024,61.244,32.641 +2020-01-21 10:15:00,140.84,234.231,61.244,32.641 +2020-01-21 10:30:00,140.25,230.209,61.244,32.641 +2020-01-21 10:45:00,139.51,228.35,61.244,32.641 +2020-01-21 11:00:00,139.77,225.236,61.16,32.641 +2020-01-21 11:15:00,140.28,223.91,61.16,32.641 +2020-01-21 11:30:00,141.43,222.003,61.16,32.641 +2020-01-21 11:45:00,141.15,221.368,61.16,32.641 +2020-01-21 12:00:00,137.98,217.40200000000002,59.09,32.641 +2020-01-21 12:15:00,136.33,217.08900000000003,59.09,32.641 +2020-01-21 12:30:00,134.76,216.75599999999997,59.09,32.641 +2020-01-21 12:45:00,135.83,217.27200000000002,59.09,32.641 +2020-01-21 13:00:00,136.03,214.812,60.21,32.641 +2020-01-21 13:15:00,137.42,214.06099999999998,60.21,32.641 +2020-01-21 13:30:00,136.56,213.168,60.21,32.641 +2020-01-21 13:45:00,136.51,213.452,60.21,32.641 +2020-01-21 14:00:00,133.43,213.22400000000002,60.673,32.641 +2020-01-21 14:15:00,132.93,213.732,60.673,32.641 +2020-01-21 14:30:00,131.47,214.611,60.673,32.641 +2020-01-21 14:45:00,132.26,215.398,60.673,32.641 +2020-01-21 15:00:00,133.45,216.356,62.232,32.641 +2020-01-21 15:15:00,131.81,217.135,62.232,32.641 +2020-01-21 15:30:00,129.92,218.83700000000002,62.232,32.641 +2020-01-21 15:45:00,129.62,219.72,62.232,32.641 +2020-01-21 16:00:00,130.89,221.453,63.611999999999995,32.641 +2020-01-21 16:15:00,130.05,223.34099999999998,63.611999999999995,32.641 +2020-01-21 16:30:00,131.99,226.138,63.611999999999995,32.641 +2020-01-21 16:45:00,133.7,227.4,63.611999999999995,32.641 +2020-01-21 17:00:00,141.22,229.834,70.658,32.641 +2020-01-21 17:15:00,139.73,230.655,70.658,32.641 +2020-01-21 17:30:00,139.98,231.72299999999998,70.658,32.641 +2020-01-21 17:45:00,140.83,231.653,70.658,32.641 +2020-01-21 18:00:00,139.47,233.438,68.361,32.641 +2020-01-21 18:15:00,137.77,230.11900000000003,68.361,32.641 +2020-01-21 18:30:00,136.43,229.109,68.361,32.641 +2020-01-21 18:45:00,136.05,229.52200000000002,68.361,32.641 +2020-01-21 19:00:00,132.77,227.708,62.922,32.641 +2020-01-21 19:15:00,131.36,223.574,62.922,32.641 +2020-01-21 19:30:00,136.19,220.53799999999998,62.922,32.641 +2020-01-21 19:45:00,137.49,217.548,62.922,32.641 +2020-01-21 20:00:00,125.64,213.32299999999998,63.251999999999995,32.641 +2020-01-21 20:15:00,119.24,206.47299999999998,63.251999999999995,32.641 +2020-01-21 20:30:00,114.43,202.202,63.251999999999995,32.641 +2020-01-21 20:45:00,114.99,201.021,63.251999999999995,32.641 +2020-01-21 21:00:00,107.66,197.293,54.47,32.641 +2020-01-21 21:15:00,113.17,194.36599999999999,54.47,32.641 +2020-01-21 21:30:00,112.31,192.32,54.47,32.641 +2020-01-21 21:45:00,106.88,190.829,54.47,32.641 +2020-01-21 22:00:00,100.25,183.455,51.12,32.641 +2020-01-21 22:15:00,96.44,177.588,51.12,32.641 +2020-01-21 22:30:00,95.56,163.696,51.12,32.641 +2020-01-21 22:45:00,98.08,155.245,51.12,32.641 +2020-01-21 23:00:00,92.47,147.793,42.156000000000006,32.641 +2020-01-21 23:15:00,91.54,146.417,42.156000000000006,32.641 +2020-01-21 23:30:00,85.61,146.672,42.156000000000006,32.641 +2020-01-21 23:45:00,89.01,146.222,42.156000000000006,32.641 +2020-01-22 00:00:00,85.62,139.774,37.192,32.641 +2020-01-22 00:15:00,81.83,139.43200000000002,37.192,32.641 +2020-01-22 00:30:00,78.79,140.88,37.192,32.641 +2020-01-22 00:45:00,84.5,142.636,37.192,32.641 +2020-01-22 01:00:00,81.61,145.673,32.24,32.641 +2020-01-22 01:15:00,82.94,145.59799999999998,32.24,32.641 +2020-01-22 01:30:00,75.82,145.93200000000002,32.24,32.641 +2020-01-22 01:45:00,81.67,146.376,32.24,32.641 +2020-01-22 02:00:00,79.49,148.722,30.34,32.641 +2020-01-22 02:15:00,76.77,150.81799999999998,30.34,32.641 +2020-01-22 02:30:00,76.68,151.94299999999998,30.34,32.641 +2020-01-22 02:45:00,75.36,154.127,30.34,32.641 +2020-01-22 03:00:00,80.37,157.10399999999998,29.129,32.641 +2020-01-22 03:15:00,81.93,158.24,29.129,32.641 +2020-01-22 03:30:00,80.31,160.061,29.129,32.641 +2020-01-22 03:45:00,81.37,162.04399999999998,29.129,32.641 +2020-01-22 04:00:00,84.25,173.516,30.075,32.641 +2020-01-22 04:15:00,85.11,185.007,30.075,32.641 +2020-01-22 04:30:00,83.8,188.795,30.075,32.641 +2020-01-22 04:45:00,87.55,191.895,30.075,32.641 +2020-01-22 05:00:00,91.73,226.801,35.684,32.641 +2020-01-22 05:15:00,94.75,254.644,35.684,32.641 +2020-01-22 05:30:00,92.38,250.34900000000002,35.684,32.641 +2020-01-22 05:45:00,96.78,243.924,35.684,32.641 +2020-01-22 06:00:00,106.02,241.03900000000002,51.49,32.641 +2020-01-22 06:15:00,111.14,247.24400000000003,51.49,32.641 +2020-01-22 06:30:00,115.37,250.47799999999998,51.49,32.641 +2020-01-22 06:45:00,120.3,255.707,51.49,32.641 +2020-01-22 07:00:00,127.38,254.05900000000003,68.242,32.641 +2020-01-22 07:15:00,127.79,259.843,68.242,32.641 +2020-01-22 07:30:00,130.4,263.16,68.242,32.641 +2020-01-22 07:45:00,128.61,264.947,68.242,32.641 +2020-01-22 08:00:00,135.29,263.547,63.619,32.641 +2020-01-22 08:15:00,132.82,263.91200000000003,63.619,32.641 +2020-01-22 08:30:00,133.56,261.265,63.619,32.641 +2020-01-22 08:45:00,131.49,258.42,63.619,32.641 +2020-01-22 09:00:00,132.7,251.735,61.333,32.641 +2020-01-22 09:15:00,134.67,248.925,61.333,32.641 +2020-01-22 09:30:00,136.96,247.155,61.333,32.641 +2020-01-22 09:45:00,137.72,243.732,61.333,32.641 +2020-01-22 10:00:00,135.4,237.84,59.663000000000004,32.641 +2020-01-22 10:15:00,135.86,234.06,59.663000000000004,32.641 +2020-01-22 10:30:00,135.23,230.037,59.663000000000004,32.641 +2020-01-22 10:45:00,135.61,228.18599999999998,59.663000000000004,32.641 +2020-01-22 11:00:00,134.31,225.054,59.771,32.641 +2020-01-22 11:15:00,134.36,223.733,59.771,32.641 +2020-01-22 11:30:00,133.1,221.829,59.771,32.641 +2020-01-22 11:45:00,129.04,221.201,59.771,32.641 +2020-01-22 12:00:00,125.42,217.24900000000002,58.723,32.641 +2020-01-22 12:15:00,124.01,216.957,58.723,32.641 +2020-01-22 12:30:00,120.56,216.608,58.723,32.641 +2020-01-22 12:45:00,119.44,217.122,58.723,32.641 +2020-01-22 13:00:00,117.85,214.667,58.727,32.641 +2020-01-22 13:15:00,117.79,213.896,58.727,32.641 +2020-01-22 13:30:00,112.65,212.987,58.727,32.641 +2020-01-22 13:45:00,116.56,213.269,58.727,32.641 +2020-01-22 14:00:00,117.4,213.078,59.803999999999995,32.641 +2020-01-22 14:15:00,121.18,213.571,59.803999999999995,32.641 +2020-01-22 14:30:00,122.83,214.44799999999998,59.803999999999995,32.641 +2020-01-22 14:45:00,124.29,215.252,59.803999999999995,32.641 +2020-01-22 15:00:00,121.07,216.226,61.05,32.641 +2020-01-22 15:15:00,118.8,216.98,61.05,32.641 +2020-01-22 15:30:00,120.0,218.66099999999997,61.05,32.641 +2020-01-22 15:45:00,121.57,219.53,61.05,32.641 +2020-01-22 16:00:00,124.33,221.263,64.012,32.641 +2020-01-22 16:15:00,123.18,223.155,64.012,32.641 +2020-01-22 16:30:00,126.48,225.959,64.012,32.641 +2020-01-22 16:45:00,129.37,227.22,64.012,32.641 +2020-01-22 17:00:00,135.39,229.643,66.751,32.641 +2020-01-22 17:15:00,135.01,230.495,66.751,32.641 +2020-01-22 17:30:00,137.03,231.59799999999998,66.751,32.641 +2020-01-22 17:45:00,138.51,231.55200000000002,66.751,32.641 +2020-01-22 18:00:00,137.18,233.358,65.91199999999999,32.641 +2020-01-22 18:15:00,135.84,230.067,65.91199999999999,32.641 +2020-01-22 18:30:00,133.59,229.06,65.91199999999999,32.641 +2020-01-22 18:45:00,134.99,229.49599999999998,65.91199999999999,32.641 +2020-01-22 19:00:00,132.74,227.643,63.324,32.641 +2020-01-22 19:15:00,130.93,223.517,63.324,32.641 +2020-01-22 19:30:00,136.71,220.49200000000002,63.324,32.641 +2020-01-22 19:45:00,135.36,217.517,63.324,32.641 +2020-01-22 20:00:00,124.6,213.267,63.573,32.641 +2020-01-22 20:15:00,117.48,206.424,63.573,32.641 +2020-01-22 20:30:00,112.16,202.15200000000002,63.573,32.641 +2020-01-22 20:45:00,113.32,200.983,63.573,32.641 +2020-01-22 21:00:00,105.36,197.236,55.073,32.641 +2020-01-22 21:15:00,110.61,194.291,55.073,32.641 +2020-01-22 21:30:00,110.22,192.24599999999998,55.073,32.641 +2020-01-22 21:45:00,104.07,190.769,55.073,32.641 +2020-01-22 22:00:00,99.2,183.387,51.321999999999996,32.641 +2020-01-22 22:15:00,94.35,177.542,51.321999999999996,32.641 +2020-01-22 22:30:00,90.06,163.64,51.321999999999996,32.641 +2020-01-22 22:45:00,89.79,155.197,51.321999999999996,32.641 +2020-01-22 23:00:00,82.0,147.72899999999998,42.09,32.641 +2020-01-22 23:15:00,82.55,146.36700000000002,42.09,32.641 +2020-01-22 23:30:00,87.0,146.639,42.09,32.641 +2020-01-22 23:45:00,88.39,146.19899999999998,42.09,32.641 +2020-01-23 00:00:00,83.32,139.778,38.399,32.641 +2020-01-23 00:15:00,78.11,139.42,38.399,32.641 +2020-01-23 00:30:00,80.92,140.849,38.399,32.641 +2020-01-23 00:45:00,83.96,142.593,38.399,32.641 +2020-01-23 01:00:00,80.81,145.619,36.94,32.641 +2020-01-23 01:15:00,75.12,145.533,36.94,32.641 +2020-01-23 01:30:00,75.03,145.861,36.94,32.641 +2020-01-23 01:45:00,72.02,146.297,36.94,32.641 +2020-01-23 02:00:00,77.81,148.651,35.275,32.641 +2020-01-23 02:15:00,79.24,150.75,35.275,32.641 +2020-01-23 02:30:00,80.05,151.886,35.275,32.641 +2020-01-23 02:45:00,75.79,154.06799999999998,35.275,32.641 +2020-01-23 03:00:00,71.98,157.043,35.329,32.641 +2020-01-23 03:15:00,76.32,158.19299999999998,35.329,32.641 +2020-01-23 03:30:00,81.55,160.012,35.329,32.641 +2020-01-23 03:45:00,81.7,162.007,35.329,32.641 +2020-01-23 04:00:00,79.93,173.46,36.275,32.641 +2020-01-23 04:15:00,78.74,184.93900000000002,36.275,32.641 +2020-01-23 04:30:00,85.06,188.732,36.275,32.641 +2020-01-23 04:45:00,87.85,191.82299999999998,36.275,32.641 +2020-01-23 05:00:00,88.57,226.69,42.193999999999996,32.641 +2020-01-23 05:15:00,86.51,254.516,42.193999999999996,32.641 +2020-01-23 05:30:00,89.63,250.215,42.193999999999996,32.641 +2020-01-23 05:45:00,95.12,243.808,42.193999999999996,32.641 +2020-01-23 06:00:00,102.82,240.94299999999998,56.422,32.641 +2020-01-23 06:15:00,109.43,247.16,56.422,32.641 +2020-01-23 06:30:00,111.99,250.392,56.422,32.641 +2020-01-23 06:45:00,117.62,255.642,56.422,32.641 +2020-01-23 07:00:00,125.86,254.014,72.569,32.641 +2020-01-23 07:15:00,132.26,259.78,72.569,32.641 +2020-01-23 07:30:00,134.8,263.07,72.569,32.641 +2020-01-23 07:45:00,132.33,264.827,72.569,32.641 +2020-01-23 08:00:00,134.07,263.414,67.704,32.641 +2020-01-23 08:15:00,132.87,263.758,67.704,32.641 +2020-01-23 08:30:00,136.34,261.065,67.704,32.641 +2020-01-23 08:45:00,132.86,258.20599999999996,67.704,32.641 +2020-01-23 09:00:00,133.61,251.513,63.434,32.641 +2020-01-23 09:15:00,133.41,248.707,63.434,32.641 +2020-01-23 09:30:00,135.43,246.957,63.434,32.641 +2020-01-23 09:45:00,135.05,243.53,63.434,32.641 +2020-01-23 10:00:00,134.75,237.644,61.88399999999999,32.641 +2020-01-23 10:15:00,136.32,233.87900000000002,61.88399999999999,32.641 +2020-01-23 10:30:00,134.68,229.854,61.88399999999999,32.641 +2020-01-23 10:45:00,136.01,228.01,61.88399999999999,32.641 +2020-01-23 11:00:00,136.01,224.862,61.481,32.641 +2020-01-23 11:15:00,134.32,223.547,61.481,32.641 +2020-01-23 11:30:00,130.61,221.64700000000002,61.481,32.641 +2020-01-23 11:45:00,133.98,221.024,61.481,32.641 +2020-01-23 12:00:00,134.51,217.088,59.527,32.641 +2020-01-23 12:15:00,132.67,216.815,59.527,32.641 +2020-01-23 12:30:00,132.94,216.449,59.527,32.641 +2020-01-23 12:45:00,133.32,216.96200000000002,59.527,32.641 +2020-01-23 13:00:00,132.61,214.511,58.794,32.641 +2020-01-23 13:15:00,132.78,213.72099999999998,58.794,32.641 +2020-01-23 13:30:00,128.88,212.799,58.794,32.641 +2020-01-23 13:45:00,128.87,213.078,58.794,32.641 +2020-01-23 14:00:00,127.62,212.922,60.32,32.641 +2020-01-23 14:15:00,129.42,213.40200000000002,60.32,32.641 +2020-01-23 14:30:00,128.31,214.275,60.32,32.641 +2020-01-23 14:45:00,128.39,215.09599999999998,60.32,32.641 +2020-01-23 15:00:00,129.2,216.08700000000002,62.52,32.641 +2020-01-23 15:15:00,128.37,216.81400000000002,62.52,32.641 +2020-01-23 15:30:00,125.84,218.475,62.52,32.641 +2020-01-23 15:45:00,125.84,219.328,62.52,32.641 +2020-01-23 16:00:00,127.22,221.063,64.199,32.641 +2020-01-23 16:15:00,127.3,222.956,64.199,32.641 +2020-01-23 16:30:00,128.99,225.766,64.199,32.641 +2020-01-23 16:45:00,131.38,227.025,64.199,32.641 +2020-01-23 17:00:00,137.71,229.438,68.19800000000001,32.641 +2020-01-23 17:15:00,137.9,230.322,68.19800000000001,32.641 +2020-01-23 17:30:00,140.75,231.459,68.19800000000001,32.641 +2020-01-23 17:45:00,138.94,231.43900000000002,68.19800000000001,32.641 +2020-01-23 18:00:00,139.21,233.265,67.899,32.641 +2020-01-23 18:15:00,140.6,230.005,67.899,32.641 +2020-01-23 18:30:00,135.9,229.002,67.899,32.641 +2020-01-23 18:45:00,134.83,229.46099999999998,67.899,32.641 +2020-01-23 19:00:00,132.4,227.56799999999998,64.72399999999999,32.641 +2020-01-23 19:15:00,130.3,223.447,64.72399999999999,32.641 +2020-01-23 19:30:00,136.27,220.435,64.72399999999999,32.641 +2020-01-23 19:45:00,136.38,217.476,64.72399999999999,32.641 +2020-01-23 20:00:00,124.03,213.202,64.062,32.641 +2020-01-23 20:15:00,118.44,206.364,64.062,32.641 +2020-01-23 20:30:00,114.15,202.092,64.062,32.641 +2020-01-23 20:45:00,111.79,200.937,64.062,32.641 +2020-01-23 21:00:00,107.54,197.17,57.971000000000004,32.641 +2020-01-23 21:15:00,110.3,194.208,57.971000000000004,32.641 +2020-01-23 21:30:00,111.1,192.162,57.971000000000004,32.641 +2020-01-23 21:45:00,106.2,190.703,57.971000000000004,32.641 +2020-01-23 22:00:00,98.72,183.31099999999998,53.715,32.641 +2020-01-23 22:15:00,96.15,177.487,53.715,32.641 +2020-01-23 22:30:00,92.08,163.576,53.715,32.641 +2020-01-23 22:45:00,91.81,155.14,53.715,32.641 +2020-01-23 23:00:00,94.02,147.657,47.8,32.641 +2020-01-23 23:15:00,94.02,146.306,47.8,32.641 +2020-01-23 23:30:00,89.04,146.597,47.8,32.641 +2020-01-23 23:45:00,85.37,146.167,47.8,32.641 +2020-01-24 00:00:00,86.99,138.808,43.656000000000006,32.641 +2020-01-24 00:15:00,84.7,138.631,43.656000000000006,32.641 +2020-01-24 00:30:00,84.43,139.84,43.656000000000006,32.641 +2020-01-24 00:45:00,78.25,141.634,43.656000000000006,32.641 +2020-01-24 01:00:00,78.02,144.345,41.263000000000005,32.641 +2020-01-24 01:15:00,81.93,145.425,41.263000000000005,32.641 +2020-01-24 01:30:00,81.42,145.365,41.263000000000005,32.641 +2020-01-24 01:45:00,76.21,145.961,41.263000000000005,32.641 +2020-01-24 02:00:00,78.49,148.269,40.799,32.641 +2020-01-24 02:15:00,80.51,150.241,40.799,32.641 +2020-01-24 02:30:00,80.14,151.866,40.799,32.641 +2020-01-24 02:45:00,75.42,154.216,40.799,32.641 +2020-01-24 03:00:00,74.46,155.875,41.398,32.641 +2020-01-24 03:15:00,81.27,158.363,41.398,32.641 +2020-01-24 03:30:00,82.2,160.202,41.398,32.641 +2020-01-24 03:45:00,82.59,162.441,41.398,32.641 +2020-01-24 04:00:00,78.55,174.128,42.38,32.641 +2020-01-24 04:15:00,81.62,185.585,42.38,32.641 +2020-01-24 04:30:00,86.32,189.485,42.38,32.641 +2020-01-24 04:45:00,88.91,191.32299999999998,42.38,32.641 +2020-01-24 05:00:00,90.44,224.72799999999998,46.181000000000004,32.641 +2020-01-24 05:15:00,88.25,254.162,46.181000000000004,32.641 +2020-01-24 05:30:00,91.02,251.11,46.181000000000004,32.641 +2020-01-24 05:45:00,95.66,244.72299999999998,46.181000000000004,32.641 +2020-01-24 06:00:00,105.46,242.351,59.33,32.641 +2020-01-24 06:15:00,108.13,246.793,59.33,32.641 +2020-01-24 06:30:00,112.97,249.0,59.33,32.641 +2020-01-24 06:45:00,118.74,256.23400000000004,59.33,32.641 +2020-01-24 07:00:00,125.57,253.513,72.454,32.641 +2020-01-24 07:15:00,127.25,260.314,72.454,32.641 +2020-01-24 07:30:00,132.41,263.71299999999997,72.454,32.641 +2020-01-24 07:45:00,133.17,264.42,72.454,32.641 +2020-01-24 08:00:00,137.53,261.54900000000004,67.175,32.641 +2020-01-24 08:15:00,135.9,261.298,67.175,32.641 +2020-01-24 08:30:00,136.3,259.728,67.175,32.641 +2020-01-24 08:45:00,136.6,255.017,67.175,32.641 +2020-01-24 09:00:00,137.1,249.24099999999999,65.365,32.641 +2020-01-24 09:15:00,138.33,246.803,65.365,32.641 +2020-01-24 09:30:00,139.13,244.678,65.365,32.641 +2020-01-24 09:45:00,139.22,241.058,65.365,32.641 +2020-01-24 10:00:00,138.08,233.864,63.95,32.641 +2020-01-24 10:15:00,138.61,230.97299999999998,63.95,32.641 +2020-01-24 10:30:00,138.28,226.773,63.95,32.641 +2020-01-24 10:45:00,137.57,224.433,63.95,32.641 +2020-01-24 11:00:00,138.32,221.21900000000002,63.92100000000001,32.641 +2020-01-24 11:15:00,138.78,219.02900000000002,63.92100000000001,32.641 +2020-01-24 11:30:00,139.38,219.236,63.92100000000001,32.641 +2020-01-24 11:45:00,138.06,218.825,63.92100000000001,32.641 +2020-01-24 12:00:00,136.09,216.109,60.79600000000001,32.641 +2020-01-24 12:15:00,134.0,213.50099999999998,60.79600000000001,32.641 +2020-01-24 12:30:00,131.28,213.28599999999997,60.79600000000001,32.641 +2020-01-24 12:45:00,132.51,214.524,60.79600000000001,32.641 +2020-01-24 13:00:00,130.47,213.08599999999998,59.393,32.641 +2020-01-24 13:15:00,129.98,213.205,59.393,32.641 +2020-01-24 13:30:00,128.13,212.16400000000002,59.393,32.641 +2020-01-24 13:45:00,128.71,212.31799999999998,59.393,32.641 +2020-01-24 14:00:00,126.8,210.986,57.943999999999996,32.641 +2020-01-24 14:15:00,129.56,211.17700000000002,57.943999999999996,32.641 +2020-01-24 14:30:00,127.19,212.415,57.943999999999996,32.641 +2020-01-24 14:45:00,127.61,213.71200000000002,57.943999999999996,32.641 +2020-01-24 15:00:00,129.17,214.165,60.153999999999996,32.641 +2020-01-24 15:15:00,125.93,214.391,60.153999999999996,32.641 +2020-01-24 15:30:00,127.46,214.347,60.153999999999996,32.641 +2020-01-24 15:45:00,126.28,215.24400000000003,60.153999999999996,32.641 +2020-01-24 16:00:00,127.34,215.765,62.933,32.641 +2020-01-24 16:15:00,126.66,217.925,62.933,32.641 +2020-01-24 16:30:00,128.07,220.878,62.933,32.641 +2020-01-24 16:45:00,131.69,222.109,62.933,32.641 +2020-01-24 17:00:00,137.39,224.52900000000002,68.657,32.641 +2020-01-24 17:15:00,135.66,224.99599999999998,68.657,32.641 +2020-01-24 17:30:00,133.93,225.78099999999998,68.657,32.641 +2020-01-24 17:45:00,135.2,225.53799999999998,68.657,32.641 +2020-01-24 18:00:00,136.43,228.196,67.111,32.641 +2020-01-24 18:15:00,134.39,224.639,67.111,32.641 +2020-01-24 18:30:00,133.68,224.102,67.111,32.641 +2020-01-24 18:45:00,133.53,224.52700000000002,67.111,32.641 +2020-01-24 19:00:00,130.37,223.533,62.434,32.641 +2020-01-24 19:15:00,128.1,220.89700000000002,62.434,32.641 +2020-01-24 19:30:00,130.9,217.423,62.434,32.641 +2020-01-24 19:45:00,136.04,214.078,62.434,32.641 +2020-01-24 20:00:00,125.76,209.85,61.763000000000005,32.641 +2020-01-24 20:15:00,117.6,202.93,61.763000000000005,32.641 +2020-01-24 20:30:00,114.72,198.669,61.763000000000005,32.641 +2020-01-24 20:45:00,110.33,198.265,61.763000000000005,32.641 +2020-01-24 21:00:00,104.51,194.895,56.785,32.641 +2020-01-24 21:15:00,102.91,192.205,56.785,32.641 +2020-01-24 21:30:00,102.15,190.22799999999998,56.785,32.641 +2020-01-24 21:45:00,104.05,189.38099999999997,56.785,32.641 +2020-01-24 22:00:00,99.83,183.11700000000002,52.693000000000005,32.641 +2020-01-24 22:15:00,94.61,177.195,52.693000000000005,32.641 +2020-01-24 22:30:00,89.29,169.947,52.693000000000005,32.641 +2020-01-24 22:45:00,85.86,165.43400000000003,52.693000000000005,32.641 +2020-01-24 23:00:00,82.29,157.219,45.443999999999996,32.641 +2020-01-24 23:15:00,81.42,153.85399999999998,45.443999999999996,32.641 +2020-01-24 23:30:00,80.16,152.725,45.443999999999996,32.641 +2020-01-24 23:45:00,77.47,151.564,45.443999999999996,32.641 +2020-01-25 00:00:00,72.4,135.27200000000002,44.738,32.459 +2020-01-25 00:15:00,71.11,130.214,44.738,32.459 +2020-01-25 00:30:00,72.19,133.029,44.738,32.459 +2020-01-25 00:45:00,70.85,135.756,44.738,32.459 +2020-01-25 01:00:00,71.22,139.132,40.303000000000004,32.459 +2020-01-25 01:15:00,72.27,138.951,40.303000000000004,32.459 +2020-01-25 01:30:00,76.25,138.406,40.303000000000004,32.459 +2020-01-25 01:45:00,75.27,138.54399999999998,40.303000000000004,32.459 +2020-01-25 02:00:00,69.95,141.80200000000002,38.61,32.459 +2020-01-25 02:15:00,68.12,143.468,38.61,32.459 +2020-01-25 02:30:00,65.73,143.929,38.61,32.459 +2020-01-25 02:45:00,65.96,146.268,38.61,32.459 +2020-01-25 03:00:00,64.75,148.87,37.554,32.459 +2020-01-25 03:15:00,65.49,150.054,37.554,32.459 +2020-01-25 03:30:00,64.8,149.96,37.554,32.459 +2020-01-25 03:45:00,65.17,152.093,37.554,32.459 +2020-01-25 04:00:00,64.99,159.15,37.176,32.459 +2020-01-25 04:15:00,65.24,167.739,37.176,32.459 +2020-01-25 04:30:00,65.75,169.365,37.176,32.459 +2020-01-25 04:45:00,66.44,170.56,37.176,32.459 +2020-01-25 05:00:00,66.97,186.34799999999998,36.893,32.459 +2020-01-25 05:15:00,67.29,195.11700000000002,36.893,32.459 +2020-01-25 05:30:00,68.56,192.21200000000002,36.893,32.459 +2020-01-25 05:45:00,69.68,191.523,36.893,32.459 +2020-01-25 06:00:00,69.94,209.548,37.803000000000004,32.459 +2020-01-25 06:15:00,70.69,232.051,37.803000000000004,32.459 +2020-01-25 06:30:00,71.35,228.36900000000003,37.803000000000004,32.459 +2020-01-25 06:45:00,73.5,225.519,37.803000000000004,32.459 +2020-01-25 07:00:00,77.71,218.623,41.086999999999996,32.459 +2020-01-25 07:15:00,78.56,224.142,41.086999999999996,32.459 +2020-01-25 07:30:00,82.94,230.46200000000002,41.086999999999996,32.459 +2020-01-25 07:45:00,84.55,235.69099999999997,41.086999999999996,32.459 +2020-01-25 08:00:00,88.98,237.581,48.222,32.459 +2020-01-25 08:15:00,90.69,241.622,48.222,32.459 +2020-01-25 08:30:00,92.22,241.90599999999998,48.222,32.459 +2020-01-25 08:45:00,95.97,240.745,48.222,32.459 +2020-01-25 09:00:00,98.38,236.59099999999998,52.791000000000004,32.459 +2020-01-25 09:15:00,98.75,234.963,52.791000000000004,32.459 +2020-01-25 09:30:00,99.49,233.83700000000002,52.791000000000004,32.459 +2020-01-25 09:45:00,101.44,230.472,52.791000000000004,32.459 +2020-01-25 10:00:00,102.45,223.484,54.341,32.459 +2020-01-25 10:15:00,103.89,220.745,54.341,32.459 +2020-01-25 10:30:00,102.9,216.793,54.341,32.459 +2020-01-25 10:45:00,104.81,216.046,54.341,32.459 +2020-01-25 11:00:00,103.72,213.127,51.94,32.459 +2020-01-25 11:15:00,105.87,210.00900000000001,51.94,32.459 +2020-01-25 11:30:00,107.38,208.875,51.94,32.459 +2020-01-25 11:45:00,106.98,207.255,51.94,32.459 +2020-01-25 12:00:00,104.05,203.44,50.973,32.459 +2020-01-25 12:15:00,103.31,201.46900000000002,50.973,32.459 +2020-01-25 12:30:00,100.71,201.627,50.973,32.459 +2020-01-25 12:45:00,99.96,201.85299999999998,50.973,32.459 +2020-01-25 13:00:00,97.04,200.072,48.06399999999999,32.459 +2020-01-25 13:15:00,97.34,197.856,48.06399999999999,32.459 +2020-01-25 13:30:00,95.63,196.245,48.06399999999999,32.459 +2020-01-25 13:45:00,94.67,197.16,48.06399999999999,32.459 +2020-01-25 14:00:00,91.46,197.327,45.707,32.459 +2020-01-25 14:15:00,92.11,197.06900000000002,45.707,32.459 +2020-01-25 14:30:00,91.72,196.25,45.707,32.459 +2020-01-25 14:45:00,91.73,197.745,45.707,32.459 +2020-01-25 15:00:00,90.8,198.975,47.567,32.459 +2020-01-25 15:15:00,90.03,200.00599999999997,47.567,32.459 +2020-01-25 15:30:00,89.45,201.695,47.567,32.459 +2020-01-25 15:45:00,88.86,202.729,47.567,32.459 +2020-01-25 16:00:00,90.25,201.65900000000002,52.031000000000006,32.459 +2020-01-25 16:15:00,89.46,204.97400000000002,52.031000000000006,32.459 +2020-01-25 16:30:00,90.4,207.842,52.031000000000006,32.459 +2020-01-25 16:45:00,93.84,210.09400000000002,52.031000000000006,32.459 +2020-01-25 17:00:00,100.62,212.105,58.218999999999994,32.459 +2020-01-25 17:15:00,102.8,214.77700000000002,58.218999999999994,32.459 +2020-01-25 17:30:00,106.7,215.502,58.218999999999994,32.459 +2020-01-25 17:45:00,106.99,214.757,58.218999999999994,32.459 +2020-01-25 18:00:00,107.5,216.775,57.65,32.459 +2020-01-25 18:15:00,107.19,215.09,57.65,32.459 +2020-01-25 18:30:00,109.91,215.905,57.65,32.459 +2020-01-25 18:45:00,105.29,212.97400000000002,57.65,32.459 +2020-01-25 19:00:00,104.0,213.22099999999998,51.261,32.459 +2020-01-25 19:15:00,102.55,210.149,51.261,32.459 +2020-01-25 19:30:00,101.86,207.44799999999998,51.261,32.459 +2020-01-25 19:45:00,102.2,203.72,51.261,32.459 +2020-01-25 20:00:00,94.93,201.78,44.068000000000005,32.459 +2020-01-25 20:15:00,92.01,197.358,44.068000000000005,32.459 +2020-01-25 20:30:00,88.94,192.80700000000002,44.068000000000005,32.459 +2020-01-25 20:45:00,87.29,191.732,44.068000000000005,32.459 +2020-01-25 21:00:00,83.53,191.08,38.861,32.459 +2020-01-25 21:15:00,82.66,188.91299999999998,38.861,32.459 +2020-01-25 21:30:00,80.26,188.308,38.861,32.459 +2020-01-25 21:45:00,80.05,187.084,38.861,32.459 +2020-01-25 22:00:00,77.81,182.33900000000003,39.485,32.459 +2020-01-25 22:15:00,76.34,179.227,39.485,32.459 +2020-01-25 22:30:00,73.65,179.05900000000003,39.485,32.459 +2020-01-25 22:45:00,72.2,176.628,39.485,32.459 +2020-01-25 23:00:00,67.88,171.222,32.027,32.459 +2020-01-25 23:15:00,68.21,165.968,32.027,32.459 +2020-01-25 23:30:00,62.43,162.59,32.027,32.459 +2020-01-25 23:45:00,63.07,158.65,32.027,32.459 +2020-01-26 00:00:00,60.64,135.531,26.96,32.459 +2020-01-26 00:15:00,59.92,130.25799999999998,26.96,32.459 +2020-01-26 00:30:00,59.03,132.641,26.96,32.459 +2020-01-26 00:45:00,58.62,136.18200000000002,26.96,32.459 +2020-01-26 01:00:00,54.93,139.338,24.295,32.459 +2020-01-26 01:15:00,55.99,140.374,24.295,32.459 +2020-01-26 01:30:00,55.67,140.451,24.295,32.459 +2020-01-26 01:45:00,54.2,140.276,24.295,32.459 +2020-01-26 02:00:00,53.39,142.659,24.268,32.459 +2020-01-26 02:15:00,54.08,143.218,24.268,32.459 +2020-01-26 02:30:00,52.98,144.661,24.268,32.459 +2020-01-26 02:45:00,53.02,147.586,24.268,32.459 +2020-01-26 03:00:00,53.08,150.45600000000002,23.373,32.459 +2020-01-26 03:15:00,50.37,150.995,23.373,32.459 +2020-01-26 03:30:00,52.45,152.66,23.373,32.459 +2020-01-26 03:45:00,52.94,154.84,23.373,32.459 +2020-01-26 04:00:00,52.89,161.61,23.874000000000002,32.459 +2020-01-26 04:15:00,53.22,169.06799999999998,23.874000000000002,32.459 +2020-01-26 04:30:00,54.0,170.55700000000002,23.874000000000002,32.459 +2020-01-26 04:45:00,54.34,172.14,23.874000000000002,32.459 +2020-01-26 05:00:00,55.82,183.72400000000002,24.871,32.459 +2020-01-26 05:15:00,56.59,189.75599999999997,24.871,32.459 +2020-01-26 05:30:00,55.44,186.71400000000003,24.871,32.459 +2020-01-26 05:45:00,56.61,186.375,24.871,32.459 +2020-01-26 06:00:00,56.99,204.688,23.84,32.459 +2020-01-26 06:15:00,56.78,225.035,23.84,32.459 +2020-01-26 06:30:00,57.8,220.16099999999997,23.84,32.459 +2020-01-26 06:45:00,58.26,216.248,23.84,32.459 +2020-01-26 07:00:00,61.45,212.21599999999998,27.430999999999997,32.459 +2020-01-26 07:15:00,62.62,216.979,27.430999999999997,32.459 +2020-01-26 07:30:00,64.42,221.612,27.430999999999997,32.459 +2020-01-26 07:45:00,65.23,225.917,27.430999999999997,32.459 +2020-01-26 08:00:00,67.41,229.857,33.891999999999996,32.459 +2020-01-26 08:15:00,70.28,233.58700000000002,33.891999999999996,32.459 +2020-01-26 08:30:00,72.53,235.627,33.891999999999996,32.459 +2020-01-26 08:45:00,74.96,236.83,33.891999999999996,32.459 +2020-01-26 09:00:00,76.29,232.225,37.571,32.459 +2020-01-26 09:15:00,77.45,231.31799999999998,37.571,32.459 +2020-01-26 09:30:00,78.94,229.967,37.571,32.459 +2020-01-26 09:45:00,80.75,226.315,37.571,32.459 +2020-01-26 10:00:00,81.35,222.179,40.594,32.459 +2020-01-26 10:15:00,83.76,220.058,40.594,32.459 +2020-01-26 10:30:00,85.49,216.775,40.594,32.459 +2020-01-26 10:45:00,88.12,213.672,40.594,32.459 +2020-01-26 11:00:00,88.23,211.86900000000003,44.133,32.459 +2020-01-26 11:15:00,92.2,208.975,44.133,32.459 +2020-01-26 11:30:00,92.74,206.762,44.133,32.459 +2020-01-26 11:45:00,93.24,205.81400000000002,44.133,32.459 +2020-01-26 12:00:00,91.73,201.201,41.198,32.459 +2020-01-26 12:15:00,89.36,201.549,41.198,32.459 +2020-01-26 12:30:00,84.83,199.968,41.198,32.459 +2020-01-26 12:45:00,83.74,199.162,41.198,32.459 +2020-01-26 13:00:00,80.41,196.597,37.014,32.459 +2020-01-26 13:15:00,80.17,197.945,37.014,32.459 +2020-01-26 13:30:00,81.07,196.236,37.014,32.459 +2020-01-26 13:45:00,80.59,196.222,37.014,32.459 +2020-01-26 14:00:00,79.11,196.545,34.934,32.459 +2020-01-26 14:15:00,78.6,197.6,34.934,32.459 +2020-01-26 14:30:00,78.67,198.37900000000002,34.934,32.459 +2020-01-26 14:45:00,79.38,199.56599999999997,34.934,32.459 +2020-01-26 15:00:00,79.93,199.014,34.588,32.459 +2020-01-26 15:15:00,78.78,201.0,34.588,32.459 +2020-01-26 15:30:00,78.6,203.368,34.588,32.459 +2020-01-26 15:45:00,77.74,205.146,34.588,32.459 +2020-01-26 16:00:00,80.38,206.40099999999998,37.874,32.459 +2020-01-26 16:15:00,79.54,208.645,37.874,32.459 +2020-01-26 16:30:00,81.49,211.685,37.874,32.459 +2020-01-26 16:45:00,84.68,214.078,37.874,32.459 +2020-01-26 17:00:00,92.75,215.987,47.303999999999995,32.459 +2020-01-26 17:15:00,94.5,218.13099999999997,47.303999999999995,32.459 +2020-01-26 17:30:00,96.47,219.12099999999998,47.303999999999995,32.459 +2020-01-26 17:45:00,98.7,220.93200000000002,47.303999999999995,32.459 +2020-01-26 18:00:00,100.59,222.25099999999998,48.879,32.459 +2020-01-26 18:15:00,99.21,222.146,48.879,32.459 +2020-01-26 18:30:00,99.63,220.636,48.879,32.459 +2020-01-26 18:45:00,98.32,219.80900000000003,48.879,32.459 +2020-01-26 19:00:00,96.9,219.37599999999998,44.826,32.459 +2020-01-26 19:15:00,95.53,217.101,44.826,32.459 +2020-01-26 19:30:00,98.34,214.25599999999997,44.826,32.459 +2020-01-26 19:45:00,100.34,212.222,44.826,32.459 +2020-01-26 20:00:00,98.08,210.243,40.154,32.459 +2020-01-26 20:15:00,89.23,206.959,40.154,32.459 +2020-01-26 20:30:00,88.79,203.717,40.154,32.459 +2020-01-26 20:45:00,85.18,201.489,40.154,32.459 +2020-01-26 21:00:00,83.88,197.894,36.549,32.459 +2020-01-26 21:15:00,90.22,195.03599999999997,36.549,32.459 +2020-01-26 21:30:00,90.09,194.83700000000002,36.549,32.459 +2020-01-26 21:45:00,86.75,193.743,36.549,32.459 +2020-01-26 22:00:00,84.32,187.405,37.663000000000004,32.459 +2020-01-26 22:15:00,87.31,183.648,37.663000000000004,32.459 +2020-01-26 22:30:00,87.15,179.94400000000002,37.663000000000004,32.459 +2020-01-26 22:45:00,85.87,176.705,37.663000000000004,32.459 +2020-01-26 23:00:00,78.89,168.15599999999998,31.945,32.459 +2020-01-26 23:15:00,82.8,164.87,31.945,32.459 +2020-01-26 23:30:00,81.36,162.487,31.945,32.459 +2020-01-26 23:45:00,80.85,159.548,31.945,32.459 +2020-01-27 00:00:00,72.01,140.157,31.533,32.641 +2020-01-27 00:15:00,74.98,138.209,31.533,32.641 +2020-01-27 00:30:00,75.17,140.77700000000002,31.533,32.641 +2020-01-27 00:45:00,73.37,143.741,31.533,32.641 +2020-01-27 01:00:00,65.99,146.861,30.56,32.641 +2020-01-27 01:15:00,72.24,147.267,30.56,32.641 +2020-01-27 01:30:00,72.74,147.346,30.56,32.641 +2020-01-27 01:45:00,73.19,147.313,30.56,32.641 +2020-01-27 02:00:00,68.81,149.619,29.55,32.641 +2020-01-27 02:15:00,72.66,152.05,29.55,32.641 +2020-01-27 02:30:00,73.33,153.86700000000002,29.55,32.641 +2020-01-27 02:45:00,72.31,156.089,29.55,32.641 +2020-01-27 03:00:00,67.49,160.394,27.059,32.641 +2020-01-27 03:15:00,73.56,162.764,27.059,32.641 +2020-01-27 03:30:00,75.02,163.99900000000002,27.059,32.641 +2020-01-27 03:45:00,71.92,165.61599999999999,27.059,32.641 +2020-01-27 04:00:00,66.96,177.02,28.384,32.641 +2020-01-27 04:15:00,68.37,188.86900000000003,28.384,32.641 +2020-01-27 04:30:00,70.66,193.035,28.384,32.641 +2020-01-27 04:45:00,72.68,194.738,28.384,32.641 +2020-01-27 05:00:00,77.25,223.778,35.915,32.641 +2020-01-27 05:15:00,79.29,251.91099999999997,35.915,32.641 +2020-01-27 05:30:00,84.6,249.452,35.915,32.641 +2020-01-27 05:45:00,89.92,242.958,35.915,32.641 +2020-01-27 06:00:00,99.95,241.782,56.18,32.641 +2020-01-27 06:15:00,104.93,246.144,56.18,32.641 +2020-01-27 06:30:00,110.18,250.06400000000002,56.18,32.641 +2020-01-27 06:45:00,113.96,255.674,56.18,32.641 +2020-01-27 07:00:00,121.51,254.377,70.877,32.641 +2020-01-27 07:15:00,123.92,260.205,70.877,32.641 +2020-01-27 07:30:00,123.09,264.038,70.877,32.641 +2020-01-27 07:45:00,125.61,265.228,70.877,32.641 +2020-01-27 08:00:00,127.27,263.626,65.65,32.641 +2020-01-27 08:15:00,127.65,265.01099999999997,65.65,32.641 +2020-01-27 08:30:00,129.37,262.36,65.65,32.641 +2020-01-27 08:45:00,125.27,259.60400000000004,65.65,32.641 +2020-01-27 09:00:00,124.61,253.929,62.037,32.641 +2020-01-27 09:15:00,126.22,249.234,62.037,32.641 +2020-01-27 09:30:00,126.26,246.88,62.037,32.641 +2020-01-27 09:45:00,122.4,243.92700000000002,62.037,32.641 +2020-01-27 10:00:00,124.71,238.52,60.409,32.641 +2020-01-27 10:15:00,124.92,236.058,60.409,32.641 +2020-01-27 10:30:00,126.43,231.824,60.409,32.641 +2020-01-27 10:45:00,127.62,229.86,60.409,32.641 +2020-01-27 11:00:00,128.14,224.88299999999998,60.211999999999996,32.641 +2020-01-27 11:15:00,129.57,224.053,60.211999999999996,32.641 +2020-01-27 11:30:00,130.44,223.34599999999998,60.211999999999996,32.641 +2020-01-27 11:45:00,132.01,221.85299999999998,60.211999999999996,32.641 +2020-01-27 12:00:00,131.87,219.55700000000002,57.733000000000004,32.641 +2020-01-27 12:15:00,130.69,219.908,57.733000000000004,32.641 +2020-01-27 12:30:00,127.99,218.766,57.733000000000004,32.641 +2020-01-27 12:45:00,128.57,219.735,57.733000000000004,32.641 +2020-01-27 13:00:00,127.25,217.735,58.695,32.641 +2020-01-27 13:15:00,127.54,217.582,58.695,32.641 +2020-01-27 13:30:00,126.3,215.213,58.695,32.641 +2020-01-27 13:45:00,124.1,215.097,58.695,32.641 +2020-01-27 14:00:00,120.85,214.834,59.505,32.641 +2020-01-27 14:15:00,122.78,215.037,59.505,32.641 +2020-01-27 14:30:00,121.73,215.206,59.505,32.641 +2020-01-27 14:45:00,122.16,216.058,59.505,32.641 +2020-01-27 15:00:00,122.79,217.581,59.946000000000005,32.641 +2020-01-27 15:15:00,122.37,217.96099999999998,59.946000000000005,32.641 +2020-01-27 15:30:00,120.21,219.23,59.946000000000005,32.641 +2020-01-27 15:45:00,121.58,220.549,59.946000000000005,32.641 +2020-01-27 16:00:00,125.44,221.766,61.766999999999996,32.641 +2020-01-27 16:15:00,124.32,223.122,61.766999999999996,32.641 +2020-01-27 16:30:00,125.38,225.149,61.766999999999996,32.641 +2020-01-27 16:45:00,128.84,226.167,61.766999999999996,32.641 +2020-01-27 17:00:00,134.98,227.94099999999997,67.85600000000001,32.641 +2020-01-27 17:15:00,133.74,228.979,67.85600000000001,32.641 +2020-01-27 17:30:00,134.91,229.42700000000002,67.85600000000001,32.641 +2020-01-27 17:45:00,134.47,229.61,67.85600000000001,32.641 +2020-01-27 18:00:00,134.73,231.514,64.564,32.641 +2020-01-27 18:15:00,131.96,229.16400000000002,64.564,32.641 +2020-01-27 18:30:00,133.73,228.49200000000002,64.564,32.641 +2020-01-27 18:45:00,132.73,228.109,64.564,32.641 +2020-01-27 19:00:00,129.78,225.815,58.536,32.641 +2020-01-27 19:15:00,126.63,222.06799999999998,58.536,32.641 +2020-01-27 19:30:00,126.74,219.84400000000002,58.536,32.641 +2020-01-27 19:45:00,131.0,216.96200000000002,58.536,32.641 +2020-01-27 20:00:00,124.95,212.45,59.888999999999996,32.641 +2020-01-27 20:15:00,116.65,206.18,59.888999999999996,32.641 +2020-01-27 20:30:00,113.27,200.757,59.888999999999996,32.641 +2020-01-27 20:45:00,109.79,200.363,59.888999999999996,32.641 +2020-01-27 21:00:00,104.5,197.43400000000003,52.652,32.641 +2020-01-27 21:15:00,102.34,193.169,52.652,32.641 +2020-01-27 21:30:00,105.41,191.98,52.652,32.641 +2020-01-27 21:45:00,106.02,190.355,52.652,32.641 +2020-01-27 22:00:00,103.67,180.989,46.17,32.641 +2020-01-27 22:15:00,92.1,175.507,46.17,32.641 +2020-01-27 22:30:00,90.73,161.47299999999998,46.17,32.641 +2020-01-27 22:45:00,86.82,152.751,46.17,32.641 +2020-01-27 23:00:00,85.71,145.082,36.281,32.641 +2020-01-27 23:15:00,90.44,145.025,36.281,32.641 +2020-01-27 23:30:00,87.95,145.799,36.281,32.641 +2020-01-27 23:45:00,82.47,145.94,36.281,32.641 +2020-01-28 00:00:00,78.26,139.671,38.821999999999996,32.641 +2020-01-28 00:15:00,75.34,139.238,38.821999999999996,32.641 +2020-01-28 00:30:00,79.52,140.57399999999998,38.821999999999996,32.641 +2020-01-28 00:45:00,82.22,142.25799999999998,38.821999999999996,32.641 +2020-01-28 01:00:00,79.73,145.21200000000002,36.936,32.641 +2020-01-28 01:15:00,76.69,145.066,36.936,32.641 +2020-01-28 01:30:00,75.14,145.361,36.936,32.641 +2020-01-28 01:45:00,76.58,145.766,36.936,32.641 +2020-01-28 02:00:00,79.49,148.15200000000002,34.42,32.641 +2020-01-28 02:15:00,79.78,150.255,34.42,32.641 +2020-01-28 02:30:00,77.63,151.44899999999998,34.42,32.641 +2020-01-28 02:45:00,72.78,153.629,34.42,32.641 +2020-01-28 03:00:00,78.0,156.595,33.585,32.641 +2020-01-28 03:15:00,81.7,157.804,33.585,32.641 +2020-01-28 03:30:00,83.51,159.614,33.585,32.641 +2020-01-28 03:45:00,82.44,161.667,33.585,32.641 +2020-01-28 04:00:00,77.73,173.03900000000002,35.622,32.641 +2020-01-28 04:15:00,84.2,184.46099999999998,35.622,32.641 +2020-01-28 04:30:00,87.03,188.28799999999998,35.622,32.641 +2020-01-28 04:45:00,89.61,191.332,35.622,32.641 +2020-01-28 05:00:00,89.09,226.02,40.599000000000004,32.641 +2020-01-28 05:15:00,87.98,253.782,40.599000000000004,32.641 +2020-01-28 05:30:00,94.26,249.44400000000002,40.599000000000004,32.641 +2020-01-28 05:45:00,98.87,243.109,40.599000000000004,32.641 +2020-01-28 06:00:00,105.04,240.338,55.203,32.641 +2020-01-28 06:15:00,109.62,246.618,55.203,32.641 +2020-01-28 06:30:00,113.28,249.817,55.203,32.641 +2020-01-28 06:45:00,117.53,255.15900000000002,55.203,32.641 +2020-01-28 07:00:00,128.45,253.637,69.029,32.641 +2020-01-28 07:15:00,128.39,259.301,69.029,32.641 +2020-01-28 07:30:00,128.01,262.45099999999996,69.029,32.641 +2020-01-28 07:45:00,129.0,264.04200000000003,69.029,32.641 +2020-01-28 08:00:00,131.4,262.563,65.85300000000001,32.641 +2020-01-28 08:15:00,131.16,262.79400000000004,65.85300000000001,32.641 +2020-01-28 08:30:00,129.91,259.853,65.85300000000001,32.641 +2020-01-28 08:45:00,128.94,256.934,65.85300000000001,32.641 +2020-01-28 09:00:00,130.01,250.21099999999998,61.566,32.641 +2020-01-28 09:15:00,130.06,247.43,61.566,32.641 +2020-01-28 09:30:00,128.11,245.77700000000002,61.566,32.641 +2020-01-28 09:45:00,126.64,242.335,61.566,32.641 +2020-01-28 10:00:00,125.99,236.479,61.244,32.641 +2020-01-28 10:15:00,125.95,232.801,61.244,32.641 +2020-01-28 10:30:00,127.09,228.778,61.244,32.641 +2020-01-28 10:45:00,125.87,226.981,61.244,32.641 +2020-01-28 11:00:00,127.54,223.747,61.16,32.641 +2020-01-28 11:15:00,126.56,222.465,61.16,32.641 +2020-01-28 11:30:00,125.17,220.58599999999998,61.16,32.641 +2020-01-28 11:45:00,127.37,220.002,61.16,32.641 +2020-01-28 12:00:00,124.87,216.142,59.09,32.641 +2020-01-28 12:15:00,123.4,215.963,59.09,32.641 +2020-01-28 12:30:00,122.31,215.505,59.09,32.641 +2020-01-28 12:45:00,119.99,216.01,59.09,32.641 +2020-01-28 13:00:00,117.55,213.6,60.21,32.641 +2020-01-28 13:15:00,119.52,212.706,60.21,32.641 +2020-01-28 13:30:00,119.94,211.708,60.21,32.641 +2020-01-28 13:45:00,117.83,211.977,60.21,32.641 +2020-01-28 14:00:00,115.55,212.02200000000002,60.673,32.641 +2020-01-28 14:15:00,117.18,212.426,60.673,32.641 +2020-01-28 14:30:00,118.0,213.271,60.673,32.641 +2020-01-28 14:45:00,119.26,214.18,60.673,32.641 +2020-01-28 15:00:00,119.6,215.243,62.232,32.641 +2020-01-28 15:15:00,119.97,215.834,62.232,32.641 +2020-01-28 15:30:00,119.48,217.37400000000002,62.232,32.641 +2020-01-28 15:45:00,120.64,218.15,62.232,32.641 +2020-01-28 16:00:00,121.9,219.89,63.611999999999995,32.641 +2020-01-28 16:15:00,122.55,221.785,63.611999999999995,32.641 +2020-01-28 16:30:00,127.29,224.62599999999998,63.611999999999995,32.641 +2020-01-28 16:45:00,127.69,225.864,63.611999999999995,32.641 +2020-01-28 17:00:00,132.53,228.227,70.658,32.641 +2020-01-28 17:15:00,136.01,229.27,70.658,32.641 +2020-01-28 17:30:00,140.13,230.585,70.658,32.641 +2020-01-28 17:45:00,139.21,230.69,70.658,32.641 +2020-01-28 18:00:00,139.49,232.614,68.361,32.641 +2020-01-28 18:15:00,137.64,229.528,68.361,32.641 +2020-01-28 18:30:00,137.81,228.544,68.361,32.641 +2020-01-28 18:45:00,137.16,229.122,68.361,32.641 +2020-01-28 19:00:00,134.44,227.023,62.922,32.641 +2020-01-28 19:15:00,132.06,222.94400000000002,62.922,32.641 +2020-01-28 19:30:00,137.09,220.002,62.922,32.641 +2020-01-28 19:45:00,138.01,217.142,62.922,32.641 +2020-01-28 20:00:00,129.62,212.735,63.251999999999995,32.641 +2020-01-28 20:15:00,119.68,205.938,63.251999999999995,32.641 +2020-01-28 20:30:00,118.14,201.671,63.251999999999995,32.641 +2020-01-28 20:45:00,115.08,200.576,63.251999999999995,32.641 +2020-01-28 21:00:00,112.27,196.71099999999998,54.47,32.641 +2020-01-28 21:15:00,114.93,193.66400000000002,54.47,32.641 +2020-01-28 21:30:00,113.2,191.614,54.47,32.641 +2020-01-28 21:45:00,108.39,190.24,54.47,32.641 +2020-01-28 22:00:00,99.19,182.795,51.12,32.641 +2020-01-28 22:15:00,103.31,177.084,51.12,32.641 +2020-01-28 22:30:00,102.8,163.095,51.12,32.641 +2020-01-28 22:45:00,99.67,154.697,51.12,32.641 +2020-01-28 23:00:00,91.53,147.145,42.156000000000006,32.641 +2020-01-28 23:15:00,92.74,145.862,42.156000000000006,32.641 +2020-01-28 23:30:00,92.86,146.243,42.156000000000006,32.641 +2020-01-28 23:45:00,91.28,145.873,42.156000000000006,32.641 +2020-01-29 00:00:00,85.71,139.624,37.192,32.641 +2020-01-29 00:15:00,85.1,139.17700000000002,37.192,32.641 +2020-01-29 00:30:00,86.49,140.494,37.192,32.641 +2020-01-29 00:45:00,83.22,142.166,37.192,32.641 +2020-01-29 01:00:00,79.05,145.10299999999998,32.24,32.641 +2020-01-29 01:15:00,81.99,144.944,32.24,32.641 +2020-01-29 01:30:00,83.1,145.232,32.24,32.641 +2020-01-29 01:45:00,82.85,145.632,32.24,32.641 +2020-01-29 02:00:00,76.67,148.023,30.34,32.641 +2020-01-29 02:15:00,79.99,150.127,30.34,32.641 +2020-01-29 02:30:00,80.46,151.332,30.34,32.641 +2020-01-29 02:45:00,82.45,153.511,30.34,32.641 +2020-01-29 03:00:00,78.21,156.477,29.129,32.641 +2020-01-29 03:15:00,81.98,157.697,29.129,32.641 +2020-01-29 03:30:00,83.78,159.504,29.129,32.641 +2020-01-29 03:45:00,84.27,161.56799999999998,29.129,32.641 +2020-01-29 04:00:00,79.14,172.926,30.075,32.641 +2020-01-29 04:15:00,81.89,184.338,30.075,32.641 +2020-01-29 04:30:00,86.55,188.173,30.075,32.641 +2020-01-29 04:45:00,86.15,191.208,30.075,32.641 +2020-01-29 05:00:00,85.56,225.864,35.684,32.641 +2020-01-29 05:15:00,86.72,253.618,35.684,32.641 +2020-01-29 05:30:00,89.23,249.269,35.684,32.641 +2020-01-29 05:45:00,94.99,242.946,35.684,32.641 +2020-01-29 06:00:00,105.54,240.192,51.49,32.641 +2020-01-29 06:15:00,109.68,246.484,51.49,32.641 +2020-01-29 06:30:00,113.38,249.673,51.49,32.641 +2020-01-29 06:45:00,116.12,255.03099999999998,51.49,32.641 +2020-01-29 07:00:00,121.07,253.53,68.242,32.641 +2020-01-29 07:15:00,126.34,259.173,68.242,32.641 +2020-01-29 07:30:00,126.89,262.293,68.242,32.641 +2020-01-29 07:45:00,128.53,263.848,68.242,32.641 +2020-01-29 08:00:00,129.79,262.355,63.619,32.641 +2020-01-29 08:15:00,129.69,262.562,63.619,32.641 +2020-01-29 08:30:00,128.46,259.569,63.619,32.641 +2020-01-29 08:45:00,129.7,256.64,63.619,32.641 +2020-01-29 09:00:00,130.75,249.91400000000002,61.333,32.641 +2020-01-29 09:15:00,133.1,247.137,61.333,32.641 +2020-01-29 09:30:00,134.98,245.503,61.333,32.641 +2020-01-29 09:45:00,134.4,242.05900000000003,61.333,32.641 +2020-01-29 10:00:00,132.46,236.209,59.663000000000004,32.641 +2020-01-29 10:15:00,133.39,232.55200000000002,59.663000000000004,32.641 +2020-01-29 10:30:00,133.22,228.53099999999998,59.663000000000004,32.641 +2020-01-29 10:45:00,132.39,226.74400000000003,59.663000000000004,32.641 +2020-01-29 11:00:00,132.31,223.495,59.771,32.641 +2020-01-29 11:15:00,133.12,222.22,59.771,32.641 +2020-01-29 11:30:00,134.86,220.34599999999998,59.771,32.641 +2020-01-29 11:45:00,133.3,219.771,59.771,32.641 +2020-01-29 12:00:00,132.7,215.925,58.723,32.641 +2020-01-29 12:15:00,132.51,215.765,58.723,32.641 +2020-01-29 12:30:00,131.59,215.287,58.723,32.641 +2020-01-29 12:45:00,130.75,215.78900000000002,58.723,32.641 +2020-01-29 13:00:00,129.37,213.39,58.727,32.641 +2020-01-29 13:15:00,129.56,212.47400000000002,58.727,32.641 +2020-01-29 13:30:00,127.85,211.46099999999998,58.727,32.641 +2020-01-29 13:45:00,128.01,211.72799999999998,58.727,32.641 +2020-01-29 14:00:00,127.61,211.81799999999998,59.803999999999995,32.641 +2020-01-29 14:15:00,127.81,212.204,59.803999999999995,32.641 +2020-01-29 14:30:00,127.7,213.043,59.803999999999995,32.641 +2020-01-29 14:45:00,127.4,213.96900000000002,59.803999999999995,32.641 +2020-01-29 15:00:00,128.03,215.045,61.05,32.641 +2020-01-29 15:15:00,127.39,215.608,61.05,32.641 +2020-01-29 15:30:00,128.43,217.12,61.05,32.641 +2020-01-29 15:45:00,124.88,217.88,61.05,32.641 +2020-01-29 16:00:00,126.8,219.623,64.012,32.641 +2020-01-29 16:15:00,126.43,221.516,64.012,32.641 +2020-01-29 16:30:00,130.89,224.363,64.012,32.641 +2020-01-29 16:45:00,131.2,225.593,64.012,32.641 +2020-01-29 17:00:00,135.48,227.94799999999998,66.751,32.641 +2020-01-29 17:15:00,135.19,229.023,66.751,32.641 +2020-01-29 17:30:00,136.26,230.373,66.751,32.641 +2020-01-29 17:45:00,136.97,230.505,66.751,32.641 +2020-01-29 18:00:00,136.56,232.447,65.91199999999999,32.641 +2020-01-29 18:15:00,134.46,229.4,65.91199999999999,32.641 +2020-01-29 18:30:00,136.21,228.42,65.91199999999999,32.641 +2020-01-29 18:45:00,134.51,229.021,65.91199999999999,32.641 +2020-01-29 19:00:00,130.95,226.882,63.324,32.641 +2020-01-29 19:15:00,130.8,222.81099999999998,63.324,32.641 +2020-01-29 19:30:00,128.57,219.885,63.324,32.641 +2020-01-29 19:45:00,133.5,217.047,63.324,32.641 +2020-01-29 20:00:00,127.01,212.614,63.573,32.641 +2020-01-29 20:15:00,117.14,205.825,63.573,32.641 +2020-01-29 20:30:00,114.01,201.562,63.573,32.641 +2020-01-29 20:45:00,109.09,200.477,63.573,32.641 +2020-01-29 21:00:00,107.15,196.59400000000002,55.073,32.641 +2020-01-29 21:15:00,111.75,193.52900000000002,55.073,32.641 +2020-01-29 21:30:00,109.55,191.48,55.073,32.641 +2020-01-29 21:45:00,104.52,190.122,55.073,32.641 +2020-01-29 22:00:00,95.88,182.666,51.321999999999996,32.641 +2020-01-29 22:15:00,96.63,176.979,51.321999999999996,32.641 +2020-01-29 22:30:00,97.76,162.968,51.321999999999996,32.641 +2020-01-29 22:45:00,95.81,154.578,51.321999999999996,32.641 +2020-01-29 23:00:00,89.08,147.014,42.09,32.641 +2020-01-29 23:15:00,83.66,145.744,42.09,32.641 +2020-01-29 23:30:00,86.56,146.143,42.09,32.641 +2020-01-29 23:45:00,88.77,145.786,42.09,32.641 +2020-01-30 00:00:00,82.65,139.567,38.399,32.641 +2020-01-30 00:15:00,78.77,139.108,38.399,32.641 +2020-01-30 00:30:00,76.46,140.406,38.399,32.641 +2020-01-30 00:45:00,81.36,142.06799999999998,38.399,32.641 +2020-01-30 01:00:00,78.58,144.985,36.94,32.641 +2020-01-30 01:15:00,78.21,144.812,36.94,32.641 +2020-01-30 01:30:00,76.11,145.093,36.94,32.641 +2020-01-30 01:45:00,79.6,145.488,36.94,32.641 +2020-01-30 02:00:00,78.45,147.884,35.275,32.641 +2020-01-30 02:15:00,77.62,149.989,35.275,32.641 +2020-01-30 02:30:00,71.61,151.20600000000002,35.275,32.641 +2020-01-30 02:45:00,72.74,153.385,35.275,32.641 +2020-01-30 03:00:00,71.23,156.349,35.329,32.641 +2020-01-30 03:15:00,71.37,157.578,35.329,32.641 +2020-01-30 03:30:00,74.11,159.38299999999998,35.329,32.641 +2020-01-30 03:45:00,80.9,161.459,35.329,32.641 +2020-01-30 04:00:00,82.14,172.805,36.275,32.641 +2020-01-30 04:15:00,83.94,184.206,36.275,32.641 +2020-01-30 04:30:00,79.65,188.05,36.275,32.641 +2020-01-30 04:45:00,79.86,191.074,36.275,32.641 +2020-01-30 05:00:00,84.84,225.699,42.193999999999996,32.641 +2020-01-30 05:15:00,87.97,253.44799999999998,42.193999999999996,32.641 +2020-01-30 05:30:00,91.94,249.08700000000002,42.193999999999996,32.641 +2020-01-30 05:45:00,96.69,242.77599999999998,42.193999999999996,32.641 +2020-01-30 06:00:00,105.79,240.03900000000002,56.422,32.641 +2020-01-30 06:15:00,111.14,246.34400000000002,56.422,32.641 +2020-01-30 06:30:00,115.88,249.519,56.422,32.641 +2020-01-30 06:45:00,119.21,254.891,56.422,32.641 +2020-01-30 07:00:00,127.68,253.41299999999998,72.569,32.641 +2020-01-30 07:15:00,130.37,259.034,72.569,32.641 +2020-01-30 07:30:00,129.83,262.124,72.569,32.641 +2020-01-30 07:45:00,131.25,263.643,72.569,32.641 +2020-01-30 08:00:00,133.61,262.135,67.704,32.641 +2020-01-30 08:15:00,133.19,262.318,67.704,32.641 +2020-01-30 08:30:00,132.87,259.27099999999996,67.704,32.641 +2020-01-30 08:45:00,132.5,256.33299999999997,67.704,32.641 +2020-01-30 09:00:00,132.2,249.60299999999998,63.434,32.641 +2020-01-30 09:15:00,133.4,246.83,63.434,32.641 +2020-01-30 09:30:00,133.88,245.217,63.434,32.641 +2020-01-30 09:45:00,134.13,241.77200000000002,63.434,32.641 +2020-01-30 10:00:00,134.86,235.928,61.88399999999999,32.641 +2020-01-30 10:15:00,134.96,232.292,61.88399999999999,32.641 +2020-01-30 10:30:00,133.39,228.273,61.88399999999999,32.641 +2020-01-30 10:45:00,134.4,226.497,61.88399999999999,32.641 +2020-01-30 11:00:00,133.27,223.232,61.481,32.641 +2020-01-30 11:15:00,134.3,221.965,61.481,32.641 +2020-01-30 11:30:00,136.88,220.09599999999998,61.481,32.641 +2020-01-30 11:45:00,137.03,219.52900000000002,61.481,32.641 +2020-01-30 12:00:00,133.83,215.699,59.527,32.641 +2020-01-30 12:15:00,133.63,215.558,59.527,32.641 +2020-01-30 12:30:00,132.45,215.06,59.527,32.641 +2020-01-30 12:45:00,132.76,215.56,59.527,32.641 +2020-01-30 13:00:00,132.63,213.172,58.794,32.641 +2020-01-30 13:15:00,131.93,212.234,58.794,32.641 +2020-01-30 13:30:00,131.32,211.206,58.794,32.641 +2020-01-30 13:45:00,131.62,211.47099999999998,58.794,32.641 +2020-01-30 14:00:00,130.86,211.605,60.32,32.641 +2020-01-30 14:15:00,130.65,211.976,60.32,32.641 +2020-01-30 14:30:00,129.29,212.80599999999998,60.32,32.641 +2020-01-30 14:45:00,129.11,213.748,60.32,32.641 +2020-01-30 15:00:00,128.71,214.838,62.52,32.641 +2020-01-30 15:15:00,127.59,215.372,62.52,32.641 +2020-01-30 15:30:00,126.25,216.857,62.52,32.641 +2020-01-30 15:45:00,127.28,217.6,62.52,32.641 +2020-01-30 16:00:00,127.19,219.34400000000002,64.199,32.641 +2020-01-30 16:15:00,126.69,221.235,64.199,32.641 +2020-01-30 16:30:00,127.31,224.08700000000002,64.199,32.641 +2020-01-30 16:45:00,129.45,225.31,64.199,32.641 +2020-01-30 17:00:00,133.14,227.657,68.19800000000001,32.641 +2020-01-30 17:15:00,136.81,228.763,68.19800000000001,32.641 +2020-01-30 17:30:00,138.93,230.148,68.19800000000001,32.641 +2020-01-30 17:45:00,139.45,230.30700000000002,68.19800000000001,32.641 +2020-01-30 18:00:00,139.02,232.267,67.899,32.641 +2020-01-30 18:15:00,140.21,229.262,67.899,32.641 +2020-01-30 18:30:00,137.12,228.28400000000002,67.899,32.641 +2020-01-30 18:45:00,137.17,228.91099999999997,67.899,32.641 +2020-01-30 19:00:00,134.6,226.73,64.72399999999999,32.641 +2020-01-30 19:15:00,131.53,222.669,64.72399999999999,32.641 +2020-01-30 19:30:00,136.23,219.75900000000001,64.72399999999999,32.641 +2020-01-30 19:45:00,136.85,216.945,64.72399999999999,32.641 +2020-01-30 20:00:00,129.72,212.484,64.062,32.641 +2020-01-30 20:15:00,119.43,205.704,64.062,32.641 +2020-01-30 20:30:00,117.24,201.445,64.062,32.641 +2020-01-30 20:45:00,114.49,200.37099999999998,64.062,32.641 +2020-01-30 21:00:00,110.45,196.468,57.971000000000004,32.641 +2020-01-30 21:15:00,114.4,193.387,57.971000000000004,32.641 +2020-01-30 21:30:00,113.1,191.33700000000002,57.971000000000004,32.641 +2020-01-30 21:45:00,108.52,189.99599999999998,57.971000000000004,32.641 +2020-01-30 22:00:00,99.82,182.52700000000002,53.715,32.641 +2020-01-30 22:15:00,95.43,176.864,53.715,32.641 +2020-01-30 22:30:00,95.06,162.83100000000002,53.715,32.641 +2020-01-30 22:45:00,97.76,154.44899999999998,53.715,32.641 +2020-01-30 23:00:00,95.28,146.873,47.8,32.641 +2020-01-30 23:15:00,93.83,145.61700000000002,47.8,32.641 +2020-01-30 23:30:00,85.99,146.034,47.8,32.641 +2020-01-30 23:45:00,83.25,145.692,47.8,32.641 +2020-01-31 00:00:00,83.54,138.534,43.656000000000006,32.641 +2020-01-31 00:15:00,86.38,138.262,43.656000000000006,32.641 +2020-01-31 00:30:00,85.86,139.339,43.656000000000006,32.641 +2020-01-31 00:45:00,81.71,141.053,43.656000000000006,32.641 +2020-01-31 01:00:00,75.55,143.645,41.263000000000005,32.641 +2020-01-31 01:15:00,81.11,144.639,41.263000000000005,32.641 +2020-01-31 01:30:00,82.73,144.529,41.263000000000005,32.641 +2020-01-31 01:45:00,83.26,145.086,41.263000000000005,32.641 +2020-01-31 02:00:00,78.12,147.43200000000002,40.799,32.641 +2020-01-31 02:15:00,77.01,149.412,40.799,32.641 +2020-01-31 02:30:00,81.31,151.12,40.799,32.641 +2020-01-31 02:45:00,81.77,153.464,40.799,32.641 +2020-01-31 03:00:00,80.53,155.114,41.398,32.641 +2020-01-31 03:15:00,78.43,157.67700000000002,41.398,32.641 +2020-01-31 03:30:00,83.05,159.502,41.398,32.641 +2020-01-31 03:45:00,85.88,161.82299999999998,41.398,32.641 +2020-01-31 04:00:00,81.83,173.408,42.38,32.641 +2020-01-31 04:15:00,79.91,184.787,42.38,32.641 +2020-01-31 04:30:00,83.03,188.74200000000002,42.38,32.641 +2020-01-31 04:45:00,88.4,190.513,42.38,32.641 +2020-01-31 05:00:00,92.99,223.68099999999998,46.181000000000004,32.641 +2020-01-31 05:15:00,86.2,253.05200000000002,46.181000000000004,32.641 +2020-01-31 05:30:00,87.66,249.93200000000002,46.181000000000004,32.641 +2020-01-31 05:45:00,92.16,243.638,46.181000000000004,32.641 +2020-01-31 06:00:00,100.18,241.388,59.33,32.641 +2020-01-31 06:15:00,103.88,245.922,59.33,32.641 +2020-01-31 06:30:00,107.37,248.06099999999998,59.33,32.641 +2020-01-31 06:45:00,111.97,255.41,59.33,32.641 +2020-01-31 07:00:00,118.81,252.83900000000003,72.454,32.641 +2020-01-31 07:15:00,121.43,259.492,72.454,32.641 +2020-01-31 07:30:00,120.61,262.686,72.454,32.641 +2020-01-31 07:45:00,123.16,263.152,72.454,32.641 +2020-01-31 08:00:00,125.9,260.182,67.175,32.641 +2020-01-31 08:15:00,125.96,259.769,67.175,32.641 +2020-01-31 08:30:00,124.1,257.837,67.175,32.641 +2020-01-31 08:45:00,122.45,253.051,67.175,32.641 +2020-01-31 09:00:00,121.29,247.245,65.365,32.641 +2020-01-31 09:15:00,120.99,244.838,65.365,32.641 +2020-01-31 09:30:00,122.63,242.851,65.365,32.641 +2020-01-31 09:45:00,125.07,239.21400000000003,65.365,32.641 +2020-01-31 10:00:00,125.19,232.065,63.95,32.641 +2020-01-31 10:15:00,123.48,229.30700000000002,63.95,32.641 +2020-01-31 10:30:00,118.43,225.118,63.95,32.641 +2020-01-31 10:45:00,116.42,222.847,63.95,32.641 +2020-01-31 11:00:00,112.88,219.519,63.92100000000001,32.641 +2020-01-31 11:15:00,114.05,217.38099999999997,63.92100000000001,32.641 +2020-01-31 11:30:00,112.59,217.62,63.92100000000001,32.641 +2020-01-31 11:45:00,113.51,217.265,63.92100000000001,32.641 +2020-01-31 12:00:00,110.81,214.657,60.79600000000001,32.641 +2020-01-31 12:15:00,108.51,212.18099999999998,60.79600000000001,32.641 +2020-01-31 12:30:00,110.61,211.828,60.79600000000001,32.641 +2020-01-31 12:45:00,107.4,213.051,60.79600000000001,32.641 +2020-01-31 13:00:00,109.64,211.68400000000003,59.393,32.641 +2020-01-31 13:15:00,114.24,211.65400000000002,59.393,32.641 +2020-01-31 13:30:00,114.13,210.50400000000002,59.393,32.641 +2020-01-31 13:45:00,116.3,210.64700000000002,59.393,32.641 +2020-01-31 14:00:00,114.74,209.613,57.943999999999996,32.641 +2020-01-31 14:15:00,117.45,209.69099999999997,57.943999999999996,32.641 +2020-01-31 14:30:00,113.57,210.88299999999998,57.943999999999996,32.641 +2020-01-31 14:45:00,113.98,212.3,57.943999999999996,32.641 +2020-01-31 15:00:00,114.17,212.85,60.153999999999996,32.641 +2020-01-31 15:15:00,115.31,212.88099999999997,60.153999999999996,32.641 +2020-01-31 15:30:00,115.47,212.65400000000002,60.153999999999996,32.641 +2020-01-31 15:45:00,118.13,213.44,60.153999999999996,32.641 +2020-01-31 16:00:00,117.86,213.96900000000002,62.933,32.641 +2020-01-31 16:15:00,117.65,216.122,62.933,32.641 +2020-01-31 16:30:00,119.13,219.115,62.933,32.641 +2020-01-31 16:45:00,120.48,220.305,62.933,32.641 +2020-01-31 17:00:00,125.21,222.662,68.657,32.641 +2020-01-31 17:15:00,125.41,223.35,68.657,32.641 +2020-01-31 17:30:00,129.61,224.38299999999998,68.657,32.641 +2020-01-31 17:45:00,127.16,224.322,68.657,32.641 +2020-01-31 18:00:00,127.56,227.113,67.111,32.641 +2020-01-31 18:15:00,125.29,223.821,67.111,32.641 +2020-01-31 18:30:00,125.6,223.31,67.111,32.641 +2020-01-31 18:45:00,124.45,223.90200000000002,67.111,32.641 +2020-01-31 19:00:00,121.9,222.61900000000003,62.434,32.641 +2020-01-31 19:15:00,121.13,220.045,62.434,32.641 +2020-01-31 19:30:00,123.82,216.67700000000002,62.434,32.641 +2020-01-31 19:45:00,118.48,213.486,62.434,32.641 +2020-01-31 20:00:00,112.24,209.06799999999998,61.763000000000005,32.641 +2020-01-31 20:15:00,106.36,202.207,61.763000000000005,32.641 +2020-01-31 20:30:00,103.04,197.96599999999998,61.763000000000005,32.641 +2020-01-31 20:45:00,102.41,197.637,61.763000000000005,32.641 +2020-01-31 21:00:00,97.4,194.132,56.785,32.641 +2020-01-31 21:15:00,102.69,191.324,56.785,32.641 +2020-01-31 21:30:00,101.42,189.345,56.785,32.641 +2020-01-31 21:45:00,97.23,188.61700000000002,56.785,32.641 +2020-01-31 22:00:00,90.73,182.27200000000002,52.693000000000005,32.641 +2020-01-31 22:15:00,85.64,176.511,52.693000000000005,32.641 +2020-01-31 22:30:00,87.1,169.132,52.693000000000005,32.641 +2020-01-31 22:45:00,89.23,164.673,52.693000000000005,32.641 +2020-01-31 23:00:00,85.55,156.369,45.443999999999996,32.641 +2020-01-31 23:15:00,83.18,153.1,45.443999999999996,32.641 +2020-01-31 23:30:00,79.02,152.094,45.443999999999996,32.641 +2020-01-31 23:45:00,83.75,151.02700000000002,45.443999999999996,32.641 +2020-02-01 00:00:00,76.71,129.81,42.033,32.431999999999995 +2020-02-01 00:15:00,73.23,124.75399999999999,42.033,32.431999999999995 +2020-02-01 00:30:00,70.83,127.20700000000001,42.033,32.431999999999995 +2020-02-01 00:45:00,74.17,129.618,42.033,32.431999999999995 +2020-02-01 01:00:00,71.69,132.734,38.255,32.431999999999995 +2020-02-01 01:15:00,71.85,132.577,38.255,32.431999999999995 +2020-02-01 01:30:00,67.13,132.036,38.255,32.431999999999995 +2020-02-01 01:45:00,71.17,132.134,38.255,32.431999999999995 +2020-02-01 02:00:00,69.97,135.287,36.404,32.431999999999995 +2020-02-01 02:15:00,69.51,136.813,36.404,32.431999999999995 +2020-02-01 02:30:00,67.27,137.26,36.404,32.431999999999995 +2020-02-01 02:45:00,73.49,139.49,36.404,32.431999999999995 +2020-02-01 03:00:00,69.19,141.94,36.083,32.431999999999995 +2020-02-01 03:15:00,67.7,143.183,36.083,32.431999999999995 +2020-02-01 03:30:00,62.05,143.143,36.083,32.431999999999995 +2020-02-01 03:45:00,63.49,145.23,36.083,32.431999999999995 +2020-02-01 04:00:00,67.97,152.216,36.102,32.431999999999995 +2020-02-01 04:15:00,68.64,160.666,36.102,32.431999999999995 +2020-02-01 04:30:00,65.01,162.115,36.102,32.431999999999995 +2020-02-01 04:45:00,63.87,163.159,36.102,32.431999999999995 +2020-02-01 05:00:00,64.66,178.486,35.284,32.431999999999995 +2020-02-01 05:15:00,65.5,187.328,35.284,32.431999999999995 +2020-02-01 05:30:00,65.69,184.41,35.284,32.431999999999995 +2020-02-01 05:45:00,67.86,183.62,35.284,32.431999999999995 +2020-02-01 06:00:00,69.24,201.104,36.265,32.431999999999995 +2020-02-01 06:15:00,71.37,222.93900000000002,36.265,32.431999999999995 +2020-02-01 06:30:00,71.93,219.313,36.265,32.431999999999995 +2020-02-01 06:45:00,74.95,216.52599999999998,36.265,32.431999999999995 +2020-02-01 07:00:00,78.52,210.303,40.714,32.431999999999995 +2020-02-01 07:15:00,80.13,215.368,40.714,32.431999999999995 +2020-02-01 07:30:00,82.2,221.09,40.714,32.431999999999995 +2020-02-01 07:45:00,85.69,225.72099999999998,40.714,32.431999999999995 +2020-02-01 08:00:00,91.13,227.44099999999997,46.692,32.431999999999995 +2020-02-01 08:15:00,91.12,231.018,46.692,32.431999999999995 +2020-02-01 08:30:00,92.79,230.90099999999998,46.692,32.431999999999995 +2020-02-01 08:45:00,94.88,229.52900000000002,46.692,32.431999999999995 +2020-02-01 09:00:00,97.7,225.294,48.925,32.431999999999995 +2020-02-01 09:15:00,97.76,223.685,48.925,32.431999999999995 +2020-02-01 09:30:00,98.69,222.65200000000002,48.925,32.431999999999995 +2020-02-01 09:45:00,101.05,219.40099999999998,48.925,32.431999999999995 +2020-02-01 10:00:00,101.31,212.862,47.799,32.431999999999995 +2020-02-01 10:15:00,101.73,210.19,47.799,32.431999999999995 +2020-02-01 10:30:00,101.39,206.543,47.799,32.431999999999995 +2020-02-01 10:45:00,106.31,205.921,47.799,32.431999999999995 +2020-02-01 11:00:00,103.02,203.19299999999998,44.309,32.431999999999995 +2020-02-01 11:15:00,103.3,200.252,44.309,32.431999999999995 +2020-02-01 11:30:00,108.4,199.29,44.309,32.431999999999995 +2020-02-01 11:45:00,112.35,197.61900000000003,44.309,32.431999999999995 +2020-02-01 12:00:00,105.64,193.695,42.367,32.431999999999995 +2020-02-01 12:15:00,104.87,191.93599999999998,42.367,32.431999999999995 +2020-02-01 12:30:00,102.75,191.995,42.367,32.431999999999995 +2020-02-01 12:45:00,101.19,192.31900000000002,42.367,32.431999999999995 +2020-02-01 13:00:00,98.77,190.88400000000001,39.036,32.431999999999995 +2020-02-01 13:15:00,102.03,188.609,39.036,32.431999999999995 +2020-02-01 13:30:00,97.87,187.08700000000002,39.036,32.431999999999995 +2020-02-01 13:45:00,97.89,187.785,39.036,32.431999999999995 +2020-02-01 14:00:00,95.54,188.062,37.995,32.431999999999995 +2020-02-01 14:15:00,95.43,187.68599999999998,37.995,32.431999999999995 +2020-02-01 14:30:00,94.24,186.92700000000002,37.995,32.431999999999995 +2020-02-01 14:45:00,95.58,188.394,37.995,32.431999999999995 +2020-02-01 15:00:00,95.01,189.855,40.71,32.431999999999995 +2020-02-01 15:15:00,94.99,190.50799999999998,40.71,32.431999999999995 +2020-02-01 15:30:00,93.2,192.142,40.71,32.431999999999995 +2020-02-01 15:45:00,93.06,193.253,40.71,32.431999999999995 +2020-02-01 16:00:00,95.23,191.983,46.998000000000005,32.431999999999995 +2020-02-01 16:15:00,95.22,195.12099999999998,46.998000000000005,32.431999999999995 +2020-02-01 16:30:00,97.78,198.024,46.998000000000005,32.431999999999995 +2020-02-01 16:45:00,99.14,200.262,46.998000000000005,32.431999999999995 +2020-02-01 17:00:00,105.82,202.077,55.431000000000004,32.431999999999995 +2020-02-01 17:15:00,106.49,204.93599999999998,55.431000000000004,32.431999999999995 +2020-02-01 17:30:00,107.67,205.97799999999998,55.431000000000004,32.431999999999995 +2020-02-01 17:45:00,109.78,205.5,55.431000000000004,32.431999999999995 +2020-02-01 18:00:00,112.26,207.61599999999999,55.989,32.431999999999995 +2020-02-01 18:15:00,110.96,206.43400000000003,55.989,32.431999999999995 +2020-02-01 18:30:00,109.62,207.187,55.989,32.431999999999995 +2020-02-01 18:45:00,108.33,204.48,55.989,32.431999999999995 +2020-02-01 19:00:00,106.53,204.679,50.882,32.431999999999995 +2020-02-01 19:15:00,105.46,201.778,50.882,32.431999999999995 +2020-02-01 19:30:00,104.21,199.399,50.882,32.431999999999995 +2020-02-01 19:45:00,102.98,195.791,50.882,32.431999999999995 +2020-02-01 20:00:00,97.91,193.72299999999998,43.172,32.431999999999995 +2020-02-01 20:15:00,94.51,189.55700000000002,43.172,32.431999999999995 +2020-02-01 20:30:00,91.8,185.125,43.172,32.431999999999995 +2020-02-01 20:45:00,90.04,184.016,43.172,32.431999999999995 +2020-02-01 21:00:00,84.99,183.33900000000003,37.599000000000004,32.431999999999995 +2020-02-01 21:15:00,84.93,181.149,37.599000000000004,32.431999999999995 +2020-02-01 21:30:00,83.67,180.52,37.599000000000004,32.431999999999995 +2020-02-01 21:45:00,82.32,179.476,37.599000000000004,32.431999999999995 +2020-02-01 22:00:00,78.77,174.79,39.047,32.431999999999995 +2020-02-01 22:15:00,79.21,171.956,39.047,32.431999999999995 +2020-02-01 22:30:00,75.78,171.52200000000002,39.047,32.431999999999995 +2020-02-01 22:45:00,74.58,169.205,39.047,32.431999999999995 +2020-02-01 23:00:00,71.15,163.97299999999998,32.339,32.431999999999995 +2020-02-01 23:15:00,70.35,158.893,32.339,32.431999999999995 +2020-02-01 23:30:00,67.66,155.857,32.339,32.431999999999995 +2020-02-01 23:45:00,67.11,152.153,32.339,32.431999999999995 +2020-02-02 00:00:00,63.53,130.029,29.988000000000003,32.431999999999995 +2020-02-02 00:15:00,63.8,124.745,29.988000000000003,32.431999999999995 +2020-02-02 00:30:00,62.55,126.78,29.988000000000003,32.431999999999995 +2020-02-02 00:45:00,61.0,129.969,29.988000000000003,32.431999999999995 +2020-02-02 01:00:00,57.82,132.872,28.531999999999996,32.431999999999995 +2020-02-02 01:15:00,58.55,133.874,28.531999999999996,32.431999999999995 +2020-02-02 01:30:00,58.11,133.923,28.531999999999996,32.431999999999995 +2020-02-02 01:45:00,57.64,133.714,28.531999999999996,32.431999999999995 +2020-02-02 02:00:00,55.93,136.03,27.805999999999997,32.431999999999995 +2020-02-02 02:15:00,56.4,136.515,27.805999999999997,32.431999999999995 +2020-02-02 02:30:00,55.63,137.906,27.805999999999997,32.431999999999995 +2020-02-02 02:45:00,55.85,140.687,27.805999999999997,32.431999999999995 +2020-02-02 03:00:00,54.61,143.411,26.193,32.431999999999995 +2020-02-02 03:15:00,55.42,144.041,26.193,32.431999999999995 +2020-02-02 03:30:00,55.76,145.66,26.193,32.431999999999995 +2020-02-02 03:45:00,55.94,147.775,26.193,32.431999999999995 +2020-02-02 04:00:00,55.92,154.489,27.19,32.431999999999995 +2020-02-02 04:15:00,57.67,161.849,27.19,32.431999999999995 +2020-02-02 04:30:00,57.96,163.20600000000002,27.19,32.431999999999995 +2020-02-02 04:45:00,58.08,164.608,27.19,32.431999999999995 +2020-02-02 05:00:00,58.81,175.93599999999998,28.166999999999998,32.431999999999995 +2020-02-02 05:15:00,60.25,182.16099999999997,28.166999999999998,32.431999999999995 +2020-02-02 05:30:00,60.89,179.095,28.166999999999998,32.431999999999995 +2020-02-02 05:45:00,61.68,178.62599999999998,28.166999999999998,32.431999999999995 +2020-02-02 06:00:00,62.19,196.321,27.16,32.431999999999995 +2020-02-02 06:15:00,62.48,216.12400000000002,27.16,32.431999999999995 +2020-02-02 06:30:00,63.17,211.331,27.16,32.431999999999995 +2020-02-02 06:45:00,64.8,207.49599999999998,27.16,32.431999999999995 +2020-02-02 07:00:00,68.38,204.016,29.578000000000003,32.431999999999995 +2020-02-02 07:15:00,68.86,208.31099999999998,29.578000000000003,32.431999999999995 +2020-02-02 07:30:00,69.25,212.454,29.578000000000003,32.431999999999995 +2020-02-02 07:45:00,71.7,216.199,29.578000000000003,32.431999999999995 +2020-02-02 08:00:00,76.25,219.887,34.650999999999996,32.431999999999995 +2020-02-02 08:15:00,77.74,223.195,34.650999999999996,32.431999999999995 +2020-02-02 08:30:00,79.71,224.77,34.650999999999996,32.431999999999995 +2020-02-02 08:45:00,79.84,225.657,34.650999999999996,32.431999999999995 +2020-02-02 09:00:00,84.28,220.99099999999999,38.080999999999996,32.431999999999995 +2020-02-02 09:15:00,85.13,220.058,38.080999999999996,32.431999999999995 +2020-02-02 09:30:00,86.37,218.822,38.080999999999996,32.431999999999995 +2020-02-02 09:45:00,88.68,215.322,38.080999999999996,32.431999999999995 +2020-02-02 10:00:00,88.73,211.52200000000002,39.934,32.431999999999995 +2020-02-02 10:15:00,89.78,209.44799999999998,39.934,32.431999999999995 +2020-02-02 10:30:00,92.29,206.45,39.934,32.431999999999995 +2020-02-02 10:45:00,94.48,203.609,39.934,32.431999999999995 +2020-02-02 11:00:00,96.74,201.93599999999998,43.74100000000001,32.431999999999995 +2020-02-02 11:15:00,100.32,199.2,43.74100000000001,32.431999999999995 +2020-02-02 11:30:00,102.7,197.222,43.74100000000001,32.431999999999995 +2020-02-02 11:45:00,103.15,196.201,43.74100000000001,32.431999999999995 +2020-02-02 12:00:00,99.58,191.542,40.001999999999995,32.431999999999995 +2020-02-02 12:15:00,98.64,191.982,40.001999999999995,32.431999999999995 +2020-02-02 12:30:00,95.5,190.382,40.001999999999995,32.431999999999995 +2020-02-02 12:45:00,93.0,189.704,40.001999999999995,32.431999999999995 +2020-02-02 13:00:00,90.71,187.521,37.855,32.431999999999995 +2020-02-02 13:15:00,89.28,188.63099999999997,37.855,32.431999999999995 +2020-02-02 13:30:00,87.1,186.988,37.855,32.431999999999995 +2020-02-02 13:45:00,86.01,186.828,37.855,32.431999999999995 +2020-02-02 14:00:00,84.14,187.291,35.946999999999996,32.431999999999995 +2020-02-02 14:15:00,84.33,188.176,35.946999999999996,32.431999999999995 +2020-02-02 14:30:00,83.61,188.908,35.946999999999996,32.431999999999995 +2020-02-02 14:45:00,84.39,190.054,35.946999999999996,32.431999999999995 +2020-02-02 15:00:00,84.93,189.815,35.138000000000005,32.431999999999995 +2020-02-02 15:15:00,84.41,191.359,35.138000000000005,32.431999999999995 +2020-02-02 15:30:00,83.67,193.63299999999998,35.138000000000005,32.431999999999995 +2020-02-02 15:45:00,83.84,195.46,35.138000000000005,32.431999999999995 +2020-02-02 16:00:00,85.14,196.38099999999997,38.672,32.431999999999995 +2020-02-02 16:15:00,85.03,198.49599999999998,38.672,32.431999999999995 +2020-02-02 16:30:00,86.66,201.58599999999998,38.672,32.431999999999995 +2020-02-02 16:45:00,89.42,203.954,38.672,32.431999999999995 +2020-02-02 17:00:00,94.56,205.68900000000002,48.684,32.431999999999995 +2020-02-02 17:15:00,97.47,208.07299999999998,48.684,32.431999999999995 +2020-02-02 17:30:00,99.16,209.385,48.684,32.431999999999995 +2020-02-02 17:45:00,100.99,211.365,48.684,32.431999999999995 +2020-02-02 18:00:00,105.14,212.826,51.568999999999996,32.431999999999995 +2020-02-02 18:15:00,103.39,213.162,51.568999999999996,32.431999999999995 +2020-02-02 18:30:00,106.37,211.679,51.568999999999996,32.431999999999995 +2020-02-02 18:45:00,102.89,210.99400000000003,51.568999999999996,32.431999999999995 +2020-02-02 19:00:00,101.38,210.583,48.608000000000004,32.431999999999995 +2020-02-02 19:15:00,99.21,208.429,48.608000000000004,32.431999999999995 +2020-02-02 19:30:00,98.13,205.91099999999997,48.608000000000004,32.431999999999995 +2020-02-02 19:45:00,98.79,203.928,48.608000000000004,32.431999999999995 +2020-02-02 20:00:00,93.72,201.81799999999998,43.733999999999995,32.431999999999995 +2020-02-02 20:15:00,91.94,198.74400000000003,43.733999999999995,32.431999999999995 +2020-02-02 20:30:00,90.61,195.58700000000002,43.733999999999995,32.431999999999995 +2020-02-02 20:45:00,89.18,193.338,43.733999999999995,32.431999999999995 +2020-02-02 21:00:00,86.51,189.835,39.283,32.431999999999995 +2020-02-02 21:15:00,86.22,186.979,39.283,32.431999999999995 +2020-02-02 21:30:00,86.04,186.727,39.283,32.431999999999995 +2020-02-02 21:45:00,87.9,185.815,39.283,32.431999999999995 +2020-02-02 22:00:00,84.32,179.638,40.111,32.431999999999995 +2020-02-02 22:15:00,85.45,176.153,40.111,32.431999999999995 +2020-02-02 22:30:00,83.23,172.30900000000003,40.111,32.431999999999995 +2020-02-02 22:45:00,82.39,169.19,40.111,32.431999999999995 +2020-02-02 23:00:00,78.06,160.942,35.791,32.431999999999995 +2020-02-02 23:15:00,78.99,157.774,35.791,32.431999999999995 +2020-02-02 23:30:00,76.63,155.68200000000002,35.791,32.431999999999995 +2020-02-02 23:45:00,77.09,152.945,35.791,32.431999999999995 +2020-02-03 00:00:00,72.55,134.424,34.311,32.613 +2020-02-03 00:15:00,73.3,132.341,34.311,32.613 +2020-02-03 00:30:00,72.72,134.543,34.311,32.613 +2020-02-03 00:45:00,71.94,137.173,34.311,32.613 +2020-02-03 01:00:00,69.66,140.04,34.585,32.613 +2020-02-03 01:15:00,70.92,140.44,34.585,32.613 +2020-02-03 01:30:00,70.34,140.496,34.585,32.613 +2020-02-03 01:45:00,70.86,140.424,34.585,32.613 +2020-02-03 02:00:00,69.94,142.671,34.111,32.613 +2020-02-03 02:15:00,71.57,144.918,34.111,32.613 +2020-02-03 02:30:00,70.86,146.672,34.111,32.613 +2020-02-03 02:45:00,72.17,148.778,34.111,32.613 +2020-02-03 03:00:00,71.64,152.88299999999998,32.435,32.613 +2020-02-03 03:15:00,71.9,155.274,32.435,32.613 +2020-02-03 03:30:00,73.09,156.496,32.435,32.613 +2020-02-03 03:45:00,73.72,158.063,32.435,32.613 +2020-02-03 04:00:00,74.52,169.278,33.04,32.613 +2020-02-03 04:15:00,75.25,180.90200000000002,33.04,32.613 +2020-02-03 04:30:00,76.93,184.803,33.04,32.613 +2020-02-03 04:45:00,80.1,186.326,33.04,32.613 +2020-02-03 05:00:00,83.89,214.47799999999998,40.399,32.613 +2020-02-03 05:15:00,86.25,242.049,40.399,32.613 +2020-02-03 05:30:00,91.33,239.489,40.399,32.613 +2020-02-03 05:45:00,95.99,233.071,40.399,32.613 +2020-02-03 06:00:00,104.74,231.924,60.226000000000006,32.613 +2020-02-03 06:15:00,110.75,236.253,60.226000000000006,32.613 +2020-02-03 06:30:00,114.63,239.93900000000002,60.226000000000006,32.613 +2020-02-03 06:45:00,119.48,245.31099999999998,60.226000000000006,32.613 +2020-02-03 07:00:00,129.14,244.451,73.578,32.613 +2020-02-03 07:15:00,130.05,249.81900000000002,73.578,32.613 +2020-02-03 07:30:00,129.17,253.173,73.578,32.613 +2020-02-03 07:45:00,130.1,253.949,73.578,32.613 +2020-02-03 08:00:00,135.43,252.31099999999998,66.58,32.613 +2020-02-03 08:15:00,135.0,253.35,66.58,32.613 +2020-02-03 08:30:00,134.72,250.42,66.58,32.613 +2020-02-03 08:45:00,136.03,247.53799999999998,66.58,32.613 +2020-02-03 09:00:00,138.01,241.83900000000003,62.0,32.613 +2020-02-03 09:15:00,139.79,237.245,62.0,32.613 +2020-02-03 09:30:00,141.36,235.02900000000002,62.0,32.613 +2020-02-03 09:45:00,139.59,232.144,62.0,32.613 +2020-02-03 10:00:00,138.43,227.13400000000001,59.099,32.613 +2020-02-03 10:15:00,138.67,224.736,59.099,32.613 +2020-02-03 10:30:00,137.73,220.82299999999998,59.099,32.613 +2020-02-03 10:45:00,137.82,219.03,59.099,32.613 +2020-02-03 11:00:00,138.07,214.34599999999998,57.729,32.613 +2020-02-03 11:15:00,139.42,213.59,57.729,32.613 +2020-02-03 11:30:00,139.86,213.06599999999997,57.729,32.613 +2020-02-03 11:45:00,140.03,211.54,57.729,32.613 +2020-02-03 12:00:00,139.07,209.048,55.615,32.613 +2020-02-03 12:15:00,138.54,209.49200000000002,55.615,32.613 +2020-02-03 12:30:00,136.96,208.28099999999998,55.615,32.613 +2020-02-03 12:45:00,137.41,209.297,55.615,32.613 +2020-02-03 13:00:00,134.33,207.68,56.515,32.613 +2020-02-03 13:15:00,132.51,207.333,56.515,32.613 +2020-02-03 13:30:00,130.0,205.06400000000002,56.515,32.613 +2020-02-03 13:45:00,128.89,204.83,56.515,32.613 +2020-02-03 14:00:00,128.86,204.725,58.1,32.613 +2020-02-03 14:15:00,127.42,204.808,58.1,32.613 +2020-02-03 14:30:00,127.1,204.94799999999998,58.1,32.613 +2020-02-03 14:45:00,128.03,205.826,58.1,32.613 +2020-02-03 15:00:00,129.69,207.56599999999997,59.801,32.613 +2020-02-03 15:15:00,135.62,207.56099999999998,59.801,32.613 +2020-02-03 15:30:00,130.78,208.795,59.801,32.613 +2020-02-03 15:45:00,132.56,210.171,59.801,32.613 +2020-02-03 16:00:00,130.0,211.095,62.901,32.613 +2020-02-03 16:15:00,128.75,212.358,62.901,32.613 +2020-02-03 16:30:00,126.25,214.46200000000002,62.901,32.613 +2020-02-03 16:45:00,126.93,215.51,62.901,32.613 +2020-02-03 17:00:00,132.31,217.107,70.418,32.613 +2020-02-03 17:15:00,134.91,218.438,70.418,32.613 +2020-02-03 17:30:00,139.16,219.222,70.418,32.613 +2020-02-03 17:45:00,137.81,219.636,70.418,32.613 +2020-02-03 18:00:00,140.89,221.644,71.726,32.613 +2020-02-03 18:15:00,138.81,219.799,71.726,32.613 +2020-02-03 18:30:00,142.27,219.105,71.726,32.613 +2020-02-03 18:45:00,138.78,218.90900000000002,71.726,32.613 +2020-02-03 19:00:00,137.55,216.71,65.997,32.613 +2020-02-03 19:15:00,133.6,213.172,65.997,32.613 +2020-02-03 19:30:00,132.16,211.24599999999998,65.997,32.613 +2020-02-03 19:45:00,130.1,208.445,65.997,32.613 +2020-02-03 20:00:00,123.56,203.873,68.09100000000001,32.613 +2020-02-03 20:15:00,121.51,197.967,68.09100000000001,32.613 +2020-02-03 20:30:00,117.5,192.733,68.09100000000001,32.613 +2020-02-03 20:45:00,116.06,192.24400000000003,68.09100000000001,32.613 +2020-02-03 21:00:00,112.08,189.36900000000003,59.617,32.613 +2020-02-03 21:15:00,119.36,185.173,59.617,32.613 +2020-02-03 21:30:00,115.51,183.97799999999998,59.617,32.613 +2020-02-03 21:45:00,112.17,182.554,59.617,32.613 +2020-02-03 22:00:00,102.03,173.428,54.938,32.613 +2020-02-03 22:15:00,101.13,168.33,54.938,32.613 +2020-02-03 22:30:00,103.23,154.47899999999998,54.938,32.613 +2020-02-03 22:45:00,102.28,146.101,54.938,32.613 +2020-02-03 23:00:00,97.78,138.697,47.43,32.613 +2020-02-03 23:15:00,93.27,138.597,47.43,32.613 +2020-02-03 23:30:00,87.74,139.533,47.43,32.613 +2020-02-03 23:45:00,93.42,139.745,47.43,32.613 +2020-02-04 00:00:00,90.86,133.845,48.354,32.613 +2020-02-04 00:15:00,91.14,133.235,48.354,32.613 +2020-02-04 00:30:00,85.35,134.276,48.354,32.613 +2020-02-04 00:45:00,87.43,135.703,48.354,32.613 +2020-02-04 01:00:00,85.05,138.391,45.68600000000001,32.613 +2020-02-04 01:15:00,86.31,138.264,45.68600000000001,32.613 +2020-02-04 01:30:00,82.43,138.523,45.68600000000001,32.613 +2020-02-04 01:45:00,87.86,138.858,45.68600000000001,32.613 +2020-02-04 02:00:00,86.41,141.167,44.269,32.613 +2020-02-04 02:15:00,87.36,143.13,44.269,32.613 +2020-02-04 02:30:00,82.98,144.28,44.269,32.613 +2020-02-04 02:45:00,87.34,146.35399999999998,44.269,32.613 +2020-02-04 03:00:00,85.37,149.16899999999998,44.187,32.613 +2020-02-04 03:15:00,87.11,150.472,44.187,32.613 +2020-02-04 03:30:00,84.96,152.24200000000002,44.187,32.613 +2020-02-04 03:45:00,83.22,154.197,44.187,32.613 +2020-02-04 04:00:00,82.45,165.356,46.126999999999995,32.613 +2020-02-04 04:15:00,83.66,176.574,46.126999999999995,32.613 +2020-02-04 04:30:00,85.46,180.15099999999998,46.126999999999995,32.613 +2020-02-04 04:45:00,87.52,182.96599999999998,46.126999999999995,32.613 +2020-02-04 05:00:00,92.46,216.549,49.666000000000004,32.613 +2020-02-04 05:15:00,94.39,243.795,49.666000000000004,32.613 +2020-02-04 05:30:00,96.94,239.44099999999997,49.666000000000004,32.613 +2020-02-04 05:45:00,101.07,233.153,49.666000000000004,32.613 +2020-02-04 06:00:00,109.2,230.50799999999998,61.077,32.613 +2020-02-04 06:15:00,111.14,236.672,61.077,32.613 +2020-02-04 06:30:00,117.58,239.65099999999998,61.077,32.613 +2020-02-04 06:45:00,123.45,244.737,61.077,32.613 +2020-02-04 07:00:00,133.3,243.669,74.717,32.613 +2020-02-04 07:15:00,131.29,248.86900000000003,74.717,32.613 +2020-02-04 07:30:00,133.43,251.56799999999998,74.717,32.613 +2020-02-04 07:45:00,133.13,252.69799999999998,74.717,32.613 +2020-02-04 08:00:00,136.35,251.172,69.033,32.613 +2020-02-04 08:15:00,136.11,251.097,69.033,32.613 +2020-02-04 08:30:00,134.66,247.888,69.033,32.613 +2020-02-04 08:45:00,134.24,244.832,69.033,32.613 +2020-02-04 09:00:00,133.77,238.14700000000002,63.113,32.613 +2020-02-04 09:15:00,135.41,235.375,63.113,32.613 +2020-02-04 09:30:00,136.68,233.847,63.113,32.613 +2020-02-04 09:45:00,135.45,230.52900000000002,63.113,32.613 +2020-02-04 10:00:00,134.9,225.05700000000002,61.461999999999996,32.613 +2020-02-04 10:15:00,134.83,221.503,61.461999999999996,32.613 +2020-02-04 10:30:00,135.76,217.795,61.461999999999996,32.613 +2020-02-04 10:45:00,134.18,216.19,61.461999999999996,32.613 +2020-02-04 11:00:00,131.66,213.17,59.614,32.613 +2020-02-04 11:15:00,132.42,211.995,59.614,32.613 +2020-02-04 11:30:00,131.61,210.327,59.614,32.613 +2020-02-04 11:45:00,129.45,209.65900000000002,59.614,32.613 +2020-02-04 12:00:00,126.71,205.668,57.415,32.613 +2020-02-04 12:15:00,125.47,205.61599999999999,57.415,32.613 +2020-02-04 12:30:00,128.08,205.076,57.415,32.613 +2020-02-04 12:45:00,125.59,205.66400000000002,57.415,32.613 +2020-02-04 13:00:00,123.19,203.65400000000002,58.534,32.613 +2020-02-04 13:15:00,122.62,202.643,58.534,32.613 +2020-02-04 13:30:00,119.8,201.68,58.534,32.613 +2020-02-04 13:45:00,120.47,201.791,58.534,32.613 +2020-02-04 14:00:00,121.58,201.998,59.415,32.613 +2020-02-04 14:15:00,123.66,202.265,59.415,32.613 +2020-02-04 14:30:00,122.74,203.05599999999998,59.415,32.613 +2020-02-04 14:45:00,123.25,203.967,59.415,32.613 +2020-02-04 15:00:00,126.72,205.26,62.071999999999996,32.613 +2020-02-04 15:15:00,128.05,205.477,62.071999999999996,32.613 +2020-02-04 15:30:00,126.41,206.958,62.071999999999996,32.613 +2020-02-04 15:45:00,125.03,207.822,62.071999999999996,32.613 +2020-02-04 16:00:00,125.33,209.22799999999998,64.99,32.613 +2020-02-04 16:15:00,124.64,211.00400000000002,64.99,32.613 +2020-02-04 16:30:00,127.25,213.877,64.99,32.613 +2020-02-04 16:45:00,128.17,215.144,64.99,32.613 +2020-02-04 17:00:00,133.12,217.313,72.658,32.613 +2020-02-04 17:15:00,136.25,218.657,72.658,32.613 +2020-02-04 17:30:00,140.8,220.261,72.658,32.613 +2020-02-04 17:45:00,141.04,220.595,72.658,32.613 +2020-02-04 18:00:00,142.74,222.60299999999998,73.645,32.613 +2020-02-04 18:15:00,143.03,220.092,73.645,32.613 +2020-02-04 18:30:00,140.63,219.092,73.645,32.613 +2020-02-04 18:45:00,138.97,219.81400000000002,73.645,32.613 +2020-02-04 19:00:00,135.76,217.78,67.085,32.613 +2020-02-04 19:15:00,134.8,213.929,67.085,32.613 +2020-02-04 19:30:00,140.27,211.31400000000002,67.085,32.613 +2020-02-04 19:45:00,133.39,208.546,67.085,32.613 +2020-02-04 20:00:00,125.17,204.082,66.138,32.613 +2020-02-04 20:15:00,128.55,197.642,66.138,32.613 +2020-02-04 20:30:00,119.49,193.523,66.138,32.613 +2020-02-04 20:45:00,118.62,192.36700000000002,66.138,32.613 +2020-02-04 21:00:00,111.08,188.612,57.512,32.613 +2020-02-04 21:15:00,115.78,185.55599999999998,57.512,32.613 +2020-02-04 21:30:00,115.31,183.541,57.512,32.613 +2020-02-04 21:45:00,112.98,182.362,57.512,32.613 +2020-02-04 22:00:00,101.24,175.079,54.545,32.613 +2020-02-04 22:15:00,101.11,169.757,54.545,32.613 +2020-02-04 22:30:00,101.29,155.946,54.545,32.613 +2020-02-04 22:45:00,102.32,147.877,54.545,32.613 +2020-02-04 23:00:00,97.65,140.57399999999998,48.605,32.613 +2020-02-04 23:15:00,94.45,139.328,48.605,32.613 +2020-02-04 23:30:00,87.36,139.886,48.605,32.613 +2020-02-04 23:45:00,92.55,139.616,48.605,32.613 +2020-02-05 00:00:00,89.46,133.739,45.675,32.613 +2020-02-05 00:15:00,90.37,133.12,45.675,32.613 +2020-02-05 00:30:00,85.45,134.141,45.675,32.613 +2020-02-05 00:45:00,84.27,135.56,45.675,32.613 +2020-02-05 01:00:00,85.32,138.22299999999998,43.015,32.613 +2020-02-05 01:15:00,85.92,138.083,43.015,32.613 +2020-02-05 01:30:00,85.0,138.333,43.015,32.613 +2020-02-05 01:45:00,83.16,138.666,43.015,32.613 +2020-02-05 02:00:00,83.15,140.976,41.0,32.613 +2020-02-05 02:15:00,77.78,142.938,41.0,32.613 +2020-02-05 02:30:00,81.84,144.102,41.0,32.613 +2020-02-05 02:45:00,86.05,146.175,41.0,32.613 +2020-02-05 03:00:00,81.25,148.99200000000002,41.318000000000005,32.613 +2020-02-05 03:15:00,88.66,150.3,41.318000000000005,32.613 +2020-02-05 03:30:00,89.0,152.067,41.318000000000005,32.613 +2020-02-05 03:45:00,89.87,154.033,41.318000000000005,32.613 +2020-02-05 04:00:00,85.75,165.185,42.544,32.613 +2020-02-05 04:15:00,82.09,176.393,42.544,32.613 +2020-02-05 04:30:00,83.82,179.982,42.544,32.613 +2020-02-05 04:45:00,88.66,182.785,42.544,32.613 +2020-02-05 05:00:00,90.45,216.34400000000002,45.161,32.613 +2020-02-05 05:15:00,92.16,243.595,45.161,32.613 +2020-02-05 05:30:00,94.66,239.22400000000002,45.161,32.613 +2020-02-05 05:45:00,100.11,232.94299999999998,45.161,32.613 +2020-02-05 06:00:00,110.31,230.312,61.86600000000001,32.613 +2020-02-05 06:15:00,112.52,236.489,61.86600000000001,32.613 +2020-02-05 06:30:00,118.94,239.447,61.86600000000001,32.613 +2020-02-05 06:45:00,122.35,244.541,61.86600000000001,32.613 +2020-02-05 07:00:00,129.73,243.495,77.814,32.613 +2020-02-05 07:15:00,130.12,248.67,77.814,32.613 +2020-02-05 07:30:00,132.0,251.338,77.814,32.613 +2020-02-05 07:45:00,130.03,252.429,77.814,32.613 +2020-02-05 08:00:00,133.79,250.885,70.251,32.613 +2020-02-05 08:15:00,132.59,250.78599999999997,70.251,32.613 +2020-02-05 08:30:00,134.41,247.52200000000002,70.251,32.613 +2020-02-05 08:45:00,131.14,244.46200000000002,70.251,32.613 +2020-02-05 09:00:00,131.14,237.77700000000002,66.965,32.613 +2020-02-05 09:15:00,131.67,235.00900000000001,66.965,32.613 +2020-02-05 09:30:00,133.35,233.50099999999998,66.965,32.613 +2020-02-05 09:45:00,132.54,230.18200000000002,66.965,32.613 +2020-02-05 10:00:00,133.0,224.718,63.628,32.613 +2020-02-05 10:15:00,130.23,221.18900000000002,63.628,32.613 +2020-02-05 10:30:00,128.04,217.488,63.628,32.613 +2020-02-05 10:45:00,128.01,215.894,63.628,32.613 +2020-02-05 11:00:00,127.57,212.861,62.516999999999996,32.613 +2020-02-05 11:15:00,126.97,211.695,62.516999999999996,32.613 +2020-02-05 11:30:00,127.26,210.033,62.516999999999996,32.613 +2020-02-05 11:45:00,128.78,209.375,62.516999999999996,32.613 +2020-02-05 12:00:00,125.28,205.40099999999998,60.888999999999996,32.613 +2020-02-05 12:15:00,123.86,205.365,60.888999999999996,32.613 +2020-02-05 12:30:00,125.88,204.801,60.888999999999996,32.613 +2020-02-05 12:45:00,122.32,205.387,60.888999999999996,32.613 +2020-02-05 13:00:00,120.11,203.394,61.57899999999999,32.613 +2020-02-05 13:15:00,120.74,202.361,61.57899999999999,32.613 +2020-02-05 13:30:00,121.03,201.382,61.57899999999999,32.613 +2020-02-05 13:45:00,121.19,201.493,61.57899999999999,32.613 +2020-02-05 14:00:00,119.29,201.74900000000002,62.602,32.613 +2020-02-05 14:15:00,121.95,201.998,62.602,32.613 +2020-02-05 14:30:00,121.58,202.77700000000002,62.602,32.613 +2020-02-05 14:45:00,122.34,203.703,62.602,32.613 +2020-02-05 15:00:00,125.21,205.007,64.259,32.613 +2020-02-05 15:15:00,123.42,205.195,64.259,32.613 +2020-02-05 15:30:00,126.69,206.644,64.259,32.613 +2020-02-05 15:45:00,122.86,207.49099999999999,64.259,32.613 +2020-02-05 16:00:00,126.84,208.898,67.632,32.613 +2020-02-05 16:15:00,124.67,210.669,67.632,32.613 +2020-02-05 16:30:00,126.69,213.547,67.632,32.613 +2020-02-05 16:45:00,128.11,214.801,67.632,32.613 +2020-02-05 17:00:00,131.41,216.96400000000003,72.583,32.613 +2020-02-05 17:15:00,135.42,218.33599999999998,72.583,32.613 +2020-02-05 17:30:00,141.15,219.976,72.583,32.613 +2020-02-05 17:45:00,142.13,220.33599999999998,72.583,32.613 +2020-02-05 18:00:00,142.56,222.359,72.744,32.613 +2020-02-05 18:15:00,140.69,219.895,72.744,32.613 +2020-02-05 18:30:00,140.23,218.899,72.744,32.613 +2020-02-05 18:45:00,138.2,219.645,72.744,32.613 +2020-02-05 19:00:00,133.78,217.56900000000002,69.684,32.613 +2020-02-05 19:15:00,135.47,213.73,69.684,32.613 +2020-02-05 19:30:00,139.35,211.13400000000001,69.684,32.613 +2020-02-05 19:45:00,137.95,208.394,69.684,32.613 +2020-02-05 20:00:00,129.71,203.90200000000002,70.036,32.613 +2020-02-05 20:15:00,121.3,197.472,70.036,32.613 +2020-02-05 20:30:00,116.66,193.362,70.036,32.613 +2020-02-05 20:45:00,118.71,192.213,70.036,32.613 +2020-02-05 21:00:00,109.48,188.44,60.431999999999995,32.613 +2020-02-05 21:15:00,115.23,185.36900000000003,60.431999999999995,32.613 +2020-02-05 21:30:00,114.27,183.355,60.431999999999995,32.613 +2020-02-05 21:45:00,111.11,182.192,60.431999999999995,32.613 +2020-02-05 22:00:00,102.57,174.896,56.2,32.613 +2020-02-05 22:15:00,98.72,169.59799999999998,56.2,32.613 +2020-02-05 22:30:00,95.62,155.756,56.2,32.613 +2020-02-05 22:45:00,97.92,147.694,56.2,32.613 +2020-02-05 23:00:00,93.72,140.384,47.927,32.613 +2020-02-05 23:15:00,95.58,139.151,47.927,32.613 +2020-02-05 23:30:00,93.57,139.726,47.927,32.613 +2020-02-05 23:45:00,93.42,139.472,47.927,32.613 +2020-02-06 00:00:00,88.03,133.625,43.794,32.613 +2020-02-06 00:15:00,88.69,132.998,43.794,32.613 +2020-02-06 00:30:00,88.37,134.0,43.794,32.613 +2020-02-06 00:45:00,83.31,135.411,43.794,32.613 +2020-02-06 01:00:00,85.13,138.046,42.397,32.613 +2020-02-06 01:15:00,86.78,137.894,42.397,32.613 +2020-02-06 01:30:00,84.13,138.134,42.397,32.613 +2020-02-06 01:45:00,81.34,138.464,42.397,32.613 +2020-02-06 02:00:00,83.95,140.776,40.010999999999996,32.613 +2020-02-06 02:15:00,85.6,142.738,40.010999999999996,32.613 +2020-02-06 02:30:00,84.94,143.915,40.010999999999996,32.613 +2020-02-06 02:45:00,83.06,145.987,40.010999999999996,32.613 +2020-02-06 03:00:00,85.59,148.806,39.181,32.613 +2020-02-06 03:15:00,88.0,150.118,39.181,32.613 +2020-02-06 03:30:00,87.64,151.882,39.181,32.613 +2020-02-06 03:45:00,86.23,153.86,39.181,32.613 +2020-02-06 04:00:00,87.6,165.005,40.39,32.613 +2020-02-06 04:15:00,83.48,176.203,40.39,32.613 +2020-02-06 04:30:00,84.04,179.805,40.39,32.613 +2020-02-06 04:45:00,86.36,182.597,40.39,32.613 +2020-02-06 05:00:00,90.39,216.13299999999998,45.504,32.613 +2020-02-06 05:15:00,92.47,243.39,45.504,32.613 +2020-02-06 05:30:00,94.94,238.99900000000002,45.504,32.613 +2020-02-06 05:45:00,99.52,232.726,45.504,32.613 +2020-02-06 06:00:00,109.99,230.108,57.748000000000005,32.613 +2020-02-06 06:15:00,113.45,236.298,57.748000000000005,32.613 +2020-02-06 06:30:00,117.48,239.236,57.748000000000005,32.613 +2020-02-06 06:45:00,122.09,244.335,57.748000000000005,32.613 +2020-02-06 07:00:00,130.35,243.31099999999998,72.138,32.613 +2020-02-06 07:15:00,131.22,248.46200000000002,72.138,32.613 +2020-02-06 07:30:00,134.4,251.09599999999998,72.138,32.613 +2020-02-06 07:45:00,135.28,252.148,72.138,32.613 +2020-02-06 08:00:00,138.42,250.588,65.542,32.613 +2020-02-06 08:15:00,139.12,250.46400000000003,65.542,32.613 +2020-02-06 08:30:00,135.68,247.144,65.542,32.613 +2020-02-06 08:45:00,135.18,244.079,65.542,32.613 +2020-02-06 09:00:00,136.0,237.396,60.523,32.613 +2020-02-06 09:15:00,137.89,234.63099999999997,60.523,32.613 +2020-02-06 09:30:00,140.17,233.143,60.523,32.613 +2020-02-06 09:45:00,143.52,229.824,60.523,32.613 +2020-02-06 10:00:00,140.22,224.36900000000003,57.449,32.613 +2020-02-06 10:15:00,141.82,220.864,57.449,32.613 +2020-02-06 10:30:00,141.15,217.17,57.449,32.613 +2020-02-06 10:45:00,141.82,215.588,57.449,32.613 +2020-02-06 11:00:00,140.12,212.542,54.505,32.613 +2020-02-06 11:15:00,141.56,211.387,54.505,32.613 +2020-02-06 11:30:00,142.0,209.731,54.505,32.613 +2020-02-06 11:45:00,141.74,209.082,54.505,32.613 +2020-02-06 12:00:00,141.83,205.12400000000002,51.50899999999999,32.613 +2020-02-06 12:15:00,140.81,205.106,51.50899999999999,32.613 +2020-02-06 12:30:00,138.96,204.518,51.50899999999999,32.613 +2020-02-06 12:45:00,136.26,205.1,51.50899999999999,32.613 +2020-02-06 13:00:00,133.75,203.12599999999998,51.303999999999995,32.613 +2020-02-06 13:15:00,132.36,202.07,51.303999999999995,32.613 +2020-02-06 13:30:00,131.1,201.075,51.303999999999995,32.613 +2020-02-06 13:45:00,131.74,201.18599999999998,51.303999999999995,32.613 +2020-02-06 14:00:00,131.95,201.49400000000003,52.785,32.613 +2020-02-06 14:15:00,131.78,201.72299999999998,52.785,32.613 +2020-02-06 14:30:00,129.87,202.489,52.785,32.613 +2020-02-06 14:45:00,131.48,203.43099999999998,52.785,32.613 +2020-02-06 15:00:00,130.36,204.74599999999998,56.458999999999996,32.613 +2020-02-06 15:15:00,130.85,204.90400000000002,56.458999999999996,32.613 +2020-02-06 15:30:00,132.16,206.32,56.458999999999996,32.613 +2020-02-06 15:45:00,129.54,207.15200000000002,56.458999999999996,32.613 +2020-02-06 16:00:00,131.64,208.558,59.388000000000005,32.613 +2020-02-06 16:15:00,130.67,210.32299999999998,59.388000000000005,32.613 +2020-02-06 16:30:00,131.07,213.205,59.388000000000005,32.613 +2020-02-06 16:45:00,131.83,214.446,59.388000000000005,32.613 +2020-02-06 17:00:00,136.93,216.605,64.462,32.613 +2020-02-06 17:15:00,139.65,218.00400000000002,64.462,32.613 +2020-02-06 17:30:00,141.81,219.679,64.462,32.613 +2020-02-06 17:45:00,143.36,220.06400000000002,64.462,32.613 +2020-02-06 18:00:00,143.08,222.104,65.128,32.613 +2020-02-06 18:15:00,141.76,219.69,65.128,32.613 +2020-02-06 18:30:00,142.88,218.695,65.128,32.613 +2020-02-06 18:45:00,140.14,219.465,65.128,32.613 +2020-02-06 19:00:00,137.45,217.34799999999998,61.316,32.613 +2020-02-06 19:15:00,136.1,213.52,61.316,32.613 +2020-02-06 19:30:00,140.99,210.94400000000002,61.316,32.613 +2020-02-06 19:45:00,140.88,208.235,61.316,32.613 +2020-02-06 20:00:00,130.65,203.713,59.845,32.613 +2020-02-06 20:15:00,123.56,197.295,59.845,32.613 +2020-02-06 20:30:00,119.45,193.192,59.845,32.613 +2020-02-06 20:45:00,118.13,192.05,59.845,32.613 +2020-02-06 21:00:00,110.84,188.26,54.83,32.613 +2020-02-06 21:15:00,117.96,185.175,54.83,32.613 +2020-02-06 21:30:00,117.04,183.16,54.83,32.613 +2020-02-06 21:45:00,112.67,182.015,54.83,32.613 +2020-02-06 22:00:00,101.49,174.704,50.933,32.613 +2020-02-06 22:15:00,101.52,169.43,50.933,32.613 +2020-02-06 22:30:00,97.68,155.55700000000002,50.933,32.613 +2020-02-06 22:45:00,100.63,147.503,50.933,32.613 +2020-02-06 23:00:00,98.2,140.183,45.32899999999999,32.613 +2020-02-06 23:15:00,98.49,138.966,45.32899999999999,32.613 +2020-02-06 23:30:00,93.7,139.555,45.32899999999999,32.613 +2020-02-06 23:45:00,88.78,139.321,45.32899999999999,32.613 +2020-02-07 00:00:00,88.69,132.535,43.74,32.613 +2020-02-07 00:15:00,91.22,132.095,43.74,32.613 +2020-02-07 00:30:00,91.01,132.892,43.74,32.613 +2020-02-07 00:45:00,85.92,134.365,43.74,32.613 +2020-02-07 01:00:00,86.38,136.671,42.555,32.613 +2020-02-07 01:15:00,86.96,137.619,42.555,32.613 +2020-02-07 01:30:00,85.7,137.502,42.555,32.613 +2020-02-07 01:45:00,78.77,137.983,42.555,32.613 +2020-02-07 02:00:00,77.21,140.266,41.68600000000001,32.613 +2020-02-07 02:15:00,78.6,142.107,41.68600000000001,32.613 +2020-02-07 02:30:00,78.82,143.773,41.68600000000001,32.613 +2020-02-07 02:45:00,79.14,145.988,41.68600000000001,32.613 +2020-02-07 03:00:00,80.57,147.57399999999998,42.278999999999996,32.613 +2020-02-07 03:15:00,87.01,150.124,42.278999999999996,32.613 +2020-02-07 03:30:00,88.53,151.899,42.278999999999996,32.613 +2020-02-07 03:45:00,91.75,154.13299999999998,42.278999999999996,32.613 +2020-02-07 04:00:00,81.9,165.515,43.742,32.613 +2020-02-07 04:15:00,81.41,176.655,43.742,32.613 +2020-02-07 04:30:00,83.77,180.39,43.742,32.613 +2020-02-07 04:45:00,85.67,181.968,43.742,32.613 +2020-02-07 05:00:00,89.49,214.108,46.973,32.613 +2020-02-07 05:15:00,90.53,242.95,46.973,32.613 +2020-02-07 05:30:00,95.28,239.739,46.973,32.613 +2020-02-07 05:45:00,99.96,233.467,46.973,32.613 +2020-02-07 06:00:00,110.48,231.317,59.63399999999999,32.613 +2020-02-07 06:15:00,113.13,235.822,59.63399999999999,32.613 +2020-02-07 06:30:00,118.49,237.761,59.63399999999999,32.613 +2020-02-07 06:45:00,122.09,244.732,59.63399999999999,32.613 +2020-02-07 07:00:00,129.43,242.68599999999998,71.631,32.613 +2020-02-07 07:15:00,129.96,248.84,71.631,32.613 +2020-02-07 07:30:00,130.46,251.521,71.631,32.613 +2020-02-07 07:45:00,133.64,251.55700000000002,71.631,32.613 +2020-02-07 08:00:00,137.27,248.618,66.181,32.613 +2020-02-07 08:15:00,133.27,247.94099999999997,66.181,32.613 +2020-02-07 08:30:00,132.95,245.679,66.181,32.613 +2020-02-07 08:45:00,131.08,240.852,66.181,32.613 +2020-02-07 09:00:00,132.18,234.997,63.086000000000006,32.613 +2020-02-07 09:15:00,132.77,232.62599999999998,63.086000000000006,32.613 +2020-02-07 09:30:00,132.22,230.766,63.086000000000006,32.613 +2020-02-07 09:45:00,132.21,227.275,63.086000000000006,32.613 +2020-02-07 10:00:00,132.16,220.571,60.886,32.613 +2020-02-07 10:15:00,134.75,217.90599999999998,60.886,32.613 +2020-02-07 10:30:00,132.78,214.06599999999997,60.886,32.613 +2020-02-07 10:45:00,131.45,212.013,60.886,32.613 +2020-02-07 11:00:00,127.82,208.912,59.391000000000005,32.613 +2020-02-07 11:15:00,128.72,206.90200000000002,59.391000000000005,32.613 +2020-02-07 11:30:00,126.6,207.252,59.391000000000005,32.613 +2020-02-07 11:45:00,125.26,206.785,59.391000000000005,32.613 +2020-02-07 12:00:00,123.4,204.003,56.172,32.613 +2020-02-07 12:15:00,122.69,201.739,56.172,32.613 +2020-02-07 12:30:00,121.83,201.28900000000002,56.172,32.613 +2020-02-07 12:45:00,121.23,202.543,56.172,32.613 +2020-02-07 13:00:00,119.63,201.565,54.406000000000006,32.613 +2020-02-07 13:15:00,119.78,201.37599999999998,54.406000000000006,32.613 +2020-02-07 13:30:00,118.9,200.287,54.406000000000006,32.613 +2020-02-07 13:45:00,118.56,200.28799999999998,54.406000000000006,32.613 +2020-02-07 14:00:00,115.99,199.468,53.578,32.613 +2020-02-07 14:15:00,117.39,199.42700000000002,53.578,32.613 +2020-02-07 14:30:00,118.46,200.57,53.578,32.613 +2020-02-07 14:45:00,121.39,201.94799999999998,53.578,32.613 +2020-02-07 15:00:00,122.71,202.743,56.568999999999996,32.613 +2020-02-07 15:15:00,118.45,202.41299999999998,56.568999999999996,32.613 +2020-02-07 15:30:00,118.49,202.174,56.568999999999996,32.613 +2020-02-07 15:45:00,118.79,203.062,56.568999999999996,32.613 +2020-02-07 16:00:00,120.97,203.285,60.169,32.613 +2020-02-07 16:15:00,123.05,205.308,60.169,32.613 +2020-02-07 16:30:00,121.89,208.31900000000002,60.169,32.613 +2020-02-07 16:45:00,123.97,209.50400000000002,60.169,32.613 +2020-02-07 17:00:00,128.63,211.71099999999998,65.497,32.613 +2020-02-07 17:15:00,131.36,212.703,65.497,32.613 +2020-02-07 17:30:00,135.74,214.044,65.497,32.613 +2020-02-07 17:45:00,136.59,214.21599999999998,65.497,32.613 +2020-02-07 18:00:00,137.38,217.047,65.082,32.613 +2020-02-07 18:15:00,136.53,214.35,65.082,32.613 +2020-02-07 18:30:00,135.49,213.798,65.082,32.613 +2020-02-07 18:45:00,134.69,214.545,65.082,32.613 +2020-02-07 19:00:00,131.86,213.304,60.968,32.613 +2020-02-07 19:15:00,131.03,210.918,60.968,32.613 +2020-02-07 19:30:00,134.97,207.908,60.968,32.613 +2020-02-07 19:45:00,136.65,204.822,60.968,32.613 +2020-02-07 20:00:00,128.29,200.338,61.123000000000005,32.613 +2020-02-07 20:15:00,120.3,193.86,61.123000000000005,32.613 +2020-02-07 20:30:00,116.56,189.77,61.123000000000005,32.613 +2020-02-07 20:45:00,113.11,189.325,61.123000000000005,32.613 +2020-02-07 21:00:00,107.82,185.94299999999998,55.416000000000004,32.613 +2020-02-07 21:15:00,111.91,183.15400000000002,55.416000000000004,32.613 +2020-02-07 21:30:00,109.99,181.205,55.416000000000004,32.613 +2020-02-07 21:45:00,109.63,180.65099999999998,55.416000000000004,32.613 +2020-02-07 22:00:00,99.9,174.41299999999998,51.631,32.613 +2020-02-07 22:15:00,94.38,169.043,51.631,32.613 +2020-02-07 22:30:00,91.58,161.64,51.631,32.613 +2020-02-07 22:45:00,96.88,157.365,51.631,32.613 +2020-02-07 23:00:00,93.8,149.38299999999998,44.898,32.613 +2020-02-07 23:15:00,96.38,146.203,44.898,32.613 +2020-02-07 23:30:00,86.3,145.392,44.898,32.613 +2020-02-07 23:45:00,82.22,144.46200000000002,44.898,32.613 +2020-02-08 00:00:00,76.07,129.066,42.033,32.431999999999995 +2020-02-08 00:15:00,81.2,123.95200000000001,42.033,32.431999999999995 +2020-02-08 00:30:00,83.22,126.271,42.033,32.431999999999995 +2020-02-08 00:45:00,85.24,128.624,42.033,32.431999999999995 +2020-02-08 01:00:00,74.78,131.55700000000002,38.255,32.431999999999995 +2020-02-08 01:15:00,73.72,131.313,38.255,32.431999999999995 +2020-02-08 01:30:00,71.69,130.709,38.255,32.431999999999995 +2020-02-08 01:45:00,80.83,130.78799999999998,38.255,32.431999999999995 +2020-02-08 02:00:00,81.35,133.951,36.404,32.431999999999995 +2020-02-08 02:15:00,79.32,135.47799999999998,36.404,32.431999999999995 +2020-02-08 02:30:00,73.69,136.015,36.404,32.431999999999995 +2020-02-08 02:45:00,72.18,138.24,36.404,32.431999999999995 +2020-02-08 03:00:00,76.73,140.701,36.083,32.431999999999995 +2020-02-08 03:15:00,78.04,141.977,36.083,32.431999999999995 +2020-02-08 03:30:00,75.79,141.916,36.083,32.431999999999995 +2020-02-08 03:45:00,72.95,144.086,36.083,32.431999999999995 +2020-02-08 04:00:00,69.34,151.02,36.102,32.431999999999995 +2020-02-08 04:15:00,69.82,159.40200000000002,36.102,32.431999999999995 +2020-02-08 04:30:00,69.75,160.933,36.102,32.431999999999995 +2020-02-08 04:45:00,73.37,161.901,36.102,32.431999999999995 +2020-02-08 05:00:00,71.6,177.055,35.284,32.431999999999995 +2020-02-08 05:15:00,72.11,185.93599999999998,35.284,32.431999999999995 +2020-02-08 05:30:00,71.73,182.887,35.284,32.431999999999995 +2020-02-08 05:45:00,75.3,182.155,35.284,32.431999999999995 +2020-02-08 06:00:00,74.8,199.732,36.265,32.431999999999995 +2020-02-08 06:15:00,74.36,221.66,36.265,32.431999999999995 +2020-02-08 06:30:00,75.45,217.892,36.265,32.431999999999995 +2020-02-08 06:45:00,77.69,215.15,36.265,32.431999999999995 +2020-02-08 07:00:00,83.57,209.08700000000002,40.714,32.431999999999995 +2020-02-08 07:15:00,87.81,213.976,40.714,32.431999999999995 +2020-02-08 07:30:00,87.5,219.47400000000002,40.714,32.431999999999995 +2020-02-08 07:45:00,88.9,223.83700000000002,40.714,32.431999999999995 +2020-02-08 08:00:00,93.52,225.43400000000003,46.692,32.431999999999995 +2020-02-08 08:15:00,95.5,228.845,46.692,32.431999999999995 +2020-02-08 08:30:00,98.42,228.342,46.692,32.431999999999995 +2020-02-08 08:45:00,100.91,226.93599999999998,46.692,32.431999999999995 +2020-02-08 09:00:00,103.5,222.706,48.925,32.431999999999995 +2020-02-08 09:15:00,103.89,221.12,48.925,32.431999999999995 +2020-02-08 09:30:00,104.75,220.226,48.925,32.431999999999995 +2020-02-08 09:45:00,107.02,216.97299999999998,48.925,32.431999999999995 +2020-02-08 10:00:00,107.36,210.49,47.799,32.431999999999995 +2020-02-08 10:15:00,107.78,207.989,47.799,32.431999999999995 +2020-02-08 10:30:00,108.48,204.386,47.799,32.431999999999995 +2020-02-08 10:45:00,109.57,203.84799999999998,47.799,32.431999999999995 +2020-02-08 11:00:00,108.73,201.024,44.309,32.431999999999995 +2020-02-08 11:15:00,110.59,198.15400000000002,44.309,32.431999999999995 +2020-02-08 11:30:00,111.33,197.233,44.309,32.431999999999995 +2020-02-08 11:45:00,110.89,195.63099999999997,44.309,32.431999999999995 +2020-02-08 12:00:00,108.2,191.81599999999997,42.367,32.431999999999995 +2020-02-08 12:15:00,106.08,190.18,42.367,32.431999999999995 +2020-02-08 12:30:00,103.17,190.074,42.367,32.431999999999995 +2020-02-08 12:45:00,102.67,190.37599999999998,42.367,32.431999999999995 +2020-02-08 13:00:00,98.62,189.06799999999998,39.036,32.431999999999995 +2020-02-08 13:15:00,98.03,186.632,39.036,32.431999999999995 +2020-02-08 13:30:00,95.95,185.00099999999998,39.036,32.431999999999995 +2020-02-08 13:45:00,94.83,185.69799999999998,39.036,32.431999999999995 +2020-02-08 14:00:00,96.32,186.321,37.995,32.431999999999995 +2020-02-08 14:15:00,93.19,185.817,37.995,32.431999999999995 +2020-02-08 14:30:00,93.21,184.97400000000002,37.995,32.431999999999995 +2020-02-08 14:45:00,93.07,186.553,37.995,32.431999999999995 +2020-02-08 15:00:00,92.13,188.085,40.71,32.431999999999995 +2020-02-08 15:15:00,92.1,188.537,40.71,32.431999999999995 +2020-02-08 15:30:00,93.08,189.947,40.71,32.431999999999995 +2020-02-08 15:45:00,94.41,190.942,40.71,32.431999999999995 +2020-02-08 16:00:00,93.8,189.675,46.998000000000005,32.431999999999995 +2020-02-08 16:15:00,94.73,192.775,46.998000000000005,32.431999999999995 +2020-02-08 16:30:00,96.17,195.707,46.998000000000005,32.431999999999995 +2020-02-08 16:45:00,97.61,197.858,46.998000000000005,32.431999999999995 +2020-02-08 17:00:00,102.44,199.639,55.431000000000004,32.431999999999995 +2020-02-08 17:15:00,103.98,202.69299999999998,55.431000000000004,32.431999999999995 +2020-02-08 17:30:00,110.41,203.977,55.431000000000004,32.431999999999995 +2020-02-08 17:45:00,109.38,203.68,55.431000000000004,32.431999999999995 +2020-02-08 18:00:00,111.7,205.91099999999997,55.989,32.431999999999995 +2020-02-08 18:15:00,111.45,205.063,55.989,32.431999999999995 +2020-02-08 18:30:00,109.88,205.833,55.989,32.431999999999995 +2020-02-08 18:45:00,108.49,203.294,55.989,32.431999999999995 +2020-02-08 19:00:00,107.67,203.201,50.882,32.431999999999995 +2020-02-08 19:15:00,106.93,200.38299999999998,50.882,32.431999999999995 +2020-02-08 19:30:00,104.88,198.138,50.882,32.431999999999995 +2020-02-08 19:45:00,102.39,194.731,50.882,32.431999999999995 +2020-02-08 20:00:00,98.25,192.463,43.172,32.431999999999995 +2020-02-08 20:15:00,94.38,188.37400000000002,43.172,32.431999999999995 +2020-02-08 20:30:00,91.97,183.99599999999998,43.172,32.431999999999995 +2020-02-08 20:45:00,89.93,182.93900000000002,43.172,32.431999999999995 +2020-02-08 21:00:00,85.08,182.136,37.599000000000004,32.431999999999995 +2020-02-08 21:15:00,83.97,179.845,37.599000000000004,32.431999999999995 +2020-02-08 21:30:00,82.71,179.21599999999998,37.599000000000004,32.431999999999995 +2020-02-08 21:45:00,82.15,178.28900000000002,37.599000000000004,32.431999999999995 +2020-02-08 22:00:00,77.71,173.505,39.047,32.431999999999995 +2020-02-08 22:15:00,77.18,170.838,39.047,32.431999999999995 +2020-02-08 22:30:00,73.7,170.201,39.047,32.431999999999995 +2020-02-08 22:45:00,72.2,167.93,39.047,32.431999999999995 +2020-02-08 23:00:00,69.4,162.638,32.339,32.431999999999995 +2020-02-08 23:15:00,68.66,157.657,32.339,32.431999999999995 +2020-02-08 23:30:00,65.61,154.733,32.339,32.431999999999995 +2020-02-08 23:45:00,64.73,151.15200000000002,32.339,32.431999999999995 +2020-02-09 00:00:00,61.27,129.226,29.988000000000003,32.431999999999995 +2020-02-09 00:15:00,60.71,123.889,29.988000000000003,32.431999999999995 +2020-02-09 00:30:00,59.99,125.789,29.988000000000003,32.431999999999995 +2020-02-09 00:45:00,59.14,128.922,29.988000000000003,32.431999999999995 +2020-02-09 01:00:00,57.53,131.635,28.531999999999996,32.431999999999995 +2020-02-09 01:15:00,56.93,132.55,28.531999999999996,32.431999999999995 +2020-02-09 01:30:00,57.32,132.533,28.531999999999996,32.431999999999995 +2020-02-09 01:45:00,57.14,132.30700000000002,28.531999999999996,32.431999999999995 +2020-02-09 02:00:00,56.24,134.63,27.805999999999997,32.431999999999995 +2020-02-09 02:15:00,57.09,135.11700000000002,27.805999999999997,32.431999999999995 +2020-02-09 02:30:00,56.53,136.59799999999998,27.805999999999997,32.431999999999995 +2020-02-09 02:45:00,54.95,139.373,27.805999999999997,32.431999999999995 +2020-02-09 03:00:00,54.55,142.111,26.193,32.431999999999995 +2020-02-09 03:15:00,55.28,142.769,26.193,32.431999999999995 +2020-02-09 03:30:00,55.8,144.366,26.193,32.431999999999995 +2020-02-09 03:45:00,56.3,146.563,26.193,32.431999999999995 +2020-02-09 04:00:00,56.5,153.233,27.19,32.431999999999995 +2020-02-09 04:15:00,57.24,160.52700000000002,27.19,32.431999999999995 +2020-02-09 04:30:00,57.91,161.968,27.19,32.431999999999995 +2020-02-09 04:45:00,57.79,163.293,27.19,32.431999999999995 +2020-02-09 05:00:00,58.77,174.454,28.166999999999998,32.431999999999995 +2020-02-09 05:15:00,58.46,180.73,28.166999999999998,32.431999999999995 +2020-02-09 05:30:00,60.97,177.52599999999998,28.166999999999998,32.431999999999995 +2020-02-09 05:45:00,62.39,177.112,28.166999999999998,32.431999999999995 +2020-02-09 06:00:00,60.88,194.896,27.16,32.431999999999995 +2020-02-09 06:15:00,63.92,214.793,27.16,32.431999999999995 +2020-02-09 06:30:00,65.05,209.84799999999998,27.16,32.431999999999995 +2020-02-09 06:45:00,63.69,206.051,27.16,32.431999999999995 +2020-02-09 07:00:00,68.95,202.731,29.578000000000003,32.431999999999995 +2020-02-09 07:15:00,68.37,206.84900000000002,29.578000000000003,32.431999999999995 +2020-02-09 07:30:00,70.21,210.766,29.578000000000003,32.431999999999995 +2020-02-09 07:45:00,72.49,214.237,29.578000000000003,32.431999999999995 +2020-02-09 08:00:00,76.04,217.799,34.650999999999996,32.431999999999995 +2020-02-09 08:15:00,76.96,220.94,34.650999999999996,32.431999999999995 +2020-02-09 08:30:00,77.09,222.12400000000002,34.650999999999996,32.431999999999995 +2020-02-09 08:45:00,77.53,222.98,34.650999999999996,32.431999999999995 +2020-02-09 09:00:00,79.01,218.326,38.080999999999996,32.431999999999995 +2020-02-09 09:15:00,78.77,217.41299999999998,38.080999999999996,32.431999999999995 +2020-02-09 09:30:00,75.58,216.317,38.080999999999996,32.431999999999995 +2020-02-09 09:45:00,79.56,212.81599999999997,38.080999999999996,32.431999999999995 +2020-02-09 10:00:00,81.3,209.075,39.934,32.431999999999995 +2020-02-09 10:15:00,76.81,207.17700000000002,39.934,32.431999999999995 +2020-02-09 10:30:00,79.12,204.226,39.934,32.431999999999995 +2020-02-09 10:45:00,81.4,201.47099999999998,39.934,32.431999999999995 +2020-02-09 11:00:00,84.98,199.705,43.74100000000001,32.431999999999995 +2020-02-09 11:15:00,91.65,197.043,43.74100000000001,32.431999999999995 +2020-02-09 11:30:00,93.87,195.105,43.74100000000001,32.431999999999995 +2020-02-09 11:45:00,94.14,194.15599999999998,43.74100000000001,32.431999999999995 +2020-02-09 12:00:00,89.58,189.606,40.001999999999995,32.431999999999995 +2020-02-09 12:15:00,87.22,190.168,40.001999999999995,32.431999999999995 +2020-02-09 12:30:00,81.41,188.4,40.001999999999995,32.431999999999995 +2020-02-09 12:45:00,83.91,187.699,40.001999999999995,32.431999999999995 +2020-02-09 13:00:00,83.42,185.649,37.855,32.431999999999995 +2020-02-09 13:15:00,83.34,186.597,37.855,32.431999999999995 +2020-02-09 13:30:00,83.38,184.843,37.855,32.431999999999995 +2020-02-09 13:45:00,83.48,184.68400000000003,37.855,32.431999999999995 +2020-02-09 14:00:00,82.03,185.49900000000002,35.946999999999996,32.431999999999995 +2020-02-09 14:15:00,81.11,186.253,35.946999999999996,32.431999999999995 +2020-02-09 14:30:00,80.88,186.899,35.946999999999996,32.431999999999995 +2020-02-09 14:45:00,80.8,188.155,35.946999999999996,32.431999999999995 +2020-02-09 15:00:00,80.61,187.985,35.138000000000005,32.431999999999995 +2020-02-09 15:15:00,81.01,189.326,35.138000000000005,32.431999999999995 +2020-02-09 15:30:00,81.24,191.368,35.138000000000005,32.431999999999995 +2020-02-09 15:45:00,82.18,193.079,35.138000000000005,32.431999999999995 +2020-02-09 16:00:00,84.12,194.005,38.672,32.431999999999995 +2020-02-09 16:15:00,82.75,196.077,38.672,32.431999999999995 +2020-02-09 16:30:00,83.87,199.19400000000002,38.672,32.431999999999995 +2020-02-09 16:45:00,85.94,201.47099999999998,38.672,32.431999999999995 +2020-02-09 17:00:00,91.05,203.174,48.684,32.431999999999995 +2020-02-09 17:15:00,91.88,205.752,48.684,32.431999999999995 +2020-02-09 17:30:00,98.58,207.305,48.684,32.431999999999995 +2020-02-09 17:45:00,98.25,209.468,48.684,32.431999999999995 +2020-02-09 18:00:00,103.28,211.042,51.568999999999996,32.431999999999995 +2020-02-09 18:15:00,100.42,211.72099999999998,51.568999999999996,32.431999999999995 +2020-02-09 18:30:00,104.15,210.25400000000002,51.568999999999996,32.431999999999995 +2020-02-09 18:45:00,100.37,209.738,51.568999999999996,32.431999999999995 +2020-02-09 19:00:00,98.81,209.03599999999997,48.608000000000004,32.431999999999995 +2020-02-09 19:15:00,97.23,206.96599999999998,48.608000000000004,32.431999999999995 +2020-02-09 19:30:00,95.94,204.58599999999998,48.608000000000004,32.431999999999995 +2020-02-09 19:45:00,94.87,202.80900000000003,48.608000000000004,32.431999999999995 +2020-02-09 20:00:00,91.94,200.498,43.733999999999995,32.431999999999995 +2020-02-09 20:15:00,91.17,197.502,43.733999999999995,32.431999999999995 +2020-02-09 20:30:00,89.34,194.40599999999998,43.733999999999995,32.431999999999995 +2020-02-09 20:45:00,90.36,192.203,43.733999999999995,32.431999999999995 +2020-02-09 21:00:00,83.75,188.577,39.283,32.431999999999995 +2020-02-09 21:15:00,84.63,185.62,39.283,32.431999999999995 +2020-02-09 21:30:00,84.54,185.36900000000003,39.283,32.431999999999995 +2020-02-09 21:45:00,87.87,184.575,39.283,32.431999999999995 +2020-02-09 22:00:00,86.37,178.296,40.111,32.431999999999995 +2020-02-09 22:15:00,84.1,174.982,40.111,32.431999999999995 +2020-02-09 22:30:00,80.59,170.921,40.111,32.431999999999995 +2020-02-09 22:45:00,80.15,167.84900000000002,40.111,32.431999999999995 +2020-02-09 23:00:00,76.59,159.545,35.791,32.431999999999995 +2020-02-09 23:15:00,78.67,156.477,35.791,32.431999999999995 +2020-02-09 23:30:00,75.47,154.495,35.791,32.431999999999995 +2020-02-09 23:45:00,74.68,151.886,35.791,32.431999999999995 +2020-02-10 00:00:00,69.25,133.565,34.311,32.613 +2020-02-10 00:15:00,70.25,131.43200000000002,34.311,32.613 +2020-02-10 00:30:00,70.75,133.498,34.311,32.613 +2020-02-10 00:45:00,70.63,136.07399999999998,34.311,32.613 +2020-02-10 01:00:00,69.11,138.743,34.585,32.613 +2020-02-10 01:15:00,68.52,139.054,34.585,32.613 +2020-02-10 01:30:00,68.82,139.04399999999998,34.585,32.613 +2020-02-10 01:45:00,69.91,138.957,34.585,32.613 +2020-02-10 02:00:00,68.03,141.208,34.111,32.613 +2020-02-10 02:15:00,68.74,143.455,34.111,32.613 +2020-02-10 02:30:00,68.09,145.303,34.111,32.613 +2020-02-10 02:45:00,68.85,147.40200000000002,34.111,32.613 +2020-02-10 03:00:00,67.44,151.52200000000002,32.435,32.613 +2020-02-10 03:15:00,68.32,153.937,32.435,32.613 +2020-02-10 03:30:00,69.58,155.138,32.435,32.613 +2020-02-10 03:45:00,70.89,156.787,32.435,32.613 +2020-02-10 04:00:00,71.36,167.96099999999998,33.04,32.613 +2020-02-10 04:15:00,73.21,179.521,33.04,32.613 +2020-02-10 04:30:00,75.25,183.50900000000001,33.04,32.613 +2020-02-10 04:45:00,77.06,184.954,33.04,32.613 +2020-02-10 05:00:00,81.16,212.946,40.399,32.613 +2020-02-10 05:15:00,83.71,240.581,40.399,32.613 +2020-02-10 05:30:00,88.12,237.87599999999998,40.399,32.613 +2020-02-10 05:45:00,91.96,231.50799999999998,40.399,32.613 +2020-02-10 06:00:00,100.85,230.446,60.226000000000006,32.613 +2020-02-10 06:15:00,104.58,234.87099999999998,60.226000000000006,32.613 +2020-02-10 06:30:00,109.61,238.394,60.226000000000006,32.613 +2020-02-10 06:45:00,114.01,243.798,60.226000000000006,32.613 +2020-02-10 07:00:00,123.45,243.1,73.578,32.613 +2020-02-10 07:15:00,122.16,248.285,73.578,32.613 +2020-02-10 07:30:00,126.33,251.41,73.578,32.613 +2020-02-10 07:45:00,124.92,251.91099999999997,73.578,32.613 +2020-02-10 08:00:00,128.37,250.143,66.58,32.613 +2020-02-10 08:15:00,127.45,251.013,66.58,32.613 +2020-02-10 08:30:00,127.24,247.687,66.58,32.613 +2020-02-10 08:45:00,126.73,244.77900000000002,66.58,32.613 +2020-02-10 09:00:00,128.93,239.095,62.0,32.613 +2020-02-10 09:15:00,132.12,234.521,62.0,32.613 +2020-02-10 09:30:00,129.88,232.447,62.0,32.613 +2020-02-10 09:45:00,132.17,229.562,62.0,32.613 +2020-02-10 10:00:00,131.38,224.611,59.099,32.613 +2020-02-10 10:15:00,127.86,222.393,59.099,32.613 +2020-02-10 10:30:00,126.07,218.533,59.099,32.613 +2020-02-10 10:45:00,129.64,216.829,59.099,32.613 +2020-02-10 11:00:00,130.68,212.051,57.729,32.613 +2020-02-10 11:15:00,131.96,211.373,57.729,32.613 +2020-02-10 11:30:00,132.31,210.891,57.729,32.613 +2020-02-10 11:45:00,136.23,209.438,57.729,32.613 +2020-02-10 12:00:00,131.68,207.05599999999998,55.615,32.613 +2020-02-10 12:15:00,132.92,207.62,55.615,32.613 +2020-02-10 12:30:00,134.19,206.238,55.615,32.613 +2020-02-10 12:45:00,129.29,207.23,55.615,32.613 +2020-02-10 13:00:00,125.98,205.753,56.515,32.613 +2020-02-10 13:15:00,124.66,205.24,56.515,32.613 +2020-02-10 13:30:00,124.24,202.86,56.515,32.613 +2020-02-10 13:45:00,128.1,202.62900000000002,56.515,32.613 +2020-02-10 14:00:00,124.97,202.88400000000001,58.1,32.613 +2020-02-10 14:15:00,125.5,202.832,58.1,32.613 +2020-02-10 14:30:00,126.78,202.88299999999998,58.1,32.613 +2020-02-10 14:45:00,128.81,203.86900000000003,58.1,32.613 +2020-02-10 15:00:00,129.17,205.675,59.801,32.613 +2020-02-10 15:15:00,129.48,205.46599999999998,59.801,32.613 +2020-02-10 15:30:00,124.6,206.46400000000003,59.801,32.613 +2020-02-10 15:45:00,123.59,207.72099999999998,59.801,32.613 +2020-02-10 16:00:00,123.44,208.65,62.901,32.613 +2020-02-10 16:15:00,123.28,209.86700000000002,62.901,32.613 +2020-02-10 16:30:00,122.68,211.997,62.901,32.613 +2020-02-10 16:45:00,123.82,212.947,62.901,32.613 +2020-02-10 17:00:00,128.52,214.516,70.418,32.613 +2020-02-10 17:15:00,127.46,216.03799999999998,70.418,32.613 +2020-02-10 17:30:00,135.21,217.065,70.418,32.613 +2020-02-10 17:45:00,135.18,217.66099999999997,70.418,32.613 +2020-02-10 18:00:00,137.86,219.78099999999998,71.726,32.613 +2020-02-10 18:15:00,135.1,218.28900000000002,71.726,32.613 +2020-02-10 18:30:00,134.0,217.61,71.726,32.613 +2020-02-10 18:45:00,133.93,217.583,71.726,32.613 +2020-02-10 19:00:00,132.08,215.093,65.997,32.613 +2020-02-10 19:15:00,130.65,211.641,65.997,32.613 +2020-02-10 19:30:00,130.87,209.857,65.997,32.613 +2020-02-10 19:45:00,134.33,207.269,65.997,32.613 +2020-02-10 20:00:00,125.69,202.49400000000003,68.09100000000001,32.613 +2020-02-10 20:15:00,123.18,196.667,68.09100000000001,32.613 +2020-02-10 20:30:00,113.96,191.49900000000002,68.09100000000001,32.613 +2020-02-10 20:45:00,114.18,191.053,68.09100000000001,32.613 +2020-02-10 21:00:00,108.98,188.05599999999998,59.617,32.613 +2020-02-10 21:15:00,111.78,183.76,59.617,32.613 +2020-02-10 21:30:00,113.19,182.56599999999997,59.617,32.613 +2020-02-10 21:45:00,110.69,181.261,59.617,32.613 +2020-02-10 22:00:00,98.01,172.03099999999998,54.938,32.613 +2020-02-10 22:15:00,97.21,167.104,54.938,32.613 +2020-02-10 22:30:00,96.16,153.02700000000002,54.938,32.613 +2020-02-10 22:45:00,99.73,144.695,54.938,32.613 +2020-02-10 23:00:00,94.89,137.238,47.43,32.613 +2020-02-10 23:15:00,95.68,137.238,47.43,32.613 +2020-02-10 23:30:00,87.21,138.285,47.43,32.613 +2020-02-10 23:45:00,87.28,138.628,47.43,32.613 +2020-02-11 00:00:00,86.73,132.929,48.354,32.613 +2020-02-11 00:15:00,86.67,132.27200000000002,48.354,32.613 +2020-02-11 00:30:00,84.13,133.178,48.354,32.613 +2020-02-11 00:45:00,83.69,134.55200000000002,48.354,32.613 +2020-02-11 01:00:00,83.21,137.034,45.68600000000001,32.613 +2020-02-11 01:15:00,84.29,136.819,45.68600000000001,32.613 +2020-02-11 01:30:00,79.62,137.00799999999998,45.68600000000001,32.613 +2020-02-11 01:45:00,77.48,137.332,45.68600000000001,32.613 +2020-02-11 02:00:00,82.18,139.641,44.269,32.613 +2020-02-11 02:15:00,83.97,141.60399999999998,44.269,32.613 +2020-02-11 02:30:00,82.96,142.84799999999998,44.269,32.613 +2020-02-11 02:45:00,77.71,144.915,44.269,32.613 +2020-02-11 03:00:00,76.55,147.748,44.187,32.613 +2020-02-11 03:15:00,81.43,149.071,44.187,32.613 +2020-02-11 03:30:00,85.46,150.81799999999998,44.187,32.613 +2020-02-11 03:45:00,86.18,152.856,44.187,32.613 +2020-02-11 04:00:00,84.27,163.98,46.126999999999995,32.613 +2020-02-11 04:15:00,84.94,175.13400000000001,46.126999999999995,32.613 +2020-02-11 04:30:00,88.91,178.803,46.126999999999995,32.613 +2020-02-11 04:45:00,90.77,181.537,46.126999999999995,32.613 +2020-02-11 05:00:00,89.12,214.968,49.666000000000004,32.613 +2020-02-11 05:15:00,89.51,242.28799999999998,49.666000000000004,32.613 +2020-02-11 05:30:00,91.15,237.78400000000002,49.666000000000004,32.613 +2020-02-11 05:45:00,95.27,231.54,49.666000000000004,32.613 +2020-02-11 06:00:00,104.24,228.979,61.077,32.613 +2020-02-11 06:15:00,107.46,235.239,61.077,32.613 +2020-02-11 06:30:00,112.26,238.045,61.077,32.613 +2020-02-11 06:45:00,115.29,243.15599999999998,61.077,32.613 +2020-02-11 07:00:00,122.79,242.25,74.717,32.613 +2020-02-11 07:15:00,123.23,247.265,74.717,32.613 +2020-02-11 07:30:00,126.38,249.733,74.717,32.613 +2020-02-11 07:45:00,126.0,250.583,74.717,32.613 +2020-02-11 08:00:00,128.06,248.925,69.033,32.613 +2020-02-11 08:15:00,126.93,248.678,69.033,32.613 +2020-02-11 08:30:00,130.01,245.06900000000002,69.033,32.613 +2020-02-11 08:45:00,126.16,241.99200000000002,69.033,32.613 +2020-02-11 09:00:00,127.51,235.327,63.113,32.613 +2020-02-11 09:15:00,129.21,232.574,63.113,32.613 +2020-02-11 09:30:00,129.61,231.18599999999998,63.113,32.613 +2020-02-11 09:45:00,128.56,227.87099999999998,63.113,32.613 +2020-02-11 10:00:00,125.66,222.46099999999998,61.461999999999996,32.613 +2020-02-11 10:15:00,124.7,219.09099999999998,61.461999999999996,32.613 +2020-02-11 10:30:00,123.35,215.44,61.461999999999996,32.613 +2020-02-11 10:45:00,125.93,213.925,61.461999999999996,32.613 +2020-02-11 11:00:00,119.21,210.815,59.614,32.613 +2020-02-11 11:15:00,119.93,209.72,59.614,32.613 +2020-02-11 11:30:00,119.99,208.093,59.614,32.613 +2020-02-11 11:45:00,121.04,207.5,59.614,32.613 +2020-02-11 12:00:00,115.58,203.62099999999998,57.415,32.613 +2020-02-11 12:15:00,116.2,203.688,57.415,32.613 +2020-02-11 12:30:00,121.86,202.972,57.415,32.613 +2020-02-11 12:45:00,119.33,203.535,57.415,32.613 +2020-02-11 13:00:00,115.97,201.671,58.534,32.613 +2020-02-11 13:15:00,115.16,200.493,58.534,32.613 +2020-02-11 13:30:00,116.39,199.42,58.534,32.613 +2020-02-11 13:45:00,118.38,199.53400000000002,58.534,32.613 +2020-02-11 14:00:00,114.61,200.108,59.415,32.613 +2020-02-11 14:15:00,116.91,200.239,59.415,32.613 +2020-02-11 14:30:00,117.94,200.933,59.415,32.613 +2020-02-11 14:45:00,120.5,201.953,59.415,32.613 +2020-02-11 15:00:00,122.94,203.31099999999998,62.071999999999996,32.613 +2020-02-11 15:15:00,119.07,203.321,62.071999999999996,32.613 +2020-02-11 15:30:00,121.84,204.55900000000003,62.071999999999996,32.613 +2020-02-11 15:45:00,120.06,205.305,62.071999999999996,32.613 +2020-02-11 16:00:00,122.44,206.71400000000003,64.99,32.613 +2020-02-11 16:15:00,120.88,208.44099999999997,64.99,32.613 +2020-02-11 16:30:00,120.1,211.33900000000003,64.99,32.613 +2020-02-11 16:45:00,121.9,212.503,64.99,32.613 +2020-02-11 17:00:00,126.56,214.648,72.658,32.613 +2020-02-11 17:15:00,130.27,216.179,72.658,32.613 +2020-02-11 17:30:00,133.97,218.02700000000002,72.658,32.613 +2020-02-11 17:45:00,133.32,218.545,72.658,32.613 +2020-02-11 18:00:00,135.96,220.662,73.645,32.613 +2020-02-11 18:15:00,134.35,218.513,73.645,32.613 +2020-02-11 18:30:00,137.41,217.52700000000002,73.645,32.613 +2020-02-11 18:45:00,134.9,218.418,73.645,32.613 +2020-02-11 19:00:00,133.28,216.093,67.085,32.613 +2020-02-11 19:15:00,131.07,212.33,67.085,32.613 +2020-02-11 19:30:00,128.99,209.861,67.085,32.613 +2020-02-11 19:45:00,130.79,207.312,67.085,32.613 +2020-02-11 20:00:00,123.69,202.644,66.138,32.613 +2020-02-11 20:15:00,117.11,196.28599999999997,66.138,32.613 +2020-02-11 20:30:00,115.68,192.236,66.138,32.613 +2020-02-11 20:45:00,116.21,191.12,66.138,32.613 +2020-02-11 21:00:00,108.07,187.245,57.512,32.613 +2020-02-11 21:15:00,113.35,184.08900000000003,57.512,32.613 +2020-02-11 21:30:00,113.57,182.076,57.512,32.613 +2020-02-11 21:45:00,109.47,181.015,57.512,32.613 +2020-02-11 22:00:00,100.17,173.62599999999998,54.545,32.613 +2020-02-11 22:15:00,99.34,168.476,54.545,32.613 +2020-02-11 22:30:00,95.56,154.42700000000002,54.545,32.613 +2020-02-11 22:45:00,96.63,146.406,54.545,32.613 +2020-02-11 23:00:00,97.75,139.053,48.605,32.613 +2020-02-11 23:15:00,94.85,137.909,48.605,32.613 +2020-02-11 23:30:00,89.6,138.576,48.605,32.613 +2020-02-11 23:45:00,87.08,138.44,48.605,32.613 +2020-02-12 00:00:00,83.69,132.765,45.675,32.613 +2020-02-12 00:15:00,81.35,132.10399999999998,45.675,32.613 +2020-02-12 00:30:00,81.62,132.99,45.675,32.613 +2020-02-12 00:45:00,87.28,134.359,45.675,32.613 +2020-02-12 01:00:00,85.43,136.80700000000002,43.015,32.613 +2020-02-12 01:15:00,86.31,136.579,43.015,32.613 +2020-02-12 01:30:00,81.81,136.75799999999998,43.015,32.613 +2020-02-12 01:45:00,85.94,137.08,43.015,32.613 +2020-02-12 02:00:00,84.66,139.388,41.0,32.613 +2020-02-12 02:15:00,84.18,141.351,41.0,32.613 +2020-02-12 02:30:00,80.13,142.608,41.0,32.613 +2020-02-12 02:45:00,89.1,144.674,41.0,32.613 +2020-02-12 03:00:00,85.93,147.511,41.318000000000005,32.613 +2020-02-12 03:15:00,86.61,148.835,41.318000000000005,32.613 +2020-02-12 03:30:00,84.88,150.577,41.318000000000005,32.613 +2020-02-12 03:45:00,88.19,152.627,41.318000000000005,32.613 +2020-02-12 04:00:00,87.93,163.749,42.544,32.613 +2020-02-12 04:15:00,84.22,174.896,42.544,32.613 +2020-02-12 04:30:00,85.75,178.579,42.544,32.613 +2020-02-12 04:45:00,92.54,181.30200000000002,42.544,32.613 +2020-02-12 05:00:00,97.5,214.71400000000003,45.161,32.613 +2020-02-12 05:15:00,96.39,242.053,45.161,32.613 +2020-02-12 05:30:00,93.69,237.52200000000002,45.161,32.613 +2020-02-12 05:45:00,97.9,231.282,45.161,32.613 +2020-02-12 06:00:00,105.8,228.73,61.86600000000001,32.613 +2020-02-12 06:15:00,112.45,235.005,61.86600000000001,32.613 +2020-02-12 06:30:00,115.07,237.782,61.86600000000001,32.613 +2020-02-12 06:45:00,119.42,242.891,61.86600000000001,32.613 +2020-02-12 07:00:00,125.77,242.01,77.814,32.613 +2020-02-12 07:15:00,125.17,246.997,77.814,32.613 +2020-02-12 07:30:00,125.81,249.429,77.814,32.613 +2020-02-12 07:45:00,127.26,250.237,77.814,32.613 +2020-02-12 08:00:00,129.7,248.55900000000003,70.251,32.613 +2020-02-12 08:15:00,127.18,248.287,70.251,32.613 +2020-02-12 08:30:00,128.56,244.618,70.251,32.613 +2020-02-12 08:45:00,125.84,241.541,70.251,32.613 +2020-02-12 09:00:00,125.04,234.88099999999997,66.965,32.613 +2020-02-12 09:15:00,127.84,232.13,66.965,32.613 +2020-02-12 09:30:00,126.14,230.763,66.965,32.613 +2020-02-12 09:45:00,126.04,227.449,66.965,32.613 +2020-02-12 10:00:00,123.44,222.047,63.628,32.613 +2020-02-12 10:15:00,124.2,218.708,63.628,32.613 +2020-02-12 10:30:00,123.23,215.067,63.628,32.613 +2020-02-12 10:45:00,121.64,213.56599999999997,63.628,32.613 +2020-02-12 11:00:00,120.34,210.44400000000002,62.516999999999996,32.613 +2020-02-12 11:15:00,122.82,209.362,62.516999999999996,32.613 +2020-02-12 11:30:00,118.97,207.74099999999999,62.516999999999996,32.613 +2020-02-12 11:45:00,119.71,207.16,62.516999999999996,32.613 +2020-02-12 12:00:00,116.2,203.298,60.888999999999996,32.613 +2020-02-12 12:15:00,117.0,203.38,60.888999999999996,32.613 +2020-02-12 12:30:00,116.14,202.638,60.888999999999996,32.613 +2020-02-12 12:45:00,117.32,203.196,60.888999999999996,32.613 +2020-02-12 13:00:00,114.38,201.357,61.57899999999999,32.613 +2020-02-12 13:15:00,115.3,200.15400000000002,61.57899999999999,32.613 +2020-02-12 13:30:00,113.12,199.065,61.57899999999999,32.613 +2020-02-12 13:45:00,117.39,199.18,61.57899999999999,32.613 +2020-02-12 14:00:00,117.12,199.81,62.602,32.613 +2020-02-12 14:15:00,116.5,199.92,62.602,32.613 +2020-02-12 14:30:00,117.79,200.59799999999998,62.602,32.613 +2020-02-12 14:45:00,117.74,201.63299999999998,62.602,32.613 +2020-02-12 15:00:00,118.94,202.998,64.259,32.613 +2020-02-12 15:15:00,120.51,202.979,64.259,32.613 +2020-02-12 15:30:00,117.33,204.179,64.259,32.613 +2020-02-12 15:45:00,117.84,204.908,64.259,32.613 +2020-02-12 16:00:00,118.53,206.317,67.632,32.613 +2020-02-12 16:15:00,121.72,208.03400000000002,67.632,32.613 +2020-02-12 16:30:00,120.12,210.935,67.632,32.613 +2020-02-12 16:45:00,121.93,212.081,67.632,32.613 +2020-02-12 17:00:00,125.32,214.22400000000002,72.583,32.613 +2020-02-12 17:15:00,126.29,215.782,72.583,32.613 +2020-02-12 17:30:00,131.21,217.66400000000002,72.583,32.613 +2020-02-12 17:45:00,134.28,218.209,72.583,32.613 +2020-02-12 18:00:00,137.59,220.34099999999998,72.744,32.613 +2020-02-12 18:15:00,135.78,218.24900000000002,72.744,32.613 +2020-02-12 18:30:00,137.74,217.264,72.744,32.613 +2020-02-12 18:45:00,138.12,218.18099999999998,72.744,32.613 +2020-02-12 19:00:00,134.84,215.812,69.684,32.613 +2020-02-12 19:15:00,131.69,212.063,69.684,32.613 +2020-02-12 19:30:00,130.22,209.61700000000002,69.684,32.613 +2020-02-12 19:45:00,132.79,207.10299999999998,69.684,32.613 +2020-02-12 20:00:00,122.05,202.40400000000002,70.036,32.613 +2020-02-12 20:15:00,117.75,196.06,70.036,32.613 +2020-02-12 20:30:00,118.3,192.02200000000002,70.036,32.613 +2020-02-12 20:45:00,115.96,190.91099999999997,70.036,32.613 +2020-02-12 21:00:00,112.05,187.018,60.431999999999995,32.613 +2020-02-12 21:15:00,114.02,183.84900000000002,60.431999999999995,32.613 +2020-02-12 21:30:00,113.2,181.83599999999998,60.431999999999995,32.613 +2020-02-12 21:45:00,108.74,180.792,60.431999999999995,32.613 +2020-02-12 22:00:00,99.68,173.387,56.2,32.613 +2020-02-12 22:15:00,100.84,168.261,56.2,32.613 +2020-02-12 22:30:00,105.11,154.175,56.2,32.613 +2020-02-12 22:45:00,104.72,146.158,56.2,32.613 +2020-02-12 23:00:00,97.34,138.80100000000002,47.927,32.613 +2020-02-12 23:15:00,89.87,137.672,47.927,32.613 +2020-02-12 23:30:00,93.4,138.35399999999998,47.927,32.613 +2020-02-12 23:45:00,93.96,138.24,47.927,32.613 +2020-02-13 00:00:00,89.19,132.594,43.794,32.613 +2020-02-13 00:15:00,88.82,131.93,43.794,32.613 +2020-02-13 00:30:00,89.62,132.795,43.794,32.613 +2020-02-13 00:45:00,88.21,134.158,43.794,32.613 +2020-02-13 01:00:00,82.63,136.571,42.397,32.613 +2020-02-13 01:15:00,82.36,136.33,42.397,32.613 +2020-02-13 01:30:00,84.87,136.498,42.397,32.613 +2020-02-13 01:45:00,85.58,136.821,42.397,32.613 +2020-02-13 02:00:00,81.21,139.126,40.010999999999996,32.613 +2020-02-13 02:15:00,80.58,141.09,40.010999999999996,32.613 +2020-02-13 02:30:00,84.33,142.36,40.010999999999996,32.613 +2020-02-13 02:45:00,85.66,144.425,40.010999999999996,32.613 +2020-02-13 03:00:00,84.48,147.266,39.181,32.613 +2020-02-13 03:15:00,84.82,148.589,39.181,32.613 +2020-02-13 03:30:00,87.48,150.329,39.181,32.613 +2020-02-13 03:45:00,87.5,152.391,39.181,32.613 +2020-02-13 04:00:00,84.29,163.512,40.39,32.613 +2020-02-13 04:15:00,82.01,174.649,40.39,32.613 +2020-02-13 04:30:00,82.76,178.34900000000002,40.39,32.613 +2020-02-13 04:45:00,84.79,181.05900000000003,40.39,32.613 +2020-02-13 05:00:00,88.04,214.454,45.504,32.613 +2020-02-13 05:15:00,91.08,241.812,45.504,32.613 +2020-02-13 05:30:00,93.75,237.25400000000002,45.504,32.613 +2020-02-13 05:45:00,99.4,231.018,45.504,32.613 +2020-02-13 06:00:00,105.36,228.475,57.748000000000005,32.613 +2020-02-13 06:15:00,108.99,234.764,57.748000000000005,32.613 +2020-02-13 06:30:00,113.77,237.51,57.748000000000005,32.613 +2020-02-13 06:45:00,118.6,242.61700000000002,57.748000000000005,32.613 +2020-02-13 07:00:00,125.32,241.76,72.138,32.613 +2020-02-13 07:15:00,124.14,246.71900000000002,72.138,32.613 +2020-02-13 07:30:00,127.06,249.11700000000002,72.138,32.613 +2020-02-13 07:45:00,125.97,249.882,72.138,32.613 +2020-02-13 08:00:00,128.01,248.18200000000002,65.542,32.613 +2020-02-13 08:15:00,126.31,247.885,65.542,32.613 +2020-02-13 08:30:00,125.65,244.155,65.542,32.613 +2020-02-13 08:45:00,126.67,241.079,65.542,32.613 +2020-02-13 09:00:00,125.47,234.424,60.523,32.613 +2020-02-13 09:15:00,126.89,231.675,60.523,32.613 +2020-02-13 09:30:00,129.73,230.329,60.523,32.613 +2020-02-13 09:45:00,129.88,227.015,60.523,32.613 +2020-02-13 10:00:00,131.24,221.625,57.449,32.613 +2020-02-13 10:15:00,135.05,218.315,57.449,32.613 +2020-02-13 10:30:00,132.88,214.68400000000003,57.449,32.613 +2020-02-13 10:45:00,133.42,213.19799999999998,57.449,32.613 +2020-02-13 11:00:00,134.39,210.065,54.505,32.613 +2020-02-13 11:15:00,130.99,208.99599999999998,54.505,32.613 +2020-02-13 11:30:00,130.46,207.382,54.505,32.613 +2020-02-13 11:45:00,131.92,206.813,54.505,32.613 +2020-02-13 12:00:00,132.04,202.96599999999998,51.50899999999999,32.613 +2020-02-13 12:15:00,132.58,203.065,51.50899999999999,32.613 +2020-02-13 12:30:00,130.4,202.295,51.50899999999999,32.613 +2020-02-13 12:45:00,130.32,202.84900000000002,51.50899999999999,32.613 +2020-02-13 13:00:00,128.38,201.03599999999997,51.303999999999995,32.613 +2020-02-13 13:15:00,128.1,199.808,51.303999999999995,32.613 +2020-02-13 13:30:00,127.22,198.702,51.303999999999995,32.613 +2020-02-13 13:45:00,127.79,198.81900000000002,51.303999999999995,32.613 +2020-02-13 14:00:00,128.62,199.505,52.785,32.613 +2020-02-13 14:15:00,126.84,199.595,52.785,32.613 +2020-02-13 14:30:00,126.35,200.25599999999997,52.785,32.613 +2020-02-13 14:45:00,128.53,201.30599999999998,52.785,32.613 +2020-02-13 15:00:00,129.93,202.678,56.458999999999996,32.613 +2020-02-13 15:15:00,128.24,202.62900000000002,56.458999999999996,32.613 +2020-02-13 15:30:00,126.87,203.79,56.458999999999996,32.613 +2020-02-13 15:45:00,125.33,204.50099999999998,56.458999999999996,32.613 +2020-02-13 16:00:00,127.19,205.91099999999997,59.388000000000005,32.613 +2020-02-13 16:15:00,124.76,207.618,59.388000000000005,32.613 +2020-02-13 16:30:00,125.2,210.52200000000002,59.388000000000005,32.613 +2020-02-13 16:45:00,126.24,211.65,59.388000000000005,32.613 +2020-02-13 17:00:00,130.87,213.791,64.462,32.613 +2020-02-13 17:15:00,132.84,215.37400000000002,64.462,32.613 +2020-02-13 17:30:00,137.45,217.291,64.462,32.613 +2020-02-13 17:45:00,138.46,217.863,64.462,32.613 +2020-02-13 18:00:00,138.81,220.00900000000001,65.128,32.613 +2020-02-13 18:15:00,139.01,217.975,65.128,32.613 +2020-02-13 18:30:00,137.4,216.99200000000002,65.128,32.613 +2020-02-13 18:45:00,137.2,217.93200000000002,65.128,32.613 +2020-02-13 19:00:00,135.8,215.521,61.316,32.613 +2020-02-13 19:15:00,131.56,211.78799999999998,61.316,32.613 +2020-02-13 19:30:00,134.76,209.36599999999999,61.316,32.613 +2020-02-13 19:45:00,138.85,206.887,61.316,32.613 +2020-02-13 20:00:00,128.5,202.157,59.845,32.613 +2020-02-13 20:15:00,118.6,195.827,59.845,32.613 +2020-02-13 20:30:00,113.73,191.801,59.845,32.613 +2020-02-13 20:45:00,114.63,190.69299999999998,59.845,32.613 +2020-02-13 21:00:00,108.1,186.78400000000002,54.83,32.613 +2020-02-13 21:15:00,111.9,183.602,54.83,32.613 +2020-02-13 21:30:00,111.83,181.59,54.83,32.613 +2020-02-13 21:45:00,111.1,180.563,54.83,32.613 +2020-02-13 22:00:00,98.11,173.14,50.933,32.613 +2020-02-13 22:15:00,97.59,168.041,50.933,32.613 +2020-02-13 22:30:00,96.24,153.912,50.933,32.613 +2020-02-13 22:45:00,91.94,145.90200000000002,50.933,32.613 +2020-02-13 23:00:00,90.76,138.541,45.32899999999999,32.613 +2020-02-13 23:15:00,94.89,137.42700000000002,45.32899999999999,32.613 +2020-02-13 23:30:00,92.24,138.123,45.32899999999999,32.613 +2020-02-13 23:45:00,90.49,138.031,45.32899999999999,32.613 +2020-02-14 00:00:00,80.41,131.44799999999998,43.74,32.613 +2020-02-14 00:15:00,85.51,130.975,43.74,32.613 +2020-02-14 00:30:00,87.17,131.634,43.74,32.613 +2020-02-14 00:45:00,86.07,133.062,43.74,32.613 +2020-02-14 01:00:00,76.17,135.139,42.555,32.613 +2020-02-14 01:15:00,80.38,135.996,42.555,32.613 +2020-02-14 01:30:00,83.38,135.805,42.555,32.613 +2020-02-14 01:45:00,82.94,136.282,42.555,32.613 +2020-02-14 02:00:00,77.22,138.555,41.68600000000001,32.613 +2020-02-14 02:15:00,79.25,140.39600000000002,41.68600000000001,32.613 +2020-02-14 02:30:00,82.1,142.158,41.68600000000001,32.613 +2020-02-14 02:45:00,84.42,144.364,41.68600000000001,32.613 +2020-02-14 03:00:00,82.15,145.974,42.278999999999996,32.613 +2020-02-14 03:15:00,82.27,148.532,42.278999999999996,32.613 +2020-02-14 03:30:00,85.54,150.282,42.278999999999996,32.613 +2020-02-14 03:45:00,84.15,152.6,42.278999999999996,32.613 +2020-02-14 04:00:00,80.78,163.96400000000003,43.742,32.613 +2020-02-14 04:15:00,81.02,175.043,43.742,32.613 +2020-02-14 04:30:00,81.93,178.87900000000002,43.742,32.613 +2020-02-14 04:45:00,84.27,180.37400000000002,43.742,32.613 +2020-02-14 05:00:00,87.26,212.38099999999997,46.973,32.613 +2020-02-14 05:15:00,88.2,241.33599999999998,46.973,32.613 +2020-02-14 05:30:00,91.52,237.951,46.973,32.613 +2020-02-14 05:45:00,95.37,231.71099999999998,46.973,32.613 +2020-02-14 06:00:00,102.89,229.63400000000001,59.63399999999999,32.613 +2020-02-14 06:15:00,106.98,234.238,59.63399999999999,32.613 +2020-02-14 06:30:00,110.84,235.976,59.63399999999999,32.613 +2020-02-14 06:45:00,116.04,242.94799999999998,59.63399999999999,32.613 +2020-02-14 07:00:00,122.39,241.06900000000002,71.631,32.613 +2020-02-14 07:15:00,121.91,247.02900000000002,71.631,32.613 +2020-02-14 07:30:00,125.24,249.47099999999998,71.631,32.613 +2020-02-14 07:45:00,123.6,249.21599999999998,71.631,32.613 +2020-02-14 08:00:00,125.27,246.135,66.181,32.613 +2020-02-14 08:15:00,123.65,245.28400000000002,66.181,32.613 +2020-02-14 08:30:00,123.84,242.607,66.181,32.613 +2020-02-14 08:45:00,123.1,237.773,66.181,32.613 +2020-02-14 09:00:00,121.88,231.95,63.086000000000006,32.613 +2020-02-14 09:15:00,127.03,229.597,63.086000000000006,32.613 +2020-02-14 09:30:00,125.21,227.877,63.086000000000006,32.613 +2020-02-14 09:45:00,125.55,224.393,63.086000000000006,32.613 +2020-02-14 10:00:00,120.82,217.755,60.886,32.613 +2020-02-14 10:15:00,120.02,215.28900000000002,60.886,32.613 +2020-02-14 10:30:00,120.71,211.518,60.886,32.613 +2020-02-14 10:45:00,119.38,209.56,60.886,32.613 +2020-02-14 11:00:00,118.72,206.375,59.391000000000005,32.613 +2020-02-14 11:15:00,118.29,204.453,59.391000000000005,32.613 +2020-02-14 11:30:00,117.37,204.847,59.391000000000005,32.613 +2020-02-14 11:45:00,114.65,204.46,59.391000000000005,32.613 +2020-02-14 12:00:00,113.88,201.791,56.172,32.613 +2020-02-14 12:15:00,113.37,199.643,56.172,32.613 +2020-02-14 12:30:00,111.06,199.00900000000001,56.172,32.613 +2020-02-14 12:45:00,114.09,200.232,56.172,32.613 +2020-02-14 13:00:00,109.71,199.421,54.406000000000006,32.613 +2020-02-14 13:15:00,109.48,199.05900000000003,54.406000000000006,32.613 +2020-02-14 13:30:00,108.28,197.857,54.406000000000006,32.613 +2020-02-14 13:45:00,110.11,197.86599999999999,54.406000000000006,32.613 +2020-02-14 14:00:00,112.37,197.43200000000002,53.578,32.613 +2020-02-14 14:15:00,116.73,197.24900000000002,53.578,32.613 +2020-02-14 14:30:00,116.58,198.28400000000002,53.578,32.613 +2020-02-14 14:45:00,115.29,199.769,53.578,32.613 +2020-02-14 15:00:00,113.44,200.61700000000002,56.568999999999996,32.613 +2020-02-14 15:15:00,112.25,200.078,56.568999999999996,32.613 +2020-02-14 15:30:00,111.0,199.579,56.568999999999996,32.613 +2020-02-14 15:45:00,113.13,200.34599999999998,56.568999999999996,32.613 +2020-02-14 16:00:00,114.03,200.571,60.169,32.613 +2020-02-14 16:15:00,115.56,202.53400000000002,60.169,32.613 +2020-02-14 16:30:00,117.3,205.56599999999997,60.169,32.613 +2020-02-14 16:45:00,117.51,206.63299999999998,60.169,32.613 +2020-02-14 17:00:00,123.02,208.825,65.497,32.613 +2020-02-14 17:15:00,123.09,209.998,65.497,32.613 +2020-02-14 17:30:00,125.89,211.582,65.497,32.613 +2020-02-14 17:45:00,129.33,211.94,65.497,32.613 +2020-02-14 18:00:00,131.38,214.87599999999998,65.082,32.613 +2020-02-14 18:15:00,129.52,212.56799999999998,65.082,32.613 +2020-02-14 18:30:00,129.37,212.02700000000002,65.082,32.613 +2020-02-14 18:45:00,130.15,212.945,65.082,32.613 +2020-02-14 19:00:00,127.94,211.408,60.968,32.613 +2020-02-14 19:15:00,128.26,209.11900000000003,60.968,32.613 +2020-02-14 19:30:00,129.49,206.266,60.968,32.613 +2020-02-14 19:45:00,134.25,203.418,60.968,32.613 +2020-02-14 20:00:00,123.84,198.725,61.123000000000005,32.613 +2020-02-14 20:15:00,113.6,192.33599999999998,61.123000000000005,32.613 +2020-02-14 20:30:00,111.96,188.327,61.123000000000005,32.613 +2020-02-14 20:45:00,109.54,187.91299999999998,61.123000000000005,32.613 +2020-02-14 21:00:00,103.24,184.41400000000002,55.416000000000004,32.613 +2020-02-14 21:15:00,107.66,181.52900000000002,55.416000000000004,32.613 +2020-02-14 21:30:00,106.5,179.583,55.416000000000004,32.613 +2020-02-14 21:45:00,100.0,179.14700000000002,55.416000000000004,32.613 +2020-02-14 22:00:00,92.22,172.795,51.631,32.613 +2020-02-14 22:15:00,89.89,167.59900000000002,51.631,32.613 +2020-02-14 22:30:00,87.77,159.931,51.631,32.613 +2020-02-14 22:45:00,89.65,155.7,51.631,32.613 +2020-02-14 23:00:00,81.69,147.679,44.898,32.613 +2020-02-14 23:15:00,87.18,144.605,44.898,32.613 +2020-02-14 23:30:00,86.71,143.9,44.898,32.613 +2020-02-14 23:45:00,86.41,143.115,44.898,32.613 +2020-02-15 00:00:00,79.08,116.521,42.033,32.431999999999995 +2020-02-15 00:15:00,72.83,111.295,42.033,32.431999999999995 +2020-02-15 00:30:00,73.01,112.603,42.033,32.431999999999995 +2020-02-15 00:45:00,78.83,113.965,42.033,32.431999999999995 +2020-02-15 01:00:00,77.6,116.42,38.255,32.431999999999995 +2020-02-15 01:15:00,76.74,116.64299999999999,38.255,32.431999999999995 +2020-02-15 01:30:00,71.1,116.18299999999999,38.255,32.431999999999995 +2020-02-15 01:45:00,68.6,116.26,38.255,32.431999999999995 +2020-02-15 02:00:00,68.99,119.05,36.404,32.431999999999995 +2020-02-15 02:15:00,77.1,119.95700000000001,36.404,32.431999999999995 +2020-02-15 02:30:00,76.22,120.164,36.404,32.431999999999995 +2020-02-15 02:45:00,74.46,122.105,36.404,32.431999999999995 +2020-02-15 03:00:00,68.01,124.14200000000001,36.083,32.431999999999995 +2020-02-15 03:15:00,70.9,125.476,36.083,32.431999999999995 +2020-02-15 03:30:00,76.79,125.72399999999999,36.083,32.431999999999995 +2020-02-15 03:45:00,72.61,127.546,36.083,32.431999999999995 +2020-02-15 04:00:00,70.23,135.398,36.102,32.431999999999995 +2020-02-15 04:15:00,69.29,144.376,36.102,32.431999999999995 +2020-02-15 04:30:00,69.13,144.93200000000002,36.102,32.431999999999995 +2020-02-15 04:45:00,70.59,145.541,36.102,32.431999999999995 +2020-02-15 05:00:00,72.01,161.107,35.284,32.431999999999995 +2020-02-15 05:15:00,73.57,171.671,35.284,32.431999999999995 +2020-02-15 05:30:00,71.26,168.453,35.284,32.431999999999995 +2020-02-15 05:45:00,73.74,166.71900000000002,35.284,32.431999999999995 +2020-02-15 06:00:00,74.58,183.00599999999997,36.265,32.431999999999995 +2020-02-15 06:15:00,75.42,203.584,36.265,32.431999999999995 +2020-02-15 06:30:00,76.43,199.912,36.265,32.431999999999995 +2020-02-15 06:45:00,78.71,196.593,36.265,32.431999999999995 +2020-02-15 07:00:00,80.29,192.623,40.714,32.431999999999995 +2020-02-15 07:15:00,81.02,196.359,40.714,32.431999999999995 +2020-02-15 07:30:00,83.66,200.294,40.714,32.431999999999995 +2020-02-15 07:45:00,87.0,203.146,40.714,32.431999999999995 +2020-02-15 08:00:00,90.02,204.477,46.692,32.431999999999995 +2020-02-15 08:15:00,90.93,206.66299999999998,46.692,32.431999999999995 +2020-02-15 08:30:00,92.08,205.672,46.692,32.431999999999995 +2020-02-15 08:45:00,93.93,203.412,46.692,32.431999999999995 +2020-02-15 09:00:00,96.51,198.43,48.925,32.431999999999995 +2020-02-15 09:15:00,95.91,196.627,48.925,32.431999999999995 +2020-02-15 09:30:00,96.0,195.517,48.925,32.431999999999995 +2020-02-15 09:45:00,96.39,192.588,48.925,32.431999999999995 +2020-02-15 10:00:00,96.58,187.47799999999998,47.799,32.431999999999995 +2020-02-15 10:15:00,96.18,184.49900000000002,47.799,32.431999999999995 +2020-02-15 10:30:00,95.93,182.03599999999997,47.799,32.431999999999995 +2020-02-15 10:45:00,96.38,181.78799999999998,47.799,32.431999999999995 +2020-02-15 11:00:00,98.86,180.104,44.309,32.431999999999995 +2020-02-15 11:15:00,100.22,177.608,44.309,32.431999999999995 +2020-02-15 11:30:00,99.83,177.332,44.309,32.431999999999995 +2020-02-15 11:45:00,98.79,175.109,44.309,32.431999999999995 +2020-02-15 12:00:00,99.97,170.31400000000002,42.367,32.431999999999995 +2020-02-15 12:15:00,98.06,168.968,42.367,32.431999999999995 +2020-02-15 12:30:00,94.95,168.99599999999998,42.367,32.431999999999995 +2020-02-15 12:45:00,93.22,169.708,42.367,32.431999999999995 +2020-02-15 13:00:00,91.16,169.28,39.036,32.431999999999995 +2020-02-15 13:15:00,90.52,166.933,39.036,32.431999999999995 +2020-02-15 13:30:00,89.43,165.641,39.036,32.431999999999995 +2020-02-15 13:45:00,90.29,165.61900000000003,39.036,32.431999999999995 +2020-02-15 14:00:00,88.86,166.04,37.995,32.431999999999995 +2020-02-15 14:15:00,88.98,165.236,37.995,32.431999999999995 +2020-02-15 14:30:00,89.39,164.611,37.995,32.431999999999995 +2020-02-15 14:45:00,92.56,165.851,37.995,32.431999999999995 +2020-02-15 15:00:00,90.72,167.85299999999998,40.71,32.431999999999995 +2020-02-15 15:15:00,93.83,167.59599999999998,40.71,32.431999999999995 +2020-02-15 15:30:00,90.34,169.00400000000002,40.71,32.431999999999995 +2020-02-15 15:45:00,89.96,170.202,40.71,32.431999999999995 +2020-02-15 16:00:00,91.14,169.48,46.998000000000005,32.431999999999995 +2020-02-15 16:15:00,90.96,172.19400000000002,46.998000000000005,32.431999999999995 +2020-02-15 16:30:00,91.81,175.07299999999998,46.998000000000005,32.431999999999995 +2020-02-15 16:45:00,93.73,177.238,46.998000000000005,32.431999999999995 +2020-02-15 17:00:00,98.33,178.832,55.431000000000004,32.431999999999995 +2020-02-15 17:15:00,99.99,182.15599999999998,55.431000000000004,32.431999999999995 +2020-02-15 17:30:00,103.94,184.06900000000002,55.431000000000004,32.431999999999995 +2020-02-15 17:45:00,107.33,184.25099999999998,55.431000000000004,32.431999999999995 +2020-02-15 18:00:00,110.74,186.635,55.989,32.431999999999995 +2020-02-15 18:15:00,108.62,187.128,55.989,32.431999999999995 +2020-02-15 18:30:00,109.33,187.54,55.989,32.431999999999995 +2020-02-15 18:45:00,107.71,185.11,55.989,32.431999999999995 +2020-02-15 19:00:00,106.51,186.041,50.882,32.431999999999995 +2020-02-15 19:15:00,104.82,183.521,50.882,32.431999999999995 +2020-02-15 19:30:00,103.08,182.215,50.882,32.431999999999995 +2020-02-15 19:45:00,102.01,178.392,50.882,32.431999999999995 +2020-02-15 20:00:00,96.44,176.037,43.172,32.431999999999995 +2020-02-15 20:15:00,92.99,172.542,43.172,32.431999999999995 +2020-02-15 20:30:00,90.07,168.233,43.172,32.431999999999995 +2020-02-15 20:45:00,89.39,166.56,43.172,32.431999999999995 +2020-02-15 21:00:00,84.61,166.14,37.599000000000004,32.431999999999995 +2020-02-15 21:15:00,83.31,164.019,37.599000000000004,32.431999999999995 +2020-02-15 21:30:00,82.48,163.214,37.599000000000004,32.431999999999995 +2020-02-15 21:45:00,81.66,162.619,37.599000000000004,32.431999999999995 +2020-02-15 22:00:00,77.87,157.94799999999998,39.047,32.431999999999995 +2020-02-15 22:15:00,76.64,155.707,39.047,32.431999999999995 +2020-02-15 22:30:00,74.14,154.209,39.047,32.431999999999995 +2020-02-15 22:45:00,73.06,152.016,39.047,32.431999999999995 +2020-02-15 23:00:00,69.29,147.38299999999998,32.339,32.431999999999995 +2020-02-15 23:15:00,69.86,142.295,32.339,32.431999999999995 +2020-02-15 23:30:00,65.81,140.22,32.339,32.431999999999995 +2020-02-15 23:45:00,64.83,137.039,32.339,32.431999999999995 +2020-02-16 00:00:00,59.92,116.76700000000001,29.988000000000003,32.431999999999995 +2020-02-16 00:15:00,60.99,111.206,29.988000000000003,32.431999999999995 +2020-02-16 00:30:00,60.45,112.125,29.988000000000003,32.431999999999995 +2020-02-16 00:45:00,59.8,114.161,29.988000000000003,32.431999999999995 +2020-02-16 01:00:00,56.82,116.45,28.531999999999996,32.431999999999995 +2020-02-16 01:15:00,58.25,117.669,28.531999999999996,32.431999999999995 +2020-02-16 01:30:00,57.58,117.70200000000001,28.531999999999996,32.431999999999995 +2020-02-16 01:45:00,56.97,117.46600000000001,28.531999999999996,32.431999999999995 +2020-02-16 02:00:00,55.01,119.524,27.805999999999997,32.431999999999995 +2020-02-16 02:15:00,55.92,119.61399999999999,27.805999999999997,32.431999999999995 +2020-02-16 02:30:00,55.77,120.67200000000001,27.805999999999997,32.431999999999995 +2020-02-16 02:45:00,55.31,123.05799999999999,27.805999999999997,32.431999999999995 +2020-02-16 03:00:00,54.28,125.42200000000001,26.193,32.431999999999995 +2020-02-16 03:15:00,56.05,126.24799999999999,26.193,32.431999999999995 +2020-02-16 03:30:00,55.3,127.836,26.193,32.431999999999995 +2020-02-16 03:45:00,55.85,129.576,26.193,32.431999999999995 +2020-02-16 04:00:00,55.44,137.189,27.19,32.431999999999995 +2020-02-16 04:15:00,56.58,145.164,27.19,32.431999999999995 +2020-02-16 04:30:00,56.86,145.828,27.19,32.431999999999995 +2020-02-16 04:45:00,58.08,146.68,27.19,32.431999999999995 +2020-02-16 05:00:00,58.38,158.819,28.166999999999998,32.431999999999995 +2020-02-16 05:15:00,58.85,167.02900000000002,28.166999999999998,32.431999999999995 +2020-02-16 05:30:00,59.76,163.615,28.166999999999998,32.431999999999995 +2020-02-16 05:45:00,60.47,162.11700000000002,28.166999999999998,32.431999999999995 +2020-02-16 06:00:00,61.37,178.252,27.16,32.431999999999995 +2020-02-16 06:15:00,64.85,197.18900000000002,27.16,32.431999999999995 +2020-02-16 06:30:00,63.55,192.395,27.16,32.431999999999995 +2020-02-16 06:45:00,65.16,188.02,27.16,32.431999999999995 +2020-02-16 07:00:00,68.28,186.46,29.578000000000003,32.431999999999995 +2020-02-16 07:15:00,67.41,189.298,29.578000000000003,32.431999999999995 +2020-02-16 07:30:00,69.82,192.037,29.578000000000003,32.431999999999995 +2020-02-16 07:45:00,72.92,194.109,29.578000000000003,32.431999999999995 +2020-02-16 08:00:00,74.96,197.226,34.650999999999996,32.431999999999995 +2020-02-16 08:15:00,75.92,199.338,34.650999999999996,32.431999999999995 +2020-02-16 08:30:00,77.55,199.925,34.650999999999996,32.431999999999995 +2020-02-16 08:45:00,79.55,199.627,34.650999999999996,32.431999999999995 +2020-02-16 09:00:00,81.5,194.25599999999997,38.080999999999996,32.431999999999995 +2020-02-16 09:15:00,82.17,192.979,38.080999999999996,32.431999999999995 +2020-02-16 09:30:00,83.33,191.74900000000002,38.080999999999996,32.431999999999995 +2020-02-16 09:45:00,84.69,188.739,38.080999999999996,32.431999999999995 +2020-02-16 10:00:00,85.76,186.095,39.934,32.431999999999995 +2020-02-16 10:15:00,85.84,183.66400000000002,39.934,32.431999999999995 +2020-02-16 10:30:00,89.06,181.803,39.934,32.431999999999995 +2020-02-16 10:45:00,89.12,179.768,39.934,32.431999999999995 +2020-02-16 11:00:00,92.11,178.949,43.74100000000001,32.431999999999995 +2020-02-16 11:15:00,95.2,176.582,43.74100000000001,32.431999999999995 +2020-02-16 11:30:00,96.92,175.487,43.74100000000001,32.431999999999995 +2020-02-16 11:45:00,95.96,173.87099999999998,43.74100000000001,32.431999999999995 +2020-02-16 12:00:00,93.67,168.581,40.001999999999995,32.431999999999995 +2020-02-16 12:15:00,92.58,169.065,40.001999999999995,32.431999999999995 +2020-02-16 12:30:00,89.3,167.671,40.001999999999995,32.431999999999995 +2020-02-16 12:45:00,90.13,167.426,40.001999999999995,32.431999999999995 +2020-02-16 13:00:00,84.56,166.325,37.855,32.431999999999995 +2020-02-16 13:15:00,84.84,166.843,37.855,32.431999999999995 +2020-02-16 13:30:00,82.46,165.296,37.855,32.431999999999995 +2020-02-16 13:45:00,82.47,164.67700000000002,37.855,32.431999999999995 +2020-02-16 14:00:00,80.52,165.43,35.946999999999996,32.431999999999995 +2020-02-16 14:15:00,80.46,165.77599999999998,35.946999999999996,32.431999999999995 +2020-02-16 14:30:00,80.76,166.28,35.946999999999996,32.431999999999995 +2020-02-16 14:45:00,80.96,167.1,35.946999999999996,32.431999999999995 +2020-02-16 15:00:00,81.11,167.64,35.138000000000005,32.431999999999995 +2020-02-16 15:15:00,79.85,168.058,35.138000000000005,32.431999999999995 +2020-02-16 15:30:00,79.76,169.99,35.138000000000005,32.431999999999995 +2020-02-16 15:45:00,80.59,171.847,35.138000000000005,32.431999999999995 +2020-02-16 16:00:00,82.28,172.89,38.672,32.431999999999995 +2020-02-16 16:15:00,81.81,174.72099999999998,38.672,32.431999999999995 +2020-02-16 16:30:00,81.95,177.882,38.672,32.431999999999995 +2020-02-16 16:45:00,84.44,180.15599999999998,38.672,32.431999999999995 +2020-02-16 17:00:00,88.25,181.767,48.684,32.431999999999995 +2020-02-16 17:15:00,89.74,184.832,48.684,32.431999999999995 +2020-02-16 17:30:00,92.59,187.075,48.684,32.431999999999995 +2020-02-16 17:45:00,97.75,189.489,48.684,32.431999999999995 +2020-02-16 18:00:00,103.5,191.37400000000002,51.568999999999996,32.431999999999995 +2020-02-16 18:15:00,103.77,193.201,51.568999999999996,32.431999999999995 +2020-02-16 18:30:00,103.86,191.58,51.568999999999996,32.431999999999995 +2020-02-16 18:45:00,101.12,190.97400000000002,51.568999999999996,32.431999999999995 +2020-02-16 19:00:00,100.36,191.597,48.608000000000004,32.431999999999995 +2020-02-16 19:15:00,97.84,189.642,48.608000000000004,32.431999999999995 +2020-02-16 19:30:00,100.63,188.19299999999998,48.608000000000004,32.431999999999995 +2020-02-16 19:45:00,103.68,185.798,48.608000000000004,32.431999999999995 +2020-02-16 20:00:00,99.8,183.389,43.733999999999995,32.431999999999995 +2020-02-16 20:15:00,93.26,180.862,43.733999999999995,32.431999999999995 +2020-02-16 20:30:00,92.18,177.79,43.733999999999995,32.431999999999995 +2020-02-16 20:45:00,90.05,174.91400000000002,43.733999999999995,32.431999999999995 +2020-02-16 21:00:00,87.92,171.957,39.283,32.431999999999995 +2020-02-16 21:15:00,88.88,169.209,39.283,32.431999999999995 +2020-02-16 21:30:00,93.52,168.675,39.283,32.431999999999995 +2020-02-16 21:45:00,95.15,168.239,39.283,32.431999999999995 +2020-02-16 22:00:00,90.26,162.445,40.111,32.431999999999995 +2020-02-16 22:15:00,89.37,159.447,40.111,32.431999999999995 +2020-02-16 22:30:00,90.03,154.82,40.111,32.431999999999995 +2020-02-16 22:45:00,90.58,151.776,40.111,32.431999999999995 +2020-02-16 23:00:00,86.82,144.405,35.791,32.431999999999995 +2020-02-16 23:15:00,85.0,141.168,35.791,32.431999999999995 +2020-02-16 23:30:00,86.25,139.881,35.791,32.431999999999995 +2020-02-16 23:45:00,84.99,137.593,35.791,32.431999999999995 +2020-02-17 00:00:00,81.05,120.693,34.311,32.613 +2020-02-17 00:15:00,80.07,118.045,34.311,32.613 +2020-02-17 00:30:00,81.59,119.06200000000001,34.311,32.613 +2020-02-17 00:45:00,81.45,120.56,34.311,32.613 +2020-02-17 01:00:00,76.96,122.838,34.585,32.613 +2020-02-17 01:15:00,76.08,123.53,34.585,32.613 +2020-02-17 01:30:00,79.32,123.61200000000001,34.585,32.613 +2020-02-17 01:45:00,79.06,123.48700000000001,34.585,32.613 +2020-02-17 02:00:00,75.2,125.52799999999999,34.111,32.613 +2020-02-17 02:15:00,77.83,127.016,34.111,32.613 +2020-02-17 02:30:00,78.92,128.418,34.111,32.613 +2020-02-17 02:45:00,80.45,130.196,34.111,32.613 +2020-02-17 03:00:00,74.01,133.811,32.435,32.613 +2020-02-17 03:15:00,76.74,136.253,32.435,32.613 +2020-02-17 03:30:00,81.58,137.588,32.435,32.613 +2020-02-17 03:45:00,81.79,138.791,32.435,32.613 +2020-02-17 04:00:00,80.2,150.708,33.04,32.613 +2020-02-17 04:15:00,77.79,162.775,33.04,32.613 +2020-02-17 04:30:00,85.23,165.579,33.04,32.613 +2020-02-17 04:45:00,86.55,166.584,33.04,32.613 +2020-02-17 05:00:00,89.34,194.2,40.399,32.613 +2020-02-17 05:15:00,84.87,222.275,40.399,32.613 +2020-02-17 05:30:00,88.55,219.07,40.399,32.613 +2020-02-17 05:45:00,93.5,212.00799999999998,40.399,32.613 +2020-02-17 06:00:00,101.21,210.477,60.226000000000006,32.613 +2020-02-17 06:15:00,106.95,214.78900000000002,60.226000000000006,32.613 +2020-02-17 06:30:00,112.4,217.851,60.226000000000006,32.613 +2020-02-17 06:45:00,116.6,222.146,60.226000000000006,32.613 +2020-02-17 07:00:00,122.33,222.924,73.578,32.613 +2020-02-17 07:15:00,122.39,226.99400000000003,73.578,32.613 +2020-02-17 07:30:00,122.25,228.925,73.578,32.613 +2020-02-17 07:45:00,124.77,228.46599999999998,73.578,32.613 +2020-02-17 08:00:00,129.11,226.765,66.58,32.613 +2020-02-17 08:15:00,127.63,226.747,66.58,32.613 +2020-02-17 08:30:00,123.89,223.28599999999997,66.58,32.613 +2020-02-17 08:45:00,122.13,219.752,66.58,32.613 +2020-02-17 09:00:00,124.23,213.396,62.0,32.613 +2020-02-17 09:15:00,125.02,208.692,62.0,32.613 +2020-02-17 09:30:00,125.32,206.49,62.0,32.613 +2020-02-17 09:45:00,122.32,203.72299999999998,62.0,32.613 +2020-02-17 10:00:00,121.29,200.037,59.099,32.613 +2020-02-17 10:15:00,125.81,197.31599999999997,59.099,32.613 +2020-02-17 10:30:00,124.92,194.606,59.099,32.613 +2020-02-17 10:45:00,127.08,193.273,59.099,32.613 +2020-02-17 11:00:00,122.8,189.91400000000002,57.729,32.613 +2020-02-17 11:15:00,123.95,189.315,57.729,32.613 +2020-02-17 11:30:00,125.08,189.584,57.729,32.613 +2020-02-17 11:45:00,121.96,187.597,57.729,32.613 +2020-02-17 12:00:00,120.65,183.946,55.615,32.613 +2020-02-17 12:15:00,122.1,184.45,55.615,32.613 +2020-02-17 12:30:00,120.86,183.24200000000002,55.615,32.613 +2020-02-17 12:45:00,120.22,184.47099999999998,55.615,32.613 +2020-02-17 13:00:00,118.27,183.98,56.515,32.613 +2020-02-17 13:15:00,119.03,183.113,56.515,32.613 +2020-02-17 13:30:00,118.32,181.043,56.515,32.613 +2020-02-17 13:45:00,117.07,180.481,56.515,32.613 +2020-02-17 14:00:00,115.45,180.667,58.1,32.613 +2020-02-17 14:15:00,118.18,180.386,58.1,32.613 +2020-02-17 14:30:00,118.2,180.34400000000002,58.1,32.613 +2020-02-17 14:45:00,115.44,181.199,58.1,32.613 +2020-02-17 15:00:00,117.71,183.47299999999998,59.801,32.613 +2020-02-17 15:15:00,119.93,182.456,59.801,32.613 +2020-02-17 15:30:00,119.86,183.56599999999997,59.801,32.613 +2020-02-17 15:45:00,115.36,184.96400000000003,59.801,32.613 +2020-02-17 16:00:00,116.04,186.22400000000002,62.901,32.613 +2020-02-17 16:15:00,117.83,187.304,62.901,32.613 +2020-02-17 16:30:00,118.12,189.513,62.901,32.613 +2020-02-17 16:45:00,120.01,190.609,62.901,32.613 +2020-02-17 17:00:00,123.07,192.03900000000002,70.418,32.613 +2020-02-17 17:15:00,128.21,194.203,70.418,32.613 +2020-02-17 17:30:00,127.74,195.93200000000002,70.418,32.613 +2020-02-17 17:45:00,131.41,196.90400000000002,70.418,32.613 +2020-02-17 18:00:00,133.59,199.207,71.726,32.613 +2020-02-17 18:15:00,132.22,198.90099999999998,71.726,32.613 +2020-02-17 18:30:00,134.7,197.90400000000002,71.726,32.613 +2020-02-17 18:45:00,132.33,198.072,71.726,32.613 +2020-02-17 19:00:00,129.8,197.084,65.997,32.613 +2020-02-17 19:15:00,128.73,194.02,65.997,32.613 +2020-02-17 19:30:00,129.34,193.07299999999998,65.997,32.613 +2020-02-17 19:45:00,129.08,189.891,65.997,32.613 +2020-02-17 20:00:00,119.74,185.122,68.09100000000001,32.613 +2020-02-17 20:15:00,116.95,180.209,68.09100000000001,32.613 +2020-02-17 20:30:00,113.25,175.343,68.09100000000001,32.613 +2020-02-17 20:45:00,111.85,174.063,68.09100000000001,32.613 +2020-02-17 21:00:00,106.25,171.602,59.617,32.613 +2020-02-17 21:15:00,111.38,167.703,59.617,32.613 +2020-02-17 21:30:00,111.19,166.368,59.617,32.613 +2020-02-17 21:45:00,107.47,165.453,59.617,32.613 +2020-02-17 22:00:00,100.26,156.791,54.938,32.613 +2020-02-17 22:15:00,96.86,152.559,54.938,32.613 +2020-02-17 22:30:00,93.99,138.485,54.938,32.613 +2020-02-17 22:45:00,92.23,130.718,54.938,32.613 +2020-02-17 23:00:00,85.74,124.1,47.43,32.613 +2020-02-17 23:15:00,87.58,123.447,47.43,32.613 +2020-02-17 23:30:00,86.64,124.87200000000001,47.43,32.613 +2020-02-17 23:45:00,94.27,125.181,47.43,32.613 +2020-02-18 00:00:00,90.43,119.78299999999999,48.354,32.613 +2020-02-18 00:15:00,87.49,118.541,48.354,32.613 +2020-02-18 00:30:00,81.76,118.62799999999999,48.354,32.613 +2020-02-18 00:45:00,80.22,119.175,48.354,32.613 +2020-02-18 01:00:00,84.53,121.22,45.68600000000001,32.613 +2020-02-18 01:15:00,84.82,121.465,45.68600000000001,32.613 +2020-02-18 01:30:00,82.61,121.7,45.68600000000001,32.613 +2020-02-18 01:45:00,78.21,121.87,45.68600000000001,32.613 +2020-02-18 02:00:00,82.26,123.896,44.269,32.613 +2020-02-18 02:15:00,84.76,125.286,44.269,32.613 +2020-02-18 02:30:00,83.23,126.115,44.269,32.613 +2020-02-18 02:45:00,78.58,127.90799999999999,44.269,32.613 +2020-02-18 03:00:00,79.52,130.341,44.187,32.613 +2020-02-18 03:15:00,84.9,131.954,44.187,32.613 +2020-02-18 03:30:00,86.95,133.75799999999998,44.187,32.613 +2020-02-18 03:45:00,85.47,135.149,44.187,32.613 +2020-02-18 04:00:00,80.95,146.857,46.126999999999995,32.613 +2020-02-18 04:15:00,86.03,158.57399999999998,46.126999999999995,32.613 +2020-02-18 04:30:00,90.64,161.08700000000002,46.126999999999995,32.613 +2020-02-18 04:45:00,93.17,163.274,46.126999999999995,32.613 +2020-02-18 05:00:00,95.59,195.803,49.666000000000004,32.613 +2020-02-18 05:15:00,96.2,223.696,49.666000000000004,32.613 +2020-02-18 05:30:00,101.55,218.959,49.666000000000004,32.613 +2020-02-18 05:45:00,105.28,211.90200000000002,49.666000000000004,32.613 +2020-02-18 06:00:00,111.44,209.226,61.077,32.613 +2020-02-18 06:15:00,110.26,215.14700000000002,61.077,32.613 +2020-02-18 06:30:00,113.34,217.54,61.077,32.613 +2020-02-18 06:45:00,118.15,221.449,61.077,32.613 +2020-02-18 07:00:00,124.13,222.07,74.717,32.613 +2020-02-18 07:15:00,123.32,225.953,74.717,32.613 +2020-02-18 07:30:00,123.67,227.312,74.717,32.613 +2020-02-18 07:45:00,124.55,227.015,74.717,32.613 +2020-02-18 08:00:00,129.02,225.399,69.033,32.613 +2020-02-18 08:15:00,128.96,224.354,69.033,32.613 +2020-02-18 08:30:00,129.77,220.667,69.033,32.613 +2020-02-18 08:45:00,127.77,216.868,69.033,32.613 +2020-02-18 09:00:00,129.25,209.703,63.113,32.613 +2020-02-18 09:15:00,133.26,206.56900000000002,63.113,32.613 +2020-02-18 09:30:00,129.51,205.06,63.113,32.613 +2020-02-18 09:45:00,127.42,202.097,63.113,32.613 +2020-02-18 10:00:00,123.91,197.831,61.461999999999996,32.613 +2020-02-18 10:15:00,124.56,194.101,61.461999999999996,32.613 +2020-02-18 10:30:00,124.88,191.581,61.461999999999996,32.613 +2020-02-18 10:45:00,127.18,190.551,61.461999999999996,32.613 +2020-02-18 11:00:00,126.45,188.638,59.614,32.613 +2020-02-18 11:15:00,127.78,187.733,59.614,32.613 +2020-02-18 11:30:00,124.02,186.854,59.614,32.613 +2020-02-18 11:45:00,124.8,185.56099999999998,59.614,32.613 +2020-02-18 12:00:00,119.33,180.585,57.415,32.613 +2020-02-18 12:15:00,122.01,180.7,57.415,32.613 +2020-02-18 12:30:00,121.83,180.183,57.415,32.613 +2020-02-18 12:45:00,118.63,181.13099999999997,57.415,32.613 +2020-02-18 13:00:00,114.34,180.268,58.534,32.613 +2020-02-18 13:15:00,116.38,179.06400000000002,58.534,32.613 +2020-02-18 13:30:00,116.49,178.125,58.534,32.613 +2020-02-18 13:45:00,118.33,177.733,58.534,32.613 +2020-02-18 14:00:00,120.0,178.25900000000001,59.415,32.613 +2020-02-18 14:15:00,120.28,178.108,59.415,32.613 +2020-02-18 14:30:00,120.23,178.66400000000002,59.415,32.613 +2020-02-18 14:45:00,123.79,179.44299999999998,59.415,32.613 +2020-02-18 15:00:00,121.51,181.301,62.071999999999996,32.613 +2020-02-18 15:15:00,120.26,180.593,62.071999999999996,32.613 +2020-02-18 15:30:00,122.85,181.87900000000002,62.071999999999996,32.613 +2020-02-18 15:45:00,121.26,182.873,62.071999999999996,32.613 +2020-02-18 16:00:00,122.32,184.453,64.99,32.613 +2020-02-18 16:15:00,121.34,185.988,64.99,32.613 +2020-02-18 16:30:00,122.1,188.827,64.99,32.613 +2020-02-18 16:45:00,124.58,190.19,64.99,32.613 +2020-02-18 17:00:00,130.96,192.155,72.658,32.613 +2020-02-18 17:15:00,129.03,194.37,72.658,32.613 +2020-02-18 17:30:00,131.39,196.77700000000002,72.658,32.613 +2020-02-18 17:45:00,134.3,197.639,72.658,32.613 +2020-02-18 18:00:00,137.03,199.84099999999998,73.645,32.613 +2020-02-18 18:15:00,135.62,199.113,73.645,32.613 +2020-02-18 18:30:00,134.52,197.81,73.645,32.613 +2020-02-18 18:45:00,134.65,198.78099999999998,73.645,32.613 +2020-02-18 19:00:00,130.82,197.82299999999998,67.085,32.613 +2020-02-18 19:15:00,130.82,194.50099999999998,67.085,32.613 +2020-02-18 19:30:00,138.13,192.917,67.085,32.613 +2020-02-18 19:45:00,138.56,189.81,67.085,32.613 +2020-02-18 20:00:00,126.03,185.172,66.138,32.613 +2020-02-18 20:15:00,119.06,179.63099999999997,66.138,32.613 +2020-02-18 20:30:00,114.73,175.77599999999998,66.138,32.613 +2020-02-18 20:45:00,113.6,173.94299999999998,66.138,32.613 +2020-02-18 21:00:00,107.89,170.787,57.512,32.613 +2020-02-18 21:15:00,114.35,167.757,57.512,32.613 +2020-02-18 21:30:00,113.36,165.707,57.512,32.613 +2020-02-18 21:45:00,111.8,165.03599999999997,57.512,32.613 +2020-02-18 22:00:00,100.85,158.043,54.545,32.613 +2020-02-18 22:15:00,99.95,153.578,54.545,32.613 +2020-02-18 22:30:00,100.92,139.555,54.545,32.613 +2020-02-18 22:45:00,102.09,132.065,54.545,32.613 +2020-02-18 23:00:00,97.23,125.461,48.605,32.613 +2020-02-18 23:15:00,94.48,123.948,48.605,32.613 +2020-02-18 23:30:00,89.92,125.03,48.605,32.613 +2020-02-18 23:45:00,91.19,124.929,48.605,32.613 +2020-02-19 00:00:00,90.06,119.571,45.675,32.613 +2020-02-19 00:15:00,88.18,118.331,45.675,32.613 +2020-02-19 00:30:00,88.53,118.399,45.675,32.613 +2020-02-19 00:45:00,85.32,118.94200000000001,45.675,32.613 +2020-02-19 01:00:00,85.6,120.95299999999999,43.015,32.613 +2020-02-19 01:15:00,87.17,121.185,43.015,32.613 +2020-02-19 01:30:00,86.58,121.40700000000001,43.015,32.613 +2020-02-19 01:45:00,82.82,121.579,43.015,32.613 +2020-02-19 02:00:00,84.77,123.601,41.0,32.613 +2020-02-19 02:15:00,86.47,124.98700000000001,41.0,32.613 +2020-02-19 02:30:00,83.73,125.83200000000001,41.0,32.613 +2020-02-19 02:45:00,83.36,127.624,41.0,32.613 +2020-02-19 03:00:00,86.31,130.064,41.318000000000005,32.613 +2020-02-19 03:15:00,87.22,131.672,41.318000000000005,32.613 +2020-02-19 03:30:00,83.52,133.47,41.318000000000005,32.613 +2020-02-19 03:45:00,86.28,134.872,41.318000000000005,32.613 +2020-02-19 04:00:00,81.82,146.582,42.544,32.613 +2020-02-19 04:15:00,86.0,158.291,42.544,32.613 +2020-02-19 04:30:00,91.07,160.819,42.544,32.613 +2020-02-19 04:45:00,93.83,162.994,42.544,32.613 +2020-02-19 05:00:00,95.03,195.505,45.161,32.613 +2020-02-19 05:15:00,92.61,223.41400000000002,45.161,32.613 +2020-02-19 05:30:00,95.38,218.649,45.161,32.613 +2020-02-19 05:45:00,101.64,211.597,45.161,32.613 +2020-02-19 06:00:00,106.45,208.93200000000002,61.86600000000001,32.613 +2020-02-19 06:15:00,110.83,214.862,61.86600000000001,32.613 +2020-02-19 06:30:00,114.29,217.22099999999998,61.86600000000001,32.613 +2020-02-19 06:45:00,118.12,221.122,61.86600000000001,32.613 +2020-02-19 07:00:00,124.11,221.766,77.814,32.613 +2020-02-19 07:15:00,126.22,225.61900000000003,77.814,32.613 +2020-02-19 07:30:00,128.75,226.94299999999998,77.814,32.613 +2020-02-19 07:45:00,127.24,226.606,77.814,32.613 +2020-02-19 08:00:00,130.4,224.96900000000002,70.251,32.613 +2020-02-19 08:15:00,128.37,223.905,70.251,32.613 +2020-02-19 08:30:00,132.94,220.16299999999998,70.251,32.613 +2020-02-19 08:45:00,130.84,216.36900000000003,70.251,32.613 +2020-02-19 09:00:00,133.42,209.21200000000002,66.965,32.613 +2020-02-19 09:15:00,135.01,206.08,66.965,32.613 +2020-02-19 09:30:00,136.59,204.59099999999998,66.965,32.613 +2020-02-19 09:45:00,138.7,201.63400000000001,66.965,32.613 +2020-02-19 10:00:00,134.17,197.37900000000002,63.628,32.613 +2020-02-19 10:15:00,135.78,193.68099999999998,63.628,32.613 +2020-02-19 10:30:00,135.61,191.174,63.628,32.613 +2020-02-19 10:45:00,136.33,190.16,63.628,32.613 +2020-02-19 11:00:00,134.83,188.235,62.516999999999996,32.613 +2020-02-19 11:15:00,132.88,187.34599999999998,62.516999999999996,32.613 +2020-02-19 11:30:00,130.69,186.47299999999998,62.516999999999996,32.613 +2020-02-19 11:45:00,126.99,185.19299999999998,62.516999999999996,32.613 +2020-02-19 12:00:00,126.05,180.234,60.888999999999996,32.613 +2020-02-19 12:15:00,122.3,180.364,60.888999999999996,32.613 +2020-02-19 12:30:00,119.64,179.81799999999998,60.888999999999996,32.613 +2020-02-19 12:45:00,118.58,180.763,60.888999999999996,32.613 +2020-02-19 13:00:00,115.52,179.93,61.57899999999999,32.613 +2020-02-19 13:15:00,116.57,178.705,61.57899999999999,32.613 +2020-02-19 13:30:00,113.94,177.752,61.57899999999999,32.613 +2020-02-19 13:45:00,115.97,177.36,61.57899999999999,32.613 +2020-02-19 14:00:00,114.21,177.94400000000002,62.602,32.613 +2020-02-19 14:15:00,115.49,177.77200000000002,62.602,32.613 +2020-02-19 14:30:00,115.54,178.308,62.602,32.613 +2020-02-19 14:45:00,118.46,179.09900000000002,62.602,32.613 +2020-02-19 15:00:00,119.72,180.96599999999998,64.259,32.613 +2020-02-19 15:15:00,122.91,180.229,64.259,32.613 +2020-02-19 15:30:00,120.97,181.476,64.259,32.613 +2020-02-19 15:45:00,121.61,182.453,64.259,32.613 +2020-02-19 16:00:00,124.31,184.03400000000002,67.632,32.613 +2020-02-19 16:15:00,123.96,185.55599999999998,67.632,32.613 +2020-02-19 16:30:00,123.8,188.396,67.632,32.613 +2020-02-19 16:45:00,124.67,189.734,67.632,32.613 +2020-02-19 17:00:00,129.4,191.702,72.583,32.613 +2020-02-19 17:15:00,130.26,193.933,72.583,32.613 +2020-02-19 17:30:00,133.14,196.37,72.583,32.613 +2020-02-19 17:45:00,133.67,197.252,72.583,32.613 +2020-02-19 18:00:00,138.63,199.46599999999998,72.744,32.613 +2020-02-19 18:15:00,135.29,198.795,72.744,32.613 +2020-02-19 18:30:00,137.36,197.49099999999999,72.744,32.613 +2020-02-19 18:45:00,134.04,198.484,72.744,32.613 +2020-02-19 19:00:00,131.09,197.487,69.684,32.613 +2020-02-19 19:15:00,130.73,194.18,69.684,32.613 +2020-02-19 19:30:00,136.9,192.62099999999998,69.684,32.613 +2020-02-19 19:45:00,137.66,189.549,69.684,32.613 +2020-02-19 20:00:00,126.33,184.882,70.036,32.613 +2020-02-19 20:15:00,117.21,179.354,70.036,32.613 +2020-02-19 20:30:00,116.21,175.517,70.036,32.613 +2020-02-19 20:45:00,114.1,173.687,70.036,32.613 +2020-02-19 21:00:00,108.92,170.518,60.431999999999995,32.613 +2020-02-19 21:15:00,114.41,167.477,60.431999999999995,32.613 +2020-02-19 21:30:00,113.68,165.428,60.431999999999995,32.613 +2020-02-19 21:45:00,113.09,164.775,60.431999999999995,32.613 +2020-02-19 22:00:00,101.66,157.768,56.2,32.613 +2020-02-19 22:15:00,97.67,153.327,56.2,32.613 +2020-02-19 22:30:00,95.3,139.265,56.2,32.613 +2020-02-19 22:45:00,95.0,131.78,56.2,32.613 +2020-02-19 23:00:00,94.34,125.169,47.927,32.613 +2020-02-19 23:15:00,97.09,123.671,47.927,32.613 +2020-02-19 23:30:00,95.13,124.764,47.927,32.613 +2020-02-19 23:45:00,91.62,124.68700000000001,47.927,32.613 +2020-02-20 00:00:00,84.69,119.354,43.794,32.613 +2020-02-20 00:15:00,87.96,118.113,43.794,32.613 +2020-02-20 00:30:00,88.54,118.163,43.794,32.613 +2020-02-20 00:45:00,87.1,118.70299999999999,43.794,32.613 +2020-02-20 01:00:00,80.45,120.679,42.397,32.613 +2020-02-20 01:15:00,79.74,120.897,42.397,32.613 +2020-02-20 01:30:00,79.29,121.10799999999999,42.397,32.613 +2020-02-20 01:45:00,84.73,121.281,42.397,32.613 +2020-02-20 02:00:00,85.65,123.29799999999999,40.010999999999996,32.613 +2020-02-20 02:15:00,86.23,124.682,40.010999999999996,32.613 +2020-02-20 02:30:00,82.4,125.541,40.010999999999996,32.613 +2020-02-20 02:45:00,80.94,127.333,40.010999999999996,32.613 +2020-02-20 03:00:00,86.23,129.78,39.181,32.613 +2020-02-20 03:15:00,87.78,131.382,39.181,32.613 +2020-02-20 03:30:00,86.6,133.175,39.181,32.613 +2020-02-20 03:45:00,82.73,134.589,39.181,32.613 +2020-02-20 04:00:00,84.08,146.3,40.39,32.613 +2020-02-20 04:15:00,82.86,158.0,40.39,32.613 +2020-02-20 04:30:00,83.77,160.543,40.39,32.613 +2020-02-20 04:45:00,86.58,162.708,40.39,32.613 +2020-02-20 05:00:00,97.42,195.2,45.504,32.613 +2020-02-20 05:15:00,99.75,223.127,45.504,32.613 +2020-02-20 05:30:00,97.65,218.333,45.504,32.613 +2020-02-20 05:45:00,102.13,211.28599999999997,45.504,32.613 +2020-02-20 06:00:00,106.74,208.63,57.748000000000005,32.613 +2020-02-20 06:15:00,110.76,214.57,57.748000000000005,32.613 +2020-02-20 06:30:00,114.34,216.894,57.748000000000005,32.613 +2020-02-20 06:45:00,118.21,220.787,57.748000000000005,32.613 +2020-02-20 07:00:00,123.28,221.452,72.138,32.613 +2020-02-20 07:15:00,123.1,225.27700000000002,72.138,32.613 +2020-02-20 07:30:00,125.66,226.56400000000002,72.138,32.613 +2020-02-20 07:45:00,125.06,226.188,72.138,32.613 +2020-02-20 08:00:00,127.31,224.528,65.542,32.613 +2020-02-20 08:15:00,125.33,223.446,65.542,32.613 +2020-02-20 08:30:00,126.93,219.65,65.542,32.613 +2020-02-20 08:45:00,122.41,215.863,65.542,32.613 +2020-02-20 09:00:00,122.95,208.71200000000002,60.523,32.613 +2020-02-20 09:15:00,123.48,205.582,60.523,32.613 +2020-02-20 09:30:00,123.66,204.113,60.523,32.613 +2020-02-20 09:45:00,123.38,201.16299999999998,60.523,32.613 +2020-02-20 10:00:00,123.79,196.918,57.449,32.613 +2020-02-20 10:15:00,122.44,193.252,57.449,32.613 +2020-02-20 10:30:00,120.99,190.75900000000001,57.449,32.613 +2020-02-20 10:45:00,121.04,189.761,57.449,32.613 +2020-02-20 11:00:00,118.26,187.826,54.505,32.613 +2020-02-20 11:15:00,118.28,186.951,54.505,32.613 +2020-02-20 11:30:00,118.21,186.08599999999998,54.505,32.613 +2020-02-20 11:45:00,117.86,184.82,54.505,32.613 +2020-02-20 12:00:00,116.74,179.87599999999998,51.50899999999999,32.613 +2020-02-20 12:15:00,118.18,180.021,51.50899999999999,32.613 +2020-02-20 12:30:00,118.55,179.447,51.50899999999999,32.613 +2020-02-20 12:45:00,118.61,180.388,51.50899999999999,32.613 +2020-02-20 13:00:00,114.9,179.585,51.303999999999995,32.613 +2020-02-20 13:15:00,115.25,178.33700000000002,51.303999999999995,32.613 +2020-02-20 13:30:00,112.8,177.372,51.303999999999995,32.613 +2020-02-20 13:45:00,116.14,176.981,51.303999999999995,32.613 +2020-02-20 14:00:00,111.39,177.622,52.785,32.613 +2020-02-20 14:15:00,115.64,177.429,52.785,32.613 +2020-02-20 14:30:00,114.88,177.945,52.785,32.613 +2020-02-20 14:45:00,116.2,178.748,52.785,32.613 +2020-02-20 15:00:00,118.06,180.623,56.458999999999996,32.613 +2020-02-20 15:15:00,115.92,179.859,56.458999999999996,32.613 +2020-02-20 15:30:00,115.99,181.065,56.458999999999996,32.613 +2020-02-20 15:45:00,117.51,182.02599999999998,56.458999999999996,32.613 +2020-02-20 16:00:00,117.85,183.607,59.388000000000005,32.613 +2020-02-20 16:15:00,118.3,185.11700000000002,59.388000000000005,32.613 +2020-02-20 16:30:00,122.42,187.958,59.388000000000005,32.613 +2020-02-20 16:45:00,122.43,189.268,59.388000000000005,32.613 +2020-02-20 17:00:00,124.49,191.24099999999999,64.462,32.613 +2020-02-20 17:15:00,125.36,193.489,64.462,32.613 +2020-02-20 17:30:00,131.6,195.954,64.462,32.613 +2020-02-20 17:45:00,131.89,196.857,64.462,32.613 +2020-02-20 18:00:00,138.28,199.081,65.128,32.613 +2020-02-20 18:15:00,136.37,198.468,65.128,32.613 +2020-02-20 18:30:00,138.13,197.16400000000002,65.128,32.613 +2020-02-20 18:45:00,136.19,198.179,65.128,32.613 +2020-02-20 19:00:00,132.24,197.143,61.316,32.613 +2020-02-20 19:15:00,136.06,193.85,61.316,32.613 +2020-02-20 19:30:00,140.35,192.31599999999997,61.316,32.613 +2020-02-20 19:45:00,136.18,189.28099999999998,61.316,32.613 +2020-02-20 20:00:00,124.63,184.584,59.845,32.613 +2020-02-20 20:15:00,117.47,179.071,59.845,32.613 +2020-02-20 20:30:00,115.88,175.25,59.845,32.613 +2020-02-20 20:45:00,113.56,173.425,59.845,32.613 +2020-02-20 21:00:00,109.82,170.24099999999999,54.83,32.613 +2020-02-20 21:15:00,107.49,167.19099999999997,54.83,32.613 +2020-02-20 21:30:00,105.41,165.142,54.83,32.613 +2020-02-20 21:45:00,107.31,164.50900000000001,54.83,32.613 +2020-02-20 22:00:00,98.64,157.486,50.933,32.613 +2020-02-20 22:15:00,102.93,153.07,50.933,32.613 +2020-02-20 22:30:00,103.82,138.967,50.933,32.613 +2020-02-20 22:45:00,105.37,131.486,50.933,32.613 +2020-02-20 23:00:00,94.47,124.87,45.32899999999999,32.613 +2020-02-20 23:15:00,90.46,123.387,45.32899999999999,32.613 +2020-02-20 23:30:00,88.59,124.492,45.32899999999999,32.613 +2020-02-20 23:45:00,88.87,124.436,45.32899999999999,32.613 +2020-02-21 00:00:00,84.07,118.053,43.74,32.613 +2020-02-21 00:15:00,90.99,117.012,43.74,32.613 +2020-02-21 00:30:00,90.98,116.915,43.74,32.613 +2020-02-21 00:45:00,87.5,117.566,43.74,32.613 +2020-02-21 01:00:00,80.73,119.194,42.555,32.613 +2020-02-21 01:15:00,82.69,120.294,42.555,32.613 +2020-02-21 01:30:00,78.88,120.279,42.555,32.613 +2020-02-21 01:45:00,79.85,120.557,42.555,32.613 +2020-02-21 02:00:00,79.02,122.663,41.68600000000001,32.613 +2020-02-21 02:15:00,86.13,123.93,41.68600000000001,32.613 +2020-02-21 02:30:00,87.4,125.331,41.68600000000001,32.613 +2020-02-21 02:45:00,87.01,127.15700000000001,41.68600000000001,32.613 +2020-02-21 03:00:00,80.72,128.641,42.278999999999996,32.613 +2020-02-21 03:15:00,80.62,131.14600000000002,42.278999999999996,32.613 +2020-02-21 03:30:00,82.45,132.915,42.278999999999996,32.613 +2020-02-21 03:45:00,82.52,134.672,42.278999999999996,32.613 +2020-02-21 04:00:00,83.5,146.612,43.742,32.613 +2020-02-21 04:15:00,88.63,158.04399999999998,43.742,32.613 +2020-02-21 04:30:00,94.41,160.84,43.742,32.613 +2020-02-21 04:45:00,95.37,161.859,43.742,32.613 +2020-02-21 05:00:00,94.16,193.079,46.973,32.613 +2020-02-21 05:15:00,92.16,222.521,46.973,32.613 +2020-02-21 05:30:00,94.26,218.762,46.973,32.613 +2020-02-21 05:45:00,99.52,211.65099999999998,46.973,32.613 +2020-02-21 06:00:00,108.6,209.43200000000002,59.63399999999999,32.613 +2020-02-21 06:15:00,111.42,213.935,59.63399999999999,32.613 +2020-02-21 06:30:00,115.46,215.37400000000002,59.63399999999999,32.613 +2020-02-21 06:45:00,117.72,220.855,59.63399999999999,32.613 +2020-02-21 07:00:00,122.43,220.72799999999998,71.631,32.613 +2020-02-21 07:15:00,123.45,225.54,71.631,32.613 +2020-02-21 07:30:00,123.07,226.58900000000003,71.631,32.613 +2020-02-21 07:45:00,124.6,225.299,71.631,32.613 +2020-02-21 08:00:00,127.22,222.543,66.181,32.613 +2020-02-21 08:15:00,125.37,221.072,66.181,32.613 +2020-02-21 08:30:00,126.6,218.17700000000002,66.181,32.613 +2020-02-21 08:45:00,123.25,212.84599999999998,66.181,32.613 +2020-02-21 09:00:00,125.59,206.114,63.086000000000006,32.613 +2020-02-21 09:15:00,125.25,203.579,63.086000000000006,32.613 +2020-02-21 09:30:00,126.21,201.69799999999998,63.086000000000006,32.613 +2020-02-21 09:45:00,128.09,198.653,63.086000000000006,32.613 +2020-02-21 10:00:00,122.61,193.30700000000002,60.886,32.613 +2020-02-21 10:15:00,124.67,190.35299999999998,60.886,32.613 +2020-02-21 10:30:00,125.15,187.81599999999997,60.886,32.613 +2020-02-21 10:45:00,124.81,186.39700000000002,60.886,32.613 +2020-02-21 11:00:00,120.18,184.442,59.391000000000005,32.613 +2020-02-21 11:15:00,120.77,182.68200000000002,59.391000000000005,32.613 +2020-02-21 11:30:00,120.33,183.52599999999998,59.391000000000005,32.613 +2020-02-21 11:45:00,120.04,182.298,59.391000000000005,32.613 +2020-02-21 12:00:00,118.73,178.43900000000002,56.172,32.613 +2020-02-21 12:15:00,118.32,176.53799999999998,56.172,32.613 +2020-02-21 12:30:00,117.18,176.092,56.172,32.613 +2020-02-21 12:45:00,119.34,177.515,56.172,32.613 +2020-02-21 13:00:00,112.77,177.671,54.406000000000006,32.613 +2020-02-21 13:15:00,112.5,177.203,54.406000000000006,32.613 +2020-02-21 13:30:00,111.04,176.26,54.406000000000006,32.613 +2020-02-21 13:45:00,111.47,175.815,54.406000000000006,32.613 +2020-02-21 14:00:00,109.79,175.38299999999998,53.578,32.613 +2020-02-21 14:15:00,110.7,175.00599999999997,53.578,32.613 +2020-02-21 14:30:00,110.76,176.04,53.578,32.613 +2020-02-21 14:45:00,110.95,177.13099999999997,53.578,32.613 +2020-02-21 15:00:00,117.42,178.542,56.568999999999996,32.613 +2020-02-21 15:15:00,120.19,177.31599999999997,56.568999999999996,32.613 +2020-02-21 15:30:00,119.17,176.987,56.568999999999996,32.613 +2020-02-21 15:45:00,115.35,178.092,56.568999999999996,32.613 +2020-02-21 16:00:00,117.52,178.511,60.169,32.613 +2020-02-21 16:15:00,115.01,180.301,60.169,32.613 +2020-02-21 16:30:00,116.7,183.232,60.169,32.613 +2020-02-21 16:45:00,119.87,184.386,60.169,32.613 +2020-02-21 17:00:00,123.86,186.592,65.497,32.613 +2020-02-21 17:15:00,126.26,188.45,65.497,32.613 +2020-02-21 17:30:00,126.38,190.63400000000001,65.497,32.613 +2020-02-21 17:45:00,128.7,191.324,65.497,32.613 +2020-02-21 18:00:00,133.63,194.234,65.082,32.613 +2020-02-21 18:15:00,132.24,193.282,65.082,32.613 +2020-02-21 18:30:00,135.09,192.361,65.082,32.613 +2020-02-21 18:45:00,134.34,193.4,65.082,32.613 +2020-02-21 19:00:00,132.35,193.229,60.968,32.613 +2020-02-21 19:15:00,136.76,191.30200000000002,60.968,32.613 +2020-02-21 19:30:00,136.33,189.387,60.968,32.613 +2020-02-21 19:45:00,136.31,185.921,60.968,32.613 +2020-02-21 20:00:00,121.81,181.247,61.123000000000005,32.613 +2020-02-21 20:15:00,119.79,175.767,61.123000000000005,32.613 +2020-02-21 20:30:00,114.65,171.91400000000002,61.123000000000005,32.613 +2020-02-21 20:45:00,115.4,170.627,61.123000000000005,32.613 +2020-02-21 21:00:00,106.52,167.949,55.416000000000004,32.613 +2020-02-21 21:15:00,104.0,165.347,55.416000000000004,32.613 +2020-02-21 21:30:00,102.97,163.342,55.416000000000004,32.613 +2020-02-21 21:45:00,103.72,163.263,55.416000000000004,32.613 +2020-02-21 22:00:00,97.94,157.187,51.631,32.613 +2020-02-21 22:15:00,94.54,152.662,51.631,32.613 +2020-02-21 22:30:00,92.36,144.856,51.631,32.613 +2020-02-21 22:45:00,96.36,140.861,51.631,32.613 +2020-02-21 23:00:00,96.29,133.83,44.898,32.613 +2020-02-21 23:15:00,93.93,130.423,44.898,32.613 +2020-02-21 23:30:00,87.56,130.07399999999998,44.898,32.613 +2020-02-21 23:45:00,86.86,129.387,44.898,32.613 +2020-02-22 00:00:00,80.69,115.046,42.033,32.431999999999995 +2020-02-22 00:15:00,85.92,109.822,42.033,32.431999999999995 +2020-02-22 00:30:00,86.31,111.001,42.033,32.431999999999995 +2020-02-22 00:45:00,83.99,112.337,42.033,32.431999999999995 +2020-02-22 01:00:00,75.49,114.553,38.255,32.431999999999995 +2020-02-22 01:15:00,77.66,114.685,38.255,32.431999999999995 +2020-02-22 01:30:00,74.25,114.135,38.255,32.431999999999995 +2020-02-22 01:45:00,78.27,114.225,38.255,32.431999999999995 +2020-02-22 02:00:00,80.13,116.98200000000001,36.404,32.431999999999995 +2020-02-22 02:15:00,81.83,117.868,36.404,32.431999999999995 +2020-02-22 02:30:00,77.76,118.184,36.404,32.431999999999995 +2020-02-22 02:45:00,77.53,120.12299999999999,36.404,32.431999999999995 +2020-02-22 03:00:00,76.75,122.20700000000001,36.083,32.431999999999995 +2020-02-22 03:15:00,80.16,123.5,36.083,32.431999999999995 +2020-02-22 03:30:00,80.23,123.714,36.083,32.431999999999995 +2020-02-22 03:45:00,78.0,125.616,36.083,32.431999999999995 +2020-02-22 04:00:00,72.92,133.475,36.102,32.431999999999995 +2020-02-22 04:15:00,73.17,142.395,36.102,32.431999999999995 +2020-02-22 04:30:00,73.24,143.054,36.102,32.431999999999995 +2020-02-22 04:45:00,74.17,143.582,36.102,32.431999999999995 +2020-02-22 05:00:00,74.51,159.02200000000002,35.284,32.431999999999995 +2020-02-22 05:15:00,75.89,169.7,35.284,32.431999999999995 +2020-02-22 05:30:00,75.3,166.28599999999997,35.284,32.431999999999995 +2020-02-22 05:45:00,76.79,164.58599999999998,35.284,32.431999999999995 +2020-02-22 06:00:00,78.47,180.94,36.265,32.431999999999995 +2020-02-22 06:15:00,79.02,201.58900000000003,36.265,32.431999999999995 +2020-02-22 06:30:00,79.89,197.68200000000002,36.265,32.431999999999995 +2020-02-22 06:45:00,80.02,194.308,36.265,32.431999999999995 +2020-02-22 07:00:00,81.54,190.49099999999999,40.714,32.431999999999995 +2020-02-22 07:15:00,85.98,194.023,40.714,32.431999999999995 +2020-02-22 07:30:00,86.18,197.71,40.714,32.431999999999995 +2020-02-22 07:45:00,87.86,200.28400000000002,40.714,32.431999999999995 +2020-02-22 08:00:00,89.81,201.467,46.692,32.431999999999995 +2020-02-22 08:15:00,91.73,203.52200000000002,46.692,32.431999999999995 +2020-02-22 08:30:00,98.01,202.149,46.692,32.431999999999995 +2020-02-22 08:45:00,99.24,199.93,46.692,32.431999999999995 +2020-02-22 09:00:00,102.43,194.99599999999998,48.925,32.431999999999995 +2020-02-22 09:15:00,102.12,193.207,48.925,32.431999999999995 +2020-02-22 09:30:00,102.49,192.239,48.925,32.431999999999995 +2020-02-22 09:45:00,99.78,189.347,48.925,32.431999999999995 +2020-02-22 10:00:00,95.56,184.31099999999998,47.799,32.431999999999995 +2020-02-22 10:15:00,99.82,181.56099999999998,47.799,32.431999999999995 +2020-02-22 10:30:00,100.4,179.19,47.799,32.431999999999995 +2020-02-22 10:45:00,99.55,179.048,47.799,32.431999999999995 +2020-02-22 11:00:00,95.77,177.29,44.309,32.431999999999995 +2020-02-22 11:15:00,95.07,174.896,44.309,32.431999999999995 +2020-02-22 11:30:00,94.71,174.668,44.309,32.431999999999995 +2020-02-22 11:45:00,97.76,172.54,44.309,32.431999999999995 +2020-02-22 12:00:00,92.83,167.857,42.367,32.431999999999995 +2020-02-22 12:15:00,92.41,166.61599999999999,42.367,32.431999999999995 +2020-02-22 12:30:00,88.04,166.44400000000002,42.367,32.431999999999995 +2020-02-22 12:45:00,86.69,167.13400000000001,42.367,32.431999999999995 +2020-02-22 13:00:00,82.02,166.915,39.036,32.431999999999995 +2020-02-22 13:15:00,85.74,164.417,39.036,32.431999999999995 +2020-02-22 13:30:00,79.5,163.02700000000002,39.036,32.431999999999995 +2020-02-22 13:45:00,80.82,163.011,39.036,32.431999999999995 +2020-02-22 14:00:00,78.34,163.83100000000002,37.995,32.431999999999995 +2020-02-22 14:15:00,78.85,162.882,37.995,32.431999999999995 +2020-02-22 14:30:00,80.14,162.118,37.995,32.431999999999995 +2020-02-22 14:45:00,81.7,163.444,37.995,32.431999999999995 +2020-02-22 15:00:00,81.24,165.505,40.71,32.431999999999995 +2020-02-22 15:15:00,82.52,165.051,40.71,32.431999999999995 +2020-02-22 15:30:00,83.61,166.18200000000002,40.71,32.431999999999995 +2020-02-22 15:45:00,85.83,167.262,40.71,32.431999999999995 +2020-02-22 16:00:00,87.34,166.548,46.998000000000005,32.431999999999995 +2020-02-22 16:15:00,91.33,169.175,46.998000000000005,32.431999999999995 +2020-02-22 16:30:00,93.53,172.06,46.998000000000005,32.431999999999995 +2020-02-22 16:45:00,92.11,174.048,46.998000000000005,32.431999999999995 +2020-02-22 17:00:00,99.16,175.66,55.431000000000004,32.431999999999995 +2020-02-22 17:15:00,98.14,179.105,55.431000000000004,32.431999999999995 +2020-02-22 17:30:00,103.35,181.222,55.431000000000004,32.431999999999995 +2020-02-22 17:45:00,104.88,181.547,55.431000000000004,32.431999999999995 +2020-02-22 18:00:00,113.78,184.007,55.989,32.431999999999995 +2020-02-22 18:15:00,112.69,184.90400000000002,55.989,32.431999999999995 +2020-02-22 18:30:00,112.95,185.31,55.989,32.431999999999995 +2020-02-22 18:45:00,112.81,183.035,55.989,32.431999999999995 +2020-02-22 19:00:00,111.89,183.692,50.882,32.431999999999995 +2020-02-22 19:15:00,112.4,181.27599999999998,50.882,32.431999999999995 +2020-02-22 19:30:00,107.62,180.136,50.882,32.431999999999995 +2020-02-22 19:45:00,106.47,176.567,50.882,32.431999999999995 +2020-02-22 20:00:00,101.54,174.00799999999998,43.172,32.431999999999995 +2020-02-22 20:15:00,99.49,170.608,43.172,32.431999999999995 +2020-02-22 20:30:00,96.48,166.41400000000002,43.172,32.431999999999995 +2020-02-22 20:45:00,98.36,164.771,43.172,32.431999999999995 +2020-02-22 21:00:00,91.47,164.25099999999998,37.599000000000004,32.431999999999995 +2020-02-22 21:15:00,90.24,162.063,37.599000000000004,32.431999999999995 +2020-02-22 21:30:00,88.52,161.263,37.599000000000004,32.431999999999995 +2020-02-22 21:45:00,88.73,160.796,37.599000000000004,32.431999999999995 +2020-02-22 22:00:00,85.26,156.02100000000002,39.047,32.431999999999995 +2020-02-22 22:15:00,84.4,153.955,39.047,32.431999999999995 +2020-02-22 22:30:00,81.79,152.175,39.047,32.431999999999995 +2020-02-22 22:45:00,81.3,150.017,39.047,32.431999999999995 +2020-02-22 23:00:00,77.58,145.344,32.339,32.431999999999995 +2020-02-22 23:15:00,77.79,140.363,32.339,32.431999999999995 +2020-02-22 23:30:00,74.96,138.365,32.339,32.431999999999995 +2020-02-22 23:45:00,73.63,135.339,32.339,32.431999999999995 +2020-02-23 00:00:00,70.14,115.244,29.988000000000003,32.431999999999995 +2020-02-23 00:15:00,69.61,109.686,29.988000000000003,32.431999999999995 +2020-02-23 00:30:00,69.59,110.479,29.988000000000003,32.431999999999995 +2020-02-23 00:45:00,69.38,112.488,29.988000000000003,32.431999999999995 +2020-02-23 01:00:00,65.46,114.53399999999999,28.531999999999996,32.431999999999995 +2020-02-23 01:15:00,66.66,115.661,28.531999999999996,32.431999999999995 +2020-02-23 01:30:00,65.52,115.603,28.531999999999996,32.431999999999995 +2020-02-23 01:45:00,65.26,115.382,28.531999999999996,32.431999999999995 +2020-02-23 02:00:00,63.83,117.405,27.805999999999997,32.431999999999995 +2020-02-23 02:15:00,64.64,117.473,27.805999999999997,32.431999999999995 +2020-02-23 02:30:00,63.77,118.64200000000001,27.805999999999997,32.431999999999995 +2020-02-23 02:45:00,64.98,121.022,27.805999999999997,32.431999999999995 +2020-02-23 03:00:00,63.94,123.436,26.193,32.431999999999995 +2020-02-23 03:15:00,64.7,124.21799999999999,26.193,32.431999999999995 +2020-02-23 03:30:00,64.1,125.774,26.193,32.431999999999995 +2020-02-23 03:45:00,64.57,127.594,26.193,32.431999999999995 +2020-02-23 04:00:00,64.91,135.216,27.19,32.431999999999995 +2020-02-23 04:15:00,64.97,143.135,27.19,32.431999999999995 +2020-02-23 04:30:00,65.65,143.904,27.19,32.431999999999995 +2020-02-23 04:45:00,66.48,144.673,27.19,32.431999999999995 +2020-02-23 05:00:00,67.26,156.69,28.166999999999998,32.431999999999995 +2020-02-23 05:15:00,67.76,165.021,28.166999999999998,32.431999999999995 +2020-02-23 05:30:00,68.15,161.405,28.166999999999998,32.431999999999995 +2020-02-23 05:45:00,68.8,159.939,28.166999999999998,32.431999999999995 +2020-02-23 06:00:00,69.41,176.139,27.16,32.431999999999995 +2020-02-23 06:15:00,69.86,195.146,27.16,32.431999999999995 +2020-02-23 06:30:00,70.71,190.112,27.16,32.431999999999995 +2020-02-23 06:45:00,69.95,185.675,27.16,32.431999999999995 +2020-02-23 07:00:00,72.62,184.268,29.578000000000003,32.431999999999995 +2020-02-23 07:15:00,73.12,186.9,29.578000000000003,32.431999999999995 +2020-02-23 07:30:00,74.51,189.389,29.578000000000003,32.431999999999995 +2020-02-23 07:45:00,76.29,191.18200000000002,29.578000000000003,32.431999999999995 +2020-02-23 08:00:00,78.45,194.149,34.650999999999996,32.431999999999995 +2020-02-23 08:15:00,78.33,196.13,34.650999999999996,32.431999999999995 +2020-02-23 08:30:00,78.73,196.333,34.650999999999996,32.431999999999995 +2020-02-23 08:45:00,79.07,196.078,34.650999999999996,32.431999999999995 +2020-02-23 09:00:00,78.69,190.75900000000001,38.080999999999996,32.431999999999995 +2020-02-23 09:15:00,78.31,189.495,38.080999999999996,32.431999999999995 +2020-02-23 09:30:00,78.05,188.407,38.080999999999996,32.431999999999995 +2020-02-23 09:45:00,77.75,185.43599999999998,38.080999999999996,32.431999999999995 +2020-02-23 10:00:00,76.21,182.868,39.934,32.431999999999995 +2020-02-23 10:15:00,75.97,180.67,39.934,32.431999999999995 +2020-02-23 10:30:00,75.82,178.90400000000002,39.934,32.431999999999995 +2020-02-23 10:45:00,76.78,176.976,39.934,32.431999999999995 +2020-02-23 11:00:00,77.47,176.084,43.74100000000001,32.431999999999995 +2020-02-23 11:15:00,80.97,173.822,43.74100000000001,32.431999999999995 +2020-02-23 11:30:00,81.83,172.77700000000002,43.74100000000001,32.431999999999995 +2020-02-23 11:45:00,84.37,171.255,43.74100000000001,32.431999999999995 +2020-02-23 12:00:00,80.33,166.08,40.001999999999995,32.431999999999995 +2020-02-23 12:15:00,75.8,166.668,40.001999999999995,32.431999999999995 +2020-02-23 12:30:00,75.85,165.06900000000002,40.001999999999995,32.431999999999995 +2020-02-23 12:45:00,72.42,164.801,40.001999999999995,32.431999999999995 +2020-02-23 13:00:00,71.1,163.916,37.855,32.431999999999995 +2020-02-23 13:15:00,69.74,164.28099999999998,37.855,32.431999999999995 +2020-02-23 13:30:00,66.49,162.636,37.855,32.431999999999995 +2020-02-23 13:45:00,65.1,162.02700000000002,37.855,32.431999999999995 +2020-02-23 14:00:00,63.85,163.18200000000002,35.946999999999996,32.431999999999995 +2020-02-23 14:15:00,67.63,163.381,35.946999999999996,32.431999999999995 +2020-02-23 14:30:00,65.18,163.741,35.946999999999996,32.431999999999995 +2020-02-23 14:45:00,66.34,164.646,35.946999999999996,32.431999999999995 +2020-02-23 15:00:00,67.38,165.24599999999998,35.138000000000005,32.431999999999995 +2020-02-23 15:15:00,71.0,165.46400000000003,35.138000000000005,32.431999999999995 +2020-02-23 15:30:00,70.06,167.115,35.138000000000005,32.431999999999995 +2020-02-23 15:45:00,71.4,168.856,35.138000000000005,32.431999999999995 +2020-02-23 16:00:00,77.36,169.905,38.672,32.431999999999995 +2020-02-23 16:15:00,75.53,171.645,38.672,32.431999999999995 +2020-02-23 16:30:00,77.71,174.812,38.672,32.431999999999995 +2020-02-23 16:45:00,81.19,176.903,38.672,32.431999999999995 +2020-02-23 17:00:00,88.54,178.53599999999997,48.684,32.431999999999995 +2020-02-23 17:15:00,86.32,181.71900000000002,48.684,32.431999999999995 +2020-02-23 17:30:00,90.78,184.165,48.684,32.431999999999995 +2020-02-23 17:45:00,96.64,186.72099999999998,48.684,32.431999999999995 +2020-02-23 18:00:00,104.83,188.683,51.568999999999996,32.431999999999995 +2020-02-23 18:15:00,107.06,190.919,51.568999999999996,32.431999999999995 +2020-02-23 18:30:00,107.23,189.291,51.568999999999996,32.431999999999995 +2020-02-23 18:45:00,103.85,188.84,51.568999999999996,32.431999999999995 +2020-02-23 19:00:00,101.61,189.18900000000002,48.608000000000004,32.431999999999995 +2020-02-23 19:15:00,102.96,187.34,48.608000000000004,32.431999999999995 +2020-02-23 19:30:00,99.45,186.058,48.608000000000004,32.431999999999995 +2020-02-23 19:45:00,100.59,183.923,48.608000000000004,32.431999999999995 +2020-02-23 20:00:00,105.69,181.30900000000003,43.733999999999995,32.431999999999995 +2020-02-23 20:15:00,97.93,178.87599999999998,43.733999999999995,32.431999999999995 +2020-02-23 20:30:00,93.18,175.926,43.733999999999995,32.431999999999995 +2020-02-23 20:45:00,98.42,173.078,43.733999999999995,32.431999999999995 +2020-02-23 21:00:00,92.64,170.021,39.283,32.431999999999995 +2020-02-23 21:15:00,88.96,167.207,39.283,32.431999999999995 +2020-02-23 21:30:00,90.04,166.68,39.283,32.431999999999995 +2020-02-23 21:45:00,93.24,166.37099999999998,39.283,32.431999999999995 +2020-02-23 22:00:00,98.28,160.47,40.111,32.431999999999995 +2020-02-23 22:15:00,92.33,157.649,40.111,32.431999999999995 +2020-02-23 22:30:00,94.51,152.731,40.111,32.431999999999995 +2020-02-23 22:45:00,91.38,149.721,40.111,32.431999999999995 +2020-02-23 23:00:00,86.18,142.312,35.791,32.431999999999995 +2020-02-23 23:15:00,83.82,139.184,35.791,32.431999999999995 +2020-02-23 23:30:00,84.12,137.974,35.791,32.431999999999995 +2020-02-23 23:45:00,89.48,135.843,35.791,32.431999999999995 +2020-02-24 00:00:00,87.4,119.12100000000001,34.311,32.613 +2020-02-24 00:15:00,85.71,116.48200000000001,34.311,32.613 +2020-02-24 00:30:00,79.25,117.37,34.311,32.613 +2020-02-24 00:45:00,80.11,118.845,34.311,32.613 +2020-02-24 01:00:00,75.53,120.87299999999999,34.585,32.613 +2020-02-24 01:15:00,82.25,121.47399999999999,34.585,32.613 +2020-02-24 01:30:00,82.39,121.463,34.585,32.613 +2020-02-24 01:45:00,83.21,121.355,34.585,32.613 +2020-02-24 02:00:00,78.47,123.35799999999999,34.111,32.613 +2020-02-24 02:15:00,81.62,124.824,34.111,32.613 +2020-02-24 02:30:00,83.49,126.336,34.111,32.613 +2020-02-24 02:45:00,85.86,128.109,34.111,32.613 +2020-02-24 03:00:00,77.68,131.778,32.435,32.613 +2020-02-24 03:15:00,84.84,134.17,32.435,32.613 +2020-02-24 03:30:00,85.17,135.471,32.435,32.613 +2020-02-24 03:45:00,85.91,136.755,32.435,32.613 +2020-02-24 04:00:00,83.63,148.685,33.04,32.613 +2020-02-24 04:15:00,83.89,160.695,33.04,32.613 +2020-02-24 04:30:00,89.11,163.607,33.04,32.613 +2020-02-24 04:45:00,86.29,164.52900000000002,33.04,32.613 +2020-02-24 05:00:00,90.81,192.02599999999998,40.399,32.613 +2020-02-24 05:15:00,91.34,220.23,40.399,32.613 +2020-02-24 05:30:00,95.51,216.81900000000002,40.399,32.613 +2020-02-24 05:45:00,100.68,209.787,40.399,32.613 +2020-02-24 06:00:00,112.19,208.31799999999998,60.226000000000006,32.613 +2020-02-24 06:15:00,114.53,212.699,60.226000000000006,32.613 +2020-02-24 06:30:00,121.55,215.514,60.226000000000006,32.613 +2020-02-24 06:45:00,120.58,219.74099999999999,60.226000000000006,32.613 +2020-02-24 07:00:00,127.09,220.673,73.578,32.613 +2020-02-24 07:15:00,128.48,224.535,73.578,32.613 +2020-02-24 07:30:00,128.83,226.213,73.578,32.613 +2020-02-24 07:45:00,128.43,225.47400000000002,73.578,32.613 +2020-02-24 08:00:00,132.65,223.62099999999998,66.58,32.613 +2020-02-24 08:15:00,130.35,223.472,66.58,32.613 +2020-02-24 08:30:00,132.82,219.62400000000002,66.58,32.613 +2020-02-24 08:45:00,128.9,216.138,66.58,32.613 +2020-02-24 09:00:00,130.95,209.83700000000002,62.0,32.613 +2020-02-24 09:15:00,129.17,205.146,62.0,32.613 +2020-02-24 09:30:00,128.34,203.085,62.0,32.613 +2020-02-24 09:45:00,127.77,200.359,62.0,32.613 +2020-02-24 10:00:00,125.53,196.74900000000002,59.099,32.613 +2020-02-24 10:15:00,127.37,194.266,59.099,32.613 +2020-02-24 10:30:00,123.36,191.65599999999998,59.099,32.613 +2020-02-24 10:45:00,127.17,190.43200000000002,59.099,32.613 +2020-02-24 11:00:00,122.95,187.0,57.729,32.613 +2020-02-24 11:15:00,126.73,186.50900000000001,57.729,32.613 +2020-02-24 11:30:00,127.56,186.827,57.729,32.613 +2020-02-24 11:45:00,123.32,184.93599999999998,57.729,32.613 +2020-02-24 12:00:00,122.5,181.40099999999998,55.615,32.613 +2020-02-24 12:15:00,126.78,182.00599999999997,55.615,32.613 +2020-02-24 12:30:00,127.67,180.593,55.615,32.613 +2020-02-24 12:45:00,124.86,181.798,55.615,32.613 +2020-02-24 13:00:00,122.88,181.528,56.515,32.613 +2020-02-24 13:15:00,123.82,180.50599999999997,56.515,32.613 +2020-02-24 13:30:00,125.65,178.34,56.515,32.613 +2020-02-24 13:45:00,128.04,177.78599999999997,56.515,32.613 +2020-02-24 14:00:00,130.27,178.38099999999997,58.1,32.613 +2020-02-24 14:15:00,128.28,177.951,58.1,32.613 +2020-02-24 14:30:00,131.05,177.762,58.1,32.613 +2020-02-24 14:45:00,130.12,178.701,58.1,32.613 +2020-02-24 15:00:00,130.46,181.032,59.801,32.613 +2020-02-24 15:15:00,131.88,179.815,59.801,32.613 +2020-02-24 15:30:00,129.22,180.639,59.801,32.613 +2020-02-24 15:45:00,129.42,181.919,59.801,32.613 +2020-02-24 16:00:00,129.55,183.18599999999998,62.901,32.613 +2020-02-24 16:15:00,129.72,184.172,62.901,32.613 +2020-02-24 16:30:00,127.89,186.385,62.901,32.613 +2020-02-24 16:45:00,129.96,187.296,62.901,32.613 +2020-02-24 17:00:00,133.58,188.75,70.418,32.613 +2020-02-24 17:15:00,136.65,191.028,70.418,32.613 +2020-02-24 17:30:00,139.14,192.96,70.418,32.613 +2020-02-24 17:45:00,138.19,194.074,70.418,32.613 +2020-02-24 18:00:00,144.78,196.45,71.726,32.613 +2020-02-24 18:15:00,140.22,196.56,71.726,32.613 +2020-02-24 18:30:00,140.06,195.55599999999998,71.726,32.613 +2020-02-24 18:45:00,141.43,195.878,71.726,32.613 +2020-02-24 19:00:00,139.29,194.61599999999999,65.997,32.613 +2020-02-24 19:15:00,138.24,191.66,65.997,32.613 +2020-02-24 19:30:00,144.32,190.885,65.997,32.613 +2020-02-24 19:45:00,141.12,187.96599999999998,65.997,32.613 +2020-02-24 20:00:00,133.01,182.99099999999999,68.09100000000001,32.613 +2020-02-24 20:15:00,125.91,178.175,68.09100000000001,32.613 +2020-02-24 20:30:00,120.23,173.433,68.09100000000001,32.613 +2020-02-24 20:45:00,121.43,172.179,68.09100000000001,32.613 +2020-02-24 21:00:00,114.0,169.61900000000003,59.617,32.613 +2020-02-24 21:15:00,117.58,165.65599999999998,59.617,32.613 +2020-02-24 21:30:00,117.76,164.327,59.617,32.613 +2020-02-24 21:45:00,115.93,163.54,59.617,32.613 +2020-02-24 22:00:00,105.83,154.77,54.938,32.613 +2020-02-24 22:15:00,101.05,150.715,54.938,32.613 +2020-02-24 22:30:00,99.67,136.342,54.938,32.613 +2020-02-24 22:45:00,98.93,128.609,54.938,32.613 +2020-02-24 23:00:00,95.34,121.956,47.43,32.613 +2020-02-24 23:15:00,94.94,121.412,47.43,32.613 +2020-02-24 23:30:00,89.82,122.914,47.43,32.613 +2020-02-24 23:45:00,89.72,123.382,47.43,32.613 +2020-02-25 00:00:00,92.74,118.161,48.354,32.613 +2020-02-25 00:15:00,93.6,116.93299999999999,48.354,32.613 +2020-02-25 00:30:00,94.11,116.89200000000001,48.354,32.613 +2020-02-25 00:45:00,89.59,117.417,48.354,32.613 +2020-02-25 01:00:00,84.84,119.20700000000001,45.68600000000001,32.613 +2020-02-25 01:15:00,86.12,119.359,45.68600000000001,32.613 +2020-02-25 01:30:00,82.8,119.501,45.68600000000001,32.613 +2020-02-25 01:45:00,83.08,119.69,45.68600000000001,32.613 +2020-02-25 02:00:00,84.51,121.675,44.269,32.613 +2020-02-25 02:15:00,87.23,123.042,44.269,32.613 +2020-02-25 02:30:00,89.0,123.98200000000001,44.269,32.613 +2020-02-25 02:45:00,93.75,125.771,44.269,32.613 +2020-02-25 03:00:00,88.41,128.25799999999998,44.187,32.613 +2020-02-25 03:15:00,89.23,129.819,44.187,32.613 +2020-02-25 03:30:00,85.06,131.588,44.187,32.613 +2020-02-25 03:45:00,89.04,133.059,44.187,32.613 +2020-02-25 04:00:00,86.27,144.786,46.126999999999995,32.613 +2020-02-25 04:15:00,86.74,156.446,46.126999999999995,32.613 +2020-02-25 04:30:00,88.33,159.06799999999998,46.126999999999995,32.613 +2020-02-25 04:45:00,91.62,161.172,46.126999999999995,32.613 +2020-02-25 05:00:00,97.3,193.585,49.666000000000004,32.613 +2020-02-25 05:15:00,96.94,221.614,49.666000000000004,32.613 +2020-02-25 05:30:00,101.12,216.666,49.666000000000004,32.613 +2020-02-25 05:45:00,105.3,209.638,49.666000000000004,32.613 +2020-02-25 06:00:00,116.13,207.021,61.077,32.613 +2020-02-25 06:15:00,118.32,213.012,61.077,32.613 +2020-02-25 06:30:00,121.9,215.149,61.077,32.613 +2020-02-25 06:45:00,123.41,218.986,61.077,32.613 +2020-02-25 07:00:00,130.25,219.761,74.717,32.613 +2020-02-25 07:15:00,129.83,223.43400000000003,74.717,32.613 +2020-02-25 07:30:00,131.97,224.53900000000002,74.717,32.613 +2020-02-25 07:45:00,129.74,223.96,74.717,32.613 +2020-02-25 08:00:00,132.69,222.18900000000002,69.033,32.613 +2020-02-25 08:15:00,133.52,221.015,69.033,32.613 +2020-02-25 08:30:00,129.26,216.93599999999998,69.033,32.613 +2020-02-25 08:45:00,128.0,213.18900000000002,69.033,32.613 +2020-02-25 09:00:00,134.63,206.084,63.113,32.613 +2020-02-25 09:15:00,134.74,202.96200000000002,63.113,32.613 +2020-02-25 09:30:00,137.02,201.59400000000002,63.113,32.613 +2020-02-25 09:45:00,135.12,198.675,63.113,32.613 +2020-02-25 10:00:00,127.82,194.487,61.461999999999996,32.613 +2020-02-25 10:15:00,127.61,190.99599999999998,61.461999999999996,32.613 +2020-02-25 10:30:00,124.56,188.578,61.461999999999996,32.613 +2020-02-25 10:45:00,124.47,187.65900000000002,61.461999999999996,32.613 +2020-02-25 11:00:00,120.98,185.67700000000002,59.614,32.613 +2020-02-25 11:15:00,121.56,184.88099999999997,59.614,32.613 +2020-02-25 11:30:00,122.41,184.05200000000002,59.614,32.613 +2020-02-25 11:45:00,121.67,182.856,59.614,32.613 +2020-02-25 12:00:00,121.07,177.997,57.415,32.613 +2020-02-25 12:15:00,120.61,178.21200000000002,57.415,32.613 +2020-02-25 12:30:00,117.95,177.486,57.415,32.613 +2020-02-25 12:45:00,118.6,178.41099999999997,57.415,32.613 +2020-02-25 13:00:00,116.09,177.77200000000002,58.534,32.613 +2020-02-25 13:15:00,116.32,176.41299999999998,58.534,32.613 +2020-02-25 13:30:00,118.93,175.377,58.534,32.613 +2020-02-25 13:45:00,116.93,174.995,58.534,32.613 +2020-02-25 14:00:00,119.27,175.935,59.415,32.613 +2020-02-25 14:15:00,117.11,175.63400000000001,59.415,32.613 +2020-02-25 14:30:00,118.09,176.03900000000002,59.415,32.613 +2020-02-25 14:45:00,119.19,176.899,59.415,32.613 +2020-02-25 15:00:00,121.2,178.81400000000002,62.071999999999996,32.613 +2020-02-25 15:15:00,119.3,177.905,62.071999999999996,32.613 +2020-02-25 15:30:00,119.76,178.90200000000002,62.071999999999996,32.613 +2020-02-25 15:45:00,121.39,179.77700000000002,62.071999999999996,32.613 +2020-02-25 16:00:00,123.07,181.363,64.99,32.613 +2020-02-25 16:15:00,125.94,182.801,64.99,32.613 +2020-02-25 16:30:00,123.48,185.644,64.99,32.613 +2020-02-25 16:45:00,125.86,186.815,64.99,32.613 +2020-02-25 17:00:00,133.26,188.80900000000003,72.658,32.613 +2020-02-25 17:15:00,133.66,191.135,72.658,32.613 +2020-02-25 17:30:00,136.58,193.74400000000003,72.658,32.613 +2020-02-25 17:45:00,134.98,194.747,72.658,32.613 +2020-02-25 18:00:00,142.9,197.021,73.645,32.613 +2020-02-25 18:15:00,140.35,196.71400000000003,73.645,32.613 +2020-02-25 18:30:00,140.6,195.40400000000002,73.645,32.613 +2020-02-25 18:45:00,140.89,196.53,73.645,32.613 +2020-02-25 19:00:00,138.89,195.298,67.085,32.613 +2020-02-25 19:15:00,137.3,192.084,67.085,32.613 +2020-02-25 19:30:00,143.66,190.675,67.085,32.613 +2020-02-25 19:45:00,146.82,187.835,67.085,32.613 +2020-02-25 20:00:00,137.15,182.99099999999999,66.138,32.613 +2020-02-25 20:15:00,124.2,177.548,66.138,32.613 +2020-02-25 20:30:00,125.13,173.822,66.138,32.613 +2020-02-25 20:45:00,122.61,172.012,66.138,32.613 +2020-02-25 21:00:00,114.79,168.75900000000001,57.512,32.613 +2020-02-25 21:15:00,117.9,165.666,57.512,32.613 +2020-02-25 21:30:00,118.78,163.622,57.512,32.613 +2020-02-25 21:45:00,115.59,163.079,57.512,32.613 +2020-02-25 22:00:00,105.29,155.977,54.545,32.613 +2020-02-25 22:15:00,105.73,151.689,54.545,32.613 +2020-02-25 22:30:00,100.32,137.36,54.545,32.613 +2020-02-25 22:45:00,103.78,129.903,54.545,32.613 +2020-02-25 23:00:00,103.37,123.266,48.605,32.613 +2020-02-25 23:15:00,102.21,121.863,48.605,32.613 +2020-02-25 23:30:00,94.38,123.02,48.605,32.613 +2020-02-25 23:45:00,93.16,123.08200000000001,48.605,32.613 +2020-02-26 00:00:00,84.21,117.904,45.675,32.613 +2020-02-26 00:15:00,85.32,116.678,45.675,32.613 +2020-02-26 00:30:00,85.53,116.619,45.675,32.613 +2020-02-26 00:45:00,85.09,117.14299999999999,45.675,32.613 +2020-02-26 01:00:00,86.21,118.89200000000001,43.015,32.613 +2020-02-26 01:15:00,91.24,119.031,43.015,32.613 +2020-02-26 01:30:00,91.43,119.15899999999999,43.015,32.613 +2020-02-26 01:45:00,91.86,119.352,43.015,32.613 +2020-02-26 02:00:00,88.93,121.329,41.0,32.613 +2020-02-26 02:15:00,90.95,122.693,41.0,32.613 +2020-02-26 02:30:00,91.59,123.649,41.0,32.613 +2020-02-26 02:45:00,90.28,125.43700000000001,41.0,32.613 +2020-02-26 03:00:00,90.74,127.93299999999999,41.318000000000005,32.613 +2020-02-26 03:15:00,95.62,129.485,41.318000000000005,32.613 +2020-02-26 03:30:00,92.99,131.248,41.318000000000005,32.613 +2020-02-26 03:45:00,89.88,132.731,41.318000000000005,32.613 +2020-02-26 04:00:00,92.31,144.463,42.544,32.613 +2020-02-26 04:15:00,89.03,156.114,42.544,32.613 +2020-02-26 04:30:00,88.74,158.754,42.544,32.613 +2020-02-26 04:45:00,89.87,160.845,42.544,32.613 +2020-02-26 05:00:00,94.12,193.24400000000003,45.161,32.613 +2020-02-26 05:15:00,95.66,221.297,45.161,32.613 +2020-02-26 05:30:00,99.71,216.31599999999997,45.161,32.613 +2020-02-26 05:45:00,104.02,209.291,45.161,32.613 +2020-02-26 06:00:00,116.54,206.68099999999998,61.86600000000001,32.613 +2020-02-26 06:15:00,119.33,212.68099999999998,61.86600000000001,32.613 +2020-02-26 06:30:00,124.91,214.77900000000002,61.86600000000001,32.613 +2020-02-26 06:45:00,124.25,218.601,61.86600000000001,32.613 +2020-02-26 07:00:00,132.05,219.398,77.814,32.613 +2020-02-26 07:15:00,131.93,223.041,77.814,32.613 +2020-02-26 07:30:00,130.23,224.108,77.814,32.613 +2020-02-26 07:45:00,131.9,223.488,77.814,32.613 +2020-02-26 08:00:00,135.76,221.69299999999998,70.251,32.613 +2020-02-26 08:15:00,134.68,220.50099999999998,70.251,32.613 +2020-02-26 08:30:00,134.89,216.365,70.251,32.613 +2020-02-26 08:45:00,132.39,212.62900000000002,70.251,32.613 +2020-02-26 09:00:00,132.02,205.533,66.965,32.613 +2020-02-26 09:15:00,137.94,202.412,66.965,32.613 +2020-02-26 09:30:00,138.13,201.06400000000002,66.965,32.613 +2020-02-26 09:45:00,140.89,198.15200000000002,66.965,32.613 +2020-02-26 10:00:00,135.54,193.976,63.628,32.613 +2020-02-26 10:15:00,133.05,190.52200000000002,63.628,32.613 +2020-02-26 10:30:00,129.5,188.12099999999998,63.628,32.613 +2020-02-26 10:45:00,130.84,187.217,63.628,32.613 +2020-02-26 11:00:00,122.72,185.227,62.516999999999996,32.613 +2020-02-26 11:15:00,122.07,184.449,62.516999999999996,32.613 +2020-02-26 11:30:00,120.82,183.62599999999998,62.516999999999996,32.613 +2020-02-26 11:45:00,120.99,182.445,62.516999999999996,32.613 +2020-02-26 12:00:00,121.64,177.602,60.888999999999996,32.613 +2020-02-26 12:15:00,119.41,177.832,60.888999999999996,32.613 +2020-02-26 12:30:00,119.83,177.074,60.888999999999996,32.613 +2020-02-26 12:45:00,121.37,177.99599999999998,60.888999999999996,32.613 +2020-02-26 13:00:00,118.3,177.392,61.57899999999999,32.613 +2020-02-26 13:15:00,121.25,176.01,61.57899999999999,32.613 +2020-02-26 13:30:00,119.3,174.96,61.57899999999999,32.613 +2020-02-26 13:45:00,117.57,174.58,61.57899999999999,32.613 +2020-02-26 14:00:00,120.42,175.582,62.602,32.613 +2020-02-26 14:15:00,120.08,175.25900000000001,62.602,32.613 +2020-02-26 14:30:00,127.13,175.64,62.602,32.613 +2020-02-26 14:45:00,123.18,176.512,62.602,32.613 +2020-02-26 15:00:00,124.28,178.433,64.259,32.613 +2020-02-26 15:15:00,121.09,177.495,64.259,32.613 +2020-02-26 15:30:00,121.21,178.44799999999998,64.259,32.613 +2020-02-26 15:45:00,124.4,179.30599999999998,64.259,32.613 +2020-02-26 16:00:00,123.44,180.894,67.632,32.613 +2020-02-26 16:15:00,128.2,182.315,67.632,32.613 +2020-02-26 16:30:00,126.24,185.15900000000002,67.632,32.613 +2020-02-26 16:45:00,129.82,186.3,67.632,32.613 +2020-02-26 17:00:00,132.44,188.3,72.583,32.613 +2020-02-26 17:15:00,132.18,190.64,72.583,32.613 +2020-02-26 17:30:00,138.18,193.27700000000002,72.583,32.613 +2020-02-26 17:45:00,137.44,194.299,72.583,32.613 +2020-02-26 18:00:00,144.08,196.584,72.744,32.613 +2020-02-26 18:15:00,142.07,196.34,72.744,32.613 +2020-02-26 18:30:00,143.05,195.028,72.744,32.613 +2020-02-26 18:45:00,143.25,196.175,72.744,32.613 +2020-02-26 19:00:00,142.61,194.905,69.684,32.613 +2020-02-26 19:15:00,143.22,191.708,69.684,32.613 +2020-02-26 19:30:00,148.53,190.325,69.684,32.613 +2020-02-26 19:45:00,145.96,187.525,69.684,32.613 +2020-02-26 20:00:00,134.53,182.65200000000002,70.036,32.613 +2020-02-26 20:15:00,128.04,177.22299999999998,70.036,32.613 +2020-02-26 20:30:00,123.73,173.518,70.036,32.613 +2020-02-26 20:45:00,122.17,171.71,70.036,32.613 +2020-02-26 21:00:00,116.79,168.44400000000002,60.431999999999995,32.613 +2020-02-26 21:15:00,121.15,165.343,60.431999999999995,32.613 +2020-02-26 21:30:00,120.23,163.3,60.431999999999995,32.613 +2020-02-26 21:45:00,113.68,162.774,60.431999999999995,32.613 +2020-02-26 22:00:00,107.74,155.657,56.2,32.613 +2020-02-26 22:15:00,104.57,151.393,56.2,32.613 +2020-02-26 22:30:00,101.62,137.017,56.2,32.613 +2020-02-26 22:45:00,102.65,129.564,56.2,32.613 +2020-02-26 23:00:00,96.31,122.925,47.927,32.613 +2020-02-26 23:15:00,102.68,121.538,47.927,32.613 +2020-02-26 23:30:00,99.27,122.704,47.927,32.613 +2020-02-26 23:45:00,95.89,122.791,47.927,32.613 +2020-02-27 00:00:00,88.18,117.63799999999999,43.794,32.613 +2020-02-27 00:15:00,88.97,116.417,43.794,32.613 +2020-02-27 00:30:00,90.67,116.34,43.794,32.613 +2020-02-27 00:45:00,87.97,116.863,43.794,32.613 +2020-02-27 01:00:00,93.08,118.571,42.397,32.613 +2020-02-27 01:15:00,91.83,118.697,42.397,32.613 +2020-02-27 01:30:00,90.3,118.811,42.397,32.613 +2020-02-27 01:45:00,86.08,119.008,42.397,32.613 +2020-02-27 02:00:00,85.82,120.977,40.010999999999996,32.613 +2020-02-27 02:15:00,93.94,122.337,40.010999999999996,32.613 +2020-02-27 02:30:00,91.53,123.309,40.010999999999996,32.613 +2020-02-27 02:45:00,92.99,125.09700000000001,40.010999999999996,32.613 +2020-02-27 03:00:00,90.02,127.602,39.181,32.613 +2020-02-27 03:15:00,93.1,129.143,39.181,32.613 +2020-02-27 03:30:00,93.75,130.901,39.181,32.613 +2020-02-27 03:45:00,90.45,132.39600000000002,39.181,32.613 +2020-02-27 04:00:00,93.08,144.13299999999998,40.39,32.613 +2020-02-27 04:15:00,98.7,155.77700000000002,40.39,32.613 +2020-02-27 04:30:00,96.19,158.435,40.39,32.613 +2020-02-27 04:45:00,92.23,160.512,40.39,32.613 +2020-02-27 05:00:00,95.1,192.89700000000002,45.504,32.613 +2020-02-27 05:15:00,97.21,220.97400000000002,45.504,32.613 +2020-02-27 05:30:00,102.16,215.96099999999998,45.504,32.613 +2020-02-27 05:45:00,104.54,208.938,45.504,32.613 +2020-02-27 06:00:00,114.7,206.334,57.748000000000005,32.613 +2020-02-27 06:15:00,118.52,212.34400000000002,57.748000000000005,32.613 +2020-02-27 06:30:00,121.44,214.40099999999998,57.748000000000005,32.613 +2020-02-27 06:45:00,123.95,218.209,57.748000000000005,32.613 +2020-02-27 07:00:00,127.56,219.028,72.138,32.613 +2020-02-27 07:15:00,129.28,222.639,72.138,32.613 +2020-02-27 07:30:00,129.58,223.67,72.138,32.613 +2020-02-27 07:45:00,132.15,223.00799999999998,72.138,32.613 +2020-02-27 08:00:00,134.97,221.19,65.542,32.613 +2020-02-27 08:15:00,134.2,219.979,65.542,32.613 +2020-02-27 08:30:00,131.35,215.787,65.542,32.613 +2020-02-27 08:45:00,126.9,212.06,65.542,32.613 +2020-02-27 09:00:00,124.52,204.975,60.523,32.613 +2020-02-27 09:15:00,124.46,201.856,60.523,32.613 +2020-02-27 09:30:00,126.69,200.528,60.523,32.613 +2020-02-27 09:45:00,127.92,197.623,60.523,32.613 +2020-02-27 10:00:00,125.86,193.459,57.449,32.613 +2020-02-27 10:15:00,123.44,190.041,57.449,32.613 +2020-02-27 10:30:00,125.61,187.658,57.449,32.613 +2020-02-27 10:45:00,126.43,186.771,57.449,32.613 +2020-02-27 11:00:00,122.4,184.77200000000002,54.505,32.613 +2020-02-27 11:15:00,127.71,184.00900000000001,54.505,32.613 +2020-02-27 11:30:00,120.16,183.195,54.505,32.613 +2020-02-27 11:45:00,118.8,182.02900000000002,54.505,32.613 +2020-02-27 12:00:00,117.29,177.203,51.50899999999999,32.613 +2020-02-27 12:15:00,116.79,177.446,51.50899999999999,32.613 +2020-02-27 12:30:00,112.3,176.657,51.50899999999999,32.613 +2020-02-27 12:45:00,115.74,177.57299999999998,51.50899999999999,32.613 +2020-02-27 13:00:00,112.3,177.00599999999997,51.303999999999995,32.613 +2020-02-27 13:15:00,119.69,175.601,51.303999999999995,32.613 +2020-02-27 13:30:00,117.02,174.53799999999998,51.303999999999995,32.613 +2020-02-27 13:45:00,114.97,174.16,51.303999999999995,32.613 +2020-02-27 14:00:00,115.47,175.22400000000002,52.785,32.613 +2020-02-27 14:15:00,115.4,174.87900000000002,52.785,32.613 +2020-02-27 14:30:00,119.74,175.236,52.785,32.613 +2020-02-27 14:45:00,121.19,176.11900000000003,52.785,32.613 +2020-02-27 15:00:00,123.14,178.046,56.458999999999996,32.613 +2020-02-27 15:15:00,116.9,177.079,56.458999999999996,32.613 +2020-02-27 15:30:00,124.45,177.988,56.458999999999996,32.613 +2020-02-27 15:45:00,126.19,178.828,56.458999999999996,32.613 +2020-02-27 16:00:00,128.02,180.417,59.388000000000005,32.613 +2020-02-27 16:15:00,126.16,181.822,59.388000000000005,32.613 +2020-02-27 16:30:00,128.55,184.666,59.388000000000005,32.613 +2020-02-27 16:45:00,130.21,185.77599999999998,59.388000000000005,32.613 +2020-02-27 17:00:00,134.52,187.782,64.462,32.613 +2020-02-27 17:15:00,134.4,190.137,64.462,32.613 +2020-02-27 17:30:00,136.14,192.80200000000002,64.462,32.613 +2020-02-27 17:45:00,140.64,193.84400000000002,64.462,32.613 +2020-02-27 18:00:00,148.99,196.137,65.128,32.613 +2020-02-27 18:15:00,149.31,195.958,65.128,32.613 +2020-02-27 18:30:00,144.63,194.645,65.128,32.613 +2020-02-27 18:45:00,147.88,195.813,65.128,32.613 +2020-02-27 19:00:00,146.09,194.50400000000002,61.316,32.613 +2020-02-27 19:15:00,145.64,191.324,61.316,32.613 +2020-02-27 19:30:00,135.54,189.967,61.316,32.613 +2020-02-27 19:45:00,136.53,187.209,61.316,32.613 +2020-02-27 20:00:00,133.14,182.305,59.845,32.613 +2020-02-27 20:15:00,130.96,176.892,59.845,32.613 +2020-02-27 20:30:00,127.67,173.207,59.845,32.613 +2020-02-27 20:45:00,123.1,171.40099999999998,59.845,32.613 +2020-02-27 21:00:00,118.51,168.123,54.83,32.613 +2020-02-27 21:15:00,116.64,165.014,54.83,32.613 +2020-02-27 21:30:00,116.78,162.971,54.83,32.613 +2020-02-27 21:45:00,116.76,162.464,54.83,32.613 +2020-02-27 22:00:00,106.08,155.33,50.933,32.613 +2020-02-27 22:15:00,104.2,151.092,50.933,32.613 +2020-02-27 22:30:00,106.52,136.667,50.933,32.613 +2020-02-27 22:45:00,108.8,129.218,50.933,32.613 +2020-02-27 23:00:00,101.23,122.57600000000001,45.32899999999999,32.613 +2020-02-27 23:15:00,100.86,121.205,45.32899999999999,32.613 +2020-02-27 23:30:00,97.66,122.382,45.32899999999999,32.613 +2020-02-27 23:45:00,99.21,122.494,45.32899999999999,32.613 +2020-02-28 00:00:00,95.24,116.29,43.74,32.613 +2020-02-28 00:15:00,90.0,115.273,43.74,32.613 +2020-02-28 00:30:00,93.54,115.04799999999999,43.74,32.613 +2020-02-28 00:45:00,93.7,115.684,43.74,32.613 +2020-02-28 01:00:00,86.92,117.04,42.555,32.613 +2020-02-28 01:15:00,89.35,118.04700000000001,42.555,32.613 +2020-02-28 01:30:00,90.93,117.935,42.555,32.613 +2020-02-28 01:45:00,91.4,118.236,42.555,32.613 +2020-02-28 02:00:00,88.43,120.294,41.68600000000001,32.613 +2020-02-28 02:15:00,90.06,121.537,41.68600000000001,32.613 +2020-02-28 02:30:00,89.98,123.05,41.68600000000001,32.613 +2020-02-28 02:45:00,91.98,124.87200000000001,41.68600000000001,32.613 +2020-02-28 03:00:00,85.75,126.415,42.278999999999996,32.613 +2020-02-28 03:15:00,90.33,128.856,42.278999999999996,32.613 +2020-02-28 03:30:00,91.82,130.59,42.278999999999996,32.613 +2020-02-28 03:45:00,92.28,132.428,42.278999999999996,32.613 +2020-02-28 04:00:00,93.53,144.399,43.742,32.613 +2020-02-28 04:15:00,94.58,155.774,43.742,32.613 +2020-02-28 04:30:00,96.07,158.686,43.742,32.613 +2020-02-28 04:45:00,88.54,159.619,43.742,32.613 +2020-02-28 05:00:00,92.18,190.735,46.973,32.613 +2020-02-28 05:15:00,94.1,220.334,46.973,32.613 +2020-02-28 05:30:00,99.17,216.352,46.973,32.613 +2020-02-28 05:45:00,101.77,209.26,46.973,32.613 +2020-02-28 06:00:00,114.52,207.092,59.63399999999999,32.613 +2020-02-28 06:15:00,116.78,211.665,59.63399999999999,32.613 +2020-02-28 06:30:00,121.82,212.828,59.63399999999999,32.613 +2020-02-28 06:45:00,121.41,218.22,59.63399999999999,32.613 +2020-02-28 07:00:00,126.51,218.248,71.631,32.613 +2020-02-28 07:15:00,130.43,222.845,71.631,32.613 +2020-02-28 07:30:00,132.73,223.635,71.631,32.613 +2020-02-28 07:45:00,131.7,222.05599999999998,71.631,32.613 +2020-02-28 08:00:00,137.02,219.141,66.181,32.613 +2020-02-28 08:15:00,131.96,217.541,66.181,32.613 +2020-02-28 08:30:00,136.24,214.248,66.181,32.613 +2020-02-28 08:45:00,130.35,208.982,66.181,32.613 +2020-02-28 09:00:00,130.85,202.32,63.086000000000006,32.613 +2020-02-28 09:15:00,128.0,199.795,63.086000000000006,32.613 +2020-02-28 09:30:00,131.49,198.054,63.086000000000006,32.613 +2020-02-28 09:45:00,128.77,195.05700000000002,63.086000000000006,32.613 +2020-02-28 10:00:00,128.05,189.793,60.886,32.613 +2020-02-28 10:15:00,129.03,187.09,60.886,32.613 +2020-02-28 10:30:00,125.57,184.666,60.886,32.613 +2020-02-28 10:45:00,132.11,183.361,60.886,32.613 +2020-02-28 11:00:00,130.54,181.342,59.391000000000005,32.613 +2020-02-28 11:15:00,127.61,179.697,59.391000000000005,32.613 +2020-02-28 11:30:00,127.14,180.593,59.391000000000005,32.613 +2020-02-28 11:45:00,124.9,179.465,59.391000000000005,32.613 +2020-02-28 12:00:00,122.01,175.725,56.172,32.613 +2020-02-28 12:15:00,126.66,173.922,56.172,32.613 +2020-02-28 12:30:00,120.31,173.257,56.172,32.613 +2020-02-28 12:45:00,122.17,174.65400000000002,56.172,32.613 +2020-02-28 13:00:00,118.8,175.051,54.406000000000006,32.613 +2020-02-28 13:15:00,122.05,174.425,54.406000000000006,32.613 +2020-02-28 13:30:00,121.88,173.385,54.406000000000006,32.613 +2020-02-28 13:45:00,125.01,172.955,54.406000000000006,32.613 +2020-02-28 14:00:00,121.64,172.949,53.578,32.613 +2020-02-28 14:15:00,117.04,172.418,53.578,32.613 +2020-02-28 14:30:00,115.28,173.28900000000002,53.578,32.613 +2020-02-28 14:45:00,116.76,174.46099999999998,53.578,32.613 +2020-02-28 15:00:00,112.41,175.921,56.568999999999996,32.613 +2020-02-28 15:15:00,116.08,174.49099999999999,56.568999999999996,32.613 +2020-02-28 15:30:00,121.56,173.861,56.568999999999996,32.613 +2020-02-28 15:45:00,123.51,174.84599999999998,56.568999999999996,32.613 +2020-02-28 16:00:00,121.03,175.273,60.169,32.613 +2020-02-28 16:15:00,119.77,176.956,60.169,32.613 +2020-02-28 16:30:00,124.79,179.888,60.169,32.613 +2020-02-28 16:45:00,130.53,180.83700000000002,60.169,32.613 +2020-02-28 17:00:00,134.83,183.079,65.497,32.613 +2020-02-28 17:15:00,137.27,185.04,65.497,32.613 +2020-02-28 17:30:00,137.09,187.424,65.497,32.613 +2020-02-28 17:45:00,141.86,188.253,65.497,32.613 +2020-02-28 18:00:00,148.36,191.23,65.082,32.613 +2020-02-28 18:15:00,143.47,190.71599999999998,65.082,32.613 +2020-02-28 18:30:00,142.41,189.78599999999997,65.082,32.613 +2020-02-28 18:45:00,144.94,190.97799999999998,65.082,32.613 +2020-02-28 19:00:00,139.83,190.535,60.968,32.613 +2020-02-28 19:15:00,136.32,188.72099999999998,60.968,32.613 +2020-02-28 19:30:00,138.61,186.987,60.968,32.613 +2020-02-28 19:45:00,138.84,183.80200000000002,60.968,32.613 +2020-02-28 20:00:00,130.09,178.919,61.123000000000005,32.613 +2020-02-28 20:15:00,127.16,173.543,61.123000000000005,32.613 +2020-02-28 20:30:00,122.02,169.83,61.123000000000005,32.613 +2020-02-28 20:45:00,118.31,168.558,61.123000000000005,32.613 +2020-02-28 21:00:00,117.05,165.787,55.416000000000004,32.613 +2020-02-28 21:15:00,114.42,163.128,55.416000000000004,32.613 +2020-02-28 21:30:00,108.61,161.128,55.416000000000004,32.613 +2020-02-28 21:45:00,103.76,161.17600000000002,55.416000000000004,32.613 +2020-02-28 22:00:00,96.68,154.987,51.631,32.613 +2020-02-28 22:15:00,95.08,150.64,51.631,32.613 +2020-02-28 22:30:00,100.47,142.507,51.631,32.613 +2020-02-28 22:45:00,100.52,138.542,51.631,32.613 +2020-02-28 23:00:00,93.73,131.487,44.898,32.613 +2020-02-28 23:15:00,87.45,128.192,44.898,32.613 +2020-02-28 23:30:00,82.28,127.915,44.898,32.613 +2020-02-28 23:45:00,85.88,127.398,44.898,32.613 +2020-02-29 00:00:00,82.24,113.23700000000001,42.033,32.431999999999995 +2020-02-29 00:15:00,85.01,108.041,42.033,32.431999999999995 +2020-02-29 00:30:00,80.52,109.09299999999999,42.033,32.431999999999995 +2020-02-29 00:45:00,80.2,110.416,42.033,32.431999999999995 +2020-02-29 01:00:00,76.23,112.354,38.255,32.431999999999995 +2020-02-29 01:15:00,75.84,112.39200000000001,38.255,32.431999999999995 +2020-02-29 01:30:00,74.66,111.743,38.255,32.431999999999995 +2020-02-29 01:45:00,74.31,111.86,38.255,32.431999999999995 +2020-02-29 02:00:00,77.97,114.565,36.404,32.431999999999995 +2020-02-29 02:15:00,79.13,115.428,36.404,32.431999999999995 +2020-02-29 02:30:00,76.43,115.85600000000001,36.404,32.431999999999995 +2020-02-29 02:45:00,74.31,117.789,36.404,32.431999999999995 +2020-02-29 03:00:00,72.93,119.935,36.083,32.431999999999995 +2020-02-29 03:15:00,71.88,121.161,36.083,32.431999999999995 +2020-02-29 03:30:00,71.31,121.338,36.083,32.431999999999995 +2020-02-29 03:45:00,71.98,123.323,36.083,32.431999999999995 +2020-02-29 04:00:00,72.01,131.215,36.102,32.431999999999995 +2020-02-29 04:15:00,72.24,140.079,36.102,32.431999999999995 +2020-02-29 04:30:00,72.51,140.856,36.102,32.431999999999995 +2020-02-29 04:45:00,73.77,141.296,36.102,32.431999999999995 +2020-02-29 05:00:00,73.63,156.637,35.284,32.431999999999995 +2020-02-29 05:15:00,74.75,167.479,35.284,32.431999999999995 +2020-02-29 05:30:00,75.74,163.838,35.284,32.431999999999995 +2020-02-29 05:45:00,77.0,162.155,35.284,32.431999999999995 +2020-02-29 06:00:00,77.1,178.55700000000002,36.265,32.431999999999995 +2020-02-29 06:15:00,78.45,199.275,36.265,32.431999999999995 +2020-02-29 06:30:00,77.71,195.088,36.265,32.431999999999995 +2020-02-29 06:45:00,79.47,191.61700000000002,36.265,32.431999999999995 +2020-02-29 07:00:00,82.82,187.955,40.714,32.431999999999995 +2020-02-29 07:15:00,85.08,191.27,40.714,32.431999999999995 +2020-02-29 07:30:00,87.37,194.697,40.714,32.431999999999995 +2020-02-29 07:45:00,90.24,196.983,40.714,32.431999999999995 +2020-02-29 08:00:00,94.26,198.00400000000002,46.692,32.431999999999995 +2020-02-29 08:15:00,93.79,199.93099999999998,46.692,32.431999999999995 +2020-02-29 08:30:00,95.57,198.158,46.692,32.431999999999995 +2020-02-29 08:45:00,97.68,196.007,46.692,32.431999999999995 +2020-02-29 09:00:00,99.64,191.146,48.925,32.431999999999995 +2020-02-29 09:15:00,101.64,189.36700000000002,48.925,32.431999999999995 +2020-02-29 09:30:00,101.61,188.537,48.925,32.431999999999995 +2020-02-29 09:45:00,103.34,185.695,48.925,32.431999999999995 +2020-02-29 10:00:00,103.19,180.743,47.799,32.431999999999995 +2020-02-29 10:15:00,103.4,178.24599999999998,47.799,32.431999999999995 +2020-02-29 10:30:00,100.05,175.993,47.799,32.431999999999995 +2020-02-29 10:45:00,101.34,175.965,47.799,32.431999999999995 +2020-02-29 11:00:00,102.61,174.145,44.309,32.431999999999995 +2020-02-29 11:15:00,104.3,171.86900000000003,44.309,32.431999999999995 +2020-02-29 11:30:00,107.59,171.69299999999998,44.309,32.431999999999995 +2020-02-29 11:45:00,110.39,169.666,44.309,32.431999999999995 +2020-02-29 12:00:00,107.28,165.10299999999998,42.367,32.431999999999995 +2020-02-29 12:15:00,108.46,163.957,42.367,32.431999999999995 +2020-02-29 12:30:00,105.82,163.566,42.367,32.431999999999995 +2020-02-29 12:45:00,103.19,164.22799999999998,42.367,32.431999999999995 +2020-02-29 13:00:00,97.8,164.255,39.036,32.431999999999995 +2020-02-29 13:15:00,97.25,161.597,39.036,32.431999999999995 +2020-02-29 13:30:00,95.99,160.111,39.036,32.431999999999995 +2020-02-29 13:45:00,96.33,160.111,39.036,32.431999999999995 +2020-02-29 14:00:00,93.08,161.362,37.995,32.431999999999995 +2020-02-29 14:15:00,93.55,160.257,37.995,32.431999999999995 +2020-02-29 14:30:00,93.15,159.326,37.995,32.431999999999995 +2020-02-29 14:45:00,93.28,160.732,37.995,32.431999999999995 +2020-02-29 15:00:00,92.46,162.841,40.71,32.431999999999995 +2020-02-29 15:15:00,91.85,162.183,40.71,32.431999999999995 +2020-02-29 15:30:00,89.88,163.00799999999998,40.71,32.431999999999995 +2020-02-29 15:45:00,88.72,163.968,40.71,32.431999999999995 +2020-02-29 16:00:00,88.8,163.262,46.998000000000005,32.431999999999995 +2020-02-29 16:15:00,87.64,165.77700000000002,46.998000000000005,32.431999999999995 +2020-02-29 16:30:00,89.92,168.66400000000002,46.998000000000005,32.431999999999995 +2020-02-29 16:45:00,92.75,170.44299999999998,46.998000000000005,32.431999999999995 +2020-02-29 17:00:00,98.43,172.093,55.431000000000004,32.431999999999995 +2020-02-29 17:15:00,99.1,175.64,55.431000000000004,32.431999999999995 +2020-02-29 17:30:00,102.07,177.953,55.431000000000004,32.431999999999995 +2020-02-29 17:45:00,105.39,178.418,55.431000000000004,32.431999999999995 +2020-02-29 18:00:00,110.77,180.94299999999998,55.989,32.431999999999995 +2020-02-29 18:15:00,113.04,182.285,55.989,32.431999999999995 +2020-02-29 18:30:00,114.76,182.68,55.989,32.431999999999995 +2020-02-29 18:45:00,114.16,180.55700000000002,55.989,32.431999999999995 +2020-02-29 19:00:00,112.1,180.942,50.882,32.431999999999995 +2020-02-29 19:15:00,110.76,178.642,50.882,32.431999999999995 +2020-02-29 19:30:00,109.34,177.685,50.882,32.431999999999995 +2020-02-29 19:45:00,110.83,174.40099999999998,50.882,32.431999999999995 +2020-02-29 20:00:00,103.05,171.63400000000001,43.172,32.431999999999995 +2020-02-29 20:15:00,100.21,168.33700000000002,43.172,32.431999999999995 +2020-02-29 20:30:00,97.84,164.28599999999997,43.172,32.431999999999995 +2020-02-29 20:45:00,96.65,162.657,43.172,32.431999999999995 +2020-02-29 21:00:00,90.85,162.047,37.599000000000004,32.431999999999995 +2020-02-29 21:15:00,91.68,159.80200000000002,37.599000000000004,32.431999999999995 +2020-02-29 21:30:00,89.56,159.00799999999998,37.599000000000004,32.431999999999995 +2020-02-29 21:45:00,89.3,158.668,37.599000000000004,32.431999999999995 +2020-02-29 22:00:00,85.48,153.77700000000002,39.047,32.431999999999995 +2020-02-29 22:15:00,84.96,151.89,39.047,32.431999999999995 +2020-02-29 22:30:00,79.47,149.774,39.047,32.431999999999995 +2020-02-29 22:45:00,81.08,147.64600000000002,39.047,32.431999999999995 +2020-02-29 23:00:00,77.22,142.952,32.339,32.431999999999995 +2020-02-29 23:15:00,77.22,138.085,32.339,32.431999999999995 +2020-02-29 23:30:00,75.06,136.158,32.339,32.431999999999995 +2020-02-29 23:45:00,73.54,133.304,32.339,32.431999999999995 +2020-03-01 00:00:00,68.61,114.87200000000001,20.007,31.988000000000003 +2020-03-01 00:15:00,70.53,108.667,20.007,31.988000000000003 +2020-03-01 00:30:00,66.5,108.73299999999999,20.007,31.988000000000003 +2020-03-01 00:45:00,67.58,110.04899999999999,20.007,31.988000000000003 +2020-03-01 01:00:00,65.55,112.008,17.378,31.988000000000003 +2020-03-01 01:15:00,65.75,113.44200000000001,17.378,31.988000000000003 +2020-03-01 01:30:00,65.35,113.366,17.378,31.988000000000003 +2020-03-01 01:45:00,65.61,113.147,17.378,31.988000000000003 +2020-03-01 02:00:00,63.96,115.15,16.145,31.988000000000003 +2020-03-01 02:15:00,64.55,114.846,16.145,31.988000000000003 +2020-03-01 02:30:00,62.76,115.854,16.145,31.988000000000003 +2020-03-01 02:45:00,63.36,118.155,16.145,31.988000000000003 +2020-03-01 03:00:00,62.76,120.616,15.427999999999999,31.988000000000003 +2020-03-01 03:15:00,63.56,121.51,15.427999999999999,31.988000000000003 +2020-03-01 03:30:00,63.25,123.181,15.427999999999999,31.988000000000003 +2020-03-01 03:45:00,63.21,124.824,15.427999999999999,31.988000000000003 +2020-03-01 04:00:00,62.91,133.92700000000002,16.663,31.988000000000003 +2020-03-01 04:15:00,63.35,143.066,16.663,31.988000000000003 +2020-03-01 04:30:00,63.9,143.304,16.663,31.988000000000003 +2020-03-01 04:45:00,63.94,143.76,16.663,31.988000000000003 +2020-03-01 05:00:00,66.16,157.566,17.271,31.988000000000003 +2020-03-01 05:15:00,66.79,168.065,17.271,31.988000000000003 +2020-03-01 05:30:00,66.13,163.91,17.271,31.988000000000003 +2020-03-01 05:45:00,66.68,161.535,17.271,31.988000000000003 +2020-03-01 06:00:00,67.98,178.033,17.612000000000002,31.988000000000003 +2020-03-01 06:15:00,67.63,197.967,17.612000000000002,31.988000000000003 +2020-03-01 06:30:00,66.96,192.521,17.612000000000002,31.988000000000003 +2020-03-01 06:45:00,67.33,187.15599999999998,17.612000000000002,31.988000000000003 +2020-03-01 07:00:00,69.6,187.088,20.88,31.988000000000003 +2020-03-01 07:15:00,70.32,188.924,20.88,31.988000000000003 +2020-03-01 07:30:00,72.12,190.649,20.88,31.988000000000003 +2020-03-01 07:45:00,75.07,191.512,20.88,31.988000000000003 +2020-03-01 08:00:00,79.08,194.41400000000002,25.861,31.988000000000003 +2020-03-01 08:15:00,78.76,195.868,25.861,31.988000000000003 +2020-03-01 08:30:00,77.97,195.63400000000001,25.861,31.988000000000003 +2020-03-01 08:45:00,78.47,194.578,25.861,31.988000000000003 +2020-03-01 09:00:00,77.62,188.77599999999998,27.921999999999997,31.988000000000003 +2020-03-01 09:15:00,77.96,187.282,27.921999999999997,31.988000000000003 +2020-03-01 09:30:00,80.81,186.24200000000002,27.921999999999997,31.988000000000003 +2020-03-01 09:45:00,87.16,183.475,27.921999999999997,31.988000000000003 +2020-03-01 10:00:00,87.22,182.304,29.048000000000002,31.988000000000003 +2020-03-01 10:15:00,85.78,180.335,29.048000000000002,31.988000000000003 +2020-03-01 10:30:00,84.44,178.554,29.048000000000002,31.988000000000003 +2020-03-01 10:45:00,83.32,176.795,29.048000000000002,31.988000000000003 +2020-03-01 11:00:00,83.84,174.87900000000002,32.02,31.988000000000003 +2020-03-01 11:15:00,81.49,172.76,32.02,31.988000000000003 +2020-03-01 11:30:00,83.37,172.18400000000003,32.02,31.988000000000003 +2020-03-01 11:45:00,82.81,171.415,32.02,31.988000000000003 +2020-03-01 12:00:00,79.55,165.91400000000002,28.55,31.988000000000003 +2020-03-01 12:15:00,76.4,166.451,28.55,31.988000000000003 +2020-03-01 12:30:00,72.82,165.06900000000002,28.55,31.988000000000003 +2020-03-01 12:45:00,71.89,164.695,28.55,31.988000000000003 +2020-03-01 13:00:00,68.82,163.696,25.601999999999997,31.988000000000003 +2020-03-01 13:15:00,67.4,163.767,25.601999999999997,31.988000000000003 +2020-03-01 13:30:00,66.06,161.689,25.601999999999997,31.988000000000003 +2020-03-01 13:45:00,65.28,160.939,25.601999999999997,31.988000000000003 +2020-03-01 14:00:00,62.31,162.632,23.916999999999998,31.988000000000003 +2020-03-01 14:15:00,63.97,162.403,23.916999999999998,31.988000000000003 +2020-03-01 14:30:00,64.32,162.54399999999998,23.916999999999998,31.988000000000003 +2020-03-01 14:45:00,66.22,163.375,23.916999999999998,31.988000000000003 +2020-03-01 15:00:00,66.27,163.856,24.064,31.988000000000003 +2020-03-01 15:15:00,67.29,163.786,24.064,31.988000000000003 +2020-03-01 15:30:00,68.58,164.68200000000002,24.064,31.988000000000003 +2020-03-01 15:45:00,70.54,165.767,24.064,31.988000000000003 +2020-03-01 16:00:00,73.29,168.68,28.189,31.988000000000003 +2020-03-01 16:15:00,73.42,170.81400000000002,28.189,31.988000000000003 +2020-03-01 16:30:00,75.75,173.27599999999998,28.189,31.988000000000003 +2020-03-01 16:45:00,78.52,174.707,28.189,31.988000000000003 +2020-03-01 17:00:00,83.11,177.141,37.576,31.988000000000003 +2020-03-01 17:15:00,85.27,180.53900000000002,37.576,31.988000000000003 +2020-03-01 17:30:00,89.46,183.257,37.576,31.988000000000003 +2020-03-01 17:45:00,92.14,185.89,37.576,31.988000000000003 +2020-03-01 18:00:00,100.96,188.957,42.669,31.988000000000003 +2020-03-01 18:15:00,103.27,191.891,42.669,31.988000000000003 +2020-03-01 18:30:00,103.76,190.107,42.669,31.988000000000003 +2020-03-01 18:45:00,102.03,190.166,42.669,31.988000000000003 +2020-03-01 19:00:00,101.46,190.812,43.538999999999994,31.988000000000003 +2020-03-01 19:15:00,100.17,188.94400000000002,43.538999999999994,31.988000000000003 +2020-03-01 19:30:00,98.2,188.227,43.538999999999994,31.988000000000003 +2020-03-01 19:45:00,96.97,185.736,43.538999999999994,31.988000000000003 +2020-03-01 20:00:00,93.91,182.748,37.330999999999996,31.988000000000003 +2020-03-01 20:15:00,93.83,180.495,37.330999999999996,31.988000000000003 +2020-03-01 20:30:00,95.91,177.46200000000002,37.330999999999996,31.988000000000003 +2020-03-01 20:45:00,97.34,173.905,37.330999999999996,31.988000000000003 +2020-03-01 21:00:00,94.5,170.889,33.856,31.988000000000003 +2020-03-01 21:15:00,89.25,168.035,33.856,31.988000000000003 +2020-03-01 21:30:00,88.14,167.192,33.856,31.988000000000003 +2020-03-01 21:45:00,89.58,167.22400000000002,33.856,31.988000000000003 +2020-03-01 22:00:00,93.6,161.214,34.711999999999996,31.988000000000003 +2020-03-01 22:15:00,95.33,158.43200000000002,34.711999999999996,31.988000000000003 +2020-03-01 22:30:00,91.18,152.474,34.711999999999996,31.988000000000003 +2020-03-01 22:45:00,83.29,149.13299999999998,34.711999999999996,31.988000000000003 +2020-03-01 23:00:00,79.65,141.811,29.698,31.988000000000003 +2020-03-01 23:15:00,80.74,138.316,29.698,31.988000000000003 +2020-03-01 23:30:00,78.27,137.503,29.698,31.988000000000003 +2020-03-01 23:45:00,78.56,135.459,29.698,31.988000000000003 +2020-03-02 00:00:00,73.51,118.774,29.983,32.166 +2020-03-02 00:15:00,74.57,115.516,29.983,32.166 +2020-03-02 00:30:00,75.5,115.632,29.983,32.166 +2020-03-02 00:45:00,73.67,116.381,29.983,32.166 +2020-03-02 01:00:00,70.05,118.355,29.122,32.166 +2020-03-02 01:15:00,71.82,119.274,29.122,32.166 +2020-03-02 01:30:00,71.29,119.28299999999999,29.122,32.166 +2020-03-02 01:45:00,72.24,119.164,29.122,32.166 +2020-03-02 02:00:00,70.05,121.193,28.676,32.166 +2020-03-02 02:15:00,71.47,122.126,28.676,32.166 +2020-03-02 02:30:00,70.78,123.491,28.676,32.166 +2020-03-02 02:45:00,71.48,125.18299999999999,28.676,32.166 +2020-03-02 03:00:00,72.12,128.914,26.552,32.166 +2020-03-02 03:15:00,72.82,131.45600000000002,26.552,32.166 +2020-03-02 03:30:00,79.44,132.96200000000002,26.552,32.166 +2020-03-02 03:45:00,81.71,134.03,26.552,32.166 +2020-03-02 04:00:00,82.58,147.67700000000002,27.44,32.166 +2020-03-02 04:15:00,77.82,161.142,27.44,32.166 +2020-03-02 04:30:00,78.23,163.398,27.44,32.166 +2020-03-02 04:45:00,79.38,164.046,27.44,32.166 +2020-03-02 05:00:00,83.72,193.69400000000002,36.825,32.166 +2020-03-02 05:15:00,86.57,224.705,36.825,32.166 +2020-03-02 05:30:00,91.53,220.55200000000002,36.825,32.166 +2020-03-02 05:45:00,96.44,212.40900000000002,36.825,32.166 +2020-03-02 06:00:00,105.65,210.571,56.589,32.166 +2020-03-02 06:15:00,110.56,215.197,56.589,32.166 +2020-03-02 06:30:00,114.34,217.84599999999998,56.589,32.166 +2020-03-02 06:45:00,116.99,221.528,56.589,32.166 +2020-03-02 07:00:00,121.98,223.831,67.49,32.166 +2020-03-02 07:15:00,122.83,227.138,67.49,32.166 +2020-03-02 07:30:00,126.26,227.96599999999998,67.49,32.166 +2020-03-02 07:45:00,128.48,226.40400000000002,67.49,32.166 +2020-03-02 08:00:00,132.44,224.445,60.028,32.166 +2020-03-02 08:15:00,131.31,223.685,60.028,32.166 +2020-03-02 08:30:00,130.5,219.38,60.028,32.166 +2020-03-02 08:45:00,131.43,215.199,60.028,32.166 +2020-03-02 09:00:00,130.52,208.362,55.018,32.166 +2020-03-02 09:15:00,130.5,203.305,55.018,32.166 +2020-03-02 09:30:00,131.0,201.21099999999998,55.018,32.166 +2020-03-02 09:45:00,131.43,198.43,55.018,32.166 +2020-03-02 10:00:00,129.42,196.28799999999998,51.183,32.166 +2020-03-02 10:15:00,130.46,194.028,51.183,32.166 +2020-03-02 10:30:00,130.39,191.372,51.183,32.166 +2020-03-02 10:45:00,130.17,190.128,51.183,32.166 +2020-03-02 11:00:00,128.8,185.782,50.065,32.166 +2020-03-02 11:15:00,130.97,185.44099999999997,50.065,32.166 +2020-03-02 11:30:00,128.5,186.283,50.065,32.166 +2020-03-02 11:45:00,124.59,185.206,50.065,32.166 +2020-03-02 12:00:00,122.07,181.143,48.141999999999996,32.166 +2020-03-02 12:15:00,122.75,181.709,48.141999999999996,32.166 +2020-03-02 12:30:00,118.19,180.388,48.141999999999996,32.166 +2020-03-02 12:45:00,115.54,181.465,48.141999999999996,32.166 +2020-03-02 13:00:00,116.29,181.18,47.887,32.166 +2020-03-02 13:15:00,111.2,179.791,47.887,32.166 +2020-03-02 13:30:00,110.44,177.218,47.887,32.166 +2020-03-02 13:45:00,110.06,176.62,47.887,32.166 +2020-03-02 14:00:00,109.1,177.69,48.571000000000005,32.166 +2020-03-02 14:15:00,110.15,176.90200000000002,48.571000000000005,32.166 +2020-03-02 14:30:00,109.82,176.476,48.571000000000005,32.166 +2020-03-02 14:45:00,112.38,177.563,48.571000000000005,32.166 +2020-03-02 15:00:00,111.64,179.74599999999998,49.937,32.166 +2020-03-02 15:15:00,113.26,178.19299999999998,49.937,32.166 +2020-03-02 15:30:00,112.72,178.34599999999998,49.937,32.166 +2020-03-02 15:45:00,112.81,178.925,49.937,32.166 +2020-03-02 16:00:00,116.61,182.183,52.963,32.166 +2020-03-02 16:15:00,115.99,183.57299999999998,52.963,32.166 +2020-03-02 16:30:00,117.48,185.021,52.963,32.166 +2020-03-02 16:45:00,119.0,185.275,52.963,32.166 +2020-03-02 17:00:00,121.05,187.44,61.163999999999994,32.166 +2020-03-02 17:15:00,122.51,189.97299999999998,61.163999999999994,32.166 +2020-03-02 17:30:00,125.34,192.14,61.163999999999994,32.166 +2020-03-02 17:45:00,127.45,193.295,61.163999999999994,32.166 +2020-03-02 18:00:00,135.41,196.68599999999998,63.788999999999994,32.166 +2020-03-02 18:15:00,136.15,197.328,63.788999999999994,32.166 +2020-03-02 18:30:00,135.28,196.08900000000003,63.788999999999994,32.166 +2020-03-02 18:45:00,134.36,197.226,63.788999999999994,32.166 +2020-03-02 19:00:00,134.74,196.25,63.913000000000004,32.166 +2020-03-02 19:15:00,130.7,193.407,63.913000000000004,32.166 +2020-03-02 19:30:00,131.09,193.16099999999997,63.913000000000004,32.166 +2020-03-02 19:45:00,129.93,189.833,63.913000000000004,32.166 +2020-03-02 20:00:00,119.75,184.363,65.44,32.166 +2020-03-02 20:15:00,118.41,179.88299999999998,65.44,32.166 +2020-03-02 20:30:00,115.33,175.128,65.44,32.166 +2020-03-02 20:45:00,114.14,173.17700000000002,65.44,32.166 +2020-03-02 21:00:00,109.07,170.59400000000002,59.117,32.166 +2020-03-02 21:15:00,111.01,166.644,59.117,32.166 +2020-03-02 21:30:00,109.45,165.047,59.117,32.166 +2020-03-02 21:45:00,107.16,164.585,59.117,32.166 +2020-03-02 22:00:00,100.92,155.52,52.301,32.166 +2020-03-02 22:15:00,97.41,151.71,52.301,32.166 +2020-03-02 22:30:00,99.9,135.923,52.301,32.166 +2020-03-02 22:45:00,100.06,127.88600000000001,52.301,32.166 +2020-03-02 23:00:00,94.69,121.306,44.373000000000005,32.166 +2020-03-02 23:15:00,88.97,120.22200000000001,44.373000000000005,32.166 +2020-03-02 23:30:00,89.49,122.09700000000001,44.373000000000005,32.166 +2020-03-02 23:45:00,90.65,122.587,44.373000000000005,32.166 +2020-03-03 00:00:00,87.69,117.507,44.647,32.166 +2020-03-03 00:15:00,84.07,115.73299999999999,44.647,32.166 +2020-03-03 00:30:00,82.45,115.021,44.647,32.166 +2020-03-03 00:45:00,85.5,114.936,44.647,32.166 +2020-03-03 01:00:00,80.31,116.613,41.433,32.166 +2020-03-03 01:15:00,82.7,117.10600000000001,41.433,32.166 +2020-03-03 01:30:00,78.1,117.24600000000001,41.433,32.166 +2020-03-03 01:45:00,76.78,117.35700000000001,41.433,32.166 +2020-03-03 02:00:00,76.01,119.30799999999999,39.909,32.166 +2020-03-03 02:15:00,76.06,120.281,39.909,32.166 +2020-03-03 02:30:00,76.62,121.045,39.909,32.166 +2020-03-03 02:45:00,81.97,122.792,39.909,32.166 +2020-03-03 03:00:00,84.87,125.315,39.14,32.166 +2020-03-03 03:15:00,86.31,127.15700000000001,39.14,32.166 +2020-03-03 03:30:00,84.73,129.112,39.14,32.166 +2020-03-03 03:45:00,85.5,130.22899999999998,39.14,32.166 +2020-03-03 04:00:00,87.66,143.525,40.015,32.166 +2020-03-03 04:15:00,81.89,156.649,40.015,32.166 +2020-03-03 04:30:00,83.96,158.612,40.015,32.166 +2020-03-03 04:45:00,89.14,160.464,40.015,32.166 +2020-03-03 05:00:00,95.89,195.05900000000003,44.93600000000001,32.166 +2020-03-03 05:15:00,98.73,225.97400000000002,44.93600000000001,32.166 +2020-03-03 05:30:00,97.84,220.359,44.93600000000001,32.166 +2020-03-03 05:45:00,100.51,212.12900000000002,44.93600000000001,32.166 +2020-03-03 06:00:00,109.28,209.315,57.271,32.166 +2020-03-03 06:15:00,112.89,215.521,57.271,32.166 +2020-03-03 06:30:00,115.24,217.46900000000002,57.271,32.166 +2020-03-03 06:45:00,116.92,220.65599999999998,57.271,32.166 +2020-03-03 07:00:00,122.15,222.825,68.352,32.166 +2020-03-03 07:15:00,122.86,225.91299999999998,68.352,32.166 +2020-03-03 07:30:00,126.57,226.18099999999998,68.352,32.166 +2020-03-03 07:45:00,127.79,224.65400000000002,68.352,32.166 +2020-03-03 08:00:00,131.05,222.767,60.717,32.166 +2020-03-03 08:15:00,131.16,220.958,60.717,32.166 +2020-03-03 08:30:00,135.43,216.447,60.717,32.166 +2020-03-03 08:45:00,132.45,211.908,60.717,32.166 +2020-03-03 09:00:00,132.71,204.32299999999998,54.603,32.166 +2020-03-03 09:15:00,132.5,200.79,54.603,32.166 +2020-03-03 09:30:00,134.11,199.454,54.603,32.166 +2020-03-03 09:45:00,133.41,196.642,54.603,32.166 +2020-03-03 10:00:00,133.09,193.778,52.308,32.166 +2020-03-03 10:15:00,132.43,190.52200000000002,52.308,32.166 +2020-03-03 10:30:00,133.18,188.062,52.308,32.166 +2020-03-03 10:45:00,133.76,187.234,52.308,32.166 +2020-03-03 11:00:00,132.89,184.308,51.838,32.166 +2020-03-03 11:15:00,134.16,183.71599999999998,51.838,32.166 +2020-03-03 11:30:00,133.77,183.303,51.838,32.166 +2020-03-03 11:45:00,134.04,182.864,51.838,32.166 +2020-03-03 12:00:00,134.11,177.479,50.375,32.166 +2020-03-03 12:15:00,134.12,177.7,50.375,32.166 +2020-03-03 12:30:00,134.26,177.143,50.375,32.166 +2020-03-03 12:45:00,136.24,178.02,50.375,32.166 +2020-03-03 13:00:00,132.14,177.34099999999998,50.735,32.166 +2020-03-03 13:15:00,140.28,175.817,50.735,32.166 +2020-03-03 13:30:00,140.27,174.355,50.735,32.166 +2020-03-03 13:45:00,138.43,173.81900000000002,50.735,32.166 +2020-03-03 14:00:00,134.59,175.283,50.946000000000005,32.166 +2020-03-03 14:15:00,133.48,174.597,50.946000000000005,32.166 +2020-03-03 14:30:00,134.65,174.787,50.946000000000005,32.166 +2020-03-03 14:45:00,139.12,175.71,50.946000000000005,32.166 +2020-03-03 15:00:00,138.89,177.46400000000003,53.18,32.166 +2020-03-03 15:15:00,136.59,176.313,53.18,32.166 +2020-03-03 15:30:00,133.42,176.609,53.18,32.166 +2020-03-03 15:45:00,137.07,176.822,53.18,32.166 +2020-03-03 16:00:00,138.31,180.324,54.928999999999995,32.166 +2020-03-03 16:15:00,137.5,182.165,54.928999999999995,32.166 +2020-03-03 16:30:00,135.91,184.196,54.928999999999995,32.166 +2020-03-03 16:45:00,136.81,184.774,54.928999999999995,32.166 +2020-03-03 17:00:00,138.14,187.50099999999998,60.913000000000004,32.166 +2020-03-03 17:15:00,138.47,190.11700000000002,60.913000000000004,32.166 +2020-03-03 17:30:00,137.24,192.908,60.913000000000004,32.166 +2020-03-03 17:45:00,134.06,193.915,60.913000000000004,32.166 +2020-03-03 18:00:00,143.82,197.111,62.214,32.166 +2020-03-03 18:15:00,146.3,197.49,62.214,32.166 +2020-03-03 18:30:00,146.08,195.918,62.214,32.166 +2020-03-03 18:45:00,142.83,197.834,62.214,32.166 +2020-03-03 19:00:00,139.19,196.77599999999998,62.38,32.166 +2020-03-03 19:15:00,140.37,193.695,62.38,32.166 +2020-03-03 19:30:00,138.01,192.799,62.38,32.166 +2020-03-03 19:45:00,138.66,189.58599999999998,62.38,32.166 +2020-03-03 20:00:00,130.23,184.28,65.018,32.166 +2020-03-03 20:15:00,123.06,179.03400000000002,65.018,32.166 +2020-03-03 20:30:00,123.81,175.297,65.018,32.166 +2020-03-03 20:45:00,121.62,172.838,65.018,32.166 +2020-03-03 21:00:00,117.63,169.65599999999998,56.416000000000004,32.166 +2020-03-03 21:15:00,108.75,166.425,56.416000000000004,32.166 +2020-03-03 21:30:00,108.4,164.136,56.416000000000004,32.166 +2020-03-03 21:45:00,110.03,163.938,56.416000000000004,32.166 +2020-03-03 22:00:00,107.4,156.55,52.846000000000004,32.166 +2020-03-03 22:15:00,105.11,152.477,52.846000000000004,32.166 +2020-03-03 22:30:00,98.01,136.759,52.846000000000004,32.166 +2020-03-03 22:45:00,100.15,128.994,52.846000000000004,32.166 +2020-03-03 23:00:00,96.08,122.352,44.435,32.166 +2020-03-03 23:15:00,96.98,120.572,44.435,32.166 +2020-03-03 23:30:00,89.43,122.104,44.435,32.166 +2020-03-03 23:45:00,87.12,122.21,44.435,32.166 +2020-03-04 00:00:00,83.92,117.189,42.527,32.166 +2020-03-04 00:15:00,81.31,115.42299999999999,42.527,32.166 +2020-03-04 00:30:00,85.2,114.69200000000001,42.527,32.166 +2020-03-04 00:45:00,87.61,114.60600000000001,42.527,32.166 +2020-03-04 01:00:00,83.3,116.243,38.655,32.166 +2020-03-04 01:15:00,83.96,116.72,38.655,32.166 +2020-03-04 01:30:00,76.44,116.84299999999999,38.655,32.166 +2020-03-04 01:45:00,77.77,116.96,38.655,32.166 +2020-03-04 02:00:00,77.27,118.90100000000001,36.912,32.166 +2020-03-04 02:15:00,79.12,119.867,36.912,32.166 +2020-03-04 02:30:00,84.05,120.65,36.912,32.166 +2020-03-04 02:45:00,84.7,122.397,36.912,32.166 +2020-03-04 03:00:00,84.41,124.934,36.98,32.166 +2020-03-04 03:15:00,86.26,126.76,36.98,32.166 +2020-03-04 03:30:00,87.42,128.709,36.98,32.166 +2020-03-04 03:45:00,87.57,129.838,36.98,32.166 +2020-03-04 04:00:00,83.7,143.138,38.052,32.166 +2020-03-04 04:15:00,88.51,156.252,38.052,32.166 +2020-03-04 04:30:00,91.89,158.232,38.052,32.166 +2020-03-04 04:45:00,93.1,160.07,38.052,32.166 +2020-03-04 05:00:00,91.47,194.645,42.455,32.166 +2020-03-04 05:15:00,91.38,225.574,42.455,32.166 +2020-03-04 05:30:00,94.97,219.925,42.455,32.166 +2020-03-04 05:45:00,95.93,211.703,42.455,32.166 +2020-03-04 06:00:00,106.42,208.898,57.986000000000004,32.166 +2020-03-04 06:15:00,112.03,215.11,57.986000000000004,32.166 +2020-03-04 06:30:00,114.0,217.014,57.986000000000004,32.166 +2020-03-04 06:45:00,116.7,220.18200000000002,57.986000000000004,32.166 +2020-03-04 07:00:00,122.52,222.373,71.868,32.166 +2020-03-04 07:15:00,122.72,225.428,71.868,32.166 +2020-03-04 07:30:00,125.92,225.655,71.868,32.166 +2020-03-04 07:45:00,128.12,224.088,71.868,32.166 +2020-03-04 08:00:00,130.83,222.176,62.225,32.166 +2020-03-04 08:15:00,130.93,220.354,62.225,32.166 +2020-03-04 08:30:00,131.84,215.78599999999997,62.225,32.166 +2020-03-04 08:45:00,131.99,211.262,62.225,32.166 +2020-03-04 09:00:00,133.12,203.688,58.802,32.166 +2020-03-04 09:15:00,134.1,200.15599999999998,58.802,32.166 +2020-03-04 09:30:00,135.91,198.843,58.802,32.166 +2020-03-04 09:45:00,136.01,196.042,58.802,32.166 +2020-03-04 10:00:00,135.9,193.19299999999998,54.122,32.166 +2020-03-04 10:15:00,137.47,189.97799999999998,54.122,32.166 +2020-03-04 10:30:00,136.84,187.53799999999998,54.122,32.166 +2020-03-04 10:45:00,137.85,186.72799999999998,54.122,32.166 +2020-03-04 11:00:00,137.84,183.793,54.368,32.166 +2020-03-04 11:15:00,140.09,183.22099999999998,54.368,32.166 +2020-03-04 11:30:00,140.75,182.81599999999997,54.368,32.166 +2020-03-04 11:45:00,141.22,182.395,54.368,32.166 +2020-03-04 12:00:00,138.17,177.02900000000002,52.74,32.166 +2020-03-04 12:15:00,140.05,177.263,52.74,32.166 +2020-03-04 12:30:00,136.13,176.671,52.74,32.166 +2020-03-04 12:45:00,137.21,177.545,52.74,32.166 +2020-03-04 13:00:00,141.05,176.90599999999998,52.544,32.166 +2020-03-04 13:15:00,139.87,175.361,52.544,32.166 +2020-03-04 13:30:00,125.18,173.886,52.544,32.166 +2020-03-04 13:45:00,130.05,173.352,52.544,32.166 +2020-03-04 14:00:00,125.22,174.88400000000001,53.602,32.166 +2020-03-04 14:15:00,123.48,174.174,53.602,32.166 +2020-03-04 14:30:00,126.37,174.335,53.602,32.166 +2020-03-04 14:45:00,123.47,175.268,53.602,32.166 +2020-03-04 15:00:00,121.69,177.032,55.59,32.166 +2020-03-04 15:15:00,118.75,175.852,55.59,32.166 +2020-03-04 15:30:00,116.12,176.09900000000002,55.59,32.166 +2020-03-04 15:45:00,120.18,176.293,55.59,32.166 +2020-03-04 16:00:00,123.92,179.798,57.586999999999996,32.166 +2020-03-04 16:15:00,124.85,181.62099999999998,57.586999999999996,32.166 +2020-03-04 16:30:00,120.88,183.65099999999998,57.586999999999996,32.166 +2020-03-04 16:45:00,124.56,184.18900000000002,57.586999999999996,32.166 +2020-03-04 17:00:00,128.84,186.928,62.111999999999995,32.166 +2020-03-04 17:15:00,130.98,189.553,62.111999999999995,32.166 +2020-03-04 17:30:00,131.7,192.36900000000003,62.111999999999995,32.166 +2020-03-04 17:45:00,131.94,193.391,62.111999999999995,32.166 +2020-03-04 18:00:00,138.98,196.595,64.605,32.166 +2020-03-04 18:15:00,143.1,197.04,64.605,32.166 +2020-03-04 18:30:00,145.58,195.46400000000003,64.605,32.166 +2020-03-04 18:45:00,139.57,197.40099999999998,64.605,32.166 +2020-03-04 19:00:00,139.2,196.305,65.55199999999999,32.166 +2020-03-04 19:15:00,137.12,193.24200000000002,65.55199999999999,32.166 +2020-03-04 19:30:00,134.82,192.373,65.55199999999999,32.166 +2020-03-04 19:45:00,128.94,189.204,65.55199999999999,32.166 +2020-03-04 20:00:00,125.39,183.868,66.778,32.166 +2020-03-04 20:15:00,122.78,178.637,66.778,32.166 +2020-03-04 20:30:00,118.75,174.926,66.778,32.166 +2020-03-04 20:45:00,115.74,172.472,66.778,32.166 +2020-03-04 21:00:00,113.84,169.278,56.103,32.166 +2020-03-04 21:15:00,111.66,166.042,56.103,32.166 +2020-03-04 21:30:00,107.31,163.753,56.103,32.166 +2020-03-04 21:45:00,108.44,163.576,56.103,32.166 +2020-03-04 22:00:00,103.17,156.173,51.371,32.166 +2020-03-04 22:15:00,102.87,152.128,51.371,32.166 +2020-03-04 22:30:00,93.83,136.359,51.371,32.166 +2020-03-04 22:45:00,94.61,128.59799999999998,51.371,32.166 +2020-03-04 23:00:00,93.52,121.95,42.798,32.166 +2020-03-04 23:15:00,93.24,120.189,42.798,32.166 +2020-03-04 23:30:00,86.27,121.729,42.798,32.166 +2020-03-04 23:45:00,85.18,121.86,42.798,32.166 +2020-03-05 00:00:00,79.97,116.865,39.069,32.166 +2020-03-05 00:15:00,84.04,115.10700000000001,39.069,32.166 +2020-03-05 00:30:00,86.08,114.35799999999999,39.069,32.166 +2020-03-05 00:45:00,83.85,114.272,39.069,32.166 +2020-03-05 01:00:00,75.83,115.867,37.043,32.166 +2020-03-05 01:15:00,75.46,116.329,37.043,32.166 +2020-03-05 01:30:00,75.14,116.435,37.043,32.166 +2020-03-05 01:45:00,77.65,116.557,37.043,32.166 +2020-03-05 02:00:00,79.69,118.488,34.625,32.166 +2020-03-05 02:15:00,80.69,119.448,34.625,32.166 +2020-03-05 02:30:00,78.05,120.249,34.625,32.166 +2020-03-05 02:45:00,76.94,121.99700000000001,34.625,32.166 +2020-03-05 03:00:00,77.22,124.545,33.812,32.166 +2020-03-05 03:15:00,82.54,126.35600000000001,33.812,32.166 +2020-03-05 03:30:00,83.02,128.299,33.812,32.166 +2020-03-05 03:45:00,80.83,129.442,33.812,32.166 +2020-03-05 04:00:00,78.0,142.746,35.236999999999995,32.166 +2020-03-05 04:15:00,80.68,155.84799999999998,35.236999999999995,32.166 +2020-03-05 04:30:00,86.04,157.845,35.236999999999995,32.166 +2020-03-05 04:45:00,87.84,159.67,35.236999999999995,32.166 +2020-03-05 05:00:00,88.83,194.22299999999998,40.375,32.166 +2020-03-05 05:15:00,87.34,225.169,40.375,32.166 +2020-03-05 05:30:00,90.9,219.488,40.375,32.166 +2020-03-05 05:45:00,95.15,211.271,40.375,32.166 +2020-03-05 06:00:00,105.43,208.47400000000002,52.316,32.166 +2020-03-05 06:15:00,108.76,214.69299999999998,52.316,32.166 +2020-03-05 06:30:00,115.44,216.55200000000002,52.316,32.166 +2020-03-05 06:45:00,115.49,219.701,52.316,32.166 +2020-03-05 07:00:00,122.69,221.91299999999998,64.115,32.166 +2020-03-05 07:15:00,123.41,224.935,64.115,32.166 +2020-03-05 07:30:00,125.69,225.12099999999998,64.115,32.166 +2020-03-05 07:45:00,128.28,223.512,64.115,32.166 +2020-03-05 08:00:00,131.57,221.575,55.033,32.166 +2020-03-05 08:15:00,127.69,219.74099999999999,55.033,32.166 +2020-03-05 08:30:00,127.62,215.11599999999999,55.033,32.166 +2020-03-05 08:45:00,124.84,210.609,55.033,32.166 +2020-03-05 09:00:00,122.67,203.046,49.411,32.166 +2020-03-05 09:15:00,118.75,199.516,49.411,32.166 +2020-03-05 09:30:00,118.32,198.22400000000002,49.411,32.166 +2020-03-05 09:45:00,118.04,195.43599999999998,49.411,32.166 +2020-03-05 10:00:00,117.58,192.6,45.82899999999999,32.166 +2020-03-05 10:15:00,119.95,189.429,45.82899999999999,32.166 +2020-03-05 10:30:00,123.89,187.00799999999998,45.82899999999999,32.166 +2020-03-05 10:45:00,127.92,186.218,45.82899999999999,32.166 +2020-03-05 11:00:00,124.66,183.275,44.333,32.166 +2020-03-05 11:15:00,129.25,182.722,44.333,32.166 +2020-03-05 11:30:00,117.7,182.325,44.333,32.166 +2020-03-05 11:45:00,115.76,181.92,44.333,32.166 +2020-03-05 12:00:00,113.86,176.574,42.95,32.166 +2020-03-05 12:15:00,115.98,176.822,42.95,32.166 +2020-03-05 12:30:00,123.05,176.19299999999998,42.95,32.166 +2020-03-05 12:45:00,119.31,177.06400000000002,42.95,32.166 +2020-03-05 13:00:00,116.22,176.468,42.489,32.166 +2020-03-05 13:15:00,117.03,174.90099999999998,42.489,32.166 +2020-03-05 13:30:00,114.34,173.41299999999998,42.489,32.166 +2020-03-05 13:45:00,112.98,172.882,42.489,32.166 +2020-03-05 14:00:00,120.48,174.481,43.448,32.166 +2020-03-05 14:15:00,125.43,173.747,43.448,32.166 +2020-03-05 14:30:00,124.36,173.87900000000002,43.448,32.166 +2020-03-05 14:45:00,124.56,174.821,43.448,32.166 +2020-03-05 15:00:00,120.75,176.59599999999998,45.994,32.166 +2020-03-05 15:15:00,117.53,175.38400000000001,45.994,32.166 +2020-03-05 15:30:00,125.28,175.582,45.994,32.166 +2020-03-05 15:45:00,123.67,175.75900000000001,45.994,32.166 +2020-03-05 16:00:00,124.31,179.269,48.167,32.166 +2020-03-05 16:15:00,121.46,181.071,48.167,32.166 +2020-03-05 16:30:00,123.02,183.101,48.167,32.166 +2020-03-05 16:45:00,120.81,183.59900000000002,48.167,32.166 +2020-03-05 17:00:00,126.49,186.34900000000002,52.637,32.166 +2020-03-05 17:15:00,122.58,188.981,52.637,32.166 +2020-03-05 17:30:00,129.9,191.82299999999998,52.637,32.166 +2020-03-05 17:45:00,131.12,192.859,52.637,32.166 +2020-03-05 18:00:00,136.47,196.07,55.739,32.166 +2020-03-05 18:15:00,136.58,196.582,55.739,32.166 +2020-03-05 18:30:00,142.41,195.00400000000002,55.739,32.166 +2020-03-05 18:45:00,143.42,196.96200000000002,55.739,32.166 +2020-03-05 19:00:00,139.76,195.826,56.36600000000001,32.166 +2020-03-05 19:15:00,135.79,192.78099999999998,56.36600000000001,32.166 +2020-03-05 19:30:00,135.89,191.94099999999997,56.36600000000001,32.166 +2020-03-05 19:45:00,135.03,188.817,56.36600000000001,32.166 +2020-03-05 20:00:00,127.56,183.449,56.338,32.166 +2020-03-05 20:15:00,124.1,178.235,56.338,32.166 +2020-03-05 20:30:00,121.73,174.549,56.338,32.166 +2020-03-05 20:45:00,117.92,172.1,56.338,32.166 +2020-03-05 21:00:00,109.2,168.894,49.894,32.166 +2020-03-05 21:15:00,112.76,165.65400000000002,49.894,32.166 +2020-03-05 21:30:00,110.85,163.363,49.894,32.166 +2020-03-05 21:45:00,109.7,163.209,49.894,32.166 +2020-03-05 22:00:00,101.13,155.79,46.687,32.166 +2020-03-05 22:15:00,103.31,151.773,46.687,32.166 +2020-03-05 22:30:00,97.08,135.952,46.687,32.166 +2020-03-05 22:45:00,93.57,128.194,46.687,32.166 +2020-03-05 23:00:00,85.09,121.542,39.211,32.166 +2020-03-05 23:15:00,84.64,119.8,39.211,32.166 +2020-03-05 23:30:00,87.76,121.34700000000001,39.211,32.166 +2020-03-05 23:45:00,89.95,121.505,39.211,32.166 +2020-03-06 00:00:00,84.63,115.272,36.616,32.166 +2020-03-06 00:15:00,80.02,113.741,36.616,32.166 +2020-03-06 00:30:00,78.58,112.88,36.616,32.166 +2020-03-06 00:45:00,79.97,112.955,36.616,32.166 +2020-03-06 01:00:00,81.81,114.15899999999999,33.799,32.166 +2020-03-06 01:15:00,83.15,115.40899999999999,33.799,32.166 +2020-03-06 01:30:00,81.42,115.375,33.799,32.166 +2020-03-06 01:45:00,77.29,115.573,33.799,32.166 +2020-03-06 02:00:00,77.39,117.697,32.968,32.166 +2020-03-06 02:15:00,82.75,118.53200000000001,32.968,32.166 +2020-03-06 02:30:00,81.3,119.965,32.968,32.166 +2020-03-06 02:45:00,79.68,121.664,32.968,32.166 +2020-03-06 03:00:00,75.05,123.374,33.533,32.166 +2020-03-06 03:15:00,80.07,125.906,33.533,32.166 +2020-03-06 03:30:00,83.67,127.795,33.533,32.166 +2020-03-06 03:45:00,79.45,129.38,33.533,32.166 +2020-03-06 04:00:00,79.38,142.92700000000002,36.102,32.166 +2020-03-06 04:15:00,84.68,155.572,36.102,32.166 +2020-03-06 04:30:00,86.79,157.934,36.102,32.166 +2020-03-06 04:45:00,89.75,158.566,36.102,32.166 +2020-03-06 05:00:00,92.61,191.81799999999998,42.423,32.166 +2020-03-06 05:15:00,88.55,224.35,42.423,32.166 +2020-03-06 05:30:00,92.57,219.692,42.423,32.166 +2020-03-06 05:45:00,95.18,211.36,42.423,32.166 +2020-03-06 06:00:00,105.52,209.019,55.38,32.166 +2020-03-06 06:15:00,109.46,213.859,55.38,32.166 +2020-03-06 06:30:00,110.06,214.83900000000003,55.38,32.166 +2020-03-06 06:45:00,112.44,219.50599999999997,55.38,32.166 +2020-03-06 07:00:00,119.09,221.028,65.929,32.166 +2020-03-06 07:15:00,120.34,225.11900000000003,65.929,32.166 +2020-03-06 07:30:00,122.16,224.828,65.929,32.166 +2020-03-06 07:45:00,120.89,222.30200000000002,65.929,32.166 +2020-03-06 08:00:00,121.85,219.385,57.336999999999996,32.166 +2020-03-06 08:15:00,119.68,217.25400000000002,57.336999999999996,32.166 +2020-03-06 08:30:00,118.65,213.497,57.336999999999996,32.166 +2020-03-06 08:45:00,117.07,207.47099999999998,57.336999999999996,32.166 +2020-03-06 09:00:00,115.36,200.05700000000002,54.226000000000006,32.166 +2020-03-06 09:15:00,114.99,197.328,54.226000000000006,32.166 +2020-03-06 09:30:00,115.37,195.553,54.226000000000006,32.166 +2020-03-06 09:45:00,113.84,192.722,54.226000000000006,32.166 +2020-03-06 10:00:00,112.66,188.787,51.298,32.166 +2020-03-06 10:15:00,112.59,186.301,51.298,32.166 +2020-03-06 10:30:00,112.69,183.907,51.298,32.166 +2020-03-06 10:45:00,112.62,182.697,51.298,32.166 +2020-03-06 11:00:00,113.85,179.75400000000002,50.839,32.166 +2020-03-06 11:15:00,114.18,178.215,50.839,32.166 +2020-03-06 11:30:00,118.13,179.46599999999998,50.839,32.166 +2020-03-06 11:45:00,115.78,178.99900000000002,50.839,32.166 +2020-03-06 12:00:00,110.8,174.774,47.976000000000006,32.166 +2020-03-06 12:15:00,112.96,172.933,47.976000000000006,32.166 +2020-03-06 12:30:00,107.04,172.435,47.976000000000006,32.166 +2020-03-06 12:45:00,113.31,173.696,47.976000000000006,32.166 +2020-03-06 13:00:00,113.56,174.122,46.299,32.166 +2020-03-06 13:15:00,112.83,173.34400000000002,46.299,32.166 +2020-03-06 13:30:00,105.26,171.96599999999998,46.299,32.166 +2020-03-06 13:45:00,104.96,171.417,46.299,32.166 +2020-03-06 14:00:00,107.2,171.88099999999997,44.971000000000004,32.166 +2020-03-06 14:15:00,110.28,171.00900000000001,44.971000000000004,32.166 +2020-03-06 14:30:00,113.01,171.803,44.971000000000004,32.166 +2020-03-06 14:45:00,113.67,172.955,44.971000000000004,32.166 +2020-03-06 15:00:00,114.93,174.264,47.48,32.166 +2020-03-06 15:15:00,115.31,172.56799999999998,47.48,32.166 +2020-03-06 15:30:00,123.49,171.176,47.48,32.166 +2020-03-06 15:45:00,122.8,171.57299999999998,47.48,32.166 +2020-03-06 16:00:00,120.52,173.84599999999998,50.648,32.166 +2020-03-06 16:15:00,118.33,175.97,50.648,32.166 +2020-03-06 16:30:00,123.41,178.06799999999998,50.648,32.166 +2020-03-06 16:45:00,122.27,178.315,50.648,32.166 +2020-03-06 17:00:00,123.95,181.48,56.251000000000005,32.166 +2020-03-06 17:15:00,125.14,183.697,56.251000000000005,32.166 +2020-03-06 17:30:00,127.64,186.27200000000002,56.251000000000005,32.166 +2020-03-06 17:45:00,129.37,187.075,56.251000000000005,32.166 +2020-03-06 18:00:00,132.2,190.96,58.982,32.166 +2020-03-06 18:15:00,134.17,191.049,58.982,32.166 +2020-03-06 18:30:00,139.97,189.84,58.982,32.166 +2020-03-06 18:45:00,137.26,191.862,58.982,32.166 +2020-03-06 19:00:00,138.5,191.671,57.293,32.166 +2020-03-06 19:15:00,136.65,190.049,57.293,32.166 +2020-03-06 19:30:00,132.19,188.83900000000003,57.293,32.166 +2020-03-06 19:45:00,128.54,185.19400000000002,57.293,32.166 +2020-03-06 20:00:00,118.1,179.83599999999998,59.433,32.166 +2020-03-06 20:15:00,120.22,174.738,59.433,32.166 +2020-03-06 20:30:00,116.22,170.981,59.433,32.166 +2020-03-06 20:45:00,116.32,168.983,59.433,32.166 +2020-03-06 21:00:00,108.24,166.416,52.153999999999996,32.166 +2020-03-06 21:15:00,107.96,163.793,52.153999999999996,32.166 +2020-03-06 21:30:00,103.98,161.531,52.153999999999996,32.166 +2020-03-06 21:45:00,100.92,161.953,52.153999999999996,32.166 +2020-03-06 22:00:00,101.26,155.461,47.125,32.166 +2020-03-06 22:15:00,98.2,151.312,47.125,32.166 +2020-03-06 22:30:00,91.48,142.222,47.125,32.166 +2020-03-06 22:45:00,86.47,138.014,47.125,32.166 +2020-03-06 23:00:00,80.32,131.118,41.236000000000004,32.166 +2020-03-06 23:15:00,86.56,127.30799999999999,41.236000000000004,32.166 +2020-03-06 23:30:00,86.05,127.225,41.236000000000004,32.166 +2020-03-06 23:45:00,84.17,126.74600000000001,41.236000000000004,32.166 +2020-03-07 00:00:00,73.36,112.361,36.484,31.988000000000003 +2020-03-07 00:15:00,72.69,106.662,36.484,31.988000000000003 +2020-03-07 00:30:00,73.63,106.99600000000001,36.484,31.988000000000003 +2020-03-07 00:45:00,79.66,107.662,36.484,31.988000000000003 +2020-03-07 01:00:00,75.3,109.476,32.391999999999996,31.988000000000003 +2020-03-07 01:15:00,77.06,109.846,32.391999999999996,31.988000000000003 +2020-03-07 01:30:00,71.97,109.189,32.391999999999996,31.988000000000003 +2020-03-07 01:45:00,68.58,109.354,32.391999999999996,31.988000000000003 +2020-03-07 02:00:00,73.65,112.00200000000001,30.194000000000003,31.988000000000003 +2020-03-07 02:15:00,74.65,112.369,30.194000000000003,31.988000000000003 +2020-03-07 02:30:00,69.35,112.65100000000001,30.194000000000003,31.988000000000003 +2020-03-07 02:45:00,68.86,114.555,30.194000000000003,31.988000000000003 +2020-03-07 03:00:00,66.7,116.7,29.677,31.988000000000003 +2020-03-07 03:15:00,65.75,117.959,29.677,31.988000000000003 +2020-03-07 03:30:00,70.1,118.37299999999999,29.677,31.988000000000003 +2020-03-07 03:45:00,72.68,120.28200000000001,29.677,31.988000000000003 +2020-03-07 04:00:00,68.24,129.653,29.616,31.988000000000003 +2020-03-07 04:15:00,67.2,139.745,29.616,31.988000000000003 +2020-03-07 04:30:00,67.39,139.829,29.616,31.988000000000003 +2020-03-07 04:45:00,67.69,140.015,29.616,31.988000000000003 +2020-03-07 05:00:00,67.99,156.97299999999998,29.625,31.988000000000003 +2020-03-07 05:15:00,67.86,169.953,29.625,31.988000000000003 +2020-03-07 05:30:00,67.93,165.817,29.625,31.988000000000003 +2020-03-07 05:45:00,70.0,163.291,29.625,31.988000000000003 +2020-03-07 06:00:00,71.25,180.298,30.551,31.988000000000003 +2020-03-07 06:15:00,72.04,201.768,30.551,31.988000000000003 +2020-03-07 06:30:00,72.04,197.2,30.551,31.988000000000003 +2020-03-07 06:45:00,72.96,192.865,30.551,31.988000000000003 +2020-03-07 07:00:00,76.51,190.575,34.865,31.988000000000003 +2020-03-07 07:15:00,78.94,193.25799999999998,34.865,31.988000000000003 +2020-03-07 07:30:00,81.32,195.71099999999998,34.865,31.988000000000003 +2020-03-07 07:45:00,83.9,197.054,34.865,31.988000000000003 +2020-03-07 08:00:00,88.57,197.97299999999998,41.456,31.988000000000003 +2020-03-07 08:15:00,89.24,199.271,41.456,31.988000000000003 +2020-03-07 08:30:00,90.68,196.99599999999998,41.456,31.988000000000003 +2020-03-07 08:45:00,94.24,194.13099999999997,41.456,31.988000000000003 +2020-03-07 09:00:00,96.14,188.805,43.001999999999995,31.988000000000003 +2020-03-07 09:15:00,98.61,186.863,43.001999999999995,31.988000000000003 +2020-03-07 09:30:00,102.12,186.041,43.001999999999995,31.988000000000003 +2020-03-07 09:45:00,98.59,183.317,43.001999999999995,31.988000000000003 +2020-03-07 10:00:00,95.01,179.757,42.047,31.988000000000003 +2020-03-07 10:15:00,98.58,177.519,42.047,31.988000000000003 +2020-03-07 10:30:00,95.97,175.25900000000001,42.047,31.988000000000003 +2020-03-07 10:45:00,94.89,175.265,42.047,31.988000000000003 +2020-03-07 11:00:00,95.66,172.479,39.894,31.988000000000003 +2020-03-07 11:15:00,96.05,170.417,39.894,31.988000000000003 +2020-03-07 11:30:00,95.32,170.642,39.894,31.988000000000003 +2020-03-07 11:45:00,95.01,169.364,39.894,31.988000000000003 +2020-03-07 12:00:00,95.78,164.368,38.122,31.988000000000003 +2020-03-07 12:15:00,92.41,163.267,38.122,31.988000000000003 +2020-03-07 12:30:00,86.18,163.02,38.122,31.988000000000003 +2020-03-07 12:45:00,85.12,163.636,38.122,31.988000000000003 +2020-03-07 13:00:00,87.26,163.61,34.645,31.988000000000003 +2020-03-07 13:15:00,86.32,160.77,34.645,31.988000000000003 +2020-03-07 13:30:00,84.06,158.97299999999998,34.645,31.988000000000003 +2020-03-07 13:45:00,83.33,158.702,34.645,31.988000000000003 +2020-03-07 14:00:00,77.3,160.404,33.739000000000004,31.988000000000003 +2020-03-07 14:15:00,74.26,158.83,33.739000000000004,31.988000000000003 +2020-03-07 14:30:00,74.73,157.79399999999998,33.739000000000004,31.988000000000003 +2020-03-07 14:45:00,79.24,159.227,33.739000000000004,31.988000000000003 +2020-03-07 15:00:00,79.87,161.2,35.908,31.988000000000003 +2020-03-07 15:15:00,82.29,160.34,35.908,31.988000000000003 +2020-03-07 15:30:00,82.88,160.406,35.908,31.988000000000003 +2020-03-07 15:45:00,83.16,160.681,35.908,31.988000000000003 +2020-03-07 16:00:00,82.92,162.039,39.249,31.988000000000003 +2020-03-07 16:15:00,82.39,164.894,39.249,31.988000000000003 +2020-03-07 16:30:00,84.42,166.97,39.249,31.988000000000003 +2020-03-07 16:45:00,86.48,168.02700000000002,39.249,31.988000000000003 +2020-03-07 17:00:00,89.54,170.46900000000002,46.045,31.988000000000003 +2020-03-07 17:15:00,90.25,174.037,46.045,31.988000000000003 +2020-03-07 17:30:00,93.17,176.52700000000002,46.045,31.988000000000003 +2020-03-07 17:45:00,96.07,177.01,46.045,31.988000000000003 +2020-03-07 18:00:00,104.2,180.562,48.238,31.988000000000003 +2020-03-07 18:15:00,106.48,182.665,48.238,31.988000000000003 +2020-03-07 18:30:00,104.75,182.90599999999998,48.238,31.988000000000003 +2020-03-07 18:45:00,105.22,181.287,48.238,31.988000000000003 +2020-03-07 19:00:00,104.77,181.752,46.785,31.988000000000003 +2020-03-07 19:15:00,101.74,179.548,46.785,31.988000000000003 +2020-03-07 19:30:00,103.23,179.188,46.785,31.988000000000003 +2020-03-07 19:45:00,99.14,175.61599999999999,46.785,31.988000000000003 +2020-03-07 20:00:00,94.25,172.472,39.830999999999996,31.988000000000003 +2020-03-07 20:15:00,90.48,169.38,39.830999999999996,31.988000000000003 +2020-03-07 20:30:00,88.69,165.206,39.830999999999996,31.988000000000003 +2020-03-07 20:45:00,89.27,163.041,39.830999999999996,31.988000000000003 +2020-03-07 21:00:00,82.31,162.475,34.063,31.988000000000003 +2020-03-07 21:15:00,82.13,160.234,34.063,31.988000000000003 +2020-03-07 21:30:00,81.1,159.189,34.063,31.988000000000003 +2020-03-07 21:45:00,79.97,159.173,34.063,31.988000000000003 +2020-03-07 22:00:00,79.99,153.97299999999998,34.455999999999996,31.988000000000003 +2020-03-07 22:15:00,77.88,152.298,34.455999999999996,31.988000000000003 +2020-03-07 22:30:00,73.56,149.155,34.455999999999996,31.988000000000003 +2020-03-07 22:45:00,72.67,146.81,34.455999999999996,31.988000000000003 +2020-03-07 23:00:00,68.96,142.2,27.840999999999998,31.988000000000003 +2020-03-07 23:15:00,69.09,136.869,27.840999999999998,31.988000000000003 +2020-03-07 23:30:00,65.76,135.38299999999998,27.840999999999998,31.988000000000003 +2020-03-07 23:45:00,64.86,132.614,27.840999999999998,31.988000000000003 +2020-03-08 00:00:00,61.0,112.605,20.007,31.988000000000003 +2020-03-08 00:15:00,61.21,106.455,20.007,31.988000000000003 +2020-03-08 00:30:00,60.15,106.39200000000001,20.007,31.988000000000003 +2020-03-08 00:45:00,60.07,107.709,20.007,31.988000000000003 +2020-03-08 01:00:00,56.47,109.37899999999999,17.378,31.988000000000003 +2020-03-08 01:15:00,57.99,110.706,17.378,31.988000000000003 +2020-03-08 01:30:00,57.0,110.509,17.378,31.988000000000003 +2020-03-08 01:45:00,57.02,110.329,17.378,31.988000000000003 +2020-03-08 02:00:00,55.64,112.262,16.145,31.988000000000003 +2020-03-08 02:15:00,56.91,111.91,16.145,31.988000000000003 +2020-03-08 02:30:00,56.07,113.05,16.145,31.988000000000003 +2020-03-08 02:45:00,56.9,115.353,16.145,31.988000000000003 +2020-03-08 03:00:00,55.26,117.9,15.427999999999999,31.988000000000003 +2020-03-08 03:15:00,55.5,118.68299999999999,15.427999999999999,31.988000000000003 +2020-03-08 03:30:00,53.73,120.31200000000001,15.427999999999999,31.988000000000003 +2020-03-08 03:45:00,55.64,122.04700000000001,15.427999999999999,31.988000000000003 +2020-03-08 04:00:00,55.63,131.18200000000002,16.663,31.988000000000003 +2020-03-08 04:15:00,54.6,140.243,16.663,31.988000000000003 +2020-03-08 04:30:00,57.94,140.602,16.663,31.988000000000003 +2020-03-08 04:45:00,58.3,140.964,16.663,31.988000000000003 +2020-03-08 05:00:00,57.65,154.621,17.271,31.988000000000003 +2020-03-08 05:15:00,59.1,165.231,17.271,31.988000000000003 +2020-03-08 05:30:00,56.61,160.846,17.271,31.988000000000003 +2020-03-08 05:45:00,59.93,158.515,17.271,31.988000000000003 +2020-03-08 06:00:00,60.8,175.07299999999998,17.612000000000002,31.988000000000003 +2020-03-08 06:15:00,60.98,195.047,17.612000000000002,31.988000000000003 +2020-03-08 06:30:00,59.58,189.28799999999998,17.612000000000002,31.988000000000003 +2020-03-08 06:45:00,60.73,183.79,17.612000000000002,31.988000000000003 +2020-03-08 07:00:00,64.07,183.87099999999998,20.88,31.988000000000003 +2020-03-08 07:15:00,64.46,185.475,20.88,31.988000000000003 +2020-03-08 07:30:00,67.14,186.912,20.88,31.988000000000003 +2020-03-08 07:45:00,68.83,187.49099999999999,20.88,31.988000000000003 +2020-03-08 08:00:00,70.55,190.217,25.861,31.988000000000003 +2020-03-08 08:15:00,68.92,191.58599999999998,25.861,31.988000000000003 +2020-03-08 08:30:00,68.33,190.949,25.861,31.988000000000003 +2020-03-08 08:45:00,68.61,190.00599999999997,25.861,31.988000000000003 +2020-03-08 09:00:00,68.42,184.287,27.921999999999997,31.988000000000003 +2020-03-08 09:15:00,68.88,182.803,27.921999999999997,31.988000000000003 +2020-03-08 09:30:00,66.98,181.915,27.921999999999997,31.988000000000003 +2020-03-08 09:45:00,66.66,179.234,27.921999999999997,31.988000000000003 +2020-03-08 10:00:00,63.08,178.157,29.048000000000002,31.988000000000003 +2020-03-08 10:15:00,65.17,176.486,29.048000000000002,31.988000000000003 +2020-03-08 10:30:00,66.69,174.85,29.048000000000002,31.988000000000003 +2020-03-08 10:45:00,68.39,173.22299999999998,29.048000000000002,31.988000000000003 +2020-03-08 11:00:00,71.32,171.245,32.02,31.988000000000003 +2020-03-08 11:15:00,68.73,169.267,32.02,31.988000000000003 +2020-03-08 11:30:00,77.87,168.745,32.02,31.988000000000003 +2020-03-08 11:45:00,71.63,168.09599999999998,32.02,31.988000000000003 +2020-03-08 12:00:00,69.65,162.733,28.55,31.988000000000003 +2020-03-08 12:15:00,64.14,163.364,28.55,31.988000000000003 +2020-03-08 12:30:00,62.02,161.72799999999998,28.55,31.988000000000003 +2020-03-08 12:45:00,62.53,161.333,28.55,31.988000000000003 +2020-03-08 13:00:00,59.46,160.626,25.601999999999997,31.988000000000003 +2020-03-08 13:15:00,59.09,160.545,25.601999999999997,31.988000000000003 +2020-03-08 13:30:00,59.19,158.379,25.601999999999997,31.988000000000003 +2020-03-08 13:45:00,58.93,157.644,25.601999999999997,31.988000000000003 +2020-03-08 14:00:00,57.84,159.814,23.916999999999998,31.988000000000003 +2020-03-08 14:15:00,58.46,159.417,23.916999999999998,31.988000000000003 +2020-03-08 14:30:00,62.93,159.34799999999998,23.916999999999998,31.988000000000003 +2020-03-08 14:45:00,64.38,160.249,23.916999999999998,31.988000000000003 +2020-03-08 15:00:00,64.9,160.799,24.064,31.988000000000003 +2020-03-08 15:15:00,65.65,160.517,24.064,31.988000000000003 +2020-03-08 15:30:00,65.05,161.069,24.064,31.988000000000003 +2020-03-08 15:45:00,64.57,162.025,24.064,31.988000000000003 +2020-03-08 16:00:00,67.33,164.97099999999998,28.189,31.988000000000003 +2020-03-08 16:15:00,68.58,166.96400000000003,28.189,31.988000000000003 +2020-03-08 16:30:00,68.93,169.421,28.189,31.988000000000003 +2020-03-08 16:45:00,71.27,170.572,28.189,31.988000000000003 +2020-03-08 17:00:00,75.95,173.09099999999998,37.576,31.988000000000003 +2020-03-08 17:15:00,76.2,176.542,37.576,31.988000000000003 +2020-03-08 17:30:00,80.05,179.435,37.576,31.988000000000003 +2020-03-08 17:45:00,83.25,182.171,37.576,31.988000000000003 +2020-03-08 18:00:00,91.18,185.28900000000002,42.669,31.988000000000003 +2020-03-08 18:15:00,96.39,188.69400000000002,42.669,31.988000000000003 +2020-03-08 18:30:00,101.38,186.88299999999998,42.669,31.988000000000003 +2020-03-08 18:45:00,99.75,187.09,42.669,31.988000000000003 +2020-03-08 19:00:00,97.87,187.46400000000003,43.538999999999994,31.988000000000003 +2020-03-08 19:15:00,93.75,185.72400000000002,43.538999999999994,31.988000000000003 +2020-03-08 19:30:00,94.16,185.204,43.538999999999994,31.988000000000003 +2020-03-08 19:45:00,89.68,183.025,43.538999999999994,31.988000000000003 +2020-03-08 20:00:00,86.68,179.81799999999998,37.330999999999996,31.988000000000003 +2020-03-08 20:15:00,86.27,177.678,37.330999999999996,31.988000000000003 +2020-03-08 20:30:00,83.87,174.827,37.330999999999996,31.988000000000003 +2020-03-08 20:45:00,83.17,171.297,37.330999999999996,31.988000000000003 +2020-03-08 21:00:00,79.53,168.202,33.856,31.988000000000003 +2020-03-08 21:15:00,80.3,165.315,33.856,31.988000000000003 +2020-03-08 21:30:00,80.55,164.47400000000002,33.856,31.988000000000003 +2020-03-08 21:45:00,85.28,164.655,33.856,31.988000000000003 +2020-03-08 22:00:00,87.48,158.54,34.711999999999996,31.988000000000003 +2020-03-08 22:15:00,88.51,155.951,34.711999999999996,31.988000000000003 +2020-03-08 22:30:00,83.88,149.629,34.711999999999996,31.988000000000003 +2020-03-08 22:45:00,79.79,146.309,34.711999999999996,31.988000000000003 +2020-03-08 23:00:00,77.62,138.95,29.698,31.988000000000003 +2020-03-08 23:15:00,82.04,135.591,29.698,31.988000000000003 +2020-03-08 23:30:00,82.56,134.83100000000002,29.698,31.988000000000003 +2020-03-08 23:45:00,79.6,132.972,29.698,31.988000000000003 +2020-03-09 00:00:00,69.92,116.464,29.983,32.166 +2020-03-09 00:15:00,69.51,113.265,29.983,32.166 +2020-03-09 00:30:00,71.32,113.25399999999999,29.983,32.166 +2020-03-09 00:45:00,76.73,114.006,29.983,32.166 +2020-03-09 01:00:00,74.19,115.686,29.122,32.166 +2020-03-09 01:15:00,73.87,116.49700000000001,29.122,32.166 +2020-03-09 01:30:00,67.47,116.385,29.122,32.166 +2020-03-09 01:45:00,74.37,116.307,29.122,32.166 +2020-03-09 02:00:00,73.55,118.26100000000001,28.676,32.166 +2020-03-09 02:15:00,73.53,119.147,28.676,32.166 +2020-03-09 02:30:00,68.13,120.645,28.676,32.166 +2020-03-09 02:45:00,75.19,122.339,28.676,32.166 +2020-03-09 03:00:00,75.27,126.15799999999999,26.552,32.166 +2020-03-09 03:15:00,74.56,128.586,26.552,32.166 +2020-03-09 03:30:00,73.02,130.048,26.552,32.166 +2020-03-09 03:45:00,77.49,131.209,26.552,32.166 +2020-03-09 04:00:00,77.93,144.89,27.44,32.166 +2020-03-09 04:15:00,76.54,158.27700000000002,27.44,32.166 +2020-03-09 04:30:00,74.57,160.655,27.44,32.166 +2020-03-09 04:45:00,74.33,161.21,27.44,32.166 +2020-03-09 05:00:00,77.36,190.71,36.825,32.166 +2020-03-09 05:15:00,83.26,221.83599999999998,36.825,32.166 +2020-03-09 05:30:00,86.0,217.45,36.825,32.166 +2020-03-09 05:45:00,90.09,209.351,36.825,32.166 +2020-03-09 06:00:00,98.46,207.56799999999998,56.589,32.166 +2020-03-09 06:15:00,105.15,212.236,56.589,32.166 +2020-03-09 06:30:00,108.02,214.56799999999998,56.589,32.166 +2020-03-09 06:45:00,110.16,218.112,56.589,32.166 +2020-03-09 07:00:00,117.31,220.562,67.49,32.166 +2020-03-09 07:15:00,118.79,223.637,67.49,32.166 +2020-03-09 07:30:00,122.33,224.175,67.49,32.166 +2020-03-09 07:45:00,125.44,222.329,67.49,32.166 +2020-03-09 08:00:00,129.3,220.19400000000002,60.028,32.166 +2020-03-09 08:15:00,131.05,219.35,60.028,32.166 +2020-03-09 08:30:00,131.67,214.641,60.028,32.166 +2020-03-09 08:45:00,131.5,210.576,60.028,32.166 +2020-03-09 09:00:00,130.44,203.824,55.018,32.166 +2020-03-09 09:15:00,134.15,198.77900000000002,55.018,32.166 +2020-03-09 09:30:00,133.09,196.834,55.018,32.166 +2020-03-09 09:45:00,134.3,194.14,55.018,32.166 +2020-03-09 10:00:00,134.13,192.09400000000002,51.183,32.166 +2020-03-09 10:15:00,134.12,190.137,51.183,32.166 +2020-03-09 10:30:00,134.67,187.627,51.183,32.166 +2020-03-09 10:45:00,135.0,186.516,51.183,32.166 +2020-03-09 11:00:00,136.7,182.109,50.065,32.166 +2020-03-09 11:15:00,135.2,181.91099999999997,50.065,32.166 +2020-03-09 11:30:00,135.31,182.80700000000002,50.065,32.166 +2020-03-09 11:45:00,135.27,181.85299999999998,50.065,32.166 +2020-03-09 12:00:00,136.04,177.92700000000002,48.141999999999996,32.166 +2020-03-09 12:15:00,132.64,178.58700000000002,48.141999999999996,32.166 +2020-03-09 12:30:00,129.11,177.00799999999998,48.141999999999996,32.166 +2020-03-09 12:45:00,129.79,178.063,48.141999999999996,32.166 +2020-03-09 13:00:00,129.66,178.076,47.887,32.166 +2020-03-09 13:15:00,129.83,176.532,47.887,32.166 +2020-03-09 13:30:00,129.35,173.873,47.887,32.166 +2020-03-09 13:45:00,131.32,173.292,47.887,32.166 +2020-03-09 14:00:00,126.29,174.843,48.571000000000005,32.166 +2020-03-09 14:15:00,126.44,173.885,48.571000000000005,32.166 +2020-03-09 14:30:00,128.22,173.24599999999998,48.571000000000005,32.166 +2020-03-09 14:45:00,126.5,174.40099999999998,48.571000000000005,32.166 +2020-03-09 15:00:00,128.64,176.653,49.937,32.166 +2020-03-09 15:15:00,125.18,174.889,49.937,32.166 +2020-03-09 15:30:00,124.09,174.695,49.937,32.166 +2020-03-09 15:45:00,123.67,175.143,49.937,32.166 +2020-03-09 16:00:00,123.86,178.435,52.963,32.166 +2020-03-09 16:15:00,124.07,179.68099999999998,52.963,32.166 +2020-03-09 16:30:00,120.57,181.12400000000002,52.963,32.166 +2020-03-09 16:45:00,120.89,181.093,52.963,32.166 +2020-03-09 17:00:00,124.22,183.347,61.163999999999994,32.166 +2020-03-09 17:15:00,125.08,185.929,61.163999999999994,32.166 +2020-03-09 17:30:00,129.2,188.269,61.163999999999994,32.166 +2020-03-09 17:45:00,128.13,189.52599999999998,61.163999999999994,32.166 +2020-03-09 18:00:00,133.54,192.96599999999998,63.788999999999994,32.166 +2020-03-09 18:15:00,133.96,194.083,63.788999999999994,32.166 +2020-03-09 18:30:00,130.46,192.81599999999997,63.788999999999994,32.166 +2020-03-09 18:45:00,129.43,194.101,63.788999999999994,32.166 +2020-03-09 19:00:00,129.65,192.855,63.913000000000004,32.166 +2020-03-09 19:15:00,125.51,190.139,63.913000000000004,32.166 +2020-03-09 19:30:00,133.89,190.092,63.913000000000004,32.166 +2020-03-09 19:45:00,132.07,187.08,63.913000000000004,32.166 +2020-03-09 20:00:00,119.33,181.389,65.44,32.166 +2020-03-09 20:15:00,115.81,177.02200000000002,65.44,32.166 +2020-03-09 20:30:00,115.88,172.455,65.44,32.166 +2020-03-09 20:45:00,115.72,170.53,65.44,32.166 +2020-03-09 21:00:00,105.11,167.868,59.117,32.166 +2020-03-09 21:15:00,99.97,163.887,59.117,32.166 +2020-03-09 21:30:00,98.25,162.292,59.117,32.166 +2020-03-09 21:45:00,97.32,161.97799999999998,59.117,32.166 +2020-03-09 22:00:00,92.52,152.808,52.301,32.166 +2020-03-09 22:15:00,93.86,149.191,52.301,32.166 +2020-03-09 22:30:00,94.64,133.034,52.301,32.166 +2020-03-09 22:45:00,93.49,125.016,52.301,32.166 +2020-03-09 23:00:00,89.93,118.40299999999999,44.373000000000005,32.166 +2020-03-09 23:15:00,83.28,117.455,44.373000000000005,32.166 +2020-03-09 23:30:00,82.01,119.383,44.373000000000005,32.166 +2020-03-09 23:45:00,79.61,120.059,44.373000000000005,32.166 +2020-03-10 00:00:00,79.63,115.156,44.647,32.166 +2020-03-10 00:15:00,81.95,113.444,44.647,32.166 +2020-03-10 00:30:00,80.93,112.60600000000001,44.647,32.166 +2020-03-10 00:45:00,77.16,112.525,44.647,32.166 +2020-03-10 01:00:00,70.56,113.905,41.433,32.166 +2020-03-10 01:15:00,76.76,114.289,41.433,32.166 +2020-03-10 01:30:00,76.51,114.307,41.433,32.166 +2020-03-10 01:45:00,79.3,114.46,41.433,32.166 +2020-03-10 02:00:00,73.31,116.336,39.909,32.166 +2020-03-10 02:15:00,73.05,117.262,39.909,32.166 +2020-03-10 02:30:00,69.81,118.15700000000001,39.909,32.166 +2020-03-10 02:45:00,71.57,119.90700000000001,39.909,32.166 +2020-03-10 03:00:00,71.1,122.52,39.14,32.166 +2020-03-10 03:15:00,78.8,124.244,39.14,32.166 +2020-03-10 03:30:00,81.43,126.156,39.14,32.166 +2020-03-10 03:45:00,78.81,127.365,39.14,32.166 +2020-03-10 04:00:00,75.34,140.69799999999998,40.015,32.166 +2020-03-10 04:15:00,74.56,153.744,40.015,32.166 +2020-03-10 04:30:00,80.72,155.83,40.015,32.166 +2020-03-10 04:45:00,85.85,157.588,40.015,32.166 +2020-03-10 05:00:00,89.69,192.037,44.93600000000001,32.166 +2020-03-10 05:15:00,87.41,223.071,44.93600000000001,32.166 +2020-03-10 05:30:00,94.15,217.21900000000002,44.93600000000001,32.166 +2020-03-10 05:45:00,99.31,209.03400000000002,44.93600000000001,32.166 +2020-03-10 06:00:00,108.19,206.273,57.271,32.166 +2020-03-10 06:15:00,106.67,212.52,57.271,32.166 +2020-03-10 06:30:00,105.56,214.145,57.271,32.166 +2020-03-10 06:45:00,110.72,217.18900000000002,57.271,32.166 +2020-03-10 07:00:00,114.85,219.50599999999997,68.352,32.166 +2020-03-10 07:15:00,122.4,222.36,68.352,32.166 +2020-03-10 07:30:00,126.34,222.338,68.352,32.166 +2020-03-10 07:45:00,126.87,220.52700000000002,68.352,32.166 +2020-03-10 08:00:00,121.17,218.46200000000002,60.717,32.166 +2020-03-10 08:15:00,121.73,216.57,60.717,32.166 +2020-03-10 08:30:00,123.57,211.655,60.717,32.166 +2020-03-10 08:45:00,119.88,207.236,60.717,32.166 +2020-03-10 09:00:00,120.24,199.739,54.603,32.166 +2020-03-10 09:15:00,125.53,196.215,54.603,32.166 +2020-03-10 09:30:00,125.18,195.02900000000002,54.603,32.166 +2020-03-10 09:45:00,117.63,192.30599999999998,54.603,32.166 +2020-03-10 10:00:00,122.65,189.53900000000002,52.308,32.166 +2020-03-10 10:15:00,122.53,186.588,52.308,32.166 +2020-03-10 10:30:00,122.83,184.278,52.308,32.166 +2020-03-10 10:45:00,123.85,183.583,52.308,32.166 +2020-03-10 11:00:00,124.85,180.6,51.838,32.166 +2020-03-10 11:15:00,124.63,180.15099999999998,51.838,32.166 +2020-03-10 11:30:00,120.0,179.793,51.838,32.166 +2020-03-10 11:45:00,112.97,179.477,51.838,32.166 +2020-03-10 12:00:00,116.62,174.23,50.375,32.166 +2020-03-10 12:15:00,124.04,174.543,50.375,32.166 +2020-03-10 12:30:00,134.05,173.727,50.375,32.166 +2020-03-10 12:45:00,127.79,174.581,50.375,32.166 +2020-03-10 13:00:00,126.21,174.203,50.735,32.166 +2020-03-10 13:15:00,131.91,172.525,50.735,32.166 +2020-03-10 13:30:00,125.28,170.977,50.735,32.166 +2020-03-10 13:45:00,123.2,170.459,50.735,32.166 +2020-03-10 14:00:00,122.54,172.40599999999998,50.946000000000005,32.166 +2020-03-10 14:15:00,121.23,171.549,50.946000000000005,32.166 +2020-03-10 14:30:00,124.05,171.524,50.946000000000005,32.166 +2020-03-10 14:45:00,124.51,172.513,50.946000000000005,32.166 +2020-03-10 15:00:00,131.26,174.335,53.18,32.166 +2020-03-10 15:15:00,123.68,172.97299999999998,53.18,32.166 +2020-03-10 15:30:00,117.32,172.918,53.18,32.166 +2020-03-10 15:45:00,118.3,173.00099999999998,53.18,32.166 +2020-03-10 16:00:00,122.14,176.535,54.928999999999995,32.166 +2020-03-10 16:15:00,120.8,178.231,54.928999999999995,32.166 +2020-03-10 16:30:00,123.17,180.257,54.928999999999995,32.166 +2020-03-10 16:45:00,120.87,180.545,54.928999999999995,32.166 +2020-03-10 17:00:00,119.71,183.364,60.913000000000004,32.166 +2020-03-10 17:15:00,128.27,186.02700000000002,60.913000000000004,32.166 +2020-03-10 17:30:00,128.23,188.989,60.913000000000004,32.166 +2020-03-10 17:45:00,132.22,190.09599999999998,60.913000000000004,32.166 +2020-03-10 18:00:00,133.86,193.33900000000003,62.214,32.166 +2020-03-10 18:15:00,138.61,194.197,62.214,32.166 +2020-03-10 18:30:00,141.3,192.59599999999998,62.214,32.166 +2020-03-10 18:45:00,137.8,194.65900000000002,62.214,32.166 +2020-03-10 19:00:00,133.97,193.333,62.38,32.166 +2020-03-10 19:15:00,133.09,190.37900000000002,62.38,32.166 +2020-03-10 19:30:00,134.88,189.68400000000003,62.38,32.166 +2020-03-10 19:45:00,130.24,186.79,62.38,32.166 +2020-03-10 20:00:00,124.19,181.265,65.018,32.166 +2020-03-10 20:15:00,120.85,176.13299999999998,65.018,32.166 +2020-03-10 20:30:00,119.04,172.58599999999998,65.018,32.166 +2020-03-10 20:45:00,115.01,170.15200000000002,65.018,32.166 +2020-03-10 21:00:00,110.97,166.892,56.416000000000004,32.166 +2020-03-10 21:15:00,109.67,163.63299999999998,56.416000000000004,32.166 +2020-03-10 21:30:00,108.54,161.345,56.416000000000004,32.166 +2020-03-10 21:45:00,105.86,161.29399999999998,56.416000000000004,32.166 +2020-03-10 22:00:00,98.25,153.8,52.846000000000004,32.166 +2020-03-10 22:15:00,100.43,149.92,52.846000000000004,32.166 +2020-03-10 22:30:00,97.18,133.826,52.846000000000004,32.166 +2020-03-10 22:45:00,91.4,126.08,52.846000000000004,32.166 +2020-03-10 23:00:00,89.03,119.40799999999999,44.435,32.166 +2020-03-10 23:15:00,91.06,117.764,44.435,32.166 +2020-03-10 23:30:00,88.04,119.34700000000001,44.435,32.166 +2020-03-10 23:45:00,81.48,119.641,44.435,32.166 +2020-03-11 00:00:00,75.53,114.79700000000001,42.527,32.166 +2020-03-11 00:15:00,80.59,113.096,42.527,32.166 +2020-03-11 00:30:00,83.26,112.241,42.527,32.166 +2020-03-11 00:45:00,83.5,112.162,42.527,32.166 +2020-03-11 01:00:00,75.29,113.49700000000001,38.655,32.166 +2020-03-11 01:15:00,74.08,113.866,38.655,32.166 +2020-03-11 01:30:00,75.7,113.865,38.655,32.166 +2020-03-11 01:45:00,74.92,114.025,38.655,32.166 +2020-03-11 02:00:00,74.13,115.889,36.912,32.166 +2020-03-11 02:15:00,81.05,116.80799999999999,36.912,32.166 +2020-03-11 02:30:00,80.33,117.723,36.912,32.166 +2020-03-11 02:45:00,79.51,119.47200000000001,36.912,32.166 +2020-03-11 03:00:00,75.06,122.09899999999999,36.98,32.166 +2020-03-11 03:15:00,75.42,123.804,36.98,32.166 +2020-03-11 03:30:00,76.42,125.709,36.98,32.166 +2020-03-11 03:45:00,80.52,126.93299999999999,36.98,32.166 +2020-03-11 04:00:00,84.62,140.27200000000002,38.052,32.166 +2020-03-11 04:15:00,85.72,153.308,38.052,32.166 +2020-03-11 04:30:00,81.45,155.411,38.052,32.166 +2020-03-11 04:45:00,84.39,157.156,38.052,32.166 +2020-03-11 05:00:00,91.07,191.585,42.455,32.166 +2020-03-11 05:15:00,95.13,222.638,42.455,32.166 +2020-03-11 05:30:00,98.48,216.752,42.455,32.166 +2020-03-11 05:45:00,96.19,208.571,42.455,32.166 +2020-03-11 06:00:00,104.58,205.817,57.986000000000004,32.166 +2020-03-11 06:15:00,107.88,212.06900000000002,57.986000000000004,32.166 +2020-03-11 06:30:00,110.59,213.645,57.986000000000004,32.166 +2020-03-11 06:45:00,113.78,216.666,57.986000000000004,32.166 +2020-03-11 07:00:00,118.72,219.00400000000002,71.868,32.166 +2020-03-11 07:15:00,127.45,221.824,71.868,32.166 +2020-03-11 07:30:00,131.59,221.76,71.868,32.166 +2020-03-11 07:45:00,132.28,219.90900000000002,71.868,32.166 +2020-03-11 08:00:00,129.65,217.81900000000002,62.225,32.166 +2020-03-11 08:15:00,131.66,215.915,62.225,32.166 +2020-03-11 08:30:00,130.64,210.942,62.225,32.166 +2020-03-11 08:45:00,131.27,206.542,62.225,32.166 +2020-03-11 09:00:00,134.97,199.05900000000003,58.802,32.166 +2020-03-11 09:15:00,139.13,195.53599999999997,58.802,32.166 +2020-03-11 09:30:00,141.53,194.37099999999998,58.802,32.166 +2020-03-11 09:45:00,138.32,191.662,58.802,32.166 +2020-03-11 10:00:00,134.68,188.90900000000002,54.122,32.166 +2020-03-11 10:15:00,135.2,186.00400000000002,54.122,32.166 +2020-03-11 10:30:00,134.26,183.71599999999998,54.122,32.166 +2020-03-11 10:45:00,133.84,183.042,54.122,32.166 +2020-03-11 11:00:00,133.84,180.051,54.368,32.166 +2020-03-11 11:15:00,136.34,179.623,54.368,32.166 +2020-03-11 11:30:00,134.04,179.273,54.368,32.166 +2020-03-11 11:45:00,134.3,178.97400000000002,54.368,32.166 +2020-03-11 12:00:00,136.13,173.747,52.74,32.166 +2020-03-11 12:15:00,139.12,174.074,52.74,32.166 +2020-03-11 12:30:00,137.79,173.21900000000002,52.74,32.166 +2020-03-11 12:45:00,132.99,174.07,52.74,32.166 +2020-03-11 13:00:00,129.17,173.736,52.544,32.166 +2020-03-11 13:15:00,130.87,172.03599999999997,52.544,32.166 +2020-03-11 13:30:00,132.75,170.476,52.544,32.166 +2020-03-11 13:45:00,134.79,169.96099999999998,52.544,32.166 +2020-03-11 14:00:00,129.47,171.979,53.602,32.166 +2020-03-11 14:15:00,126.72,171.09799999999998,53.602,32.166 +2020-03-11 14:30:00,128.13,171.03900000000002,53.602,32.166 +2020-03-11 14:45:00,124.89,172.03799999999998,53.602,32.166 +2020-03-11 15:00:00,129.33,173.86900000000003,55.59,32.166 +2020-03-11 15:15:00,130.78,172.476,55.59,32.166 +2020-03-11 15:30:00,132.29,172.37,55.59,32.166 +2020-03-11 15:45:00,130.2,172.433,55.59,32.166 +2020-03-11 16:00:00,126.12,175.97400000000002,57.586999999999996,32.166 +2020-03-11 16:15:00,122.31,177.64700000000002,57.586999999999996,32.166 +2020-03-11 16:30:00,122.71,179.671,57.586999999999996,32.166 +2020-03-11 16:45:00,123.16,179.915,57.586999999999996,32.166 +2020-03-11 17:00:00,125.12,182.75099999999998,62.111999999999995,32.166 +2020-03-11 17:15:00,128.58,185.418,62.111999999999995,32.166 +2020-03-11 17:30:00,127.81,188.40200000000002,62.111999999999995,32.166 +2020-03-11 17:45:00,130.41,189.523,62.111999999999995,32.166 +2020-03-11 18:00:00,142.61,192.77200000000002,64.605,32.166 +2020-03-11 18:15:00,140.13,193.701,64.605,32.166 +2020-03-11 18:30:00,143.86,192.095,64.605,32.166 +2020-03-11 18:45:00,134.22,194.179,64.605,32.166 +2020-03-11 19:00:00,135.54,192.81400000000002,65.55199999999999,32.166 +2020-03-11 19:15:00,132.58,189.88,65.55199999999999,32.166 +2020-03-11 19:30:00,135.41,189.21400000000003,65.55199999999999,32.166 +2020-03-11 19:45:00,134.32,186.36900000000003,65.55199999999999,32.166 +2020-03-11 20:00:00,127.68,180.81099999999998,66.778,32.166 +2020-03-11 20:15:00,121.46,175.696,66.778,32.166 +2020-03-11 20:30:00,114.11,172.178,66.778,32.166 +2020-03-11 20:45:00,112.76,169.74599999999998,66.778,32.166 +2020-03-11 21:00:00,103.63,166.476,56.103,32.166 +2020-03-11 21:15:00,103.76,163.215,56.103,32.166 +2020-03-11 21:30:00,107.96,160.925,56.103,32.166 +2020-03-11 21:45:00,109.11,160.89700000000002,56.103,32.166 +2020-03-11 22:00:00,106.52,153.386,51.371,32.166 +2020-03-11 22:15:00,101.13,149.535,51.371,32.166 +2020-03-11 22:30:00,100.38,133.38299999999998,51.371,32.166 +2020-03-11 22:45:00,98.36,125.639,51.371,32.166 +2020-03-11 23:00:00,94.04,118.964,42.798,32.166 +2020-03-11 23:15:00,90.56,117.34100000000001,42.798,32.166 +2020-03-11 23:30:00,90.47,118.929,42.798,32.166 +2020-03-11 23:45:00,92.85,119.251,42.798,32.166 +2020-03-12 00:00:00,90.26,114.434,39.069,32.166 +2020-03-12 00:15:00,83.72,112.744,39.069,32.166 +2020-03-12 00:30:00,85.51,111.87,39.069,32.166 +2020-03-12 00:45:00,85.74,111.79299999999999,39.069,32.166 +2020-03-12 01:00:00,82.15,113.084,37.043,32.166 +2020-03-12 01:15:00,81.61,113.43700000000001,37.043,32.166 +2020-03-12 01:30:00,83.56,113.417,37.043,32.166 +2020-03-12 01:45:00,83.38,113.586,37.043,32.166 +2020-03-12 02:00:00,79.78,115.43700000000001,34.625,32.166 +2020-03-12 02:15:00,79.93,116.348,34.625,32.166 +2020-03-12 02:30:00,80.09,117.28200000000001,34.625,32.166 +2020-03-12 02:45:00,83.47,119.03299999999999,34.625,32.166 +2020-03-12 03:00:00,79.75,121.67200000000001,33.812,32.166 +2020-03-12 03:15:00,83.53,123.359,33.812,32.166 +2020-03-12 03:30:00,85.78,125.258,33.812,32.166 +2020-03-12 03:45:00,86.38,126.495,33.812,32.166 +2020-03-12 04:00:00,83.32,139.841,35.236999999999995,32.166 +2020-03-12 04:15:00,85.25,152.864,35.236999999999995,32.166 +2020-03-12 04:30:00,89.13,154.987,35.236999999999995,32.166 +2020-03-12 04:45:00,87.85,156.718,35.236999999999995,32.166 +2020-03-12 05:00:00,87.66,191.12900000000002,40.375,32.166 +2020-03-12 05:15:00,89.16,222.2,40.375,32.166 +2020-03-12 05:30:00,92.45,216.27900000000002,40.375,32.166 +2020-03-12 05:45:00,94.7,208.10299999999998,40.375,32.166 +2020-03-12 06:00:00,106.37,205.355,52.316,32.166 +2020-03-12 06:15:00,109.43,211.613,52.316,32.166 +2020-03-12 06:30:00,110.52,213.139,52.316,32.166 +2020-03-12 06:45:00,112.03,216.136,52.316,32.166 +2020-03-12 07:00:00,115.95,218.495,64.115,32.166 +2020-03-12 07:15:00,118.25,221.282,64.115,32.166 +2020-03-12 07:30:00,121.19,221.176,64.115,32.166 +2020-03-12 07:45:00,121.25,219.28400000000002,64.115,32.166 +2020-03-12 08:00:00,121.28,217.168,55.033,32.166 +2020-03-12 08:15:00,120.35,215.253,55.033,32.166 +2020-03-12 08:30:00,121.03,210.222,55.033,32.166 +2020-03-12 08:45:00,122.01,205.84099999999998,55.033,32.166 +2020-03-12 09:00:00,121.47,198.373,49.411,32.166 +2020-03-12 09:15:00,118.49,194.851,49.411,32.166 +2020-03-12 09:30:00,119.35,193.707,49.411,32.166 +2020-03-12 09:45:00,117.69,191.012,49.411,32.166 +2020-03-12 10:00:00,118.5,188.274,45.82899999999999,32.166 +2020-03-12 10:15:00,119.49,185.41400000000002,45.82899999999999,32.166 +2020-03-12 10:30:00,118.65,183.149,45.82899999999999,32.166 +2020-03-12 10:45:00,120.54,182.495,45.82899999999999,32.166 +2020-03-12 11:00:00,120.4,179.497,44.333,32.166 +2020-03-12 11:15:00,120.2,179.092,44.333,32.166 +2020-03-12 11:30:00,120.88,178.748,44.333,32.166 +2020-03-12 11:45:00,118.34,178.46900000000002,44.333,32.166 +2020-03-12 12:00:00,117.08,173.262,42.95,32.166 +2020-03-12 12:15:00,116.25,173.6,42.95,32.166 +2020-03-12 12:30:00,115.75,172.706,42.95,32.166 +2020-03-12 12:45:00,109.47,173.554,42.95,32.166 +2020-03-12 13:00:00,108.09,173.266,42.489,32.166 +2020-03-12 13:15:00,115.15,171.544,42.489,32.166 +2020-03-12 13:30:00,115.05,169.97099999999998,42.489,32.166 +2020-03-12 13:45:00,111.73,169.46099999999998,42.489,32.166 +2020-03-12 14:00:00,112.25,171.55,43.448,32.166 +2020-03-12 14:15:00,113.98,170.643,43.448,32.166 +2020-03-12 14:30:00,117.08,170.551,43.448,32.166 +2020-03-12 14:45:00,119.53,171.55900000000003,43.448,32.166 +2020-03-12 15:00:00,118.97,173.39700000000002,45.994,32.166 +2020-03-12 15:15:00,118.68,171.975,45.994,32.166 +2020-03-12 15:30:00,117.13,171.81599999999997,45.994,32.166 +2020-03-12 15:45:00,119.59,171.862,45.994,32.166 +2020-03-12 16:00:00,119.06,175.407,48.167,32.166 +2020-03-12 16:15:00,118.71,177.05700000000002,48.167,32.166 +2020-03-12 16:30:00,116.06,179.081,48.167,32.166 +2020-03-12 16:45:00,116.15,179.28099999999998,48.167,32.166 +2020-03-12 17:00:00,117.1,182.13099999999997,52.637,32.166 +2020-03-12 17:15:00,117.47,184.803,52.637,32.166 +2020-03-12 17:30:00,124.27,187.81,52.637,32.166 +2020-03-12 17:45:00,130.48,188.94400000000002,52.637,32.166 +2020-03-12 18:00:00,135.41,192.19799999999998,55.739,32.166 +2020-03-12 18:15:00,134.81,193.199,55.739,32.166 +2020-03-12 18:30:00,135.02,191.58900000000003,55.739,32.166 +2020-03-12 18:45:00,141.44,193.692,55.739,32.166 +2020-03-12 19:00:00,139.92,192.29,56.36600000000001,32.166 +2020-03-12 19:15:00,135.83,189.375,56.36600000000001,32.166 +2020-03-12 19:30:00,133.89,188.739,56.36600000000001,32.166 +2020-03-12 19:45:00,136.57,185.94099999999997,56.36600000000001,32.166 +2020-03-12 20:00:00,127.47,180.352,56.338,32.166 +2020-03-12 20:15:00,117.38,175.25400000000002,56.338,32.166 +2020-03-12 20:30:00,114.25,171.765,56.338,32.166 +2020-03-12 20:45:00,118.49,169.33599999999998,56.338,32.166 +2020-03-12 21:00:00,110.15,166.05599999999998,49.894,32.166 +2020-03-12 21:15:00,109.53,162.791,49.894,32.166 +2020-03-12 21:30:00,99.6,160.502,49.894,32.166 +2020-03-12 21:45:00,103.89,160.494,49.894,32.166 +2020-03-12 22:00:00,96.79,152.968,46.687,32.166 +2020-03-12 22:15:00,101.17,149.144,46.687,32.166 +2020-03-12 22:30:00,99.91,132.936,46.687,32.166 +2020-03-12 22:45:00,97.37,125.19200000000001,46.687,32.166 +2020-03-12 23:00:00,90.19,118.515,39.211,32.166 +2020-03-12 23:15:00,91.27,116.912,39.211,32.166 +2020-03-12 23:30:00,89.72,118.506,39.211,32.166 +2020-03-12 23:45:00,90.08,118.85600000000001,39.211,32.166 +2020-03-13 00:00:00,79.34,112.801,36.616,32.166 +2020-03-13 00:15:00,81.17,111.344,36.616,32.166 +2020-03-13 00:30:00,84.9,110.35700000000001,36.616,32.166 +2020-03-13 00:45:00,84.62,110.445,36.616,32.166 +2020-03-13 01:00:00,76.48,111.34,33.799,32.166 +2020-03-13 01:15:00,75.14,112.479,33.799,32.166 +2020-03-13 01:30:00,75.63,112.32,33.799,32.166 +2020-03-13 01:45:00,78.52,112.565,33.799,32.166 +2020-03-13 02:00:00,80.3,114.60600000000001,32.968,32.166 +2020-03-13 02:15:00,78.45,115.39399999999999,32.968,32.166 +2020-03-13 02:30:00,75.39,116.959,32.968,32.166 +2020-03-13 02:45:00,74.75,118.661,32.968,32.166 +2020-03-13 03:00:00,80.8,120.464,33.533,32.166 +2020-03-13 03:15:00,81.43,122.869,33.533,32.166 +2020-03-13 03:30:00,80.63,124.714,33.533,32.166 +2020-03-13 03:45:00,76.96,126.39299999999999,33.533,32.166 +2020-03-13 04:00:00,77.78,139.984,36.102,32.166 +2020-03-13 04:15:00,76.01,152.549,36.102,32.166 +2020-03-13 04:30:00,78.16,155.04,36.102,32.166 +2020-03-13 04:45:00,80.35,155.576,36.102,32.166 +2020-03-13 05:00:00,83.6,188.688,42.423,32.166 +2020-03-13 05:15:00,86.08,221.351,42.423,32.166 +2020-03-13 05:30:00,89.34,216.452,42.423,32.166 +2020-03-13 05:45:00,93.73,208.157,42.423,32.166 +2020-03-13 06:00:00,103.64,205.862,55.38,32.166 +2020-03-13 06:15:00,107.57,210.74,55.38,32.166 +2020-03-13 06:30:00,110.64,211.385,55.38,32.166 +2020-03-13 06:45:00,114.48,215.893,55.38,32.166 +2020-03-13 07:00:00,119.66,217.563,65.929,32.166 +2020-03-13 07:15:00,122.04,221.418,65.929,32.166 +2020-03-13 07:30:00,125.84,220.833,65.929,32.166 +2020-03-13 07:45:00,127.92,218.025,65.929,32.166 +2020-03-13 08:00:00,130.98,214.92700000000002,57.336999999999996,32.166 +2020-03-13 08:15:00,131.41,212.718,57.336999999999996,32.166 +2020-03-13 08:30:00,133.25,208.553,57.336999999999996,32.166 +2020-03-13 08:45:00,132.73,202.65900000000002,57.336999999999996,32.166 +2020-03-13 09:00:00,131.91,195.34099999999998,54.226000000000006,32.166 +2020-03-13 09:15:00,133.92,192.61900000000003,54.226000000000006,32.166 +2020-03-13 09:30:00,134.68,190.99200000000002,54.226000000000006,32.166 +2020-03-13 09:45:00,135.73,188.257,54.226000000000006,32.166 +2020-03-13 10:00:00,134.92,184.42,51.298,32.166 +2020-03-13 10:15:00,135.4,182.248,51.298,32.166 +2020-03-13 10:30:00,134.47,180.012,51.298,32.166 +2020-03-13 10:45:00,135.19,178.93900000000002,51.298,32.166 +2020-03-13 11:00:00,135.89,175.94299999999998,50.839,32.166 +2020-03-13 11:15:00,138.7,174.553,50.839,32.166 +2020-03-13 11:30:00,137.93,175.858,50.839,32.166 +2020-03-13 11:45:00,136.68,175.517,50.839,32.166 +2020-03-13 12:00:00,135.2,171.43099999999998,47.976000000000006,32.166 +2020-03-13 12:15:00,133.97,169.679,47.976000000000006,32.166 +2020-03-13 12:30:00,132.57,168.91400000000002,47.976000000000006,32.166 +2020-03-13 12:45:00,133.27,170.15200000000002,47.976000000000006,32.166 +2020-03-13 13:00:00,131.19,170.889,46.299,32.166 +2020-03-13 13:15:00,131.49,169.957,46.299,32.166 +2020-03-13 13:30:00,130.77,168.495,46.299,32.166 +2020-03-13 13:45:00,129.96,167.967,46.299,32.166 +2020-03-13 14:00:00,127.92,168.924,44.971000000000004,32.166 +2020-03-13 14:15:00,127.13,167.878,44.971000000000004,32.166 +2020-03-13 14:30:00,128.48,168.445,44.971000000000004,32.166 +2020-03-13 14:45:00,133.54,169.662,44.971000000000004,32.166 +2020-03-13 15:00:00,132.39,171.033,47.48,32.166 +2020-03-13 15:15:00,129.23,169.125,47.48,32.166 +2020-03-13 15:30:00,123.71,167.375,47.48,32.166 +2020-03-13 15:45:00,127.43,167.642,47.48,32.166 +2020-03-13 16:00:00,125.11,169.949,50.648,32.166 +2020-03-13 16:15:00,125.31,171.919,50.648,32.166 +2020-03-13 16:30:00,131.08,174.01,50.648,32.166 +2020-03-13 16:45:00,131.36,173.954,50.648,32.166 +2020-03-13 17:00:00,133.17,177.222,56.251000000000005,32.166 +2020-03-13 17:15:00,127.14,179.476,56.251000000000005,32.166 +2020-03-13 17:30:00,131.27,182.215,56.251000000000005,32.166 +2020-03-13 17:45:00,129.12,183.114,56.251000000000005,32.166 +2020-03-13 18:00:00,131.24,187.041,58.982,32.166 +2020-03-13 18:15:00,135.17,187.62099999999998,58.982,32.166 +2020-03-13 18:30:00,139.57,186.37900000000002,58.982,32.166 +2020-03-13 18:45:00,139.51,188.546,58.982,32.166 +2020-03-13 19:00:00,133.36,188.09,57.293,32.166 +2020-03-13 19:15:00,128.85,186.59799999999998,57.293,32.166 +2020-03-13 19:30:00,132.49,185.59400000000002,57.293,32.166 +2020-03-13 19:45:00,126.88,182.27900000000002,57.293,32.166 +2020-03-13 20:00:00,119.57,176.699,59.433,32.166 +2020-03-13 20:15:00,115.51,171.71900000000002,59.433,32.166 +2020-03-13 20:30:00,114.29,168.16099999999997,59.433,32.166 +2020-03-13 20:45:00,118.37,166.18200000000002,59.433,32.166 +2020-03-13 21:00:00,112.96,163.543,52.153999999999996,32.166 +2020-03-13 21:15:00,110.25,160.898,52.153999999999996,32.166 +2020-03-13 21:30:00,97.71,158.635,52.153999999999996,32.166 +2020-03-13 21:45:00,100.27,159.203,52.153999999999996,32.166 +2020-03-13 22:00:00,97.93,152.60299999999998,47.125,32.166 +2020-03-13 22:15:00,92.71,148.64700000000002,47.125,32.166 +2020-03-13 22:30:00,96.04,139.16299999999998,47.125,32.166 +2020-03-13 22:45:00,96.36,134.97,47.125,32.166 +2020-03-13 23:00:00,92.48,128.05200000000002,41.236000000000004,32.166 +2020-03-13 23:15:00,86.81,124.382,41.236000000000004,32.166 +2020-03-13 23:30:00,83.75,124.344,41.236000000000004,32.166 +2020-03-13 23:45:00,80.73,124.059,41.236000000000004,32.166 +2020-03-14 00:00:00,77.88,109.852,36.484,31.988000000000003 +2020-03-14 00:15:00,83.78,104.229,36.484,31.988000000000003 +2020-03-14 00:30:00,82.86,104.44,36.484,31.988000000000003 +2020-03-14 00:45:00,81.65,105.12,36.484,31.988000000000003 +2020-03-14 01:00:00,70.41,106.62100000000001,32.391999999999996,31.988000000000003 +2020-03-14 01:15:00,73.58,106.88,32.391999999999996,31.988000000000003 +2020-03-14 01:30:00,71.51,106.098,32.391999999999996,31.988000000000003 +2020-03-14 01:45:00,71.4,106.311,32.391999999999996,31.988000000000003 +2020-03-14 02:00:00,70.84,108.875,30.194000000000003,31.988000000000003 +2020-03-14 02:15:00,70.05,109.193,30.194000000000003,31.988000000000003 +2020-03-14 02:30:00,68.78,109.60700000000001,30.194000000000003,31.988000000000003 +2020-03-14 02:45:00,70.66,111.51299999999999,30.194000000000003,31.988000000000003 +2020-03-14 03:00:00,74.8,113.75299999999999,29.677,31.988000000000003 +2020-03-14 03:15:00,77.05,114.881,29.677,31.988000000000003 +2020-03-14 03:30:00,75.79,115.251,29.677,31.988000000000003 +2020-03-14 03:45:00,74.51,117.256,29.677,31.988000000000003 +2020-03-14 04:00:00,73.14,126.67299999999999,29.616,31.988000000000003 +2020-03-14 04:15:00,76.99,136.685,29.616,31.988000000000003 +2020-03-14 04:30:00,79.29,136.898,29.616,31.988000000000003 +2020-03-14 04:45:00,79.22,136.989,29.616,31.988000000000003 +2020-03-14 05:00:00,73.08,153.809,29.625,31.988000000000003 +2020-03-14 05:15:00,73.44,166.923,29.625,31.988000000000003 +2020-03-14 05:30:00,78.54,162.54399999999998,29.625,31.988000000000003 +2020-03-14 05:45:00,82.22,160.054,29.625,31.988000000000003 +2020-03-14 06:00:00,85.25,177.106,30.551,31.988000000000003 +2020-03-14 06:15:00,78.08,198.613,30.551,31.988000000000003 +2020-03-14 06:30:00,81.79,193.703,30.551,31.988000000000003 +2020-03-14 06:45:00,83.22,189.206,30.551,31.988000000000003 +2020-03-14 07:00:00,89.51,187.062,34.865,31.988000000000003 +2020-03-14 07:15:00,91.04,189.51,34.865,31.988000000000003 +2020-03-14 07:30:00,91.15,191.669,34.865,31.988000000000003 +2020-03-14 07:45:00,90.05,192.73,34.865,31.988000000000003 +2020-03-14 08:00:00,95.18,193.46599999999998,41.456,31.988000000000003 +2020-03-14 08:15:00,100.55,194.687,41.456,31.988000000000003 +2020-03-14 08:30:00,104.99,192.00400000000002,41.456,31.988000000000003 +2020-03-14 08:45:00,103.55,189.274,41.456,31.988000000000003 +2020-03-14 09:00:00,103.12,184.047,43.001999999999995,31.988000000000003 +2020-03-14 09:15:00,109.55,182.112,43.001999999999995,31.988000000000003 +2020-03-14 09:30:00,113.29,181.43599999999998,43.001999999999995,31.988000000000003 +2020-03-14 09:45:00,114.02,178.81,43.001999999999995,31.988000000000003 +2020-03-14 10:00:00,110.04,175.35,42.047,31.988000000000003 +2020-03-14 10:15:00,109.56,173.428,42.047,31.988000000000003 +2020-03-14 10:30:00,115.95,171.328,42.047,31.988000000000003 +2020-03-14 10:45:00,117.43,171.47400000000002,42.047,31.988000000000003 +2020-03-14 11:00:00,116.25,168.637,39.894,31.988000000000003 +2020-03-14 11:15:00,113.56,166.72400000000002,39.894,31.988000000000003 +2020-03-14 11:30:00,120.33,167.00400000000002,39.894,31.988000000000003 +2020-03-14 11:45:00,122.42,165.852,39.894,31.988000000000003 +2020-03-14 12:00:00,112.56,160.997,38.122,31.988000000000003 +2020-03-14 12:15:00,110.8,159.982,38.122,31.988000000000003 +2020-03-14 12:30:00,105.88,159.467,38.122,31.988000000000003 +2020-03-14 12:45:00,104.72,160.058,38.122,31.988000000000003 +2020-03-14 13:00:00,102.48,160.347,34.645,31.988000000000003 +2020-03-14 13:15:00,101.32,157.35299999999998,34.645,31.988000000000003 +2020-03-14 13:30:00,99.94,155.47299999999998,34.645,31.988000000000003 +2020-03-14 13:45:00,98.58,155.225,34.645,31.988000000000003 +2020-03-14 14:00:00,96.86,157.421,33.739000000000004,31.988000000000003 +2020-03-14 14:15:00,96.28,155.673,33.739000000000004,31.988000000000003 +2020-03-14 14:30:00,96.6,154.406,33.739000000000004,31.988000000000003 +2020-03-14 14:45:00,97.2,155.903,33.739000000000004,31.988000000000003 +2020-03-14 15:00:00,96.67,157.937,35.908,31.988000000000003 +2020-03-14 15:15:00,95.72,156.866,35.908,31.988000000000003 +2020-03-14 15:30:00,95.92,156.571,35.908,31.988000000000003 +2020-03-14 15:45:00,95.09,156.715,35.908,31.988000000000003 +2020-03-14 16:00:00,95.88,158.109,39.249,31.988000000000003 +2020-03-14 16:15:00,95.55,160.806,39.249,31.988000000000003 +2020-03-14 16:30:00,95.24,162.874,39.249,31.988000000000003 +2020-03-14 16:45:00,95.42,163.626,39.249,31.988000000000003 +2020-03-14 17:00:00,99.69,166.172,46.045,31.988000000000003 +2020-03-14 17:15:00,99.32,169.774,46.045,31.988000000000003 +2020-03-14 17:30:00,101.07,172.42700000000002,46.045,31.988000000000003 +2020-03-14 17:45:00,102.78,173.00599999999997,46.045,31.988000000000003 +2020-03-14 18:00:00,107.78,176.59599999999998,48.238,31.988000000000003 +2020-03-14 18:15:00,108.04,179.19299999999998,48.238,31.988000000000003 +2020-03-14 18:30:00,111.45,179.40099999999998,48.238,31.988000000000003 +2020-03-14 18:45:00,111.41,177.924,48.238,31.988000000000003 +2020-03-14 19:00:00,111.15,178.12599999999998,46.785,31.988000000000003 +2020-03-14 19:15:00,109.51,176.055,46.785,31.988000000000003 +2020-03-14 19:30:00,108.23,175.903,46.785,31.988000000000003 +2020-03-14 19:45:00,106.14,172.66299999999998,46.785,31.988000000000003 +2020-03-14 20:00:00,101.01,169.296,39.830999999999996,31.988000000000003 +2020-03-14 20:15:00,97.68,166.32299999999998,39.830999999999996,31.988000000000003 +2020-03-14 20:30:00,93.02,162.352,39.830999999999996,31.988000000000003 +2020-03-14 20:45:00,93.19,160.203,39.830999999999996,31.988000000000003 +2020-03-14 21:00:00,89.17,159.56799999999998,34.063,31.988000000000003 +2020-03-14 21:15:00,86.26,157.30700000000002,34.063,31.988000000000003 +2020-03-14 21:30:00,89.1,156.261,34.063,31.988000000000003 +2020-03-14 21:45:00,87.56,156.39,34.063,31.988000000000003 +2020-03-14 22:00:00,82.87,151.08100000000002,34.455999999999996,31.988000000000003 +2020-03-14 22:15:00,83.61,149.59799999999998,34.455999999999996,31.988000000000003 +2020-03-14 22:30:00,77.72,146.056,34.455999999999996,31.988000000000003 +2020-03-14 22:45:00,79.25,143.726,34.455999999999996,31.988000000000003 +2020-03-14 23:00:00,75.28,139.096,27.840999999999998,31.988000000000003 +2020-03-14 23:15:00,72.99,133.905,27.840999999999998,31.988000000000003 +2020-03-14 23:30:00,72.93,132.463,27.840999999999998,31.988000000000003 +2020-03-14 23:45:00,71.64,129.89,27.840999999999998,31.988000000000003 +2020-03-15 00:00:00,66.91,110.05799999999999,20.007,31.988000000000003 +2020-03-15 00:15:00,67.0,103.98700000000001,20.007,31.988000000000003 +2020-03-15 00:30:00,66.54,103.802,20.007,31.988000000000003 +2020-03-15 00:45:00,65.35,105.135,20.007,31.988000000000003 +2020-03-15 01:00:00,62.97,106.49,17.378,31.988000000000003 +2020-03-15 01:15:00,63.28,107.705,17.378,31.988000000000003 +2020-03-15 01:30:00,60.74,107.381,17.378,31.988000000000003 +2020-03-15 01:45:00,63.48,107.251,17.378,31.988000000000003 +2020-03-15 02:00:00,61.21,109.098,16.145,31.988000000000003 +2020-03-15 02:15:00,61.18,108.698,16.145,31.988000000000003 +2020-03-15 02:30:00,60.17,109.969,16.145,31.988000000000003 +2020-03-15 02:45:00,61.4,112.275,16.145,31.988000000000003 +2020-03-15 03:00:00,60.81,114.917,15.427999999999999,31.988000000000003 +2020-03-15 03:15:00,61.75,115.56700000000001,15.427999999999999,31.988000000000003 +2020-03-15 03:30:00,61.32,117.15100000000001,15.427999999999999,31.988000000000003 +2020-03-15 03:45:00,62.75,118.98200000000001,15.427999999999999,31.988000000000003 +2020-03-15 04:00:00,62.97,128.166,16.663,31.988000000000003 +2020-03-15 04:15:00,63.98,137.14700000000002,16.663,31.988000000000003 +2020-03-15 04:30:00,63.98,137.636,16.663,31.988000000000003 +2020-03-15 04:45:00,63.9,137.90200000000002,16.663,31.988000000000003 +2020-03-15 05:00:00,63.92,151.424,17.271,31.988000000000003 +2020-03-15 05:15:00,62.64,162.171,17.271,31.988000000000003 +2020-03-15 05:30:00,63.97,157.541,17.271,31.988000000000003 +2020-03-15 05:45:00,64.06,155.245,17.271,31.988000000000003 +2020-03-15 06:00:00,63.06,171.845,17.612000000000002,31.988000000000003 +2020-03-15 06:15:00,61.28,191.857,17.612000000000002,31.988000000000003 +2020-03-15 06:30:00,58.8,185.75099999999998,17.612000000000002,31.988000000000003 +2020-03-15 06:45:00,61.36,180.08599999999998,17.612000000000002,31.988000000000003 +2020-03-15 07:00:00,63.14,180.313,20.88,31.988000000000003 +2020-03-15 07:15:00,62.27,181.68,20.88,31.988000000000003 +2020-03-15 07:30:00,66.04,182.82299999999998,20.88,31.988000000000003 +2020-03-15 07:45:00,68.94,183.12,20.88,31.988000000000003 +2020-03-15 08:00:00,71.34,185.66299999999998,25.861,31.988000000000003 +2020-03-15 08:15:00,73.91,186.957,25.861,31.988000000000003 +2020-03-15 08:30:00,74.04,185.912,25.861,31.988000000000003 +2020-03-15 08:45:00,74.07,185.106,25.861,31.988000000000003 +2020-03-15 09:00:00,74.58,179.49,27.921999999999997,31.988000000000003 +2020-03-15 09:15:00,73.36,178.012,27.921999999999997,31.988000000000003 +2020-03-15 09:30:00,75.48,177.27,27.921999999999997,31.988000000000003 +2020-03-15 09:45:00,75.64,174.68599999999998,27.921999999999997,31.988000000000003 +2020-03-15 10:00:00,75.27,173.71099999999998,29.048000000000002,31.988000000000003 +2020-03-15 10:15:00,74.22,172.36,29.048000000000002,31.988000000000003 +2020-03-15 10:30:00,73.91,170.886,29.048000000000002,31.988000000000003 +2020-03-15 10:45:00,74.56,169.398,29.048000000000002,31.988000000000003 +2020-03-15 11:00:00,77.3,167.37099999999998,32.02,31.988000000000003 +2020-03-15 11:15:00,80.08,165.544,32.02,31.988000000000003 +2020-03-15 11:30:00,82.09,165.077,32.02,31.988000000000003 +2020-03-15 11:45:00,79.85,164.555,32.02,31.988000000000003 +2020-03-15 12:00:00,78.18,159.333,28.55,31.988000000000003 +2020-03-15 12:15:00,79.4,160.05,28.55,31.988000000000003 +2020-03-15 12:30:00,76.27,158.143,28.55,31.988000000000003 +2020-03-15 12:45:00,75.58,157.72299999999998,28.55,31.988000000000003 +2020-03-15 13:00:00,71.35,157.334,25.601999999999997,31.988000000000003 +2020-03-15 13:15:00,71.38,157.099,25.601999999999997,31.988000000000003 +2020-03-15 13:30:00,70.92,154.85,25.601999999999997,31.988000000000003 +2020-03-15 13:45:00,72.05,154.139,25.601999999999997,31.988000000000003 +2020-03-15 14:00:00,72.64,156.80700000000002,23.916999999999998,31.988000000000003 +2020-03-15 14:15:00,71.93,156.235,23.916999999999998,31.988000000000003 +2020-03-15 14:30:00,71.59,155.931,23.916999999999998,31.988000000000003 +2020-03-15 14:45:00,71.9,156.895,23.916999999999998,31.988000000000003 +2020-03-15 15:00:00,73.94,157.505,24.064,31.988000000000003 +2020-03-15 15:15:00,72.87,157.013,24.064,31.988000000000003 +2020-03-15 15:30:00,73.03,157.2,24.064,31.988000000000003 +2020-03-15 15:45:00,74.33,158.025,24.064,31.988000000000003 +2020-03-15 16:00:00,76.14,161.00799999999998,28.189,31.988000000000003 +2020-03-15 16:15:00,77.61,162.841,28.189,31.988000000000003 +2020-03-15 16:30:00,78.45,165.28900000000002,28.189,31.988000000000003 +2020-03-15 16:45:00,83.21,166.13099999999997,28.189,31.988000000000003 +2020-03-15 17:00:00,85.3,168.75799999999998,37.576,31.988000000000003 +2020-03-15 17:15:00,86.88,172.24,37.576,31.988000000000003 +2020-03-15 17:30:00,88.48,175.292,37.576,31.988000000000003 +2020-03-15 17:45:00,89.58,178.122,37.576,31.988000000000003 +2020-03-15 18:00:00,97.68,181.27700000000002,42.669,31.988000000000003 +2020-03-15 18:15:00,98.02,185.18,42.669,31.988000000000003 +2020-03-15 18:30:00,103.29,183.335,42.669,31.988000000000003 +2020-03-15 18:45:00,103.7,183.683,42.669,31.988000000000003 +2020-03-15 19:00:00,105.01,183.797,43.538999999999994,31.988000000000003 +2020-03-15 19:15:00,102.92,182.188,43.538999999999994,31.988000000000003 +2020-03-15 19:30:00,101.41,181.878,43.538999999999994,31.988000000000003 +2020-03-15 19:45:00,99.98,180.03400000000002,43.538999999999994,31.988000000000003 +2020-03-15 20:00:00,98.07,176.605,37.330999999999996,31.988000000000003 +2020-03-15 20:15:00,98.05,174.584,37.330999999999996,31.988000000000003 +2020-03-15 20:30:00,102.06,171.94,37.330999999999996,31.988000000000003 +2020-03-15 20:45:00,101.51,168.424,37.330999999999996,31.988000000000003 +2020-03-15 21:00:00,92.01,165.261,33.856,31.988000000000003 +2020-03-15 21:15:00,96.09,162.356,33.856,31.988000000000003 +2020-03-15 21:30:00,95.45,161.513,33.856,31.988000000000003 +2020-03-15 21:45:00,98.39,161.839,33.856,31.988000000000003 +2020-03-15 22:00:00,96.08,155.615,34.711999999999996,31.988000000000003 +2020-03-15 22:15:00,93.54,153.219,34.711999999999996,31.988000000000003 +2020-03-15 22:30:00,88.54,146.49200000000002,34.711999999999996,31.988000000000003 +2020-03-15 22:45:00,88.54,143.184,34.711999999999996,31.988000000000003 +2020-03-15 23:00:00,90.01,135.808,29.698,31.988000000000003 +2020-03-15 23:15:00,90.99,132.591,29.698,31.988000000000003 +2020-03-15 23:30:00,88.54,131.872,29.698,31.988000000000003 +2020-03-15 23:45:00,84.36,130.211,29.698,31.988000000000003 +2020-03-16 00:00:00,75.75,98.40299999999999,29.983,32.166 +2020-03-16 00:15:00,83.47,96.28299999999999,29.983,32.166 +2020-03-16 00:30:00,84.42,95.56,29.983,32.166 +2020-03-16 00:45:00,82.25,95.21799999999999,29.983,32.166 +2020-03-16 01:00:00,77.57,96.861,29.122,32.166 +2020-03-16 01:15:00,80.67,97.205,29.122,32.166 +2020-03-16 01:30:00,81.83,96.76299999999999,29.122,32.166 +2020-03-16 01:45:00,81.92,96.56,29.122,32.166 +2020-03-16 02:00:00,76.07,98.52600000000001,28.676,32.166 +2020-03-16 02:15:00,79.7,98.59200000000001,28.676,32.166 +2020-03-16 02:30:00,80.65,100.493,28.676,32.166 +2020-03-16 02:45:00,81.61,101.565,28.676,32.166 +2020-03-16 03:00:00,77.46,105.42399999999999,26.552,32.166 +2020-03-16 03:15:00,79.91,107.682,26.552,32.166 +2020-03-16 03:30:00,82.47,108.404,26.552,32.166 +2020-03-16 03:45:00,82.23,109.36399999999999,26.552,32.166 +2020-03-16 04:00:00,80.26,122.883,27.44,32.166 +2020-03-16 04:15:00,78.08,136.07299999999998,27.44,32.166 +2020-03-16 04:30:00,78.99,137.483,27.44,32.166 +2020-03-16 04:45:00,83.48,138.162,27.44,32.166 +2020-03-16 05:00:00,85.58,168.11,36.825,32.166 +2020-03-16 05:15:00,89.84,199.683,36.825,32.166 +2020-03-16 05:30:00,94.03,193.502,36.825,32.166 +2020-03-16 05:45:00,98.78,184.55700000000002,36.825,32.166 +2020-03-16 06:00:00,104.82,183.747,56.589,32.166 +2020-03-16 06:15:00,108.78,188.298,56.589,32.166 +2020-03-16 06:30:00,111.69,189.327,56.589,32.166 +2020-03-16 06:45:00,114.28,191.799,56.589,32.166 +2020-03-16 07:00:00,119.75,194.726,67.49,32.166 +2020-03-16 07:15:00,118.99,196.967,67.49,32.166 +2020-03-16 07:30:00,121.18,196.90599999999998,67.49,32.166 +2020-03-16 07:45:00,121.94,194.672,67.49,32.166 +2020-03-16 08:00:00,124.7,193.282,60.028,32.166 +2020-03-16 08:15:00,122.52,192.105,60.028,32.166 +2020-03-16 08:30:00,121.4,187.63,60.028,32.166 +2020-03-16 08:45:00,119.86,184.179,60.028,32.166 +2020-03-16 09:00:00,117.82,178.373,55.018,32.166 +2020-03-16 09:15:00,116.63,174.179,55.018,32.166 +2020-03-16 09:30:00,117.73,173.43099999999998,55.018,32.166 +2020-03-16 09:45:00,115.81,171.18599999999998,55.018,32.166 +2020-03-16 10:00:00,113.41,168.915,51.183,32.166 +2020-03-16 10:15:00,116.0,167.81400000000002,51.183,32.166 +2020-03-16 10:30:00,115.46,165.19799999999998,51.183,32.166 +2020-03-16 10:45:00,117.05,164.111,51.183,32.166 +2020-03-16 11:00:00,113.1,158.343,50.065,32.166 +2020-03-16 11:15:00,113.17,158.403,50.065,32.166 +2020-03-16 11:30:00,113.75,160.048,50.065,32.166 +2020-03-16 11:45:00,114.16,160.379,50.065,32.166 +2020-03-16 12:00:00,111.33,156.921,48.141999999999996,32.166 +2020-03-16 12:15:00,113.06,157.437,48.141999999999996,32.166 +2020-03-16 12:30:00,110.37,156.293,48.141999999999996,32.166 +2020-03-16 12:45:00,110.4,157.15200000000002,48.141999999999996,32.166 +2020-03-16 13:00:00,108.44,157.352,47.887,32.166 +2020-03-16 13:15:00,107.98,155.593,47.887,32.166 +2020-03-16 13:30:00,107.97,152.868,47.887,32.166 +2020-03-16 13:45:00,108.56,152.096,47.887,32.166 +2020-03-16 14:00:00,111.41,153.593,48.571000000000005,32.166 +2020-03-16 14:15:00,108.91,152.257,48.571000000000005,32.166 +2020-03-16 14:30:00,108.43,151.621,48.571000000000005,32.166 +2020-03-16 14:45:00,109.18,152.847,48.571000000000005,32.166 +2020-03-16 15:00:00,110.53,154.037,49.937,32.166 +2020-03-16 15:15:00,110.99,152.216,49.937,32.166 +2020-03-16 15:30:00,110.57,151.31,49.937,32.166 +2020-03-16 15:45:00,112.88,150.865,49.937,32.166 +2020-03-16 16:00:00,113.73,154.678,52.963,32.166 +2020-03-16 16:15:00,114.16,156.461,52.963,32.166 +2020-03-16 16:30:00,116.67,156.502,52.963,32.166 +2020-03-16 16:45:00,119.49,155.469,52.963,32.166 +2020-03-16 17:00:00,120.61,156.90200000000002,61.163999999999994,32.166 +2020-03-16 17:15:00,121.52,159.283,61.163999999999994,32.166 +2020-03-16 17:30:00,124.46,161.386,61.163999999999994,32.166 +2020-03-16 17:45:00,126.0,162.251,61.163999999999994,32.166 +2020-03-16 18:00:00,128.25,166.34799999999998,63.788999999999994,32.166 +2020-03-16 18:15:00,128.87,167.03099999999998,63.788999999999994,32.166 +2020-03-16 18:30:00,132.59,165.72,63.788999999999994,32.166 +2020-03-16 18:45:00,135.2,169.049,63.788999999999994,32.166 +2020-03-16 19:00:00,135.47,167.59,63.913000000000004,32.166 +2020-03-16 19:15:00,135.93,165.708,63.913000000000004,32.166 +2020-03-16 19:30:00,136.42,165.69400000000002,63.913000000000004,32.166 +2020-03-16 19:45:00,139.91,163.987,63.913000000000004,32.166 +2020-03-16 20:00:00,131.55,157.97,65.44,32.166 +2020-03-16 20:15:00,123.08,154.694,65.44,32.166 +2020-03-16 20:30:00,115.5,151.886,65.44,32.166 +2020-03-16 20:45:00,115.34,150.49200000000002,65.44,32.166 +2020-03-16 21:00:00,111.28,145.696,59.117,32.166 +2020-03-16 21:15:00,109.77,142.936,59.117,32.166 +2020-03-16 21:30:00,108.18,142.46200000000002,59.117,32.166 +2020-03-16 21:45:00,108.66,141.463,59.117,32.166 +2020-03-16 22:00:00,100.71,133.118,52.301,32.166 +2020-03-16 22:15:00,101.8,129.96,52.301,32.166 +2020-03-16 22:30:00,98.84,115.694,52.301,32.166 +2020-03-16 22:45:00,97.68,108.209,52.301,32.166 +2020-03-16 23:00:00,98.26,101.245,44.373000000000005,32.166 +2020-03-16 23:15:00,99.14,100.038,44.373000000000005,32.166 +2020-03-16 23:30:00,95.57,100.943,44.373000000000005,32.166 +2020-03-16 23:45:00,90.74,101.705,44.373000000000005,32.166 +2020-03-17 00:00:00,83.29,96.788,44.647,32.166 +2020-03-17 00:15:00,85.18,96.06,44.647,32.166 +2020-03-17 00:30:00,88.73,94.814,44.647,32.166 +2020-03-17 00:45:00,90.01,93.965,44.647,32.166 +2020-03-17 01:00:00,86.46,95.25399999999999,41.433,32.166 +2020-03-17 01:15:00,85.81,95.272,41.433,32.166 +2020-03-17 01:30:00,81.39,94.90299999999999,41.433,32.166 +2020-03-17 01:45:00,87.26,94.779,41.433,32.166 +2020-03-17 02:00:00,87.84,96.57700000000001,39.909,32.166 +2020-03-17 02:15:00,88.12,96.916,39.909,32.166 +2020-03-17 02:30:00,80.08,98.25200000000001,39.909,32.166 +2020-03-17 02:45:00,81.87,99.441,39.909,32.166 +2020-03-17 03:00:00,88.39,102.213,39.14,32.166 +2020-03-17 03:15:00,89.75,104.1,39.14,32.166 +2020-03-17 03:30:00,82.45,105.178,39.14,32.166 +2020-03-17 03:45:00,84.57,105.939,39.14,32.166 +2020-03-17 04:00:00,86.69,118.898,40.015,32.166 +2020-03-17 04:15:00,91.6,131.80700000000002,40.015,32.166 +2020-03-17 04:30:00,94.85,132.954,40.015,32.166 +2020-03-17 04:45:00,92.46,134.70600000000002,40.015,32.166 +2020-03-17 05:00:00,92.95,168.956,44.93600000000001,32.166 +2020-03-17 05:15:00,94.92,200.56099999999998,44.93600000000001,32.166 +2020-03-17 05:30:00,98.34,193.25,44.93600000000001,32.166 +2020-03-17 05:45:00,99.36,184.088,44.93600000000001,32.166 +2020-03-17 06:00:00,109.4,182.72,57.271,32.166 +2020-03-17 06:15:00,112.51,188.58599999999998,57.271,32.166 +2020-03-17 06:30:00,114.99,188.97299999999998,57.271,32.166 +2020-03-17 06:45:00,116.99,190.85,57.271,32.166 +2020-03-17 07:00:00,118.61,193.696,68.352,32.166 +2020-03-17 07:15:00,121.29,195.703,68.352,32.166 +2020-03-17 07:30:00,121.0,195.18,68.352,32.166 +2020-03-17 07:45:00,121.45,192.78,68.352,32.166 +2020-03-17 08:00:00,121.87,191.438,60.717,32.166 +2020-03-17 08:15:00,119.41,189.315,60.717,32.166 +2020-03-17 08:30:00,117.8,184.7,60.717,32.166 +2020-03-17 08:45:00,117.14,180.785,60.717,32.166 +2020-03-17 09:00:00,114.19,174.417,54.603,32.166 +2020-03-17 09:15:00,117.59,171.476,54.603,32.166 +2020-03-17 09:30:00,123.71,171.49200000000002,54.603,32.166 +2020-03-17 09:45:00,123.51,169.47299999999998,54.603,32.166 +2020-03-17 10:00:00,123.65,166.34900000000002,52.308,32.166 +2020-03-17 10:15:00,126.87,164.386,52.308,32.166 +2020-03-17 10:30:00,129.22,161.95,52.308,32.166 +2020-03-17 10:45:00,132.76,161.387,52.308,32.166 +2020-03-17 11:00:00,132.25,156.829,51.838,32.166 +2020-03-17 11:15:00,132.0,156.743,51.838,32.166 +2020-03-17 11:30:00,132.65,157.129,51.838,32.166 +2020-03-17 11:45:00,134.64,157.938,51.838,32.166 +2020-03-17 12:00:00,132.83,153.315,50.375,32.166 +2020-03-17 12:15:00,132.99,153.584,50.375,32.166 +2020-03-17 12:30:00,132.12,153.22299999999998,50.375,32.166 +2020-03-17 12:45:00,133.93,154.017,50.375,32.166 +2020-03-17 13:00:00,132.01,153.819,50.735,32.166 +2020-03-17 13:15:00,130.45,152.21200000000002,50.735,32.166 +2020-03-17 13:30:00,124.25,150.447,50.735,32.166 +2020-03-17 13:45:00,128.13,149.58700000000002,50.735,32.166 +2020-03-17 14:00:00,127.73,151.502,50.946000000000005,32.166 +2020-03-17 14:15:00,128.74,150.222,50.946000000000005,32.166 +2020-03-17 14:30:00,128.92,150.161,50.946000000000005,32.166 +2020-03-17 14:45:00,130.63,151.126,50.946000000000005,32.166 +2020-03-17 15:00:00,132.16,151.93,53.18,32.166 +2020-03-17 15:15:00,132.92,150.592,53.18,32.166 +2020-03-17 15:30:00,132.15,149.77,53.18,32.166 +2020-03-17 15:45:00,132.5,149.059,53.18,32.166 +2020-03-17 16:00:00,131.86,153.01,54.928999999999995,32.166 +2020-03-17 16:15:00,131.47,155.187,54.928999999999995,32.166 +2020-03-17 16:30:00,129.12,155.662,54.928999999999995,32.166 +2020-03-17 16:45:00,130.42,155.007,54.928999999999995,32.166 +2020-03-17 17:00:00,131.53,156.974,60.913000000000004,32.166 +2020-03-17 17:15:00,135.08,159.489,60.913000000000004,32.166 +2020-03-17 17:30:00,143.36,162.031,60.913000000000004,32.166 +2020-03-17 17:45:00,144.28,162.69899999999998,60.913000000000004,32.166 +2020-03-17 18:00:00,137.97,166.455,62.214,32.166 +2020-03-17 18:15:00,138.35,167.197,62.214,32.166 +2020-03-17 18:30:00,140.04,165.55599999999998,62.214,32.166 +2020-03-17 18:45:00,139.14,169.495,62.214,32.166 +2020-03-17 19:00:00,138.8,167.745,62.38,32.166 +2020-03-17 19:15:00,134.96,165.702,62.38,32.166 +2020-03-17 19:30:00,135.63,165.108,62.38,32.166 +2020-03-17 19:45:00,132.59,163.565,62.38,32.166 +2020-03-17 20:00:00,123.95,157.756,65.018,32.166 +2020-03-17 20:15:00,121.08,153.566,65.018,32.166 +2020-03-17 20:30:00,114.15,151.616,65.018,32.166 +2020-03-17 20:45:00,114.92,149.891,65.018,32.166 +2020-03-17 21:00:00,114.69,144.774,56.416000000000004,32.166 +2020-03-17 21:15:00,109.67,142.322,56.416000000000004,32.166 +2020-03-17 21:30:00,107.76,141.31,56.416000000000004,32.166 +2020-03-17 21:45:00,105.9,140.57399999999998,56.416000000000004,32.166 +2020-03-17 22:00:00,108.3,133.65,52.846000000000004,32.166 +2020-03-17 22:15:00,108.41,130.213,52.846000000000004,32.166 +2020-03-17 22:30:00,101.96,116.061,52.846000000000004,32.166 +2020-03-17 22:45:00,96.25,108.795,52.846000000000004,32.166 +2020-03-17 23:00:00,91.66,101.624,44.435,32.166 +2020-03-17 23:15:00,90.02,100.15700000000001,44.435,32.166 +2020-03-17 23:30:00,86.99,100.775,44.435,32.166 +2020-03-17 23:45:00,89.71,101.256,44.435,32.166 +2020-03-18 00:00:00,83.73,96.42299999999999,42.527,32.166 +2020-03-18 00:15:00,89.73,95.705,42.527,32.166 +2020-03-18 00:30:00,86.8,94.446,42.527,32.166 +2020-03-18 00:45:00,85.84,93.59899999999999,42.527,32.166 +2020-03-18 01:00:00,80.74,94.855,38.655,32.166 +2020-03-18 01:15:00,80.02,94.855,38.655,32.166 +2020-03-18 01:30:00,85.25,94.46799999999999,38.655,32.166 +2020-03-18 01:45:00,87.28,94.34899999999999,38.655,32.166 +2020-03-18 02:00:00,86.8,96.135,36.912,32.166 +2020-03-18 02:15:00,84.18,96.462,36.912,32.166 +2020-03-18 02:30:00,79.64,97.82,36.912,32.166 +2020-03-18 02:45:00,86.07,99.01100000000001,36.912,32.166 +2020-03-18 03:00:00,88.69,101.79799999999999,36.98,32.166 +2020-03-18 03:15:00,89.95,103.663,36.98,32.166 +2020-03-18 03:30:00,85.36,104.735,36.98,32.166 +2020-03-18 03:45:00,81.81,105.51,36.98,32.166 +2020-03-18 04:00:00,86.79,118.46600000000001,38.052,32.166 +2020-03-18 04:15:00,90.82,131.358,38.052,32.166 +2020-03-18 04:30:00,92.75,132.518,38.052,32.166 +2020-03-18 04:45:00,88.52,134.257,38.052,32.166 +2020-03-18 05:00:00,90.89,168.46599999999998,42.455,32.166 +2020-03-18 05:15:00,92.19,200.058,42.455,32.166 +2020-03-18 05:30:00,96.66,192.72400000000002,42.455,32.166 +2020-03-18 05:45:00,100.78,183.581,42.455,32.166 +2020-03-18 06:00:00,108.49,182.225,57.986000000000004,32.166 +2020-03-18 06:15:00,111.7,188.088,57.986000000000004,32.166 +2020-03-18 06:30:00,114.37,188.43599999999998,57.986000000000004,32.166 +2020-03-18 06:45:00,118.53,190.293,57.986000000000004,32.166 +2020-03-18 07:00:00,124.03,193.15400000000002,71.868,32.166 +2020-03-18 07:15:00,124.83,195.13,71.868,32.166 +2020-03-18 07:30:00,127.19,194.567,71.868,32.166 +2020-03-18 07:45:00,130.33,192.137,71.868,32.166 +2020-03-18 08:00:00,133.17,190.771,62.225,32.166 +2020-03-18 08:15:00,131.53,188.65099999999998,62.225,32.166 +2020-03-18 08:30:00,133.54,183.987,62.225,32.166 +2020-03-18 08:45:00,132.96,180.095,62.225,32.166 +2020-03-18 09:00:00,129.67,173.738,58.802,32.166 +2020-03-18 09:15:00,131.06,170.799,58.802,32.166 +2020-03-18 09:30:00,130.32,170.835,58.802,32.166 +2020-03-18 09:45:00,125.59,168.834,58.802,32.166 +2020-03-18 10:00:00,123.25,165.722,54.122,32.166 +2020-03-18 10:15:00,118.73,163.805,54.122,32.166 +2020-03-18 10:30:00,115.43,161.391,54.122,32.166 +2020-03-18 10:45:00,117.34,160.849,54.122,32.166 +2020-03-18 11:00:00,117.69,156.282,54.368,32.166 +2020-03-18 11:15:00,118.38,156.218,54.368,32.166 +2020-03-18 11:30:00,118.73,156.61,54.368,32.166 +2020-03-18 11:45:00,118.1,157.438,54.368,32.166 +2020-03-18 12:00:00,117.22,152.83700000000002,52.74,32.166 +2020-03-18 12:15:00,117.72,153.116,52.74,32.166 +2020-03-18 12:30:00,117.55,152.716,52.74,32.166 +2020-03-18 12:45:00,115.41,153.509,52.74,32.166 +2020-03-18 13:00:00,116.23,153.35399999999998,52.544,32.166 +2020-03-18 13:15:00,118.56,151.731,52.544,32.166 +2020-03-18 13:30:00,118.92,149.958,52.544,32.166 +2020-03-18 13:45:00,118.2,149.1,52.544,32.166 +2020-03-18 14:00:00,119.88,151.083,53.602,32.166 +2020-03-18 14:15:00,117.8,149.78,53.602,32.166 +2020-03-18 14:30:00,116.98,149.68200000000002,53.602,32.166 +2020-03-18 14:45:00,119.05,150.655,53.602,32.166 +2020-03-18 15:00:00,115.92,151.475,55.59,32.166 +2020-03-18 15:15:00,111.49,150.108,55.59,32.166 +2020-03-18 15:30:00,111.91,149.237,55.59,32.166 +2020-03-18 15:45:00,121.29,148.507,55.59,32.166 +2020-03-18 16:00:00,119.54,152.477,57.586999999999996,32.166 +2020-03-18 16:15:00,120.09,154.63,57.586999999999996,32.166 +2020-03-18 16:30:00,114.99,155.105,57.586999999999996,32.166 +2020-03-18 16:45:00,117.72,154.4,57.586999999999996,32.166 +2020-03-18 17:00:00,122.47,156.395,62.111999999999995,32.166 +2020-03-18 17:15:00,129.68,158.906,62.111999999999995,32.166 +2020-03-18 17:30:00,131.77,161.46200000000002,62.111999999999995,32.166 +2020-03-18 17:45:00,132.41,162.132,62.111999999999995,32.166 +2020-03-18 18:00:00,131.64,165.895,64.605,32.166 +2020-03-18 18:15:00,126.8,166.695,64.605,32.166 +2020-03-18 18:30:00,135.48,165.045,64.605,32.166 +2020-03-18 18:45:00,144.49,169.00099999999998,64.605,32.166 +2020-03-18 19:00:00,144.79,167.22,65.55199999999999,32.166 +2020-03-18 19:15:00,137.82,165.19299999999998,65.55199999999999,32.166 +2020-03-18 19:30:00,133.32,164.62400000000002,65.55199999999999,32.166 +2020-03-18 19:45:00,131.22,163.12,65.55199999999999,32.166 +2020-03-18 20:00:00,129.19,157.282,66.778,32.166 +2020-03-18 20:15:00,125.46,153.107,66.778,32.166 +2020-03-18 20:30:00,120.34,151.188,66.778,32.166 +2020-03-18 20:45:00,117.63,149.47299999999998,66.778,32.166 +2020-03-18 21:00:00,114.44,144.349,56.103,32.166 +2020-03-18 21:15:00,114.71,141.899,56.103,32.166 +2020-03-18 21:30:00,109.71,140.885,56.103,32.166 +2020-03-18 21:45:00,108.39,140.174,56.103,32.166 +2020-03-18 22:00:00,107.26,133.243,51.371,32.166 +2020-03-18 22:15:00,105.38,129.833,51.371,32.166 +2020-03-18 22:30:00,98.14,115.63600000000001,51.371,32.166 +2020-03-18 22:45:00,94.88,108.369,51.371,32.166 +2020-03-18 23:00:00,88.42,101.18700000000001,42.798,32.166 +2020-03-18 23:15:00,89.09,99.745,42.798,32.166 +2020-03-18 23:30:00,91.31,100.363,42.798,32.166 +2020-03-18 23:45:00,92.36,100.868,42.798,32.166 +2020-03-19 00:00:00,87.88,96.053,39.069,32.166 +2020-03-19 00:15:00,85.89,95.34700000000001,39.069,32.166 +2020-03-19 00:30:00,87.68,94.073,39.069,32.166 +2020-03-19 00:45:00,87.98,93.228,39.069,32.166 +2020-03-19 01:00:00,84.61,94.45200000000001,37.043,32.166 +2020-03-19 01:15:00,79.55,94.434,37.043,32.166 +2020-03-19 01:30:00,77.18,94.027,37.043,32.166 +2020-03-19 01:45:00,78.42,93.915,37.043,32.166 +2020-03-19 02:00:00,79.65,95.69,34.625,32.166 +2020-03-19 02:15:00,86.0,96.006,34.625,32.166 +2020-03-19 02:30:00,83.94,97.384,34.625,32.166 +2020-03-19 02:45:00,86.89,98.575,34.625,32.166 +2020-03-19 03:00:00,82.57,101.37899999999999,33.812,32.166 +2020-03-19 03:15:00,86.09,103.221,33.812,32.166 +2020-03-19 03:30:00,88.78,104.287,33.812,32.166 +2020-03-19 03:45:00,86.84,105.07799999999999,33.812,32.166 +2020-03-19 04:00:00,86.04,118.03,35.236999999999995,32.166 +2020-03-19 04:15:00,87.46,130.905,35.236999999999995,32.166 +2020-03-19 04:30:00,90.13,132.077,35.236999999999995,32.166 +2020-03-19 04:45:00,89.22,133.804,35.236999999999995,32.166 +2020-03-19 05:00:00,87.18,167.97099999999998,40.375,32.166 +2020-03-19 05:15:00,92.65,199.551,40.375,32.166 +2020-03-19 05:30:00,91.79,192.196,40.375,32.166 +2020-03-19 05:45:00,99.25,183.07,40.375,32.166 +2020-03-19 06:00:00,101.84,181.726,52.316,32.166 +2020-03-19 06:15:00,108.97,187.58700000000002,52.316,32.166 +2020-03-19 06:30:00,112.26,187.893,52.316,32.166 +2020-03-19 06:45:00,114.99,189.731,52.316,32.166 +2020-03-19 07:00:00,119.6,192.607,64.115,32.166 +2020-03-19 07:15:00,119.74,194.554,64.115,32.166 +2020-03-19 07:30:00,122.99,193.949,64.115,32.166 +2020-03-19 07:45:00,125.11,191.489,64.115,32.166 +2020-03-19 08:00:00,132.62,190.1,55.033,32.166 +2020-03-19 08:15:00,133.14,187.981,55.033,32.166 +2020-03-19 08:30:00,132.06,183.27,55.033,32.166 +2020-03-19 08:45:00,130.67,179.4,55.033,32.166 +2020-03-19 09:00:00,131.44,173.053,49.411,32.166 +2020-03-19 09:15:00,128.19,170.11599999999999,49.411,32.166 +2020-03-19 09:30:00,130.89,170.173,49.411,32.166 +2020-03-19 09:45:00,133.55,168.19299999999998,49.411,32.166 +2020-03-19 10:00:00,135.03,165.09099999999998,45.82899999999999,32.166 +2020-03-19 10:15:00,135.81,163.22,45.82899999999999,32.166 +2020-03-19 10:30:00,135.42,160.829,45.82899999999999,32.166 +2020-03-19 10:45:00,133.48,160.30700000000002,45.82899999999999,32.166 +2020-03-19 11:00:00,134.38,155.732,44.333,32.166 +2020-03-19 11:15:00,136.87,155.691,44.333,32.166 +2020-03-19 11:30:00,136.53,156.089,44.333,32.166 +2020-03-19 11:45:00,135.15,156.935,44.333,32.166 +2020-03-19 12:00:00,134.45,152.355,42.95,32.166 +2020-03-19 12:15:00,132.99,152.64600000000002,42.95,32.166 +2020-03-19 12:30:00,130.93,152.20600000000002,42.95,32.166 +2020-03-19 12:45:00,131.7,152.998,42.95,32.166 +2020-03-19 13:00:00,131.55,152.886,42.489,32.166 +2020-03-19 13:15:00,136.58,151.246,42.489,32.166 +2020-03-19 13:30:00,137.49,149.465,42.489,32.166 +2020-03-19 13:45:00,130.19,148.61,42.489,32.166 +2020-03-19 14:00:00,126.15,150.662,43.448,32.166 +2020-03-19 14:15:00,126.13,149.335,43.448,32.166 +2020-03-19 14:30:00,128.46,149.201,43.448,32.166 +2020-03-19 14:45:00,126.37,150.18,43.448,32.166 +2020-03-19 15:00:00,127.54,151.016,45.994,32.166 +2020-03-19 15:15:00,133.12,149.622,45.994,32.166 +2020-03-19 15:30:00,132.92,148.701,45.994,32.166 +2020-03-19 15:45:00,133.74,147.954,45.994,32.166 +2020-03-19 16:00:00,129.51,151.94,48.167,32.166 +2020-03-19 16:15:00,133.23,154.07,48.167,32.166 +2020-03-19 16:30:00,134.2,154.545,48.167,32.166 +2020-03-19 16:45:00,133.59,153.78799999999998,48.167,32.166 +2020-03-19 17:00:00,130.78,155.813,52.637,32.166 +2020-03-19 17:15:00,129.72,158.317,52.637,32.166 +2020-03-19 17:30:00,130.87,160.888,52.637,32.166 +2020-03-19 17:45:00,140.13,161.56,52.637,32.166 +2020-03-19 18:00:00,142.78,165.331,55.739,32.166 +2020-03-19 18:15:00,139.96,166.188,55.739,32.166 +2020-03-19 18:30:00,137.06,164.53099999999998,55.739,32.166 +2020-03-19 18:45:00,135.97,168.502,55.739,32.166 +2020-03-19 19:00:00,140.2,166.69099999999997,56.36600000000001,32.166 +2020-03-19 19:15:00,139.3,164.68,56.36600000000001,32.166 +2020-03-19 19:30:00,137.47,164.135,56.36600000000001,32.166 +2020-03-19 19:45:00,134.57,162.673,56.36600000000001,32.166 +2020-03-19 20:00:00,124.92,156.805,56.338,32.166 +2020-03-19 20:15:00,122.58,152.644,56.338,32.166 +2020-03-19 20:30:00,124.08,150.756,56.338,32.166 +2020-03-19 20:45:00,121.71,149.05200000000002,56.338,32.166 +2020-03-19 21:00:00,111.74,143.921,49.894,32.166 +2020-03-19 21:15:00,110.78,141.474,49.894,32.166 +2020-03-19 21:30:00,112.46,140.45600000000002,49.894,32.166 +2020-03-19 21:45:00,110.73,139.77100000000002,49.894,32.166 +2020-03-19 22:00:00,106.07,132.833,46.687,32.166 +2020-03-19 22:15:00,102.14,129.44899999999998,46.687,32.166 +2020-03-19 22:30:00,100.56,115.20700000000001,46.687,32.166 +2020-03-19 22:45:00,100.02,107.939,46.687,32.166 +2020-03-19 23:00:00,93.88,100.74600000000001,39.211,32.166 +2020-03-19 23:15:00,92.98,99.32700000000001,39.211,32.166 +2020-03-19 23:30:00,90.87,99.948,39.211,32.166 +2020-03-19 23:45:00,92.97,100.475,39.211,32.166 +2020-03-20 00:00:00,81.31,94.274,36.616,32.166 +2020-03-20 00:15:00,83.1,93.804,36.616,32.166 +2020-03-20 00:30:00,80.74,92.495,36.616,32.166 +2020-03-20 00:45:00,87.41,91.87299999999999,36.616,32.166 +2020-03-20 01:00:00,85.22,92.698,33.799,32.166 +2020-03-20 01:15:00,85.31,93.18799999999999,33.799,32.166 +2020-03-20 01:30:00,79.72,92.814,33.799,32.166 +2020-03-20 01:45:00,79.16,92.711,33.799,32.166 +2020-03-20 02:00:00,75.56,94.837,32.968,32.166 +2020-03-20 02:15:00,78.31,95.037,32.968,32.166 +2020-03-20 02:30:00,84.24,97.10799999999999,32.968,32.166 +2020-03-20 02:45:00,84.88,98.116,32.968,32.166 +2020-03-20 03:00:00,84.31,100.412,33.533,32.166 +2020-03-20 03:15:00,80.11,102.57,33.533,32.166 +2020-03-20 03:30:00,86.07,103.539,33.533,32.166 +2020-03-20 03:45:00,86.71,104.882,33.533,32.166 +2020-03-20 04:00:00,89.04,118.052,36.102,32.166 +2020-03-20 04:15:00,86.87,130.207,36.102,32.166 +2020-03-20 04:30:00,82.36,131.881,36.102,32.166 +2020-03-20 04:45:00,84.69,132.501,36.102,32.166 +2020-03-20 05:00:00,89.4,165.488,42.423,32.166 +2020-03-20 05:15:00,90.87,198.532,42.423,32.166 +2020-03-20 05:30:00,93.45,192.05200000000002,42.423,32.166 +2020-03-20 05:45:00,99.81,182.74599999999998,42.423,32.166 +2020-03-20 06:00:00,105.17,181.84099999999998,55.38,32.166 +2020-03-20 06:15:00,110.26,186.604,55.38,32.166 +2020-03-20 06:30:00,112.63,186.18099999999998,55.38,32.166 +2020-03-20 06:45:00,116.56,189.22299999999998,55.38,32.166 +2020-03-20 07:00:00,123.53,191.669,65.929,32.166 +2020-03-20 07:15:00,124.74,194.675,65.929,32.166 +2020-03-20 07:30:00,128.22,193.28099999999998,65.929,32.166 +2020-03-20 07:45:00,131.67,190.02700000000002,65.929,32.166 +2020-03-20 08:00:00,135.19,187.975,57.336999999999996,32.166 +2020-03-20 08:15:00,135.75,185.748,57.336999999999996,32.166 +2020-03-20 08:30:00,136.9,181.74200000000002,57.336999999999996,32.166 +2020-03-20 08:45:00,138.65,176.588,57.336999999999996,32.166 +2020-03-20 09:00:00,138.65,169.93400000000003,54.226000000000006,32.166 +2020-03-20 09:15:00,141.78,168.017,54.226000000000006,32.166 +2020-03-20 09:30:00,141.41,167.548,54.226000000000006,32.166 +2020-03-20 09:45:00,140.33,165.61,54.226000000000006,32.166 +2020-03-20 10:00:00,137.44,161.526,51.298,32.166 +2020-03-20 10:15:00,138.85,160.215,51.298,32.166 +2020-03-20 10:30:00,139.49,157.942,51.298,32.166 +2020-03-20 10:45:00,140.16,157.05,51.298,32.166 +2020-03-20 11:00:00,135.6,152.503,50.839,32.166 +2020-03-20 11:15:00,134.52,151.446,50.839,32.166 +2020-03-20 11:30:00,136.04,153.204,50.839,32.166 +2020-03-20 11:45:00,132.53,153.84799999999998,50.839,32.166 +2020-03-20 12:00:00,128.08,150.317,47.976000000000006,32.166 +2020-03-20 12:15:00,131.48,148.69899999999998,47.976000000000006,32.166 +2020-03-20 12:30:00,131.48,148.384,47.976000000000006,32.166 +2020-03-20 12:45:00,128.91,149.39600000000002,47.976000000000006,32.166 +2020-03-20 13:00:00,125.64,150.276,46.299,32.166 +2020-03-20 13:15:00,127.45,149.352,46.299,32.166 +2020-03-20 13:30:00,121.83,147.784,46.299,32.166 +2020-03-20 13:45:00,121.45,146.958,46.299,32.166 +2020-03-20 14:00:00,122.13,147.91299999999998,44.971000000000004,32.166 +2020-03-20 14:15:00,122.95,146.52700000000002,44.971000000000004,32.166 +2020-03-20 14:30:00,119.93,147.18,44.971000000000004,32.166 +2020-03-20 14:45:00,118.5,148.24,44.971000000000004,32.166 +2020-03-20 15:00:00,118.31,148.666,47.48,32.166 +2020-03-20 15:15:00,118.3,146.813,47.48,32.166 +2020-03-20 15:30:00,117.43,144.417,47.48,32.166 +2020-03-20 15:45:00,118.9,143.97299999999998,47.48,32.166 +2020-03-20 16:00:00,119.03,146.799,50.648,32.166 +2020-03-20 16:15:00,117.4,149.28,50.648,32.166 +2020-03-20 16:30:00,117.62,149.784,50.648,32.166 +2020-03-20 16:45:00,116.65,148.667,50.648,32.166 +2020-03-20 17:00:00,118.31,151.38299999999998,56.251000000000005,32.166 +2020-03-20 17:15:00,119.69,153.495,56.251000000000005,32.166 +2020-03-20 17:30:00,121.15,155.864,56.251000000000005,32.166 +2020-03-20 17:45:00,124.53,156.298,56.251000000000005,32.166 +2020-03-20 18:00:00,130.12,160.666,58.982,32.166 +2020-03-20 18:15:00,129.06,160.994,58.982,32.166 +2020-03-20 18:30:00,130.46,159.622,58.982,32.166 +2020-03-20 18:45:00,132.02,163.724,58.982,32.166 +2020-03-20 19:00:00,134.39,162.879,57.293,32.166 +2020-03-20 19:15:00,130.14,162.16299999999998,57.293,32.166 +2020-03-20 19:30:00,128.96,161.32399999999998,57.293,32.166 +2020-03-20 19:45:00,127.91,159.238,57.293,32.166 +2020-03-20 20:00:00,121.85,153.34799999999998,59.433,32.166 +2020-03-20 20:15:00,124.01,149.438,59.433,32.166 +2020-03-20 20:30:00,121.62,147.405,59.433,32.166 +2020-03-20 20:45:00,115.58,145.928,59.433,32.166 +2020-03-20 21:00:00,107.59,141.58700000000002,52.153999999999996,32.166 +2020-03-20 21:15:00,107.1,139.983,52.153999999999996,32.166 +2020-03-20 21:30:00,102.66,138.957,52.153999999999996,32.166 +2020-03-20 21:45:00,102.64,138.795,52.153999999999996,32.166 +2020-03-20 22:00:00,95.47,132.6,47.125,32.166 +2020-03-20 22:15:00,92.65,129.063,47.125,32.166 +2020-03-20 22:30:00,94.55,121.314,47.125,32.166 +2020-03-20 22:45:00,96.74,117.149,47.125,32.166 +2020-03-20 23:00:00,91.54,110.07799999999999,41.236000000000004,32.166 +2020-03-20 23:15:00,84.93,106.649,41.236000000000004,32.166 +2020-03-20 23:30:00,83.48,105.56200000000001,41.236000000000004,32.166 +2020-03-20 23:45:00,79.79,105.538,41.236000000000004,32.166 +2020-03-21 00:00:00,76.15,92.06,36.484,31.988000000000003 +2020-03-21 00:15:00,76.52,88.061,36.484,31.988000000000003 +2020-03-21 00:30:00,75.95,87.62700000000001,36.484,31.988000000000003 +2020-03-21 00:45:00,78.77,87.333,36.484,31.988000000000003 +2020-03-21 01:00:00,78.62,88.72,32.391999999999996,31.988000000000003 +2020-03-21 01:15:00,79.74,88.615,32.391999999999996,31.988000000000003 +2020-03-21 01:30:00,75.68,87.559,32.391999999999996,31.988000000000003 +2020-03-21 01:45:00,75.56,87.695,32.391999999999996,31.988000000000003 +2020-03-21 02:00:00,77.03,90.04299999999999,30.194000000000003,31.988000000000003 +2020-03-21 02:15:00,75.94,89.686,30.194000000000003,31.988000000000003 +2020-03-21 02:30:00,75.96,90.661,30.194000000000003,31.988000000000003 +2020-03-21 02:45:00,70.86,92.00399999999999,30.194000000000003,31.988000000000003 +2020-03-21 03:00:00,76.24,94.37,29.677,31.988000000000003 +2020-03-21 03:15:00,77.68,95.339,29.677,31.988000000000003 +2020-03-21 03:30:00,77.67,95.18299999999999,29.677,31.988000000000003 +2020-03-21 03:45:00,73.57,97.11200000000001,29.677,31.988000000000003 +2020-03-21 04:00:00,69.82,106.507,29.616,31.988000000000003 +2020-03-21 04:15:00,70.66,116.397,29.616,31.988000000000003 +2020-03-21 04:30:00,72.22,115.867,29.616,31.988000000000003 +2020-03-21 04:45:00,72.34,116.185,29.616,31.988000000000003 +2020-03-21 05:00:00,73.28,134.24200000000002,29.625,31.988000000000003 +2020-03-21 05:15:00,70.66,149.116,29.625,31.988000000000003 +2020-03-21 05:30:00,72.31,143.395,29.625,31.988000000000003 +2020-03-21 05:45:00,71.97,139.756,29.625,31.988000000000003 +2020-03-21 06:00:00,72.01,156.734,30.551,31.988000000000003 +2020-03-21 06:15:00,74.11,176.644,30.551,31.988000000000003 +2020-03-21 06:30:00,70.85,171.11,30.551,31.988000000000003 +2020-03-21 06:45:00,75.14,166.157,30.551,31.988000000000003 +2020-03-21 07:00:00,77.83,164.997,34.865,31.988000000000003 +2020-03-21 07:15:00,78.22,166.58599999999998,34.865,31.988000000000003 +2020-03-21 07:30:00,79.47,167.74200000000002,34.865,31.988000000000003 +2020-03-21 07:45:00,80.88,167.858,34.865,31.988000000000003 +2020-03-21 08:00:00,82.4,168.946,41.456,31.988000000000003 +2020-03-21 08:15:00,82.02,169.545,41.456,31.988000000000003 +2020-03-21 08:30:00,82.13,166.77,41.456,31.988000000000003 +2020-03-21 08:45:00,83.0,164.435,41.456,31.988000000000003 +2020-03-21 09:00:00,84.22,160.035,43.001999999999995,31.988000000000003 +2020-03-21 09:15:00,84.04,158.866,43.001999999999995,31.988000000000003 +2020-03-21 09:30:00,83.23,159.287,43.001999999999995,31.988000000000003 +2020-03-21 09:45:00,82.37,157.369,43.001999999999995,31.988000000000003 +2020-03-21 10:00:00,81.8,153.608,42.047,31.988000000000003 +2020-03-21 10:15:00,80.91,152.577,42.047,31.988000000000003 +2020-03-21 10:30:00,79.58,150.36700000000002,42.047,31.988000000000003 +2020-03-21 10:45:00,80.21,150.458,42.047,31.988000000000003 +2020-03-21 11:00:00,81.27,145.994,39.894,31.988000000000003 +2020-03-21 11:15:00,82.37,144.628,39.894,31.988000000000003 +2020-03-21 11:30:00,82.93,145.55,39.894,31.988000000000003 +2020-03-21 11:45:00,86.04,145.608,39.894,31.988000000000003 +2020-03-21 12:00:00,81.5,141.393,38.122,31.988000000000003 +2020-03-21 12:15:00,78.76,140.543,38.122,31.988000000000003 +2020-03-21 12:30:00,77.88,140.417,38.122,31.988000000000003 +2020-03-21 12:45:00,78.92,140.98,38.122,31.988000000000003 +2020-03-21 13:00:00,73.16,141.30200000000002,34.645,31.988000000000003 +2020-03-21 13:15:00,75.26,138.506,34.645,31.988000000000003 +2020-03-21 13:30:00,72.49,136.60399999999998,34.645,31.988000000000003 +2020-03-21 13:45:00,72.8,135.825,34.645,31.988000000000003 +2020-03-21 14:00:00,71.82,137.874,33.739000000000004,31.988000000000003 +2020-03-21 14:15:00,68.31,135.69299999999998,33.739000000000004,31.988000000000003 +2020-03-21 14:30:00,69.19,134.681,33.739000000000004,31.988000000000003 +2020-03-21 14:45:00,72.21,136.053,33.739000000000004,31.988000000000003 +2020-03-21 15:00:00,73.63,137.141,35.908,31.988000000000003 +2020-03-21 15:15:00,74.28,136.116,35.908,31.988000000000003 +2020-03-21 15:30:00,72.5,135.011,35.908,31.988000000000003 +2020-03-21 15:45:00,76.03,134.32299999999998,35.908,31.988000000000003 +2020-03-21 16:00:00,78.29,136.875,39.249,31.988000000000003 +2020-03-21 16:15:00,79.04,139.828,39.249,31.988000000000003 +2020-03-21 16:30:00,80.66,140.35,39.249,31.988000000000003 +2020-03-21 16:45:00,82.94,139.906,39.249,31.988000000000003 +2020-03-21 17:00:00,86.08,141.953,46.045,31.988000000000003 +2020-03-21 17:15:00,88.1,144.812,46.045,31.988000000000003 +2020-03-21 17:30:00,90.2,147.079,46.045,31.988000000000003 +2020-03-21 17:45:00,92.79,147.321,46.045,31.988000000000003 +2020-03-21 18:00:00,97.5,151.714,48.238,31.988000000000003 +2020-03-21 18:15:00,97.87,154.032,48.238,31.988000000000003 +2020-03-21 18:30:00,100.71,154.118,48.238,31.988000000000003 +2020-03-21 18:45:00,101.17,154.534,48.238,31.988000000000003 +2020-03-21 19:00:00,107.14,153.971,46.785,31.988000000000003 +2020-03-21 19:15:00,105.31,152.583,46.785,31.988000000000003 +2020-03-21 19:30:00,103.63,152.597,46.785,31.988000000000003 +2020-03-21 19:45:00,103.35,150.908,46.785,31.988000000000003 +2020-03-21 20:00:00,97.14,147.029,39.830999999999996,31.988000000000003 +2020-03-21 20:15:00,94.26,144.661,39.830999999999996,31.988000000000003 +2020-03-21 20:30:00,91.73,142.124,39.830999999999996,31.988000000000003 +2020-03-21 20:45:00,90.18,140.87,39.830999999999996,31.988000000000003 +2020-03-21 21:00:00,84.4,137.915,34.063,31.988000000000003 +2020-03-21 21:15:00,85.72,136.566,34.063,31.988000000000003 +2020-03-21 21:30:00,84.05,136.578,34.063,31.988000000000003 +2020-03-21 21:45:00,83.62,135.95600000000002,34.063,31.988000000000003 +2020-03-21 22:00:00,79.7,130.829,34.455999999999996,31.988000000000003 +2020-03-21 22:15:00,78.84,129.39,34.455999999999996,31.988000000000003 +2020-03-21 22:30:00,73.84,126.485,34.455999999999996,31.988000000000003 +2020-03-21 22:45:00,75.89,123.93,34.455999999999996,31.988000000000003 +2020-03-21 23:00:00,70.82,118.62,27.840999999999998,31.988000000000003 +2020-03-21 23:15:00,70.94,114.022,27.840999999999998,31.988000000000003 +2020-03-21 23:30:00,69.79,112.245,27.840999999999998,31.988000000000003 +2020-03-21 23:45:00,68.39,110.39399999999999,27.840999999999998,31.988000000000003 +2020-03-22 00:00:00,60.08,92.443,20.007,31.988000000000003 +2020-03-22 00:15:00,58.06,87.84200000000001,20.007,31.988000000000003 +2020-03-22 00:30:00,55.51,87.055,20.007,31.988000000000003 +2020-03-22 00:45:00,56.45,87.271,20.007,31.988000000000003 +2020-03-22 01:00:00,54.53,88.59700000000001,17.378,31.988000000000003 +2020-03-22 01:15:00,52.88,89.23700000000001,17.378,31.988000000000003 +2020-03-22 01:30:00,52.07,88.521,17.378,31.988000000000003 +2020-03-22 01:45:00,53.98,88.29799999999999,17.378,31.988000000000003 +2020-03-22 02:00:00,48.89,90.07600000000001,16.145,31.988000000000003 +2020-03-22 02:15:00,53.69,89.28200000000001,16.145,31.988000000000003 +2020-03-22 02:30:00,51.28,90.99600000000001,16.145,31.988000000000003 +2020-03-22 02:45:00,53.95,92.603,16.145,31.988000000000003 +2020-03-22 03:00:00,50.72,95.43799999999999,15.427999999999999,31.988000000000003 +2020-03-22 03:15:00,54.24,96.069,15.427999999999999,31.988000000000003 +2020-03-22 03:30:00,51.64,96.738,15.427999999999999,31.988000000000003 +2020-03-22 03:45:00,51.01,98.361,15.427999999999999,31.988000000000003 +2020-03-22 04:00:00,50.48,107.544,16.663,31.988000000000003 +2020-03-22 04:15:00,51.91,116.501,16.663,31.988000000000003 +2020-03-22 04:30:00,51.98,116.48,16.663,31.988000000000003 +2020-03-22 04:45:00,52.83,116.838,16.663,31.988000000000003 +2020-03-22 05:00:00,51.89,132.247,17.271,31.988000000000003 +2020-03-22 05:15:00,52.74,145.017,17.271,31.988000000000003 +2020-03-22 05:30:00,52.54,139.015,17.271,31.988000000000003 +2020-03-22 05:45:00,54.38,135.485,17.271,31.988000000000003 +2020-03-22 06:00:00,55.46,151.58700000000002,17.612000000000002,31.988000000000003 +2020-03-22 06:15:00,53.48,170.449,17.612000000000002,31.988000000000003 +2020-03-22 06:30:00,55.9,163.799,17.612000000000002,31.988000000000003 +2020-03-22 06:45:00,53.71,157.689,17.612000000000002,31.988000000000003 +2020-03-22 07:00:00,55.53,158.501,20.88,31.988000000000003 +2020-03-22 07:15:00,59.67,158.875,20.88,31.988000000000003 +2020-03-22 07:30:00,62.15,159.44299999999998,20.88,31.988000000000003 +2020-03-22 07:45:00,61.32,158.92700000000002,20.88,31.988000000000003 +2020-03-22 08:00:00,67.05,161.621,25.861,31.988000000000003 +2020-03-22 08:15:00,67.97,162.512,25.861,31.988000000000003 +2020-03-22 08:30:00,69.68,161.264,25.861,31.988000000000003 +2020-03-22 08:45:00,69.61,160.52,25.861,31.988000000000003 +2020-03-22 09:00:00,74.12,155.761,27.921999999999997,31.988000000000003 +2020-03-22 09:15:00,75.03,154.89,27.921999999999997,31.988000000000003 +2020-03-22 09:30:00,74.98,155.334,27.921999999999997,31.988000000000003 +2020-03-22 09:45:00,73.91,153.639,27.921999999999997,31.988000000000003 +2020-03-22 10:00:00,76.48,152.053,29.048000000000002,31.988000000000003 +2020-03-22 10:15:00,73.74,151.536,29.048000000000002,31.988000000000003 +2020-03-22 10:30:00,75.72,149.9,29.048000000000002,31.988000000000003 +2020-03-22 10:45:00,74.12,148.77100000000002,29.048000000000002,31.988000000000003 +2020-03-22 11:00:00,72.15,144.928,32.02,31.988000000000003 +2020-03-22 11:15:00,77.67,143.569,32.02,31.988000000000003 +2020-03-22 11:30:00,77.66,143.93200000000002,32.02,31.988000000000003 +2020-03-22 11:45:00,82.07,144.57399999999998,32.02,31.988000000000003 +2020-03-22 12:00:00,72.99,140.191,28.55,31.988000000000003 +2020-03-22 12:15:00,75.01,140.744,28.55,31.988000000000003 +2020-03-22 12:30:00,71.52,139.441,28.55,31.988000000000003 +2020-03-22 12:45:00,69.58,139.036,28.55,31.988000000000003 +2020-03-22 13:00:00,68.21,138.718,25.601999999999997,31.988000000000003 +2020-03-22 13:15:00,67.52,138.237,25.601999999999997,31.988000000000003 +2020-03-22 13:30:00,66.81,135.855,25.601999999999997,31.988000000000003 +2020-03-22 13:45:00,67.38,134.836,25.601999999999997,31.988000000000003 +2020-03-22 14:00:00,60.59,137.475,23.916999999999998,31.988000000000003 +2020-03-22 14:15:00,59.39,136.376,23.916999999999998,31.988000000000003 +2020-03-22 14:30:00,58.18,136.016,23.916999999999998,31.988000000000003 +2020-03-22 14:45:00,60.55,136.768,23.916999999999998,31.988000000000003 +2020-03-22 15:00:00,59.16,136.64700000000002,24.064,31.988000000000003 +2020-03-22 15:15:00,60.01,136.006,24.064,31.988000000000003 +2020-03-22 15:30:00,56.92,135.28799999999998,24.064,31.988000000000003 +2020-03-22 15:45:00,58.07,135.231,24.064,31.988000000000003 +2020-03-22 16:00:00,61.44,138.836,28.189,31.988000000000003 +2020-03-22 16:15:00,62.16,141.086,28.189,31.988000000000003 +2020-03-22 16:30:00,62.09,142.095,28.189,31.988000000000003 +2020-03-22 16:45:00,63.69,141.726,28.189,31.988000000000003 +2020-03-22 17:00:00,63.44,143.861,37.576,31.988000000000003 +2020-03-22 17:15:00,65.92,146.877,37.576,31.988000000000003 +2020-03-22 17:30:00,69.07,149.62,37.576,31.988000000000003 +2020-03-22 17:45:00,69.42,151.817,37.576,31.988000000000003 +2020-03-22 18:00:00,77.2,155.935,42.669,31.988000000000003 +2020-03-22 18:15:00,75.08,159.263,42.669,31.988000000000003 +2020-03-22 18:30:00,76.8,157.589,42.669,31.988000000000003 +2020-03-22 18:45:00,76.23,159.547,42.669,31.988000000000003 +2020-03-22 19:00:00,79.58,159.303,43.538999999999994,31.988000000000003 +2020-03-22 19:15:00,77.87,158.088,43.538999999999994,31.988000000000003 +2020-03-22 19:30:00,78.53,157.924,43.538999999999994,31.988000000000003 +2020-03-22 19:45:00,80.41,157.30700000000002,43.538999999999994,31.988000000000003 +2020-03-22 20:00:00,79.37,153.393,37.330999999999996,31.988000000000003 +2020-03-22 20:15:00,79.52,151.787,37.330999999999996,31.988000000000003 +2020-03-22 20:30:00,78.47,150.509,37.330999999999996,31.988000000000003 +2020-03-22 20:45:00,77.25,147.811,37.330999999999996,31.988000000000003 +2020-03-22 21:00:00,74.47,142.727,33.856,31.988000000000003 +2020-03-22 21:15:00,73.54,140.791,33.856,31.988000000000003 +2020-03-22 21:30:00,73.03,140.843,33.856,31.988000000000003 +2020-03-22 21:45:00,72.54,140.459,33.856,31.988000000000003 +2020-03-22 22:00:00,69.45,134.984,34.711999999999996,31.988000000000003 +2020-03-22 22:15:00,69.2,132.468,34.711999999999996,31.988000000000003 +2020-03-22 22:30:00,64.59,126.84,34.711999999999996,31.988000000000003 +2020-03-22 22:45:00,67.2,123.23,34.711999999999996,31.988000000000003 +2020-03-22 23:00:00,62.87,115.586,29.698,31.988000000000003 +2020-03-22 23:15:00,62.97,112.869,29.698,31.988000000000003 +2020-03-22 23:30:00,58.12,111.583,29.698,31.988000000000003 +2020-03-22 23:45:00,60.08,110.514,29.698,31.988000000000003 +2020-03-23 00:00:00,82.34,95.79,29.983,32.166 +2020-03-23 00:15:00,83.55,93.75399999999999,29.983,32.166 +2020-03-23 00:30:00,79.21,92.929,29.983,32.166 +2020-03-23 00:45:00,78.16,92.605,29.983,32.166 +2020-03-23 01:00:00,79.25,94.01700000000001,29.122,32.166 +2020-03-23 01:15:00,80.78,94.23700000000001,29.122,32.166 +2020-03-23 01:30:00,76.93,93.65899999999999,29.122,32.166 +2020-03-23 01:45:00,77.19,93.501,29.122,32.166 +2020-03-23 02:00:00,77.95,95.383,28.676,32.166 +2020-03-23 02:15:00,78.39,95.369,28.676,32.166 +2020-03-23 02:30:00,81.25,97.412,28.676,32.166 +2020-03-23 02:45:00,76.31,98.49799999999999,28.676,32.166 +2020-03-23 03:00:00,80.56,102.464,26.552,32.166 +2020-03-23 03:15:00,79.77,104.568,26.552,32.166 +2020-03-23 03:30:00,79.74,105.24799999999999,26.552,32.166 +2020-03-23 03:45:00,77.39,106.31200000000001,26.552,32.166 +2020-03-23 04:00:00,81.0,119.811,27.44,32.166 +2020-03-23 04:15:00,84.58,132.873,27.44,32.166 +2020-03-23 04:30:00,82.05,134.373,27.44,32.166 +2020-03-23 04:45:00,82.35,134.965,27.44,32.166 +2020-03-23 05:00:00,81.8,164.62599999999998,36.825,32.166 +2020-03-23 05:15:00,86.07,196.108,36.825,32.166 +2020-03-23 05:30:00,94.52,189.77700000000002,36.825,32.166 +2020-03-23 05:45:00,100.38,180.954,36.825,32.166 +2020-03-23 06:00:00,108.8,180.22799999999998,56.589,32.166 +2020-03-23 06:15:00,114.59,184.75799999999998,56.589,32.166 +2020-03-23 06:30:00,111.33,185.50099999999998,56.589,32.166 +2020-03-23 06:45:00,111.22,187.83,56.589,32.166 +2020-03-23 07:00:00,120.07,190.862,67.49,32.166 +2020-03-23 07:15:00,121.71,192.895,67.49,32.166 +2020-03-23 07:30:00,125.53,192.547,67.49,32.166 +2020-03-23 07:45:00,126.83,190.105,67.49,32.166 +2020-03-23 08:00:00,132.73,188.553,60.028,32.166 +2020-03-23 08:15:00,130.98,187.391,60.028,32.166 +2020-03-23 08:30:00,131.58,182.579,60.028,32.166 +2020-03-23 08:45:00,133.7,179.28900000000002,60.028,32.166 +2020-03-23 09:00:00,132.55,173.55599999999998,55.018,32.166 +2020-03-23 09:15:00,133.07,169.37900000000002,55.018,32.166 +2020-03-23 09:30:00,135.0,168.774,55.018,32.166 +2020-03-23 09:45:00,134.08,166.667,55.018,32.166 +2020-03-23 10:00:00,129.98,164.472,51.183,32.166 +2020-03-23 10:15:00,130.53,163.696,51.183,32.166 +2020-03-23 10:30:00,128.68,161.246,51.183,32.166 +2020-03-23 10:45:00,130.19,160.298,51.183,32.166 +2020-03-23 11:00:00,126.38,154.475,50.065,32.166 +2020-03-23 11:15:00,128.38,154.691,50.065,32.166 +2020-03-23 11:30:00,129.3,156.38,50.065,32.166 +2020-03-23 11:45:00,130.58,156.843,50.065,32.166 +2020-03-23 12:00:00,126.27,153.531,48.141999999999996,32.166 +2020-03-23 12:15:00,128.79,154.124,48.141999999999996,32.166 +2020-03-23 12:30:00,127.53,152.702,48.141999999999996,32.166 +2020-03-23 12:45:00,127.44,153.553,48.141999999999996,32.166 +2020-03-23 13:00:00,125.66,154.064,47.887,32.166 +2020-03-23 13:15:00,135.19,152.185,47.887,32.166 +2020-03-23 13:30:00,135.03,149.401,47.887,32.166 +2020-03-23 13:45:00,132.18,148.64700000000002,47.887,32.166 +2020-03-23 14:00:00,128.57,150.628,48.571000000000005,32.166 +2020-03-23 14:15:00,127.42,149.127,48.571000000000005,32.166 +2020-03-23 14:30:00,130.25,148.237,48.571000000000005,32.166 +2020-03-23 14:45:00,133.63,149.51,48.571000000000005,32.166 +2020-03-23 15:00:00,134.03,150.812,49.937,32.166 +2020-03-23 15:15:00,132.5,148.795,49.937,32.166 +2020-03-23 15:30:00,125.71,147.537,49.937,32.166 +2020-03-23 15:45:00,123.67,146.966,49.937,32.166 +2020-03-23 16:00:00,123.22,150.901,52.963,32.166 +2020-03-23 16:15:00,127.85,152.52,52.963,32.166 +2020-03-23 16:30:00,129.07,152.556,52.963,32.166 +2020-03-23 16:45:00,128.76,151.165,52.963,32.166 +2020-03-23 17:00:00,126.16,152.803,61.163999999999994,32.166 +2020-03-23 17:15:00,121.94,155.144,61.163999999999994,32.166 +2020-03-23 17:30:00,125.11,157.346,61.163999999999994,32.166 +2020-03-23 17:45:00,128.06,158.218,61.163999999999994,32.166 +2020-03-23 18:00:00,127.94,162.369,63.788999999999994,32.166 +2020-03-23 18:15:00,126.59,163.451,63.788999999999994,32.166 +2020-03-23 18:30:00,126.68,162.086,63.788999999999994,32.166 +2020-03-23 18:45:00,129.76,165.52599999999998,63.788999999999994,32.166 +2020-03-23 19:00:00,131.55,163.859,63.913000000000004,32.166 +2020-03-23 19:15:00,124.43,162.089,63.913000000000004,32.166 +2020-03-23 19:30:00,127.1,162.246,63.913000000000004,32.166 +2020-03-23 19:45:00,128.86,160.82399999999998,63.913000000000004,32.166 +2020-03-23 20:00:00,128.96,154.60299999999998,65.44,32.166 +2020-03-23 20:15:00,120.05,151.42600000000002,65.44,32.166 +2020-03-23 20:30:00,119.49,148.838,65.44,32.166 +2020-03-23 20:45:00,118.33,147.517,65.44,32.166 +2020-03-23 21:00:00,111.59,142.67700000000002,59.117,32.166 +2020-03-23 21:15:00,108.56,139.935,59.117,32.166 +2020-03-23 21:30:00,108.54,139.444,59.117,32.166 +2020-03-23 21:45:00,110.95,138.616,59.117,32.166 +2020-03-23 22:00:00,104.38,130.222,52.301,32.166 +2020-03-23 22:15:00,99.93,127.251,52.301,32.166 +2020-03-23 22:30:00,99.37,112.665,52.301,32.166 +2020-03-23 22:45:00,98.94,105.17399999999999,52.301,32.166 +2020-03-23 23:00:00,93.28,98.132,44.373000000000005,32.166 +2020-03-23 23:15:00,87.35,97.09299999999999,44.373000000000005,32.166 +2020-03-23 23:30:00,90.84,98.01299999999999,44.373000000000005,32.166 +2020-03-23 23:45:00,92.8,98.931,44.373000000000005,32.166 +2020-03-24 00:00:00,87.21,94.149,44.647,32.166 +2020-03-24 00:15:00,81.24,93.507,44.647,32.166 +2020-03-24 00:30:00,77.75,92.161,44.647,32.166 +2020-03-24 00:45:00,80.38,91.33200000000001,44.647,32.166 +2020-03-24 01:00:00,79.49,92.387,41.433,32.166 +2020-03-24 01:15:00,81.5,92.28,41.433,32.166 +2020-03-24 01:30:00,81.05,91.77600000000001,41.433,32.166 +2020-03-24 01:45:00,82.59,91.697,41.433,32.166 +2020-03-24 02:00:00,74.4,93.40899999999999,39.909,32.166 +2020-03-24 02:15:00,83.06,93.669,39.909,32.166 +2020-03-24 02:30:00,82.28,95.147,39.909,32.166 +2020-03-24 02:45:00,84.99,96.35,39.909,32.166 +2020-03-24 03:00:00,80.16,99.23100000000001,39.14,32.166 +2020-03-24 03:15:00,82.68,100.959,39.14,32.166 +2020-03-24 03:30:00,86.24,101.995,39.14,32.166 +2020-03-24 03:45:00,85.57,102.86200000000001,39.14,32.166 +2020-03-24 04:00:00,85.36,115.8,40.015,32.166 +2020-03-24 04:15:00,84.21,128.582,40.015,32.166 +2020-03-24 04:30:00,88.34,129.81799999999998,40.015,32.166 +2020-03-24 04:45:00,83.04,131.484,40.015,32.166 +2020-03-24 05:00:00,83.64,165.446,44.93600000000001,32.166 +2020-03-24 05:15:00,88.14,196.958,44.93600000000001,32.166 +2020-03-24 05:30:00,90.73,189.49900000000002,44.93600000000001,32.166 +2020-03-24 05:45:00,92.93,180.458,44.93600000000001,32.166 +2020-03-24 06:00:00,104.65,179.174,57.271,32.166 +2020-03-24 06:15:00,109.33,185.017,57.271,32.166 +2020-03-24 06:30:00,113.7,185.11599999999999,57.271,32.166 +2020-03-24 06:45:00,113.97,186.847,57.271,32.166 +2020-03-24 07:00:00,123.2,189.797,68.352,32.166 +2020-03-24 07:15:00,124.58,191.597,68.352,32.166 +2020-03-24 07:30:00,126.5,190.78599999999997,68.352,32.166 +2020-03-24 07:45:00,129.13,188.18099999999998,68.352,32.166 +2020-03-24 08:00:00,133.3,186.676,60.717,32.166 +2020-03-24 08:15:00,134.37,184.57,60.717,32.166 +2020-03-24 08:30:00,134.29,179.618,60.717,32.166 +2020-03-24 08:45:00,134.4,175.868,60.717,32.166 +2020-03-24 09:00:00,132.5,169.576,54.603,32.166 +2020-03-24 09:15:00,131.97,166.65,54.603,32.166 +2020-03-24 09:30:00,130.44,166.808,54.603,32.166 +2020-03-24 09:45:00,126.35,164.928,54.603,32.166 +2020-03-24 10:00:00,123.85,161.881,52.308,32.166 +2020-03-24 10:15:00,123.58,160.246,52.308,32.166 +2020-03-24 10:30:00,121.34,157.974,52.308,32.166 +2020-03-24 10:45:00,117.88,157.553,52.308,32.166 +2020-03-24 11:00:00,119.32,152.941,51.838,32.166 +2020-03-24 11:15:00,119.66,153.012,51.838,32.166 +2020-03-24 11:30:00,122.6,153.441,51.838,32.166 +2020-03-24 11:45:00,119.94,154.38299999999998,51.838,32.166 +2020-03-24 12:00:00,116.79,149.907,50.375,32.166 +2020-03-24 12:15:00,116.39,150.251,50.375,32.166 +2020-03-24 12:30:00,117.13,149.612,50.375,32.166 +2020-03-24 12:45:00,113.27,150.39600000000002,50.375,32.166 +2020-03-24 13:00:00,113.92,150.511,50.735,32.166 +2020-03-24 13:15:00,118.49,148.784,50.735,32.166 +2020-03-24 13:30:00,117.98,146.963,50.735,32.166 +2020-03-24 13:45:00,116.71,146.122,50.735,32.166 +2020-03-24 14:00:00,115.13,148.52100000000002,50.946000000000005,32.166 +2020-03-24 14:15:00,113.45,147.077,50.946000000000005,32.166 +2020-03-24 14:30:00,110.95,146.75799999999998,50.946000000000005,32.166 +2020-03-24 14:45:00,110.16,147.769,50.946000000000005,32.166 +2020-03-24 15:00:00,114.6,148.683,53.18,32.166 +2020-03-24 15:15:00,116.6,147.15,53.18,32.166 +2020-03-24 15:30:00,109.45,145.975,53.18,32.166 +2020-03-24 15:45:00,117.18,145.137,53.18,32.166 +2020-03-24 16:00:00,117.12,149.214,54.928999999999995,32.166 +2020-03-24 16:15:00,118.32,151.22299999999998,54.928999999999995,32.166 +2020-03-24 16:30:00,119.29,151.694,54.928999999999995,32.166 +2020-03-24 16:45:00,117.54,150.678,54.928999999999995,32.166 +2020-03-24 17:00:00,119.96,152.85299999999998,60.913000000000004,32.166 +2020-03-24 17:15:00,118.56,155.32399999999998,60.913000000000004,32.166 +2020-03-24 17:30:00,119.39,157.963,60.913000000000004,32.166 +2020-03-24 17:45:00,119.91,158.639,60.913000000000004,32.166 +2020-03-24 18:00:00,123.8,162.444,62.214,32.166 +2020-03-24 18:15:00,123.93,163.588,62.214,32.166 +2020-03-24 18:30:00,123.06,161.892,62.214,32.166 +2020-03-24 18:45:00,120.31,165.94099999999997,62.214,32.166 +2020-03-24 19:00:00,128.27,163.985,62.38,32.166 +2020-03-24 19:15:00,128.22,162.053,62.38,32.166 +2020-03-24 19:30:00,127.86,161.632,62.38,32.166 +2020-03-24 19:45:00,126.09,160.375,62.38,32.166 +2020-03-24 20:00:00,128.41,154.361,65.018,32.166 +2020-03-24 20:15:00,124.95,150.27200000000002,65.018,32.166 +2020-03-24 20:30:00,121.53,148.543,65.018,32.166 +2020-03-24 20:45:00,118.91,146.891,65.018,32.166 +2020-03-24 21:00:00,112.76,141.732,56.416000000000004,32.166 +2020-03-24 21:15:00,116.6,139.299,56.416000000000004,32.166 +2020-03-24 21:30:00,112.66,138.269,56.416000000000004,32.166 +2020-03-24 21:45:00,107.6,137.704,56.416000000000004,32.166 +2020-03-24 22:00:00,104.55,130.731,52.846000000000004,32.166 +2020-03-24 22:15:00,103.78,127.48100000000001,52.846000000000004,32.166 +2020-03-24 22:30:00,102.03,113.005,52.846000000000004,32.166 +2020-03-24 22:45:00,97.36,105.73200000000001,52.846000000000004,32.166 +2020-03-24 23:00:00,93.61,98.484,44.435,32.166 +2020-03-24 23:15:00,97.37,97.18700000000001,44.435,32.166 +2020-03-24 23:30:00,91.61,97.818,44.435,32.166 +2020-03-24 23:45:00,86.1,98.45700000000001,44.435,32.166 +2020-03-25 00:00:00,86.32,93.757,42.527,32.166 +2020-03-25 00:15:00,85.72,93.12899999999999,42.527,32.166 +2020-03-25 00:30:00,83.35,91.76899999999999,42.527,32.166 +2020-03-25 00:45:00,80.67,90.944,42.527,32.166 +2020-03-25 01:00:00,76.45,91.96600000000001,38.655,32.166 +2020-03-25 01:15:00,74.51,91.84,38.655,32.166 +2020-03-25 01:30:00,78.1,91.316,38.655,32.166 +2020-03-25 01:45:00,84.0,91.244,38.655,32.166 +2020-03-25 02:00:00,80.41,92.943,36.912,32.166 +2020-03-25 02:15:00,80.95,93.19200000000001,36.912,32.166 +2020-03-25 02:30:00,75.56,94.69,36.912,32.166 +2020-03-25 02:45:00,79.65,95.89399999999999,36.912,32.166 +2020-03-25 03:00:00,81.67,98.792,36.98,32.166 +2020-03-25 03:15:00,81.92,100.49799999999999,36.98,32.166 +2020-03-25 03:30:00,82.53,101.52600000000001,36.98,32.166 +2020-03-25 03:45:00,81.28,102.40899999999999,36.98,32.166 +2020-03-25 04:00:00,76.87,115.345,38.052,32.166 +2020-03-25 04:15:00,82.91,128.107,38.052,32.166 +2020-03-25 04:30:00,87.12,129.356,38.052,32.166 +2020-03-25 04:45:00,89.8,131.01,38.052,32.166 +2020-03-25 05:00:00,84.78,164.93,42.455,32.166 +2020-03-25 05:15:00,86.31,196.429,42.455,32.166 +2020-03-25 05:30:00,91.91,188.94799999999998,42.455,32.166 +2020-03-25 05:45:00,94.23,179.926,42.455,32.166 +2020-03-25 06:00:00,103.12,178.65200000000002,57.986000000000004,32.166 +2020-03-25 06:15:00,110.12,184.49099999999999,57.986000000000004,32.166 +2020-03-25 06:30:00,113.23,184.548,57.986000000000004,32.166 +2020-03-25 06:45:00,114.61,186.25900000000001,57.986000000000004,32.166 +2020-03-25 07:00:00,121.68,189.222,71.868,32.166 +2020-03-25 07:15:00,122.82,190.99200000000002,71.868,32.166 +2020-03-25 07:30:00,123.06,190.141,71.868,32.166 +2020-03-25 07:45:00,125.56,187.507,71.868,32.166 +2020-03-25 08:00:00,131.42,185.979,62.225,32.166 +2020-03-25 08:15:00,131.94,183.877,62.225,32.166 +2020-03-25 08:30:00,129.5,178.877,62.225,32.166 +2020-03-25 08:45:00,130.38,175.15099999999998,62.225,32.166 +2020-03-25 09:00:00,128.7,168.872,58.802,32.166 +2020-03-25 09:15:00,130.92,165.947,58.802,32.166 +2020-03-25 09:30:00,130.3,166.12400000000002,58.802,32.166 +2020-03-25 09:45:00,129.49,164.265,58.802,32.166 +2020-03-25 10:00:00,129.92,161.23,54.122,32.166 +2020-03-25 10:15:00,129.34,159.642,54.122,32.166 +2020-03-25 10:30:00,131.48,157.39600000000002,54.122,32.166 +2020-03-25 10:45:00,131.23,156.995,54.122,32.166 +2020-03-25 11:00:00,133.57,152.376,54.368,32.166 +2020-03-25 11:15:00,133.01,152.469,54.368,32.166 +2020-03-25 11:30:00,135.91,152.905,54.368,32.166 +2020-03-25 11:45:00,134.24,153.866,54.368,32.166 +2020-03-25 12:00:00,134.65,149.411,52.74,32.166 +2020-03-25 12:15:00,134.3,149.766,52.74,32.166 +2020-03-25 12:30:00,131.7,149.085,52.74,32.166 +2020-03-25 12:45:00,131.3,149.868,52.74,32.166 +2020-03-25 13:00:00,128.24,150.028,52.544,32.166 +2020-03-25 13:15:00,135.9,148.285,52.544,32.166 +2020-03-25 13:30:00,134.63,146.457,52.544,32.166 +2020-03-25 13:45:00,130.07,145.619,52.544,32.166 +2020-03-25 14:00:00,125.5,148.08700000000002,53.602,32.166 +2020-03-25 14:15:00,124.21,146.62,53.602,32.166 +2020-03-25 14:30:00,121.48,146.262,53.602,32.166 +2020-03-25 14:45:00,124.82,147.278,53.602,32.166 +2020-03-25 15:00:00,128.87,148.209,55.59,32.166 +2020-03-25 15:15:00,132.49,146.649,55.59,32.166 +2020-03-25 15:30:00,130.48,145.423,55.59,32.166 +2020-03-25 15:45:00,125.19,144.566,55.59,32.166 +2020-03-25 16:00:00,124.83,148.662,57.586999999999996,32.166 +2020-03-25 16:15:00,120.76,150.645,57.586999999999996,32.166 +2020-03-25 16:30:00,126.07,151.116,57.586999999999996,32.166 +2020-03-25 16:45:00,129.45,150.047,57.586999999999996,32.166 +2020-03-25 17:00:00,129.43,152.254,62.111999999999995,32.166 +2020-03-25 17:15:00,125.36,154.717,62.111999999999995,32.166 +2020-03-25 17:30:00,128.18,157.368,62.111999999999995,32.166 +2020-03-25 17:45:00,127.2,158.043,62.111999999999995,32.166 +2020-03-25 18:00:00,131.15,161.856,64.605,32.166 +2020-03-25 18:15:00,124.35,163.05700000000002,64.605,32.166 +2020-03-25 18:30:00,128.29,161.352,64.605,32.166 +2020-03-25 18:45:00,127.92,165.417,64.605,32.166 +2020-03-25 19:00:00,132.44,163.43200000000002,65.55199999999999,32.166 +2020-03-25 19:15:00,123.83,161.515,65.55199999999999,32.166 +2020-03-25 19:30:00,129.2,161.12,65.55199999999999,32.166 +2020-03-25 19:45:00,131.36,159.905,65.55199999999999,32.166 +2020-03-25 20:00:00,129.62,153.862,66.778,32.166 +2020-03-25 20:15:00,119.8,149.78799999999998,66.778,32.166 +2020-03-25 20:30:00,115.17,148.091,66.778,32.166 +2020-03-25 20:45:00,115.5,146.44899999999998,66.778,32.166 +2020-03-25 21:00:00,109.54,141.285,56.103,32.166 +2020-03-25 21:15:00,115.91,138.856,56.103,32.166 +2020-03-25 21:30:00,110.45,137.822,56.103,32.166 +2020-03-25 21:45:00,111.02,137.282,56.103,32.166 +2020-03-25 22:00:00,101.35,130.30200000000002,51.371,32.166 +2020-03-25 22:15:00,99.56,127.07799999999999,51.371,32.166 +2020-03-25 22:30:00,100.15,112.553,51.371,32.166 +2020-03-25 22:45:00,100.47,105.279,51.371,32.166 +2020-03-25 23:00:00,91.64,98.023,42.798,32.166 +2020-03-25 23:15:00,90.67,96.74799999999999,42.798,32.166 +2020-03-25 23:30:00,91.31,97.381,42.798,32.166 +2020-03-25 23:45:00,89.57,98.04299999999999,42.798,32.166 +2020-03-26 00:00:00,81.94,93.36200000000001,39.069,32.166 +2020-03-26 00:15:00,81.2,92.749,39.069,32.166 +2020-03-26 00:30:00,84.64,91.374,39.069,32.166 +2020-03-26 00:45:00,84.28,90.554,39.069,32.166 +2020-03-26 01:00:00,81.91,91.542,37.043,32.166 +2020-03-26 01:15:00,73.61,91.398,37.043,32.166 +2020-03-26 01:30:00,80.41,90.853,37.043,32.166 +2020-03-26 01:45:00,81.37,90.79,37.043,32.166 +2020-03-26 02:00:00,77.06,92.475,34.625,32.166 +2020-03-26 02:15:00,76.31,92.712,34.625,32.166 +2020-03-26 02:30:00,71.77,94.23,34.625,32.166 +2020-03-26 02:45:00,75.66,95.43700000000001,34.625,32.166 +2020-03-26 03:00:00,77.66,98.35,33.812,32.166 +2020-03-26 03:15:00,81.48,100.03200000000001,33.812,32.166 +2020-03-26 03:30:00,74.62,101.055,33.812,32.166 +2020-03-26 03:45:00,78.13,101.95299999999999,33.812,32.166 +2020-03-26 04:00:00,75.81,114.887,35.236999999999995,32.166 +2020-03-26 04:15:00,75.77,127.63,35.236999999999995,32.166 +2020-03-26 04:30:00,74.86,128.892,35.236999999999995,32.166 +2020-03-26 04:45:00,78.41,130.533,35.236999999999995,32.166 +2020-03-26 05:00:00,84.12,164.412,40.375,32.166 +2020-03-26 05:15:00,86.05,195.898,40.375,32.166 +2020-03-26 05:30:00,87.24,188.39700000000002,40.375,32.166 +2020-03-26 05:45:00,92.96,179.391,40.375,32.166 +2020-03-26 06:00:00,98.47,178.128,52.316,32.166 +2020-03-26 06:15:00,106.48,183.963,52.316,32.166 +2020-03-26 06:30:00,108.98,183.97799999999998,52.316,32.166 +2020-03-26 06:45:00,110.31,185.66400000000002,52.316,32.166 +2020-03-26 07:00:00,119.63,188.642,64.115,32.166 +2020-03-26 07:15:00,121.23,190.38400000000001,64.115,32.166 +2020-03-26 07:30:00,122.47,189.49099999999999,64.115,32.166 +2020-03-26 07:45:00,125.54,186.829,64.115,32.166 +2020-03-26 08:00:00,126.35,185.27700000000002,55.033,32.166 +2020-03-26 08:15:00,127.67,183.18099999999998,55.033,32.166 +2020-03-26 08:30:00,128.36,178.132,55.033,32.166 +2020-03-26 08:45:00,129.96,174.43099999999998,55.033,32.166 +2020-03-26 09:00:00,128.86,168.16400000000002,49.411,32.166 +2020-03-26 09:15:00,126.99,165.24200000000002,49.411,32.166 +2020-03-26 09:30:00,128.41,165.438,49.411,32.166 +2020-03-26 09:45:00,128.5,163.6,49.411,32.166 +2020-03-26 10:00:00,125.96,160.575,45.82899999999999,32.166 +2020-03-26 10:15:00,126.19,159.036,45.82899999999999,32.166 +2020-03-26 10:30:00,122.45,156.815,45.82899999999999,32.166 +2020-03-26 10:45:00,122.6,156.434,45.82899999999999,32.166 +2020-03-26 11:00:00,124.89,151.809,44.333,32.166 +2020-03-26 11:15:00,127.98,151.92600000000002,44.333,32.166 +2020-03-26 11:30:00,126.17,152.36700000000002,44.333,32.166 +2020-03-26 11:45:00,127.4,153.346,44.333,32.166 +2020-03-26 12:00:00,122.97,148.91299999999998,42.95,32.166 +2020-03-26 12:15:00,124.07,149.278,42.95,32.166 +2020-03-26 12:30:00,129.47,148.55700000000002,42.95,32.166 +2020-03-26 12:45:00,132.19,149.338,42.95,32.166 +2020-03-26 13:00:00,126.6,149.54399999999998,42.489,32.166 +2020-03-26 13:15:00,121.2,147.784,42.489,32.166 +2020-03-26 13:30:00,122.77,145.94799999999998,42.489,32.166 +2020-03-26 13:45:00,122.03,145.113,42.489,32.166 +2020-03-26 14:00:00,118.12,147.65200000000002,43.448,32.166 +2020-03-26 14:15:00,120.48,146.16,43.448,32.166 +2020-03-26 14:30:00,124.87,145.765,43.448,32.166 +2020-03-26 14:45:00,124.56,146.787,43.448,32.166 +2020-03-26 15:00:00,122.41,147.733,45.994,32.166 +2020-03-26 15:15:00,117.85,146.145,45.994,32.166 +2020-03-26 15:30:00,118.99,144.86700000000002,45.994,32.166 +2020-03-26 15:45:00,120.98,143.993,45.994,32.166 +2020-03-26 16:00:00,116.33,148.107,48.167,32.166 +2020-03-26 16:15:00,113.12,150.065,48.167,32.166 +2020-03-26 16:30:00,110.23,150.535,48.167,32.166 +2020-03-26 16:45:00,103.8,149.41299999999998,48.167,32.166 +2020-03-26 17:00:00,103.8,151.651,52.637,32.166 +2020-03-26 17:15:00,109.33,154.106,52.637,32.166 +2020-03-26 17:30:00,115.38,156.77100000000002,52.637,32.166 +2020-03-26 17:45:00,118.72,157.446,52.637,32.166 +2020-03-26 18:00:00,121.33,161.264,55.739,32.166 +2020-03-26 18:15:00,118.59,162.52200000000002,55.739,32.166 +2020-03-26 18:30:00,113.07,160.809,55.739,32.166 +2020-03-26 18:45:00,113.68,164.889,55.739,32.166 +2020-03-26 19:00:00,113.24,162.876,56.36600000000001,32.166 +2020-03-26 19:15:00,115.41,160.976,56.36600000000001,32.166 +2020-03-26 19:30:00,121.08,160.605,56.36600000000001,32.166 +2020-03-26 19:45:00,117.92,159.431,56.36600000000001,32.166 +2020-03-26 20:00:00,115.8,153.36,56.338,32.166 +2020-03-26 20:15:00,112.12,149.299,56.338,32.166 +2020-03-26 20:30:00,109.36,147.637,56.338,32.166 +2020-03-26 20:45:00,108.08,146.005,56.338,32.166 +2020-03-26 21:00:00,100.24,140.836,49.894,32.166 +2020-03-26 21:15:00,98.69,138.41,49.894,32.166 +2020-03-26 21:30:00,92.64,137.374,49.894,32.166 +2020-03-26 21:45:00,91.85,136.857,49.894,32.166 +2020-03-26 22:00:00,85.8,129.87,46.687,32.166 +2020-03-26 22:15:00,85.45,126.67299999999999,46.687,32.166 +2020-03-26 22:30:00,78.83,112.09899999999999,46.687,32.166 +2020-03-26 22:45:00,80.03,104.823,46.687,32.166 +2020-03-26 23:00:00,73.44,97.556,39.211,32.166 +2020-03-26 23:15:00,74.45,96.307,39.211,32.166 +2020-03-26 23:30:00,71.79,96.94200000000001,39.211,32.166 +2020-03-26 23:45:00,69.13,97.62700000000001,39.211,32.166 +2020-03-27 00:00:00,81.29,91.55799999999999,36.616,32.166 +2020-03-27 00:15:00,83.4,91.184,36.616,32.166 +2020-03-27 00:30:00,84.17,89.77600000000001,36.616,32.166 +2020-03-27 00:45:00,83.96,89.18,36.616,32.166 +2020-03-27 01:00:00,77.52,89.76700000000001,33.799,32.166 +2020-03-27 01:15:00,76.7,90.131,33.799,32.166 +2020-03-27 01:30:00,76.43,89.62,33.799,32.166 +2020-03-27 01:45:00,77.15,89.56700000000001,33.799,32.166 +2020-03-27 02:00:00,76.99,91.602,32.968,32.166 +2020-03-27 02:15:00,77.28,91.721,32.968,32.166 +2020-03-27 02:30:00,77.43,93.932,32.968,32.166 +2020-03-27 02:45:00,79.33,94.956,32.968,32.166 +2020-03-27 03:00:00,79.5,97.36200000000001,33.533,32.166 +2020-03-27 03:15:00,77.66,99.35600000000001,33.533,32.166 +2020-03-27 03:30:00,81.27,100.28299999999999,33.533,32.166 +2020-03-27 03:45:00,79.74,101.734,33.533,32.166 +2020-03-27 04:00:00,79.68,114.887,36.102,32.166 +2020-03-27 04:15:00,79.42,126.90899999999999,36.102,32.166 +2020-03-27 04:30:00,77.92,128.674,36.102,32.166 +2020-03-27 04:45:00,80.87,129.208,36.102,32.166 +2020-03-27 05:00:00,85.69,161.907,42.423,32.166 +2020-03-27 05:15:00,87.55,194.855,42.423,32.166 +2020-03-27 05:30:00,89.94,188.229,42.423,32.166 +2020-03-27 05:45:00,94.07,179.046,42.423,32.166 +2020-03-27 06:00:00,101.92,178.21599999999998,55.38,32.166 +2020-03-27 06:15:00,105.46,182.955,55.38,32.166 +2020-03-27 06:30:00,108.23,182.237,55.38,32.166 +2020-03-27 06:45:00,111.45,185.12599999999998,55.38,32.166 +2020-03-27 07:00:00,118.3,187.672,65.929,32.166 +2020-03-27 07:15:00,118.8,190.475,65.929,32.166 +2020-03-27 07:30:00,122.1,188.794,65.929,32.166 +2020-03-27 07:45:00,124.3,185.33900000000003,65.929,32.166 +2020-03-27 08:00:00,127.28,183.12400000000002,57.336999999999996,32.166 +2020-03-27 08:15:00,127.22,180.92,57.336999999999996,32.166 +2020-03-27 08:30:00,127.94,176.578,57.336999999999996,32.166 +2020-03-27 08:45:00,128.1,171.59599999999998,57.336999999999996,32.166 +2020-03-27 09:00:00,127.49,165.024,54.226000000000006,32.166 +2020-03-27 09:15:00,128.67,163.12,54.226000000000006,32.166 +2020-03-27 09:30:00,129.09,162.789,54.226000000000006,32.166 +2020-03-27 09:45:00,128.83,160.995,54.226000000000006,32.166 +2020-03-27 10:00:00,127.42,156.991,51.298,32.166 +2020-03-27 10:15:00,127.88,156.01,51.298,32.166 +2020-03-27 10:30:00,126.44,153.909,51.298,32.166 +2020-03-27 10:45:00,127.39,153.159,51.298,32.166 +2020-03-27 11:00:00,126.52,148.564,50.839,32.166 +2020-03-27 11:15:00,127.61,147.666,50.839,32.166 +2020-03-27 11:30:00,125.8,149.467,50.839,32.166 +2020-03-27 11:45:00,125.03,150.244,50.839,32.166 +2020-03-27 12:00:00,122.0,146.861,47.976000000000006,32.166 +2020-03-27 12:15:00,122.03,145.314,47.976000000000006,32.166 +2020-03-27 12:30:00,121.02,144.716,47.976000000000006,32.166 +2020-03-27 12:45:00,120.19,145.718,47.976000000000006,32.166 +2020-03-27 13:00:00,119.24,146.917,46.299,32.166 +2020-03-27 13:15:00,120.7,145.874,46.299,32.166 +2020-03-27 13:30:00,117.46,144.253,46.299,32.166 +2020-03-27 13:45:00,119.26,143.44799999999998,46.299,32.166 +2020-03-27 14:00:00,117.14,144.891,44.971000000000004,32.166 +2020-03-27 14:15:00,113.78,143.339,44.971000000000004,32.166 +2020-03-27 14:30:00,110.02,143.72899999999998,44.971000000000004,32.166 +2020-03-27 14:45:00,113.2,144.83100000000002,44.971000000000004,32.166 +2020-03-27 15:00:00,114.23,145.365,47.48,32.166 +2020-03-27 15:15:00,112.17,143.32,47.48,32.166 +2020-03-27 15:30:00,106.47,140.567,47.48,32.166 +2020-03-27 15:45:00,107.87,139.994,47.48,32.166 +2020-03-27 16:00:00,110.03,142.95,50.648,32.166 +2020-03-27 16:15:00,110.03,145.256,50.648,32.166 +2020-03-27 16:30:00,112.39,145.755,50.648,32.166 +2020-03-27 16:45:00,112.32,144.27,50.648,32.166 +2020-03-27 17:00:00,115.94,147.203,56.251000000000005,32.166 +2020-03-27 17:15:00,116.05,149.262,56.251000000000005,32.166 +2020-03-27 17:30:00,117.7,151.722,56.251000000000005,32.166 +2020-03-27 17:45:00,118.13,152.159,56.251000000000005,32.166 +2020-03-27 18:00:00,121.7,156.57299999999998,58.982,32.166 +2020-03-27 18:15:00,122.86,157.303,58.982,32.166 +2020-03-27 18:30:00,122.89,155.875,58.982,32.166 +2020-03-27 18:45:00,124.43,160.083,58.982,32.166 +2020-03-27 19:00:00,125.7,159.037,57.293,32.166 +2020-03-27 19:15:00,121.2,158.434,57.293,32.166 +2020-03-27 19:30:00,121.4,157.769,57.293,32.166 +2020-03-27 19:45:00,119.95,155.972,57.293,32.166 +2020-03-27 20:00:00,116.48,149.879,59.433,32.166 +2020-03-27 20:15:00,114.79,146.07,59.433,32.166 +2020-03-27 20:30:00,113.96,144.265,59.433,32.166 +2020-03-27 20:45:00,113.11,142.858,59.433,32.166 +2020-03-27 21:00:00,108.02,138.48,52.153999999999996,32.166 +2020-03-27 21:15:00,105.58,136.90200000000002,52.153999999999996,32.166 +2020-03-27 21:30:00,99.98,135.85399999999998,52.153999999999996,32.166 +2020-03-27 21:45:00,100.41,135.86,52.153999999999996,32.166 +2020-03-27 22:00:00,93.64,129.616,47.125,32.166 +2020-03-27 22:15:00,92.04,126.265,47.125,32.166 +2020-03-27 22:30:00,91.62,118.181,47.125,32.166 +2020-03-27 22:45:00,93.54,114.007,47.125,32.166 +2020-03-27 23:00:00,88.63,106.86399999999999,41.236000000000004,32.166 +2020-03-27 23:15:00,85.58,103.60600000000001,41.236000000000004,32.166 +2020-03-27 23:30:00,83.35,102.531,41.236000000000004,32.166 +2020-03-27 23:45:00,81.51,102.666,41.236000000000004,32.166 +2020-03-28 00:00:00,57.71,89.321,36.484,31.988000000000003 +2020-03-28 00:15:00,54.17,85.42,36.484,31.988000000000003 +2020-03-28 00:30:00,56.34,84.88799999999999,36.484,31.988000000000003 +2020-03-28 00:45:00,54.63,84.62299999999999,36.484,31.988000000000003 +2020-03-28 01:00:00,49.06,85.77,32.391999999999996,31.988000000000003 +2020-03-28 01:15:00,52.94,85.538,32.391999999999996,31.988000000000003 +2020-03-28 01:30:00,49.74,84.344,32.391999999999996,31.988000000000003 +2020-03-28 01:45:00,52.99,84.53,32.391999999999996,31.988000000000003 +2020-03-28 02:00:00,51.54,86.786,30.194000000000003,31.988000000000003 +2020-03-28 02:15:00,52.29,86.351,30.194000000000003,31.988000000000003 +2020-03-28 02:30:00,52.34,87.463,30.194000000000003,31.988000000000003 +2020-03-28 02:45:00,50.12,88.823,30.194000000000003,31.988000000000003 +2020-03-28 03:00:00,50.63,91.3,29.677,31.988000000000003 +2020-03-28 03:15:00,47.78,92.103,29.677,31.988000000000003 +2020-03-28 03:30:00,48.43,91.905,29.677,31.988000000000003 +2020-03-28 03:45:00,51.9,93.94200000000001,29.677,31.988000000000003 +2020-03-28 04:00:00,49.36,103.32,29.616,31.988000000000003 +2020-03-28 04:15:00,52.2,113.07700000000001,29.616,31.988000000000003 +2020-03-28 04:30:00,53.04,112.63799999999999,29.616,31.988000000000003 +2020-03-28 04:45:00,51.37,112.87,29.616,31.988000000000003 +2020-03-28 05:00:00,55.2,130.638,29.625,31.988000000000003 +2020-03-28 05:15:00,53.94,145.415,29.625,31.988000000000003 +2020-03-28 05:30:00,57.62,139.55100000000002,29.625,31.988000000000003 +2020-03-28 05:45:00,59.67,136.032,29.625,31.988000000000003 +2020-03-28 06:00:00,60.42,153.085,30.551,31.988000000000003 +2020-03-28 06:15:00,63.03,172.97099999999998,30.551,31.988000000000003 +2020-03-28 06:30:00,60.51,167.139,30.551,31.988000000000003 +2020-03-28 06:45:00,61.22,162.032,30.551,31.988000000000003 +2020-03-28 07:00:00,66.83,160.97,34.865,31.988000000000003 +2020-03-28 07:15:00,68.4,162.357,34.865,31.988000000000003 +2020-03-28 07:30:00,67.37,163.225,34.865,31.988000000000003 +2020-03-28 07:45:00,72.4,163.143,34.865,31.988000000000003 +2020-03-28 08:00:00,72.28,164.06799999999998,41.456,31.988000000000003 +2020-03-28 08:15:00,77.29,164.692,41.456,31.988000000000003 +2020-03-28 08:30:00,80.4,161.583,41.456,31.988000000000003 +2020-03-28 08:45:00,80.83,159.421,41.456,31.988000000000003 +2020-03-28 09:00:00,86.02,155.105,43.001999999999995,31.988000000000003 +2020-03-28 09:15:00,86.55,153.95,43.001999999999995,31.988000000000003 +2020-03-28 09:30:00,87.98,154.50799999999998,43.001999999999995,31.988000000000003 +2020-03-28 09:45:00,89.07,152.734,43.001999999999995,31.988000000000003 +2020-03-28 10:00:00,88.02,149.053,42.047,31.988000000000003 +2020-03-28 10:15:00,91.44,148.35399999999998,42.047,31.988000000000003 +2020-03-28 10:30:00,89.49,146.31799999999998,42.047,31.988000000000003 +2020-03-28 10:45:00,95.01,146.55200000000002,42.047,31.988000000000003 +2020-03-28 11:00:00,94.88,142.039,39.894,31.988000000000003 +2020-03-28 11:15:00,97.49,140.834,39.894,31.988000000000003 +2020-03-28 11:30:00,97.87,141.798,39.894,31.988000000000003 +2020-03-28 11:45:00,97.43,141.989,39.894,31.988000000000003 +2020-03-28 12:00:00,92.61,137.923,38.122,31.988000000000003 +2020-03-28 12:15:00,94.95,137.143,38.122,31.988000000000003 +2020-03-28 12:30:00,90.34,136.732,38.122,31.988000000000003 +2020-03-28 12:45:00,86.38,137.285,38.122,31.988000000000003 +2020-03-28 13:00:00,82.75,137.92700000000002,34.645,31.988000000000003 +2020-03-28 13:15:00,84.34,135.013,34.645,31.988000000000003 +2020-03-28 13:30:00,84.37,133.059,34.645,31.988000000000003 +2020-03-28 13:45:00,84.04,132.303,34.645,31.988000000000003 +2020-03-28 14:00:00,79.67,134.838,33.739000000000004,31.988000000000003 +2020-03-28 14:15:00,81.26,132.49200000000002,33.739000000000004,31.988000000000003 +2020-03-28 14:30:00,79.9,131.214,33.739000000000004,31.988000000000003 +2020-03-28 14:45:00,79.54,132.628,33.739000000000004,31.988000000000003 +2020-03-28 15:00:00,78.41,133.822,35.908,31.988000000000003 +2020-03-28 15:15:00,80.8,132.606,35.908,31.988000000000003 +2020-03-28 15:30:00,80.27,131.143,35.908,31.988000000000003 +2020-03-28 15:45:00,77.77,130.329,35.908,31.988000000000003 +2020-03-28 16:00:00,80.98,133.009,39.249,31.988000000000003 +2020-03-28 16:15:00,80.76,135.78799999999998,39.249,31.988000000000003 +2020-03-28 16:30:00,81.37,136.305,39.249,31.988000000000003 +2020-03-28 16:45:00,79.09,135.488,39.249,31.988000000000003 +2020-03-28 17:00:00,83.53,137.756,46.045,31.988000000000003 +2020-03-28 17:15:00,82.0,140.559,46.045,31.988000000000003 +2020-03-28 17:30:00,86.27,142.915,46.045,31.988000000000003 +2020-03-28 17:45:00,87.5,143.157,46.045,31.988000000000003 +2020-03-28 18:00:00,89.89,147.595,48.238,31.988000000000003 +2020-03-28 18:15:00,89.13,150.316,48.238,31.988000000000003 +2020-03-28 18:30:00,88.57,150.346,48.238,31.988000000000003 +2020-03-28 18:45:00,85.87,150.866,48.238,31.988000000000003 +2020-03-28 19:00:00,91.17,150.105,46.785,31.988000000000003 +2020-03-28 19:15:00,86.77,148.829,46.785,31.988000000000003 +2020-03-28 19:30:00,89.79,149.017,46.785,31.988000000000003 +2020-03-28 19:45:00,91.35,147.619,46.785,31.988000000000003 +2020-03-28 20:00:00,86.33,143.537,39.830999999999996,31.988000000000003 +2020-03-28 20:15:00,86.08,141.27,39.830999999999996,31.988000000000003 +2020-03-28 20:30:00,84.34,138.96200000000002,39.830999999999996,31.988000000000003 +2020-03-28 20:45:00,82.32,137.77700000000002,39.830999999999996,31.988000000000003 +2020-03-28 21:00:00,78.48,134.78799999999998,34.063,31.988000000000003 +2020-03-28 21:15:00,78.09,133.465,34.063,31.988000000000003 +2020-03-28 21:30:00,73.54,133.45600000000002,34.063,31.988000000000003 +2020-03-28 21:45:00,76.09,133.001,34.063,31.988000000000003 +2020-03-28 22:00:00,73.18,127.82700000000001,34.455999999999996,31.988000000000003 +2020-03-28 22:15:00,71.44,126.572,34.455999999999996,31.988000000000003 +2020-03-28 22:30:00,69.55,123.32799999999999,34.455999999999996,31.988000000000003 +2020-03-28 22:45:00,69.05,120.762,34.455999999999996,31.988000000000003 +2020-03-28 23:00:00,66.49,115.384,27.840999999999998,31.988000000000003 +2020-03-28 23:15:00,65.31,110.95700000000001,27.840999999999998,31.988000000000003 +2020-03-28 23:30:00,60.24,109.191,27.840999999999998,31.988000000000003 +2020-03-28 23:45:00,61.54,107.5,27.840999999999998,31.988000000000003 +2020-03-29 00:00:00,63.42,89.681,20.007,31.988000000000003 +2020-03-29 00:15:00,63.84,85.18,20.007,31.988000000000003 +2020-03-29 00:30:00,63.41,84.29700000000001,20.007,31.988000000000003 +2020-03-29 00:45:00,63.81,84.542,20.007,31.988000000000003 +2020-03-29 01:00:00,61.41,85.62700000000001,17.378,31.988000000000003 +2020-03-29 01:15:00,61.91,86.14200000000001,17.378,31.988000000000003 +2020-03-29 01:30:00,61.2,85.287,17.378,31.988000000000003 +2020-03-29 01:45:00,61.64,85.115,17.378,31.988000000000003 +2020-03-29 03:00:00,60.55,92.348,15.427999999999999,31.988000000000003 +2020-03-29 03:15:00,61.96,92.81200000000001,15.427999999999999,31.988000000000003 +2020-03-29 03:30:00,61.93,93.43799999999999,15.427999999999999,31.988000000000003 +2020-03-29 03:45:00,61.8,95.17,15.427999999999999,31.988000000000003 +2020-03-29 04:00:00,62.52,104.337,16.663,31.988000000000003 +2020-03-29 04:15:00,62.17,113.15899999999999,16.663,31.988000000000003 +2020-03-29 04:30:00,61.27,113.23,16.663,31.988000000000003 +2020-03-29 04:45:00,61.62,113.501,16.663,31.988000000000003 +2020-03-29 05:00:00,59.26,128.622,17.271,31.988000000000003 +2020-03-29 05:15:00,61.48,141.29399999999998,17.271,31.988000000000003 +2020-03-29 05:30:00,61.34,135.15,17.271,31.988000000000003 +2020-03-29 05:45:00,61.02,131.74200000000002,17.271,31.988000000000003 +2020-03-29 06:00:00,61.85,147.918,17.612000000000002,31.988000000000003 +2020-03-29 06:15:00,63.29,166.752,17.612000000000002,31.988000000000003 +2020-03-29 06:30:00,64.04,159.804,17.612000000000002,31.988000000000003 +2020-03-29 06:45:00,65.71,153.536,17.612000000000002,31.988000000000003 +2020-03-29 07:00:00,65.1,154.444,20.88,31.988000000000003 +2020-03-29 07:15:00,67.09,154.61700000000002,20.88,31.988000000000003 +2020-03-29 07:30:00,68.29,154.899,20.88,31.988000000000003 +2020-03-29 07:45:00,66.11,154.187,20.88,31.988000000000003 +2020-03-29 08:00:00,70.13,156.718,25.861,31.988000000000003 +2020-03-29 08:15:00,69.2,157.636,25.861,31.988000000000003 +2020-03-29 08:30:00,68.25,156.053,25.861,31.988000000000003 +2020-03-29 08:45:00,67.57,155.486,25.861,31.988000000000003 +2020-03-29 09:00:00,66.99,150.813,27.921999999999997,31.988000000000003 +2020-03-29 09:15:00,65.9,149.954,27.921999999999997,31.988000000000003 +2020-03-29 09:30:00,65.28,150.535,27.921999999999997,31.988000000000003 +2020-03-29 09:45:00,65.68,148.985,27.921999999999997,31.988000000000003 +2020-03-29 10:00:00,66.04,147.47799999999998,29.048000000000002,31.988000000000003 +2020-03-29 10:15:00,67.95,147.296,29.048000000000002,31.988000000000003 +2020-03-29 10:30:00,68.96,145.834,29.048000000000002,31.988000000000003 +2020-03-29 10:45:00,69.67,144.84799999999998,29.048000000000002,31.988000000000003 +2020-03-29 11:00:00,66.38,140.961,32.02,31.988000000000003 +2020-03-29 11:15:00,66.11,139.761,32.02,31.988000000000003 +2020-03-29 11:30:00,60.65,140.167,32.02,31.988000000000003 +2020-03-29 11:45:00,61.22,140.942,32.02,31.988000000000003 +2020-03-29 12:00:00,57.94,136.707,28.55,31.988000000000003 +2020-03-29 12:15:00,57.98,137.329,28.55,31.988000000000003 +2020-03-29 12:30:00,57.03,135.741,28.55,31.988000000000003 +2020-03-29 12:45:00,58.14,135.325,28.55,31.988000000000003 +2020-03-29 13:00:00,56.4,135.33,25.601999999999997,31.988000000000003 +2020-03-29 13:15:00,53.99,134.73,25.601999999999997,31.988000000000003 +2020-03-29 13:30:00,54.04,132.297,25.601999999999997,31.988000000000003 +2020-03-29 13:45:00,55.14,131.30200000000002,25.601999999999997,31.988000000000003 +2020-03-29 14:00:00,58.23,134.429,23.916999999999998,31.988000000000003 +2020-03-29 14:15:00,57.08,133.165,23.916999999999998,31.988000000000003 +2020-03-29 14:30:00,54.64,132.536,23.916999999999998,31.988000000000003 +2020-03-29 14:45:00,59.63,133.328,23.916999999999998,31.988000000000003 +2020-03-29 15:00:00,60.63,133.313,24.064,31.988000000000003 +2020-03-29 15:15:00,61.57,132.48,24.064,31.988000000000003 +2020-03-29 15:30:00,63.18,131.404,24.064,31.988000000000003 +2020-03-29 15:45:00,66.09,131.221,24.064,31.988000000000003 +2020-03-29 16:00:00,70.77,134.955,28.189,31.988000000000003 +2020-03-29 16:15:00,72.49,137.029,28.189,31.988000000000003 +2020-03-29 16:30:00,74.01,138.034,28.189,31.988000000000003 +2020-03-29 16:45:00,75.04,137.291,28.189,31.988000000000003 +2020-03-29 17:00:00,82.83,139.64700000000002,37.576,31.988000000000003 +2020-03-29 17:15:00,84.34,142.606,37.576,31.988000000000003 +2020-03-29 17:30:00,85.73,145.435,37.576,31.988000000000003 +2020-03-29 17:45:00,88.1,147.631,37.576,31.988000000000003 +2020-03-29 18:00:00,92.01,151.79,42.669,31.988000000000003 +2020-03-29 18:15:00,90.43,155.52200000000002,42.669,31.988000000000003 +2020-03-29 18:30:00,93.35,153.791,42.669,31.988000000000003 +2020-03-29 18:45:00,100.38,155.852,42.669,31.988000000000003 +2020-03-29 19:00:00,104.31,155.41299999999998,43.538999999999994,31.988000000000003 +2020-03-29 19:15:00,103.88,154.311,43.538999999999994,31.988000000000003 +2020-03-29 19:30:00,94.49,154.321,43.538999999999994,31.988000000000003 +2020-03-29 19:45:00,95.0,153.996,43.538999999999994,31.988000000000003 +2020-03-29 20:00:00,89.36,149.881,37.330999999999996,31.988000000000003 +2020-03-29 20:15:00,94.08,148.375,37.330999999999996,31.988000000000003 +2020-03-29 20:30:00,95.79,147.328,37.330999999999996,31.988000000000003 +2020-03-29 20:45:00,98.77,144.69799999999998,37.330999999999996,31.988000000000003 +2020-03-29 21:00:00,92.39,139.58100000000002,33.856,31.988000000000003 +2020-03-29 21:15:00,90.85,137.675,33.856,31.988000000000003 +2020-03-29 21:30:00,88.72,137.703,33.856,31.988000000000003 +2020-03-29 21:45:00,86.64,137.485,33.856,31.988000000000003 +2020-03-29 22:00:00,86.5,131.963,34.711999999999996,31.988000000000003 +2020-03-29 22:15:00,89.79,129.631,34.711999999999996,31.988000000000003 +2020-03-29 22:30:00,88.27,123.661,34.711999999999996,31.988000000000003 +2020-03-29 22:45:00,85.25,120.038,34.711999999999996,31.988000000000003 +2020-03-29 23:00:00,76.97,112.32799999999999,29.698,31.988000000000003 +2020-03-29 23:15:00,78.47,109.78299999999999,29.698,31.988000000000003 +2020-03-29 23:30:00,76.39,108.507,29.698,31.988000000000003 +2020-03-29 23:45:00,76.76,107.598,29.698,31.988000000000003 +2020-03-30 00:00:00,74.91,93.007,29.983,32.166 +2020-03-30 00:15:00,74.62,91.073,29.983,32.166 +2020-03-30 00:30:00,74.93,90.152,29.983,32.166 +2020-03-30 00:45:00,74.48,89.861,29.983,32.166 +2020-03-30 01:00:00,72.07,91.03,29.122,32.166 +2020-03-30 01:15:00,73.81,91.124,29.122,32.166 +2020-03-30 01:30:00,74.52,90.40700000000001,29.122,32.166 +2020-03-30 01:45:00,73.97,90.3,29.122,32.166 +2020-03-30 02:00:00,73.49,92.089,28.676,32.166 +2020-03-30 02:15:00,75.66,91.99600000000001,28.676,32.166 +2020-03-30 02:30:00,77.39,94.175,28.676,32.166 +2020-03-30 02:45:00,76.44,95.27600000000001,28.676,32.166 +2020-03-30 03:00:00,75.13,99.355,26.552,32.166 +2020-03-30 03:15:00,76.32,101.29,26.552,32.166 +2020-03-30 03:30:00,77.54,101.928,26.552,32.166 +2020-03-30 03:45:00,79.94,103.101,26.552,32.166 +2020-03-30 04:00:00,82.14,116.584,27.44,32.166 +2020-03-30 04:15:00,84.58,129.511,27.44,32.166 +2020-03-30 04:30:00,89.34,131.10299999999998,27.44,32.166 +2020-03-30 04:45:00,93.84,131.608,27.44,32.166 +2020-03-30 05:00:00,101.97,160.982,36.825,32.166 +2020-03-30 05:15:00,106.72,192.364,36.825,32.166 +2020-03-30 05:30:00,107.82,185.893,36.825,32.166 +2020-03-30 05:45:00,108.72,177.19099999999997,36.825,32.166 +2020-03-30 06:00:00,116.23,176.537,56.589,32.166 +2020-03-30 06:15:00,116.59,181.03900000000002,56.589,32.166 +2020-03-30 06:30:00,119.93,181.482,56.589,32.166 +2020-03-30 06:45:00,121.38,183.65099999999998,56.589,32.166 +2020-03-30 07:00:00,124.55,186.778,67.49,32.166 +2020-03-30 07:15:00,124.25,188.61,67.49,32.166 +2020-03-30 07:30:00,123.4,187.977,67.49,32.166 +2020-03-30 07:45:00,123.32,185.34099999999998,67.49,32.166 +2020-03-30 08:00:00,122.13,183.62599999999998,60.028,32.166 +2020-03-30 08:15:00,121.81,182.493,60.028,32.166 +2020-03-30 08:30:00,123.07,177.347,60.028,32.166 +2020-03-30 08:45:00,122.24,174.237,60.028,32.166 +2020-03-30 09:00:00,119.81,168.592,55.018,32.166 +2020-03-30 09:15:00,120.83,164.426,55.018,32.166 +2020-03-30 09:30:00,120.02,163.955,55.018,32.166 +2020-03-30 09:45:00,120.56,161.996,55.018,32.166 +2020-03-30 10:00:00,117.61,159.881,51.183,32.166 +2020-03-30 10:15:00,118.77,159.44,51.183,32.166 +2020-03-30 10:30:00,118.87,157.166,51.183,32.166 +2020-03-30 10:45:00,120.03,156.362,51.183,32.166 +2020-03-30 11:00:00,116.72,150.494,50.065,32.166 +2020-03-30 11:15:00,118.45,150.871,50.065,32.166 +2020-03-30 11:30:00,116.15,152.602,50.065,32.166 +2020-03-30 11:45:00,116.99,153.19799999999998,50.065,32.166 +2020-03-30 12:00:00,115.58,150.036,48.141999999999996,32.166 +2020-03-30 12:15:00,118.73,150.696,48.141999999999996,32.166 +2020-03-30 12:30:00,119.08,148.989,48.141999999999996,32.166 +2020-03-30 12:45:00,116.73,149.827,48.141999999999996,32.166 +2020-03-30 13:00:00,116.77,150.662,47.887,32.166 +2020-03-30 13:15:00,116.21,148.665,47.887,32.166 +2020-03-30 13:30:00,116.04,145.832,47.887,32.166 +2020-03-30 13:45:00,119.23,145.102,47.887,32.166 +2020-03-30 14:00:00,119.84,147.571,48.571000000000005,32.166 +2020-03-30 14:15:00,118.19,145.906,48.571000000000005,32.166 +2020-03-30 14:30:00,115.09,144.745,48.571000000000005,32.166 +2020-03-30 14:45:00,114.72,146.056,48.571000000000005,32.166 +2020-03-30 15:00:00,113.97,147.463,49.937,32.166 +2020-03-30 15:15:00,115.44,145.256,49.937,32.166 +2020-03-30 15:30:00,117.05,143.638,49.937,32.166 +2020-03-30 15:45:00,117.4,142.941,49.937,32.166 +2020-03-30 16:00:00,119.4,147.00799999999998,52.963,32.166 +2020-03-30 16:15:00,117.12,148.44799999999998,52.963,32.166 +2020-03-30 16:30:00,120.03,148.47899999999998,52.963,32.166 +2020-03-30 16:45:00,120.1,146.713,52.963,32.166 +2020-03-30 17:00:00,122.72,148.575,61.163999999999994,32.166 +2020-03-30 17:15:00,121.92,150.855,61.163999999999994,32.166 +2020-03-30 17:30:00,123.43,153.141,61.163999999999994,32.166 +2020-03-30 17:45:00,123.83,154.01,61.163999999999994,32.166 +2020-03-30 18:00:00,125.34,158.202,63.788999999999994,32.166 +2020-03-30 18:15:00,122.68,159.688,63.788999999999994,32.166 +2020-03-30 18:30:00,124.68,158.264,63.788999999999994,32.166 +2020-03-30 18:45:00,125.28,161.806,63.788999999999994,32.166 +2020-03-30 19:00:00,122.28,159.947,63.913000000000004,32.166 +2020-03-30 19:15:00,118.3,158.28799999999998,63.913000000000004,32.166 +2020-03-30 19:30:00,114.81,158.621,63.913000000000004,32.166 +2020-03-30 19:45:00,112.8,157.49200000000002,63.913000000000004,32.166 +2020-03-30 20:00:00,106.59,151.07,65.44,32.166 +2020-03-30 20:15:00,106.52,147.994,65.44,32.166 +2020-03-30 20:30:00,106.51,145.638,65.44,32.166 +2020-03-30 20:45:00,105.14,144.384,65.44,32.166 +2020-03-30 21:00:00,100.49,139.514,59.117,32.166 +2020-03-30 21:15:00,99.35,136.80100000000002,59.117,32.166 +2020-03-30 21:30:00,95.33,136.287,59.117,32.166 +2020-03-30 21:45:00,94.73,135.624,59.117,32.166 +2020-03-30 22:00:00,91.38,127.18299999999999,52.301,32.166 +2020-03-30 22:15:00,91.17,124.395,52.301,32.166 +2020-03-30 22:30:00,89.5,109.465,52.301,32.166 +2020-03-30 22:45:00,88.98,101.96,52.301,32.166 +2020-03-30 23:00:00,66.74,94.854,44.373000000000005,32.166 +2020-03-30 23:15:00,64.17,93.986,44.373000000000005,32.166 +2020-03-30 23:30:00,64.34,94.916,44.373000000000005,32.166 +2020-03-30 23:45:00,63.43,95.994,44.373000000000005,32.166 +2020-03-31 00:00:00,62.68,91.345,44.647,32.166 +2020-03-31 00:15:00,60.68,90.807,44.647,32.166 +2020-03-31 00:30:00,59.93,89.366,44.647,32.166 +2020-03-31 00:45:00,61.17,88.571,44.647,32.166 +2020-03-31 01:00:00,59.63,89.383,41.433,32.166 +2020-03-31 01:15:00,60.51,89.15,41.433,32.166 +2020-03-31 01:30:00,59.6,88.506,41.433,32.166 +2020-03-31 01:45:00,65.34,88.48,41.433,32.166 +2020-03-31 02:00:00,67.35,90.09700000000001,39.909,32.166 +2020-03-31 02:15:00,70.17,90.27799999999999,39.909,32.166 +2020-03-31 02:30:00,68.14,91.891,39.909,32.166 +2020-03-31 02:45:00,65.63,93.11,39.909,32.166 +2020-03-31 03:00:00,72.05,96.104,39.14,32.166 +2020-03-31 03:15:00,71.12,97.663,39.14,32.166 +2020-03-31 03:30:00,68.78,98.656,39.14,32.166 +2020-03-31 03:45:00,71.02,99.632,39.14,32.166 +2020-03-31 04:00:00,72.64,112.556,40.015,32.166 +2020-03-31 04:15:00,77.31,125.20100000000001,40.015,32.166 +2020-03-31 04:30:00,78.77,126.529,40.015,32.166 +2020-03-31 04:45:00,82.3,128.109,40.015,32.166 +2020-03-31 05:00:00,94.54,161.782,44.93600000000001,32.166 +2020-03-31 05:15:00,98.42,193.196,44.93600000000001,32.166 +2020-03-31 05:30:00,103.0,185.597,44.93600000000001,32.166 +2020-03-31 05:45:00,102.84,176.67700000000002,44.93600000000001,32.166 +2020-03-31 06:00:00,112.12,175.46099999999998,57.271,32.166 +2020-03-31 06:15:00,114.11,181.27599999999998,57.271,32.166 +2020-03-31 06:30:00,120.03,181.074,57.271,32.166 +2020-03-31 06:45:00,120.49,182.643,57.271,32.166 +2020-03-31 07:00:00,123.04,185.68599999999998,68.352,32.166 +2020-03-31 07:15:00,121.38,187.287,68.352,32.166 +2020-03-31 07:30:00,118.56,186.19099999999997,68.352,32.166 +2020-03-31 07:45:00,119.78,183.394,68.352,32.166 +2020-03-31 08:00:00,118.34,181.725,60.717,32.166 +2020-03-31 08:15:00,116.42,179.653,60.717,32.166 +2020-03-31 08:30:00,119.31,174.368,60.717,32.166 +2020-03-31 08:45:00,115.45,170.799,60.717,32.166 +2020-03-31 09:00:00,110.19,164.595,54.603,32.166 +2020-03-31 09:15:00,107.11,161.68,54.603,32.166 +2020-03-31 09:30:00,108.81,161.972,54.603,32.166 +2020-03-31 09:45:00,111.86,160.24,54.603,32.166 +2020-03-31 10:00:00,107.21,157.274,52.308,32.166 +2020-03-31 10:15:00,106.49,155.975,52.308,32.166 +2020-03-31 10:30:00,102.82,153.881,52.308,32.166 +2020-03-31 10:45:00,103.11,153.60299999999998,52.308,32.166 +2020-03-31 11:00:00,103.17,148.94899999999998,51.838,32.166 +2020-03-31 11:15:00,105.15,149.18200000000002,51.838,32.166 +2020-03-31 11:30:00,107.81,149.653,51.838,32.166 +2020-03-31 11:45:00,107.37,150.726,51.838,32.166 +2020-03-31 12:00:00,103.49,146.401,50.375,32.166 +2020-03-31 12:15:00,105.66,146.811,50.375,32.166 +2020-03-31 12:30:00,104.92,145.886,50.375,32.166 +2020-03-31 12:45:00,104.34,146.658,50.375,32.166 +2020-03-31 13:00:00,95.18,147.097,50.735,32.166 +2020-03-31 13:15:00,98.54,145.253,50.735,32.166 +2020-03-31 13:30:00,96.68,143.38299999999998,50.735,32.166 +2020-03-31 13:45:00,93.25,142.56799999999998,50.735,32.166 +2020-03-31 14:00:00,100.54,145.45600000000002,50.946000000000005,32.166 +2020-03-31 14:15:00,105.54,143.846,50.946000000000005,32.166 +2020-03-31 14:30:00,101.06,143.255,50.946000000000005,32.166 +2020-03-31 14:45:00,100.21,144.303,50.946000000000005,32.166 +2020-03-31 15:00:00,103.43,145.321,53.18,32.166 +2020-03-31 15:15:00,106.7,143.6,53.18,32.166 +2020-03-31 15:30:00,103.14,142.063,53.18,32.166 +2020-03-31 15:45:00,104.16,141.099,53.18,32.166 +2020-03-31 16:00:00,108.84,145.308,54.928999999999995,32.166 +2020-03-31 16:15:00,109.82,147.137,54.928999999999995,32.166 +2020-03-31 16:30:00,114.81,147.60299999999998,54.928999999999995,32.166 +2020-03-31 16:45:00,110.58,146.209,54.928999999999995,32.166 +2020-03-31 17:00:00,116.98,148.611,60.913000000000004,32.166 +2020-03-31 17:15:00,117.4,151.02,60.913000000000004,32.166 +2020-03-31 17:30:00,117.54,153.74,60.913000000000004,32.166 +2020-03-31 17:45:00,112.39,154.411,60.913000000000004,32.166 +2020-03-31 18:00:00,119.14,158.256,62.214,32.166 +2020-03-31 18:15:00,118.51,159.803,62.214,32.166 +2020-03-31 18:30:00,117.89,158.049,62.214,32.166 +2020-03-31 18:45:00,116.94,162.19799999999998,62.214,32.166 +2020-03-31 19:00:00,116.89,160.05200000000002,62.38,32.166 +2020-03-31 19:15:00,117.46,158.23,62.38,32.166 +2020-03-31 19:30:00,116.74,157.986,62.38,32.166 +2020-03-31 19:45:00,114.19,157.02200000000002,62.38,32.166 +2020-03-31 20:00:00,107.41,150.808,65.018,32.166 +2020-03-31 20:15:00,106.35,146.821,65.018,32.166 +2020-03-31 20:30:00,107.79,145.326,65.018,32.166 +2020-03-31 20:45:00,101.84,143.74,65.018,32.166 +2020-03-31 21:00:00,91.56,138.55100000000002,56.416000000000004,32.166 +2020-03-31 21:15:00,99.16,136.15,56.416000000000004,32.166 +2020-03-31 21:30:00,94.28,135.096,56.416000000000004,32.166 +2020-03-31 21:45:00,95.13,134.695,56.416000000000004,32.166 +2020-03-31 22:00:00,83.39,127.675,52.846000000000004,32.166 +2020-03-31 22:15:00,86.65,124.60799999999999,52.846000000000004,32.166 +2020-03-31 22:30:00,87.84,109.78200000000001,52.846000000000004,32.166 +2020-03-31 22:45:00,84.34,102.49700000000001,52.846000000000004,32.166 +2020-03-31 23:00:00,75.86,95.185,44.435,32.166 +2020-03-31 23:15:00,71.86,94.06,44.435,32.166 +2020-03-31 23:30:00,74.23,94.70100000000001,44.435,32.166 +2020-03-31 23:45:00,72.45,95.499,44.435,32.166 +2020-04-01 00:00:00,69.69,79.456,39.061,30.736 +2020-04-01 00:15:00,75.94,79.935,39.061,30.736 +2020-04-01 00:30:00,78.31,78.125,39.061,30.736 +2020-04-01 00:45:00,79.1,76.366,39.061,30.736 +2020-04-01 01:00:00,70.26,77.627,35.795,30.736 +2020-04-01 01:15:00,71.06,76.926,35.795,30.736 +2020-04-01 01:30:00,68.96,75.87100000000001,35.795,30.736 +2020-04-01 01:45:00,67.64,75.392,35.795,30.736 +2020-04-01 02:00:00,67.11,77.184,33.316,30.736 +2020-04-01 02:15:00,71.63,76.73899999999999,33.316,30.736 +2020-04-01 02:30:00,68.87,79.04899999999999,33.316,30.736 +2020-04-01 02:45:00,69.72,79.654,33.316,30.736 +2020-04-01 03:00:00,70.41,83.104,32.803000000000004,30.736 +2020-04-01 03:15:00,70.75,85.094,32.803000000000004,30.736 +2020-04-01 03:30:00,72.02,84.946,32.803000000000004,30.736 +2020-04-01 03:45:00,74.47,85.49700000000001,32.803000000000004,30.736 +2020-04-01 04:00:00,78.97,98.23200000000001,34.235,30.736 +2020-04-01 04:15:00,81.76,111.244,34.235,30.736 +2020-04-01 04:30:00,84.98,111.566,34.235,30.736 +2020-04-01 04:45:00,89.2,113.62700000000001,34.235,30.736 +2020-04-01 05:00:00,96.35,149.659,38.65,30.736 +2020-04-01 05:15:00,99.7,183.483,38.65,30.736 +2020-04-01 05:30:00,102.53,173.278,38.65,30.736 +2020-04-01 05:45:00,104.9,162.628,38.65,30.736 +2020-04-01 06:00:00,111.67,163.61700000000002,54.951,30.736 +2020-04-01 06:15:00,109.26,169.40099999999998,54.951,30.736 +2020-04-01 06:30:00,111.94,167.69799999999998,54.951,30.736 +2020-04-01 06:45:00,115.16,168.525,54.951,30.736 +2020-04-01 07:00:00,117.0,171.296,67.328,30.736 +2020-04-01 07:15:00,115.4,172.46400000000003,67.328,30.736 +2020-04-01 07:30:00,113.66,171.299,67.328,30.736 +2020-04-01 07:45:00,114.86,168.247,67.328,30.736 +2020-04-01 08:00:00,114.85,167.385,60.23,30.736 +2020-04-01 08:15:00,112.81,165.22400000000002,60.23,30.736 +2020-04-01 08:30:00,115.93,160.542,60.23,30.736 +2020-04-01 08:45:00,116.76,157.317,60.23,30.736 +2020-04-01 09:00:00,116.8,151.364,56.845,30.736 +2020-04-01 09:15:00,118.8,148.974,56.845,30.736 +2020-04-01 09:30:00,116.67,150.576,56.845,30.736 +2020-04-01 09:45:00,112.16,149.53,56.845,30.736 +2020-04-01 10:00:00,107.33,145.305,53.832,30.736 +2020-04-01 10:15:00,106.59,144.53,53.832,30.736 +2020-04-01 10:30:00,110.06,142.187,53.832,30.736 +2020-04-01 10:45:00,112.49,141.908,53.832,30.736 +2020-04-01 11:00:00,111.37,136.292,53.225,30.736 +2020-04-01 11:15:00,112.05,136.708,53.225,30.736 +2020-04-01 11:30:00,108.51,137.773,53.225,30.736 +2020-04-01 11:45:00,105.82,139.032,53.225,30.736 +2020-04-01 12:00:00,99.92,134.388,50.676,30.736 +2020-04-01 12:15:00,105.63,134.911,50.676,30.736 +2020-04-01 12:30:00,106.93,134.461,50.676,30.736 +2020-04-01 12:45:00,103.6,135.236,50.676,30.736 +2020-04-01 13:00:00,103.07,135.707,50.646,30.736 +2020-04-01 13:15:00,102.9,134.39600000000002,50.646,30.736 +2020-04-01 13:30:00,102.03,132.364,50.646,30.736 +2020-04-01 13:45:00,103.27,131.08700000000002,50.646,30.736 +2020-04-01 14:00:00,105.31,132.503,50.786,30.736 +2020-04-01 14:15:00,102.66,131.495,50.786,30.736 +2020-04-01 14:30:00,103.31,131.606,50.786,30.736 +2020-04-01 14:45:00,96.63,132.4,50.786,30.736 +2020-04-01 15:00:00,93.87,132.792,51.535,30.736 +2020-04-01 15:15:00,93.7,131.388,51.535,30.736 +2020-04-01 15:30:00,99.59,130.69299999999998,51.535,30.736 +2020-04-01 15:45:00,101.6,130.118,51.535,30.736 +2020-04-01 16:00:00,98.99,131.167,53.157,30.736 +2020-04-01 16:15:00,101.34,132.373,53.157,30.736 +2020-04-01 16:30:00,101.19,132.377,53.157,30.736 +2020-04-01 16:45:00,103.38,131.024,53.157,30.736 +2020-04-01 17:00:00,106.1,130.7,57.793,30.736 +2020-04-01 17:15:00,106.86,133.321,57.793,30.736 +2020-04-01 17:30:00,107.36,135.382,57.793,30.736 +2020-04-01 17:45:00,109.06,136.537,57.793,30.736 +2020-04-01 18:00:00,110.87,139.363,59.872,30.736 +2020-04-01 18:15:00,109.26,140.57,59.872,30.736 +2020-04-01 18:30:00,110.29,139.069,59.872,30.736 +2020-04-01 18:45:00,111.93,144.532,59.872,30.736 +2020-04-01 19:00:00,113.65,143.02200000000002,60.17100000000001,30.736 +2020-04-01 19:15:00,109.64,141.8,60.17100000000001,30.736 +2020-04-01 19:30:00,110.52,141.476,60.17100000000001,30.736 +2020-04-01 19:45:00,105.99,141.282,60.17100000000001,30.736 +2020-04-01 20:00:00,102.05,136.425,65.015,30.736 +2020-04-01 20:15:00,107.29,133.08100000000002,65.015,30.736 +2020-04-01 20:30:00,106.45,132.442,65.015,30.736 +2020-04-01 20:45:00,102.76,131.428,65.015,30.736 +2020-04-01 21:00:00,92.13,125.118,57.805,30.736 +2020-04-01 21:15:00,93.99,123.633,57.805,30.736 +2020-04-01 21:30:00,88.08,123.904,57.805,30.736 +2020-04-01 21:45:00,86.39,122.70100000000001,57.805,30.736 +2020-04-01 22:00:00,81.08,116.471,52.115,30.736 +2020-04-01 22:15:00,86.83,113.48200000000001,52.115,30.736 +2020-04-01 22:30:00,86.9,100.95,52.115,30.736 +2020-04-01 22:45:00,86.76,93.775,52.115,30.736 +2020-04-01 23:00:00,76.32,84.93299999999999,42.871,30.736 +2020-04-01 23:15:00,73.01,84.014,42.871,30.736 +2020-04-01 23:30:00,73.23,83.148,42.871,30.736 +2020-04-01 23:45:00,75.85,83.984,42.871,30.736 +2020-04-02 00:00:00,71.82,79.054,39.203,30.736 +2020-04-02 00:15:00,78.53,79.546,39.203,30.736 +2020-04-02 00:30:00,80.03,77.726,39.203,30.736 +2020-04-02 00:45:00,79.26,75.968,39.203,30.736 +2020-04-02 01:00:00,71.4,77.212,37.118,30.736 +2020-04-02 01:15:00,75.84,76.488,37.118,30.736 +2020-04-02 01:30:00,76.57,75.41199999999999,37.118,30.736 +2020-04-02 01:45:00,78.33,74.938,37.118,30.736 +2020-04-02 02:00:00,73.97,76.719,35.647,30.736 +2020-04-02 02:15:00,75.9,76.257,35.647,30.736 +2020-04-02 02:30:00,78.09,78.58800000000001,35.647,30.736 +2020-04-02 02:45:00,78.5,79.19800000000001,35.647,30.736 +2020-04-02 03:00:00,76.84,82.665,34.585,30.736 +2020-04-02 03:15:00,79.44,84.63,34.585,30.736 +2020-04-02 03:30:00,81.12,84.477,34.585,30.736 +2020-04-02 03:45:00,81.18,85.04700000000001,34.585,30.736 +2020-04-02 04:00:00,79.89,97.76100000000001,36.184,30.736 +2020-04-02 04:15:00,81.67,110.743,36.184,30.736 +2020-04-02 04:30:00,83.77,111.07,36.184,30.736 +2020-04-02 04:45:00,87.27,113.12,36.184,30.736 +2020-04-02 05:00:00,96.26,149.07299999999998,41.019,30.736 +2020-04-02 05:15:00,98.79,182.831,41.019,30.736 +2020-04-02 05:30:00,101.22,172.627,41.019,30.736 +2020-04-02 05:45:00,102.52,162.015,41.019,30.736 +2020-04-02 06:00:00,111.5,163.025,53.963,30.736 +2020-04-02 06:15:00,109.85,168.795,53.963,30.736 +2020-04-02 06:30:00,113.43,167.063,53.963,30.736 +2020-04-02 06:45:00,114.03,167.875,53.963,30.736 +2020-04-02 07:00:00,119.23,170.65400000000002,66.512,30.736 +2020-04-02 07:15:00,120.65,171.798,66.512,30.736 +2020-04-02 07:30:00,116.77,170.592,66.512,30.736 +2020-04-02 07:45:00,112.67,167.525,66.512,30.736 +2020-04-02 08:00:00,115.24,166.643,58.86,30.736 +2020-04-02 08:15:00,117.46,164.503,58.86,30.736 +2020-04-02 08:30:00,115.91,159.782,58.86,30.736 +2020-04-02 08:45:00,115.21,156.585,58.86,30.736 +2020-04-02 09:00:00,115.87,150.637,52.156000000000006,30.736 +2020-04-02 09:15:00,114.66,148.252,52.156000000000006,30.736 +2020-04-02 09:30:00,119.53,149.874,52.156000000000006,30.736 +2020-04-02 09:45:00,123.85,148.858,52.156000000000006,30.736 +2020-04-02 10:00:00,118.77,144.639,49.034,30.736 +2020-04-02 10:15:00,126.97,143.914,49.034,30.736 +2020-04-02 10:30:00,125.46,141.596,49.034,30.736 +2020-04-02 10:45:00,123.88,141.338,49.034,30.736 +2020-04-02 11:00:00,122.49,135.713,46.53,30.736 +2020-04-02 11:15:00,116.42,136.153,46.53,30.736 +2020-04-02 11:30:00,110.72,137.222,46.53,30.736 +2020-04-02 11:45:00,120.11,138.501,46.53,30.736 +2020-04-02 12:00:00,118.87,133.88299999999998,43.318000000000005,30.736 +2020-04-02 12:15:00,120.48,134.416,43.318000000000005,30.736 +2020-04-02 12:30:00,117.06,133.922,43.318000000000005,30.736 +2020-04-02 12:45:00,111.35,134.69899999999998,43.318000000000005,30.736 +2020-04-02 13:00:00,111.13,135.213,41.608000000000004,30.736 +2020-04-02 13:15:00,108.37,133.891,41.608000000000004,30.736 +2020-04-02 13:30:00,110.04,131.856,41.608000000000004,30.736 +2020-04-02 13:45:00,110.04,130.58100000000002,41.608000000000004,30.736 +2020-04-02 14:00:00,105.79,132.067,41.786,30.736 +2020-04-02 14:15:00,97.68,131.036,41.786,30.736 +2020-04-02 14:30:00,108.28,131.105,41.786,30.736 +2020-04-02 14:45:00,118.85,131.90200000000002,41.786,30.736 +2020-04-02 15:00:00,124.11,132.321,44.181999999999995,30.736 +2020-04-02 15:15:00,123.02,130.893,44.181999999999995,30.736 +2020-04-02 15:30:00,118.52,130.14700000000002,44.181999999999995,30.736 +2020-04-02 15:45:00,111.31,129.553,44.181999999999995,30.736 +2020-04-02 16:00:00,113.66,130.637,45.956,30.736 +2020-04-02 16:15:00,119.09,131.816,45.956,30.736 +2020-04-02 16:30:00,118.53,131.822,45.956,30.736 +2020-04-02 16:45:00,121.92,130.407,45.956,30.736 +2020-04-02 17:00:00,124.5,130.131,50.702,30.736 +2020-04-02 17:15:00,120.31,132.734,50.702,30.736 +2020-04-02 17:30:00,120.7,134.798,50.702,30.736 +2020-04-02 17:45:00,121.11,135.939,50.702,30.736 +2020-04-02 18:00:00,123.23,138.77700000000002,53.595,30.736 +2020-04-02 18:15:00,120.26,140.024,53.595,30.736 +2020-04-02 18:30:00,115.9,138.511,53.595,30.736 +2020-04-02 18:45:00,114.56,143.986,53.595,30.736 +2020-04-02 19:00:00,121.11,142.455,54.207,30.736 +2020-04-02 19:15:00,119.24,141.243,54.207,30.736 +2020-04-02 19:30:00,117.39,140.938,54.207,30.736 +2020-04-02 19:45:00,107.85,140.776,54.207,30.736 +2020-04-02 20:00:00,108.15,135.892,56.948,30.736 +2020-04-02 20:15:00,111.42,132.55700000000002,56.948,30.736 +2020-04-02 20:30:00,106.44,131.955,56.948,30.736 +2020-04-02 20:45:00,101.23,130.964,56.948,30.736 +2020-04-02 21:00:00,100.35,124.652,52.157,30.736 +2020-04-02 21:15:00,101.03,123.176,52.157,30.736 +2020-04-02 21:30:00,107.53,123.44,52.157,30.736 +2020-04-02 21:45:00,103.46,122.266,52.157,30.736 +2020-04-02 22:00:00,96.94,116.041,47.483000000000004,30.736 +2020-04-02 22:15:00,99.13,113.08,47.483000000000004,30.736 +2020-04-02 22:30:00,96.8,100.515,47.483000000000004,30.736 +2020-04-02 22:45:00,92.58,93.336,47.483000000000004,30.736 +2020-04-02 23:00:00,83.94,84.47,41.978,30.736 +2020-04-02 23:15:00,83.81,83.583,41.978,30.736 +2020-04-02 23:30:00,88.75,82.71600000000001,41.978,30.736 +2020-04-02 23:45:00,87.76,83.568,41.978,30.736 +2020-04-03 00:00:00,76.87,76.975,39.301,30.736 +2020-04-03 00:15:00,73.29,77.727,39.301,30.736 +2020-04-03 00:30:00,70.69,75.961,39.301,30.736 +2020-04-03 00:45:00,72.11,74.514,39.301,30.736 +2020-04-03 01:00:00,75.99,75.328,37.976,30.736 +2020-04-03 01:15:00,77.44,74.82,37.976,30.736 +2020-04-03 01:30:00,73.59,73.983,37.976,30.736 +2020-04-03 01:45:00,79.01,73.439,37.976,30.736 +2020-04-03 02:00:00,73.89,75.78399999999999,37.041,30.736 +2020-04-03 02:15:00,73.49,75.205,37.041,30.736 +2020-04-03 02:30:00,79.15,78.352,37.041,30.736 +2020-04-03 02:45:00,75.85,78.607,37.041,30.736 +2020-04-03 03:00:00,74.11,81.914,37.575,30.736 +2020-04-03 03:15:00,79.18,83.74799999999999,37.575,30.736 +2020-04-03 03:30:00,76.15,83.445,37.575,30.736 +2020-04-03 03:45:00,85.07,84.735,37.575,30.736 +2020-04-03 04:00:00,91.35,97.646,39.058,30.736 +2020-04-03 04:15:00,92.78,109.551,39.058,30.736 +2020-04-03 04:30:00,92.59,110.568,39.058,30.736 +2020-04-03 04:45:00,93.75,111.53399999999999,39.058,30.736 +2020-04-03 05:00:00,102.17,146.35299999999998,43.256,30.736 +2020-04-03 05:15:00,102.67,181.516,43.256,30.736 +2020-04-03 05:30:00,104.0,172.08599999999998,43.256,30.736 +2020-04-03 05:45:00,106.32,161.215,43.256,30.736 +2020-04-03 06:00:00,114.66,162.671,56.093999999999994,30.736 +2020-04-03 06:15:00,113.85,167.59900000000002,56.093999999999994,30.736 +2020-04-03 06:30:00,117.45,165.268,56.093999999999994,30.736 +2020-04-03 06:45:00,116.33,167.011,56.093999999999994,30.736 +2020-04-03 07:00:00,117.74,169.637,66.92699999999999,30.736 +2020-04-03 07:15:00,116.74,171.90900000000002,66.92699999999999,30.736 +2020-04-03 07:30:00,116.53,169.487,66.92699999999999,30.736 +2020-04-03 07:45:00,115.69,165.718,66.92699999999999,30.736 +2020-04-03 08:00:00,114.23,164.50900000000001,60.332,30.736 +2020-04-03 08:15:00,113.28,162.47799999999998,60.332,30.736 +2020-04-03 08:30:00,113.5,158.321,60.332,30.736 +2020-04-03 08:45:00,111.4,154.023,60.332,30.736 +2020-04-03 09:00:00,109.66,147.19899999999998,56.085,30.736 +2020-04-03 09:15:00,109.0,146.171,56.085,30.736 +2020-04-03 09:30:00,107.02,147.174,56.085,30.736 +2020-04-03 09:45:00,107.16,146.306,56.085,30.736 +2020-04-03 10:00:00,105.85,141.186,52.91,30.736 +2020-04-03 10:15:00,107.46,140.898,52.91,30.736 +2020-04-03 10:30:00,105.47,138.81799999999998,52.91,30.736 +2020-04-03 10:45:00,107.79,138.22299999999998,52.91,30.736 +2020-04-03 11:00:00,102.99,132.667,52.278999999999996,30.736 +2020-04-03 11:15:00,101.82,131.98,52.278999999999996,30.736 +2020-04-03 11:30:00,101.04,134.144,52.278999999999996,30.736 +2020-04-03 11:45:00,100.88,135.03,52.278999999999996,30.736 +2020-04-03 12:00:00,96.79,131.445,49.023999999999994,30.736 +2020-04-03 12:15:00,100.16,130.16,49.023999999999994,30.736 +2020-04-03 12:30:00,94.47,129.792,49.023999999999994,30.736 +2020-04-03 12:45:00,100.82,130.594,49.023999999999994,30.736 +2020-04-03 13:00:00,98.58,132.121,46.82,30.736 +2020-04-03 13:15:00,96.92,131.47299999999998,46.82,30.736 +2020-04-03 13:30:00,95.45,129.8,46.82,30.736 +2020-04-03 13:45:00,97.9,128.616,46.82,30.736 +2020-04-03 14:00:00,100.06,128.971,45.756,30.736 +2020-04-03 14:15:00,94.08,127.977,45.756,30.736 +2020-04-03 14:30:00,89.77,129.05200000000002,45.756,30.736 +2020-04-03 14:45:00,89.15,129.773,45.756,30.736 +2020-04-03 15:00:00,99.64,129.828,47.56,30.736 +2020-04-03 15:15:00,97.56,127.941,47.56,30.736 +2020-04-03 15:30:00,100.39,125.76799999999999,47.56,30.736 +2020-04-03 15:45:00,96.0,125.601,47.56,30.736 +2020-04-03 16:00:00,102.98,125.535,49.581,30.736 +2020-04-03 16:15:00,107.36,127.12799999999999,49.581,30.736 +2020-04-03 16:30:00,107.41,127.117,49.581,30.736 +2020-04-03 16:45:00,105.69,125.18799999999999,49.581,30.736 +2020-04-03 17:00:00,111.53,125.978,53.918,30.736 +2020-04-03 17:15:00,114.78,128.192,53.918,30.736 +2020-04-03 17:30:00,110.72,130.119,53.918,30.736 +2020-04-03 17:45:00,114.51,130.999,53.918,30.736 +2020-04-03 18:00:00,121.12,134.387,54.266000000000005,30.736 +2020-04-03 18:15:00,115.78,134.931,54.266000000000005,30.736 +2020-04-03 18:30:00,115.51,133.621,54.266000000000005,30.736 +2020-04-03 18:45:00,117.31,139.316,54.266000000000005,30.736 +2020-04-03 19:00:00,117.9,138.845,54.092,30.736 +2020-04-03 19:15:00,113.83,138.86700000000002,54.092,30.736 +2020-04-03 19:30:00,113.99,138.334,54.092,30.736 +2020-04-03 19:45:00,112.93,137.374,54.092,30.736 +2020-04-03 20:00:00,109.53,132.425,59.038999999999994,30.736 +2020-04-03 20:15:00,98.4,129.518,59.038999999999994,30.736 +2020-04-03 20:30:00,94.66,128.66899999999998,59.038999999999994,30.736 +2020-04-03 20:45:00,99.46,127.654,59.038999999999994,30.736 +2020-04-03 21:00:00,89.12,122.366,53.346000000000004,30.736 +2020-04-03 21:15:00,94.16,122.066,53.346000000000004,30.736 +2020-04-03 21:30:00,91.73,122.272,53.346000000000004,30.736 +2020-04-03 21:45:00,92.13,121.59700000000001,53.346000000000004,30.736 +2020-04-03 22:00:00,84.0,115.95,47.938,30.736 +2020-04-03 22:15:00,85.47,112.79700000000001,47.938,30.736 +2020-04-03 22:30:00,85.97,106.915,47.938,30.736 +2020-04-03 22:45:00,86.43,102.51899999999999,47.938,30.736 +2020-04-03 23:00:00,79.39,94.219,40.266,30.736 +2020-04-03 23:15:00,73.78,91.25299999999999,40.266,30.736 +2020-04-03 23:30:00,75.79,88.464,40.266,30.736 +2020-04-03 23:45:00,76.89,88.825,40.266,30.736 +2020-04-04 00:00:00,74.66,75.469,39.184,30.618000000000002 +2020-04-04 00:15:00,73.9,73.183,39.184,30.618000000000002 +2020-04-04 00:30:00,65.93,71.98100000000001,39.184,30.618000000000002 +2020-04-04 00:45:00,68.89,70.57600000000001,39.184,30.618000000000002 +2020-04-04 01:00:00,71.51,71.94,34.692,30.618000000000002 +2020-04-04 01:15:00,76.86,71.125,34.692,30.618000000000002 +2020-04-04 01:30:00,71.53,69.49,34.692,30.618000000000002 +2020-04-04 01:45:00,68.36,69.517,34.692,30.618000000000002 +2020-04-04 02:00:00,66.58,71.745,32.919000000000004,30.618000000000002 +2020-04-04 02:15:00,62.82,70.472,32.919000000000004,30.618000000000002 +2020-04-04 02:30:00,63.39,72.505,32.919000000000004,30.618000000000002 +2020-04-04 02:45:00,64.96,73.27199999999999,32.919000000000004,30.618000000000002 +2020-04-04 03:00:00,65.71,76.22399999999999,32.024,30.618000000000002 +2020-04-04 03:15:00,63.91,76.88600000000001,32.024,30.618000000000002 +2020-04-04 03:30:00,63.74,75.78699999999999,32.024,30.618000000000002 +2020-04-04 03:45:00,64.35,78.009,32.024,30.618000000000002 +2020-04-04 04:00:00,65.42,87.32600000000001,31.958000000000002,30.618000000000002 +2020-04-04 04:15:00,68.5,97.13600000000001,31.958000000000002,30.618000000000002 +2020-04-04 04:30:00,69.35,95.876,31.958000000000002,30.618000000000002 +2020-04-04 04:45:00,69.44,96.686,31.958000000000002,30.618000000000002 +2020-04-04 05:00:00,72.92,117.115,32.75,30.618000000000002 +2020-04-04 05:15:00,68.32,134.414,32.75,30.618000000000002 +2020-04-04 05:30:00,70.28,126.084,32.75,30.618000000000002 +2020-04-04 05:45:00,69.35,121.125,32.75,30.618000000000002 +2020-04-04 06:00:00,75.32,140.066,34.461999999999996,30.618000000000002 +2020-04-04 06:15:00,76.84,159.474,34.461999999999996,30.618000000000002 +2020-04-04 06:30:00,77.92,152.194,34.461999999999996,30.618000000000002 +2020-04-04 06:45:00,79.19,146.56799999999998,34.461999999999996,30.618000000000002 +2020-04-04 07:00:00,82.21,145.606,37.736,30.618000000000002 +2020-04-04 07:15:00,81.98,146.351,37.736,30.618000000000002 +2020-04-04 07:30:00,81.71,146.433,37.736,30.618000000000002 +2020-04-04 07:45:00,82.74,145.686,37.736,30.618000000000002 +2020-04-04 08:00:00,81.59,147.055,42.34,30.618000000000002 +2020-04-04 08:15:00,81.68,147.328,42.34,30.618000000000002 +2020-04-04 08:30:00,81.19,144.188,42.34,30.618000000000002 +2020-04-04 08:45:00,81.27,142.493,42.34,30.618000000000002 +2020-04-04 09:00:00,78.35,138.297,43.571999999999996,30.618000000000002 +2020-04-04 09:15:00,76.95,138.02700000000002,43.571999999999996,30.618000000000002 +2020-04-04 09:30:00,77.5,139.909,43.571999999999996,30.618000000000002 +2020-04-04 09:45:00,76.01,138.953,43.571999999999996,30.618000000000002 +2020-04-04 10:00:00,74.59,134.138,40.514,30.618000000000002 +2020-04-04 10:15:00,75.0,134.186,40.514,30.618000000000002 +2020-04-04 10:30:00,75.58,132.084,40.514,30.618000000000002 +2020-04-04 10:45:00,74.33,132.252,40.514,30.618000000000002 +2020-04-04 11:00:00,72.45,126.696,36.388000000000005,30.618000000000002 +2020-04-04 11:15:00,71.39,125.946,36.388000000000005,30.618000000000002 +2020-04-04 11:30:00,68.52,127.45,36.388000000000005,30.618000000000002 +2020-04-04 11:45:00,68.28,127.98700000000001,36.388000000000005,30.618000000000002 +2020-04-04 12:00:00,64.47,123.79799999999999,35.217,30.618000000000002 +2020-04-04 12:15:00,63.5,123.37200000000001,35.217,30.618000000000002 +2020-04-04 12:30:00,60.6,123.133,35.217,30.618000000000002 +2020-04-04 12:45:00,61.67,123.70200000000001,35.217,30.618000000000002 +2020-04-04 13:00:00,59.52,124.51700000000001,32.001999999999995,30.618000000000002 +2020-04-04 13:15:00,58.68,122.109,32.001999999999995,30.618000000000002 +2020-04-04 13:30:00,59.1,120.18700000000001,32.001999999999995,30.618000000000002 +2020-04-04 13:45:00,60.24,118.756,32.001999999999995,30.618000000000002 +2020-04-04 14:00:00,58.61,120.09200000000001,31.304000000000002,30.618000000000002 +2020-04-04 14:15:00,58.6,118.131,31.304000000000002,30.618000000000002 +2020-04-04 14:30:00,59.62,117.635,31.304000000000002,30.618000000000002 +2020-04-04 14:45:00,60.05,118.73200000000001,31.304000000000002,30.618000000000002 +2020-04-04 15:00:00,61.39,119.492,34.731,30.618000000000002 +2020-04-04 15:15:00,61.93,118.48299999999999,34.731,30.618000000000002 +2020-04-04 15:30:00,62.76,117.48299999999999,34.731,30.618000000000002 +2020-04-04 15:45:00,65.09,116.90299999999999,34.731,30.618000000000002 +2020-04-04 16:00:00,68.45,117.296,38.769,30.618000000000002 +2020-04-04 16:15:00,69.5,119.079,38.769,30.618000000000002 +2020-04-04 16:30:00,75.03,119.14,38.769,30.618000000000002 +2020-04-04 16:45:00,75.19,117.76299999999999,38.769,30.618000000000002 +2020-04-04 17:00:00,79.59,117.855,44.928000000000004,30.618000000000002 +2020-04-04 17:15:00,80.67,120.13799999999999,44.928000000000004,30.618000000000002 +2020-04-04 17:30:00,82.2,121.93700000000001,44.928000000000004,30.618000000000002 +2020-04-04 17:45:00,83.92,122.765,44.928000000000004,30.618000000000002 +2020-04-04 18:00:00,85.73,126.61399999999999,47.786,30.618000000000002 +2020-04-04 18:15:00,83.59,129.254,47.786,30.618000000000002 +2020-04-04 18:30:00,83.84,129.518,47.786,30.618000000000002 +2020-04-04 18:45:00,85.08,131.211,47.786,30.618000000000002 +2020-04-04 19:00:00,87.47,130.589,47.463,30.618000000000002 +2020-04-04 19:15:00,90.07,129.77700000000002,47.463,30.618000000000002 +2020-04-04 19:30:00,88.24,130.158,47.463,30.618000000000002 +2020-04-04 19:45:00,84.51,130.01,47.463,30.618000000000002 +2020-04-04 20:00:00,81.76,126.954,43.735,30.618000000000002 +2020-04-04 20:15:00,80.12,125.14200000000001,43.735,30.618000000000002 +2020-04-04 20:30:00,76.99,123.64399999999999,43.735,30.618000000000002 +2020-04-04 20:45:00,76.56,123.34,43.735,30.618000000000002 +2020-04-04 21:00:00,70.25,118.79799999999999,40.346,30.618000000000002 +2020-04-04 21:15:00,69.31,118.615,40.346,30.618000000000002 +2020-04-04 21:30:00,66.75,119.72200000000001,40.346,30.618000000000002 +2020-04-04 21:45:00,66.4,118.525,40.346,30.618000000000002 +2020-04-04 22:00:00,62.9,113.759,39.323,30.618000000000002 +2020-04-04 22:15:00,62.01,112.402,39.323,30.618000000000002 +2020-04-04 22:30:00,59.88,110.391,39.323,30.618000000000002 +2020-04-04 22:45:00,58.88,107.417,39.323,30.618000000000002 +2020-04-04 23:00:00,54.7,100.368,33.716,30.618000000000002 +2020-04-04 23:15:00,54.75,96.571,33.716,30.618000000000002 +2020-04-04 23:30:00,52.99,93.90100000000001,33.716,30.618000000000002 +2020-04-04 23:45:00,53.7,92.859,33.716,30.618000000000002 +2020-04-05 00:00:00,49.93,76.092,28.703000000000003,30.618000000000002 +2020-04-05 00:15:00,50.91,72.97800000000001,28.703000000000003,30.618000000000002 +2020-04-05 00:30:00,49.61,71.447,28.703000000000003,30.618000000000002 +2020-04-05 00:45:00,49.78,70.42,28.703000000000003,30.618000000000002 +2020-04-05 01:00:00,48.79,71.824,26.171,30.618000000000002 +2020-04-05 01:15:00,49.86,71.562,26.171,30.618000000000002 +2020-04-05 01:30:00,49.35,70.15,26.171,30.618000000000002 +2020-04-05 01:45:00,49.74,69.775,26.171,30.618000000000002 +2020-04-05 02:00:00,49.25,71.568,25.326999999999998,30.618000000000002 +2020-04-05 02:15:00,49.88,70.153,25.326999999999998,30.618000000000002 +2020-04-05 02:30:00,49.37,72.839,25.326999999999998,30.618000000000002 +2020-04-05 02:45:00,49.41,73.73100000000001,25.326999999999998,30.618000000000002 +2020-04-05 03:00:00,48.8,77.26,24.311999999999998,30.618000000000002 +2020-04-05 03:15:00,49.81,77.726,24.311999999999998,30.618000000000002 +2020-04-05 03:30:00,50.35,77.057,24.311999999999998,30.618000000000002 +2020-04-05 03:45:00,51.13,78.79899999999999,24.311999999999998,30.618000000000002 +2020-04-05 04:00:00,51.43,87.915,25.33,30.618000000000002 +2020-04-05 04:15:00,52.55,96.831,25.33,30.618000000000002 +2020-04-05 04:30:00,54.44,96.38600000000001,25.33,30.618000000000002 +2020-04-05 04:45:00,58.04,97.081,25.33,30.618000000000002 +2020-04-05 05:00:00,57.28,115.42299999999999,25.309,30.618000000000002 +2020-04-05 05:15:00,58.24,130.764,25.309,30.618000000000002 +2020-04-05 05:30:00,55.96,122.105,25.309,30.618000000000002 +2020-04-05 05:45:00,57.4,117.171,25.309,30.618000000000002 +2020-04-05 06:00:00,58.75,134.681,25.945999999999998,30.618000000000002 +2020-04-05 06:15:00,58.9,153.49,25.945999999999998,30.618000000000002 +2020-04-05 06:30:00,59.93,145.111,25.945999999999998,30.618000000000002 +2020-04-05 06:45:00,63.35,138.262,25.945999999999998,30.618000000000002 +2020-04-05 07:00:00,64.84,138.942,27.87,30.618000000000002 +2020-04-05 07:15:00,67.39,138.22799999999998,27.87,30.618000000000002 +2020-04-05 07:30:00,67.1,138.191,27.87,30.618000000000002 +2020-04-05 07:45:00,64.74,136.928,27.87,30.618000000000002 +2020-04-05 08:00:00,65.25,139.78799999999998,32.114000000000004,30.618000000000002 +2020-04-05 08:15:00,65.1,140.642,32.114000000000004,30.618000000000002 +2020-04-05 08:30:00,64.21,139.002,32.114000000000004,30.618000000000002 +2020-04-05 08:45:00,63.33,138.611,32.114000000000004,30.618000000000002 +2020-04-05 09:00:00,64.31,134.07,34.222,30.618000000000002 +2020-04-05 09:15:00,61.83,133.924,34.222,30.618000000000002 +2020-04-05 09:30:00,60.69,135.941,34.222,30.618000000000002 +2020-04-05 09:45:00,60.76,135.444,34.222,30.618000000000002 +2020-04-05 10:00:00,61.17,132.583,34.544000000000004,30.618000000000002 +2020-04-05 10:15:00,63.73,133.114,34.544000000000004,30.618000000000002 +2020-04-05 10:30:00,64.51,131.56,34.544000000000004,30.618000000000002 +2020-04-05 10:45:00,63.78,130.931,34.544000000000004,30.618000000000002 +2020-04-05 11:00:00,60.97,125.81200000000001,36.368,30.618000000000002 +2020-04-05 11:15:00,58.9,124.97,36.368,30.618000000000002 +2020-04-05 11:30:00,56.67,126.111,36.368,30.618000000000002 +2020-04-05 11:45:00,56.1,127.214,36.368,30.618000000000002 +2020-04-05 12:00:00,52.5,123.1,32.433,30.618000000000002 +2020-04-05 12:15:00,54.28,123.76299999999999,32.433,30.618000000000002 +2020-04-05 12:30:00,51.11,122.53399999999999,32.433,30.618000000000002 +2020-04-05 12:45:00,51.46,122.119,32.433,30.618000000000002 +2020-04-05 13:00:00,50.91,122.304,28.971999999999998,30.618000000000002 +2020-04-05 13:15:00,50.33,121.807,28.971999999999998,30.618000000000002 +2020-04-05 13:30:00,49.91,119.228,28.971999999999998,30.618000000000002 +2020-04-05 13:45:00,50.1,117.82600000000001,28.971999999999998,30.618000000000002 +2020-04-05 14:00:00,50.18,119.947,25.531999999999996,30.618000000000002 +2020-04-05 14:15:00,50.22,119.027,25.531999999999996,30.618000000000002 +2020-04-05 14:30:00,50.84,118.82700000000001,25.531999999999996,30.618000000000002 +2020-04-05 14:45:00,51.63,119.15,25.531999999999996,30.618000000000002 +2020-04-05 15:00:00,52.57,118.89200000000001,25.766,30.618000000000002 +2020-04-05 15:15:00,53.17,118.04299999999999,25.766,30.618000000000002 +2020-04-05 15:30:00,54.25,117.337,25.766,30.618000000000002 +2020-04-05 15:45:00,57.67,117.369,25.766,30.618000000000002 +2020-04-05 16:00:00,62.85,118.23899999999999,29.232,30.618000000000002 +2020-04-05 16:15:00,63.15,119.47,29.232,30.618000000000002 +2020-04-05 16:30:00,67.45,120.189,29.232,30.618000000000002 +2020-04-05 16:45:00,71.68,118.87299999999999,29.232,30.618000000000002 +2020-04-05 17:00:00,75.8,119.089,37.431,30.618000000000002 +2020-04-05 17:15:00,78.13,121.87700000000001,37.431,30.618000000000002 +2020-04-05 17:30:00,80.23,124.274,37.431,30.618000000000002 +2020-04-05 17:45:00,80.84,126.838,37.431,30.618000000000002 +2020-04-05 18:00:00,83.42,130.58700000000002,41.251999999999995,30.618000000000002 +2020-04-05 18:15:00,82.93,133.947,41.251999999999995,30.618000000000002 +2020-04-05 18:30:00,85.4,132.678,41.251999999999995,30.618000000000002 +2020-04-05 18:45:00,86.36,135.68,41.251999999999995,30.618000000000002 +2020-04-05 19:00:00,89.34,135.89700000000002,41.784,30.618000000000002 +2020-04-05 19:15:00,96.35,134.91899999999998,41.784,30.618000000000002 +2020-04-05 19:30:00,97.94,135.082,41.784,30.618000000000002 +2020-04-05 19:45:00,90.74,135.688,41.784,30.618000000000002 +2020-04-05 20:00:00,83.74,132.64,40.804,30.618000000000002 +2020-04-05 20:15:00,84.54,131.41299999999998,40.804,30.618000000000002 +2020-04-05 20:30:00,88.61,131.183,40.804,30.618000000000002 +2020-04-05 20:45:00,93.47,129.24200000000002,40.804,30.618000000000002 +2020-04-05 21:00:00,90.23,122.90100000000001,38.379,30.618000000000002 +2020-04-05 21:15:00,85.48,122.161,38.379,30.618000000000002 +2020-04-05 21:30:00,81.64,123.109,38.379,30.618000000000002 +2020-04-05 21:45:00,78.03,122.22,38.379,30.618000000000002 +2020-04-05 22:00:00,72.89,117.758,37.87,30.618000000000002 +2020-04-05 22:15:00,76.43,115.053,37.87,30.618000000000002 +2020-04-05 22:30:00,78.18,110.678,37.87,30.618000000000002 +2020-04-05 22:45:00,78.53,106.478,37.87,30.618000000000002 +2020-04-05 23:00:00,72.6,97.42299999999999,33.332,30.618000000000002 +2020-04-05 23:15:00,69.86,95.53299999999999,33.332,30.618000000000002 +2020-04-05 23:30:00,67.11,93.111,33.332,30.618000000000002 +2020-04-05 23:45:00,71.83,92.75,33.332,30.618000000000002 +2020-04-06 00:00:00,72.06,79.122,34.698,30.736 +2020-04-06 00:15:00,73.34,78.301,34.698,30.736 +2020-04-06 00:30:00,69.38,76.625,34.698,30.736 +2020-04-06 00:45:00,66.41,75.048,34.698,30.736 +2020-04-06 01:00:00,65.32,76.639,32.889,30.736 +2020-04-06 01:15:00,71.76,76.038,32.889,30.736 +2020-04-06 01:30:00,72.41,74.84100000000001,32.889,30.736 +2020-04-06 01:45:00,73.18,74.486,32.889,30.736 +2020-04-06 02:00:00,68.6,76.492,32.06,30.736 +2020-04-06 02:15:00,70.89,75.381,32.06,30.736 +2020-04-06 02:30:00,74.64,78.384,32.06,30.736 +2020-04-06 02:45:00,75.36,78.82,32.06,30.736 +2020-04-06 03:00:00,73.31,83.4,30.515,30.736 +2020-04-06 03:15:00,75.92,85.24799999999999,30.515,30.736 +2020-04-06 03:30:00,80.31,84.79899999999999,30.515,30.736 +2020-04-06 03:45:00,82.56,85.961,30.515,30.736 +2020-04-06 04:00:00,85.18,99.426,31.436,30.736 +2020-04-06 04:15:00,84.27,112.485,31.436,30.736 +2020-04-06 04:30:00,87.01,113.088,31.436,30.736 +2020-04-06 04:45:00,88.33,114.081,31.436,30.736 +2020-04-06 05:00:00,97.35,145.80200000000002,38.997,30.736 +2020-04-06 05:15:00,102.88,179.06,38.997,30.736 +2020-04-06 05:30:00,104.6,169.676,38.997,30.736 +2020-04-06 05:45:00,103.3,159.631,38.997,30.736 +2020-04-06 06:00:00,111.48,160.849,54.97,30.736 +2020-04-06 06:15:00,112.43,165.453,54.97,30.736 +2020-04-06 06:30:00,112.7,164.183,54.97,30.736 +2020-04-06 06:45:00,114.94,165.671,54.97,30.736 +2020-04-06 07:00:00,116.47,168.513,66.032,30.736 +2020-04-06 07:15:00,114.52,169.805,66.032,30.736 +2020-04-06 07:30:00,112.82,168.767,66.032,30.736 +2020-04-06 07:45:00,111.27,166.037,66.032,30.736 +2020-04-06 08:00:00,109.22,165.03599999999997,59.941,30.736 +2020-04-06 08:15:00,108.67,163.886,59.941,30.736 +2020-04-06 08:30:00,107.94,159.04,59.941,30.736 +2020-04-06 08:45:00,106.68,156.61,59.941,30.736 +2020-04-06 09:00:00,105.31,151.07299999999998,54.016000000000005,30.736 +2020-04-06 09:15:00,105.6,147.696,54.016000000000005,30.736 +2020-04-06 09:30:00,104.71,148.596,54.016000000000005,30.736 +2020-04-06 09:45:00,103.31,147.168,54.016000000000005,30.736 +2020-04-06 10:00:00,101.9,144.064,50.63,30.736 +2020-04-06 10:15:00,104.54,144.34799999999998,50.63,30.736 +2020-04-06 10:30:00,103.43,141.99200000000002,50.63,30.736 +2020-04-06 10:45:00,105.54,141.142,50.63,30.736 +2020-04-06 11:00:00,100.25,134.44,49.951,30.736 +2020-04-06 11:15:00,103.21,135.025,49.951,30.736 +2020-04-06 11:30:00,98.48,137.47,49.951,30.736 +2020-04-06 11:45:00,100.55,138.54,49.951,30.736 +2020-04-06 12:00:00,103.01,135.093,46.913000000000004,30.736 +2020-04-06 12:15:00,108.32,135.816,46.913000000000004,30.736 +2020-04-06 12:30:00,107.08,134.238,46.913000000000004,30.736 +2020-04-06 12:45:00,101.4,134.923,46.913000000000004,30.736 +2020-04-06 13:00:00,102.39,136.09,47.093999999999994,30.736 +2020-04-06 13:15:00,101.73,134.179,47.093999999999994,30.736 +2020-04-06 13:30:00,102.31,131.29,47.093999999999994,30.736 +2020-04-06 13:45:00,109.82,130.314,47.093999999999994,30.736 +2020-04-06 14:00:00,111.7,131.673,46.678000000000004,30.736 +2020-04-06 14:15:00,107.6,130.526,46.678000000000004,30.736 +2020-04-06 14:30:00,101.94,129.817,46.678000000000004,30.736 +2020-04-06 14:45:00,103.94,131.03799999999998,46.678000000000004,30.736 +2020-04-06 15:00:00,103.09,131.95600000000002,47.715,30.736 +2020-04-06 15:15:00,104.75,129.77100000000002,47.715,30.736 +2020-04-06 15:30:00,107.02,128.752,47.715,30.736 +2020-04-06 15:45:00,106.7,128.225,47.715,30.736 +2020-04-06 16:00:00,111.87,129.474,49.81100000000001,30.736 +2020-04-06 16:15:00,109.46,130.173,49.81100000000001,30.736 +2020-04-06 16:30:00,112.43,129.903,49.81100000000001,30.736 +2020-04-06 16:45:00,110.12,127.697,49.81100000000001,30.736 +2020-04-06 17:00:00,116.7,127.12899999999999,55.591,30.736 +2020-04-06 17:15:00,115.27,129.43200000000002,55.591,30.736 +2020-04-06 17:30:00,116.49,131.267,55.591,30.736 +2020-04-06 17:45:00,114.97,132.599,55.591,30.736 +2020-04-06 18:00:00,118.43,136.04,56.523,30.736 +2020-04-06 18:15:00,116.2,137.036,56.523,30.736 +2020-04-06 18:30:00,115.3,135.815,56.523,30.736 +2020-04-06 18:45:00,112.64,140.894,56.523,30.736 +2020-04-06 19:00:00,111.96,139.827,56.044,30.736 +2020-04-06 19:15:00,117.29,138.756,56.044,30.736 +2020-04-06 19:30:00,115.88,139.08,56.044,30.736 +2020-04-06 19:45:00,110.57,138.85299999999998,56.044,30.736 +2020-04-06 20:00:00,102.39,133.55,61.715,30.736 +2020-04-06 20:15:00,99.43,131.42600000000002,61.715,30.736 +2020-04-06 20:30:00,100.45,130.275,61.715,30.736 +2020-04-06 20:45:00,98.33,129.54,61.715,30.736 +2020-04-06 21:00:00,101.37,123.23200000000001,56.24,30.736 +2020-04-06 21:15:00,100.59,121.96799999999999,56.24,30.736 +2020-04-06 21:30:00,94.39,122.585,56.24,30.736 +2020-04-06 21:45:00,88.76,121.28200000000001,56.24,30.736 +2020-04-06 22:00:00,84.27,113.863,50.437,30.736 +2020-04-06 22:15:00,87.71,111.35700000000001,50.437,30.736 +2020-04-06 22:30:00,87.15,98.445,50.437,30.736 +2020-04-06 22:45:00,85.4,91.066,50.437,30.736 +2020-04-06 23:00:00,73.0,82.488,42.756,30.736 +2020-04-06 23:15:00,73.24,81.514,42.756,30.736 +2020-04-06 23:30:00,69.53,80.881,42.756,30.736 +2020-04-06 23:45:00,72.17,81.986,42.756,30.736 +2020-04-07 00:00:00,69.6,77.01899999999999,39.857,30.736 +2020-04-07 00:15:00,70.65,77.579,39.857,30.736 +2020-04-07 00:30:00,69.86,75.704,39.857,30.736 +2020-04-07 00:45:00,70.53,73.96600000000001,39.857,30.736 +2020-04-07 01:00:00,69.2,75.119,37.233000000000004,30.736 +2020-04-07 01:15:00,73.23,74.282,37.233000000000004,30.736 +2020-04-07 01:30:00,72.34,73.097,37.233000000000004,30.736 +2020-04-07 01:45:00,73.13,72.648,37.233000000000004,30.736 +2020-04-07 02:00:00,72.15,74.37,35.856,30.736 +2020-04-07 02:15:00,72.53,73.82600000000001,35.856,30.736 +2020-04-07 02:30:00,68.0,76.265,35.856,30.736 +2020-04-07 02:45:00,74.06,76.898,35.856,30.736 +2020-04-07 03:00:00,72.61,80.45100000000001,34.766999999999996,30.736 +2020-04-07 03:15:00,72.94,82.287,34.766999999999996,30.736 +2020-04-07 03:30:00,74.44,82.10799999999999,34.766999999999996,30.736 +2020-04-07 03:45:00,76.22,82.777,34.766999999999996,30.736 +2020-04-07 04:00:00,80.49,95.38600000000001,35.468,30.736 +2020-04-07 04:15:00,82.48,108.21,35.468,30.736 +2020-04-07 04:30:00,85.73,108.561,35.468,30.736 +2020-04-07 04:45:00,90.58,110.554,35.468,30.736 +2020-04-07 05:00:00,100.18,146.112,40.399,30.736 +2020-04-07 05:15:00,102.56,179.535,40.399,30.736 +2020-04-07 05:30:00,104.29,169.34400000000002,40.399,30.736 +2020-04-07 05:45:00,106.96,158.921,40.399,30.736 +2020-04-07 06:00:00,110.4,160.033,54.105,30.736 +2020-04-07 06:15:00,110.63,165.732,54.105,30.736 +2020-04-07 06:30:00,112.93,163.855,54.105,30.736 +2020-04-07 06:45:00,116.12,164.59900000000002,54.105,30.736 +2020-04-07 07:00:00,116.23,167.41099999999997,63.083,30.736 +2020-04-07 07:15:00,115.06,168.44,63.083,30.736 +2020-04-07 07:30:00,115.2,167.024,63.083,30.736 +2020-04-07 07:45:00,113.5,163.887,63.083,30.736 +2020-04-07 08:00:00,110.51,162.91,57.254,30.736 +2020-04-07 08:15:00,109.08,160.877,57.254,30.736 +2020-04-07 08:30:00,109.23,155.965,57.254,30.736 +2020-04-07 08:45:00,109.56,152.909,57.254,30.736 +2020-04-07 09:00:00,107.69,146.993,51.395,30.736 +2020-04-07 09:15:00,108.09,144.628,51.395,30.736 +2020-04-07 09:30:00,104.87,146.349,51.395,30.736 +2020-04-07 09:45:00,106.35,145.477,51.395,30.736 +2020-04-07 10:00:00,103.01,141.299,48.201,30.736 +2020-04-07 10:15:00,102.97,140.826,48.201,30.736 +2020-04-07 10:30:00,100.59,138.631,48.201,30.736 +2020-04-07 10:45:00,102.15,138.48,48.201,30.736 +2020-04-07 11:00:00,99.8,132.81,46.133,30.736 +2020-04-07 11:15:00,99.29,133.372,46.133,30.736 +2020-04-07 11:30:00,97.42,134.46,46.133,30.736 +2020-04-07 11:45:00,99.27,135.839,46.133,30.736 +2020-04-07 12:00:00,102.69,131.346,44.243,30.736 +2020-04-07 12:15:00,103.58,131.928,44.243,30.736 +2020-04-07 12:30:00,101.04,131.215,44.243,30.736 +2020-04-07 12:45:00,96.93,131.998,44.243,30.736 +2020-04-07 13:00:00,95.94,132.735,45.042,30.736 +2020-04-07 13:15:00,97.03,131.35399999999998,45.042,30.736 +2020-04-07 13:30:00,100.96,129.305,45.042,30.736 +2020-04-07 13:45:00,103.24,128.042,45.042,30.736 +2020-04-07 14:00:00,101.28,129.875,44.062,30.736 +2020-04-07 14:15:00,102.93,128.732,44.062,30.736 +2020-04-07 14:30:00,106.26,128.583,44.062,30.736 +2020-04-07 14:45:00,104.54,129.403,44.062,30.736 +2020-04-07 15:00:00,100.32,129.96200000000002,46.461999999999996,30.736 +2020-04-07 15:15:00,100.71,128.40200000000002,46.461999999999996,30.736 +2020-04-07 15:30:00,104.76,127.404,46.461999999999996,30.736 +2020-04-07 15:45:00,106.51,126.71600000000001,46.461999999999996,30.736 +2020-04-07 16:00:00,105.14,127.979,48.802,30.736 +2020-04-07 16:15:00,108.47,129.028,48.802,30.736 +2020-04-07 16:30:00,110.54,129.042,48.802,30.736 +2020-04-07 16:45:00,111.8,127.31,48.802,30.736 +2020-04-07 17:00:00,111.67,127.27799999999999,55.672,30.736 +2020-04-07 17:15:00,118.31,129.785,55.672,30.736 +2020-04-07 17:30:00,117.23,131.864,55.672,30.736 +2020-04-07 17:45:00,116.51,132.929,55.672,30.736 +2020-04-07 18:00:00,113.28,135.825,57.006,30.736 +2020-04-07 18:15:00,116.51,137.267,57.006,30.736 +2020-04-07 18:30:00,115.8,135.695,57.006,30.736 +2020-04-07 18:45:00,116.19,141.224,57.006,30.736 +2020-04-07 19:00:00,111.29,139.591,57.148,30.736 +2020-04-07 19:15:00,116.99,138.437,57.148,30.736 +2020-04-07 19:30:00,116.1,138.225,57.148,30.736 +2020-04-07 19:45:00,113.98,138.225,57.148,30.736 +2020-04-07 20:00:00,104.78,133.197,61.895,30.736 +2020-04-07 20:15:00,109.17,129.917,61.895,30.736 +2020-04-07 20:30:00,105.18,129.49200000000002,61.895,30.736 +2020-04-07 20:45:00,104.44,128.619,61.895,30.736 +2020-04-07 21:00:00,94.22,122.29899999999999,54.78,30.736 +2020-04-07 21:15:00,98.11,120.87200000000001,54.78,30.736 +2020-04-07 21:30:00,95.36,121.095,54.78,30.736 +2020-04-07 21:45:00,95.09,120.073,54.78,30.736 +2020-04-07 22:00:00,82.41,113.87100000000001,50.76,30.736 +2020-04-07 22:15:00,81.69,111.046,50.76,30.736 +2020-04-07 22:30:00,86.54,98.31200000000001,50.76,30.736 +2020-04-07 22:45:00,86.26,91.10600000000001,50.76,30.736 +2020-04-07 23:00:00,79.29,82.12700000000001,44.162,30.736 +2020-04-07 23:15:00,77.16,81.404,44.162,30.736 +2020-04-07 23:30:00,74.84,80.531,44.162,30.736 +2020-04-07 23:45:00,80.07,81.457,44.162,30.736 +2020-04-08 00:00:00,77.66,76.607,39.061,30.736 +2020-04-08 00:15:00,75.81,77.182,39.061,30.736 +2020-04-08 00:30:00,72.88,75.296,39.061,30.736 +2020-04-08 00:45:00,69.77,73.563,39.061,30.736 +2020-04-08 01:00:00,73.18,74.697,35.795,30.736 +2020-04-08 01:15:00,76.79,73.837,35.795,30.736 +2020-04-08 01:30:00,75.5,72.631,35.795,30.736 +2020-04-08 01:45:00,70.46,72.187,35.795,30.736 +2020-04-08 02:00:00,71.43,73.89699999999999,33.316,30.736 +2020-04-08 02:15:00,70.31,73.337,33.316,30.736 +2020-04-08 02:30:00,70.6,75.797,33.316,30.736 +2020-04-08 02:45:00,78.12,76.435,33.316,30.736 +2020-04-08 03:00:00,79.43,80.005,32.803000000000004,30.736 +2020-04-08 03:15:00,79.12,81.814,32.803000000000004,30.736 +2020-04-08 03:30:00,75.54,81.631,32.803000000000004,30.736 +2020-04-08 03:45:00,77.28,82.321,32.803000000000004,30.736 +2020-04-08 04:00:00,80.55,94.90899999999999,34.235,30.736 +2020-04-08 04:15:00,81.61,107.698,34.235,30.736 +2020-04-08 04:30:00,85.38,108.055,34.235,30.736 +2020-04-08 04:45:00,90.05,110.037,34.235,30.736 +2020-04-08 05:00:00,97.97,145.516,38.65,30.736 +2020-04-08 05:15:00,99.52,178.87,38.65,30.736 +2020-04-08 05:30:00,102.97,168.683,38.65,30.736 +2020-04-08 05:45:00,107.04,158.297,38.65,30.736 +2020-04-08 06:00:00,111.56,159.431,54.951,30.736 +2020-04-08 06:15:00,113.89,165.113,54.951,30.736 +2020-04-08 06:30:00,116.9,163.208,54.951,30.736 +2020-04-08 06:45:00,118.83,163.938,54.951,30.736 +2020-04-08 07:00:00,121.7,166.757,67.328,30.736 +2020-04-08 07:15:00,121.52,167.764,67.328,30.736 +2020-04-08 07:30:00,120.53,166.30700000000002,67.328,30.736 +2020-04-08 07:45:00,119.16,163.157,67.328,30.736 +2020-04-08 08:00:00,115.39,162.161,60.23,30.736 +2020-04-08 08:15:00,117.51,160.15,60.23,30.736 +2020-04-08 08:30:00,118.44,155.19899999999998,60.23,30.736 +2020-04-08 08:45:00,119.61,152.173,60.23,30.736 +2020-04-08 09:00:00,120.78,146.264,56.845,30.736 +2020-04-08 09:15:00,122.55,143.903,56.845,30.736 +2020-04-08 09:30:00,122.95,145.643,56.845,30.736 +2020-04-08 09:45:00,122.4,144.80100000000002,56.845,30.736 +2020-04-08 10:00:00,122.17,140.63,53.832,30.736 +2020-04-08 10:15:00,121.08,140.207,53.832,30.736 +2020-04-08 10:30:00,124.05,138.03799999999998,53.832,30.736 +2020-04-08 10:45:00,121.53,137.908,53.832,30.736 +2020-04-08 11:00:00,113.64,132.23,53.225,30.736 +2020-04-08 11:15:00,107.84,132.815,53.225,30.736 +2020-04-08 11:30:00,106.89,133.906,53.225,30.736 +2020-04-08 11:45:00,109.26,135.306,53.225,30.736 +2020-04-08 12:00:00,115.23,130.839,50.676,30.736 +2020-04-08 12:15:00,114.32,131.429,50.676,30.736 +2020-04-08 12:30:00,105.49,130.672,50.676,30.736 +2020-04-08 12:45:00,103.19,131.458,50.676,30.736 +2020-04-08 13:00:00,101.47,132.237,50.646,30.736 +2020-04-08 13:15:00,107.86,130.846,50.646,30.736 +2020-04-08 13:30:00,106.14,128.796,50.646,30.736 +2020-04-08 13:45:00,114.59,127.53399999999999,50.646,30.736 +2020-04-08 14:00:00,122.83,129.437,50.786,30.736 +2020-04-08 14:15:00,123.75,128.27200000000002,50.786,30.736 +2020-04-08 14:30:00,119.06,128.078,50.786,30.736 +2020-04-08 14:45:00,114.36,128.901,50.786,30.736 +2020-04-08 15:00:00,108.95,129.488,51.535,30.736 +2020-04-08 15:15:00,110.42,127.902,51.535,30.736 +2020-04-08 15:30:00,107.51,126.854,51.535,30.736 +2020-04-08 15:45:00,110.73,126.148,51.535,30.736 +2020-04-08 16:00:00,116.16,127.445,53.157,30.736 +2020-04-08 16:15:00,113.87,128.469,53.157,30.736 +2020-04-08 16:30:00,113.4,128.485,53.157,30.736 +2020-04-08 16:45:00,118.97,126.69,53.157,30.736 +2020-04-08 17:00:00,120.55,126.706,57.793,30.736 +2020-04-08 17:15:00,115.72,129.194,57.793,30.736 +2020-04-08 17:30:00,117.9,131.276,57.793,30.736 +2020-04-08 17:45:00,119.34,132.32399999999998,57.793,30.736 +2020-04-08 18:00:00,118.83,135.232,59.872,30.736 +2020-04-08 18:15:00,114.99,136.711,59.872,30.736 +2020-04-08 18:30:00,117.03,135.128,59.872,30.736 +2020-04-08 18:45:00,117.74,140.667,59.872,30.736 +2020-04-08 19:00:00,117.3,139.016,60.17100000000001,30.736 +2020-04-08 19:15:00,117.96,137.872,60.17100000000001,30.736 +2020-04-08 19:30:00,117.33,137.67700000000002,60.17100000000001,30.736 +2020-04-08 19:45:00,114.3,137.71,60.17100000000001,30.736 +2020-04-08 20:00:00,102.99,132.656,65.015,30.736 +2020-04-08 20:15:00,103.02,129.386,65.015,30.736 +2020-04-08 20:30:00,107.09,128.996,65.015,30.736 +2020-04-08 20:45:00,106.0,128.14600000000002,65.015,30.736 +2020-04-08 21:00:00,97.85,121.82600000000001,57.805,30.736 +2020-04-08 21:15:00,90.97,120.40899999999999,57.805,30.736 +2020-04-08 21:30:00,89.79,120.62299999999999,57.805,30.736 +2020-04-08 21:45:00,86.79,119.63,57.805,30.736 +2020-04-08 22:00:00,81.91,113.434,52.115,30.736 +2020-04-08 22:15:00,83.05,110.63600000000001,52.115,30.736 +2020-04-08 22:30:00,86.93,97.867,52.115,30.736 +2020-04-08 22:45:00,87.46,90.656,52.115,30.736 +2020-04-08 23:00:00,83.24,81.655,42.871,30.736 +2020-04-08 23:15:00,77.77,80.96300000000001,42.871,30.736 +2020-04-08 23:30:00,78.21,80.09,42.871,30.736 +2020-04-08 23:45:00,81.71,81.031,42.871,30.736 +2020-04-09 00:00:00,75.02,76.195,39.203,30.736 +2020-04-09 00:15:00,76.58,76.783,39.203,30.736 +2020-04-09 00:30:00,70.26,74.887,39.203,30.736 +2020-04-09 00:45:00,71.93,73.15899999999999,39.203,30.736 +2020-04-09 01:00:00,73.86,74.275,37.118,30.736 +2020-04-09 01:15:00,78.67,73.393,37.118,30.736 +2020-04-09 01:30:00,78.31,72.165,37.118,30.736 +2020-04-09 01:45:00,73.67,71.726,37.118,30.736 +2020-04-09 02:00:00,71.77,73.42399999999999,35.647,30.736 +2020-04-09 02:15:00,74.57,72.848,35.647,30.736 +2020-04-09 02:30:00,78.65,75.328,35.647,30.736 +2020-04-09 02:45:00,79.41,75.971,35.647,30.736 +2020-04-09 03:00:00,73.94,79.559,34.585,30.736 +2020-04-09 03:15:00,75.69,81.342,34.585,30.736 +2020-04-09 03:30:00,76.28,81.153,34.585,30.736 +2020-04-09 03:45:00,77.96,81.862,34.585,30.736 +2020-04-09 04:00:00,84.48,94.429,36.184,30.736 +2020-04-09 04:15:00,90.28,107.18700000000001,36.184,30.736 +2020-04-09 04:30:00,90.56,107.54799999999999,36.184,30.736 +2020-04-09 04:45:00,91.01,109.51899999999999,36.184,30.736 +2020-04-09 05:00:00,97.64,144.918,41.019,30.736 +2020-04-09 05:15:00,101.38,178.203,41.019,30.736 +2020-04-09 05:30:00,106.13,168.021,41.019,30.736 +2020-04-09 05:45:00,108.39,157.672,41.019,30.736 +2020-04-09 06:00:00,111.79,158.826,53.963,30.736 +2020-04-09 06:15:00,114.39,164.495,53.963,30.736 +2020-04-09 06:30:00,116.98,162.559,53.963,30.736 +2020-04-09 06:45:00,121.2,163.276,53.963,30.736 +2020-04-09 07:00:00,121.1,166.09900000000002,66.512,30.736 +2020-04-09 07:15:00,120.6,167.085,66.512,30.736 +2020-04-09 07:30:00,119.91,165.58700000000002,66.512,30.736 +2020-04-09 07:45:00,118.39,162.42600000000002,66.512,30.736 +2020-04-09 08:00:00,117.51,161.411,58.86,30.736 +2020-04-09 08:15:00,117.76,159.423,58.86,30.736 +2020-04-09 08:30:00,121.48,154.435,58.86,30.736 +2020-04-09 08:45:00,121.83,151.436,58.86,30.736 +2020-04-09 09:00:00,120.69,145.533,52.156000000000006,30.736 +2020-04-09 09:15:00,115.59,143.17700000000002,52.156000000000006,30.736 +2020-04-09 09:30:00,110.08,144.937,52.156000000000006,30.736 +2020-04-09 09:45:00,113.84,144.123,52.156000000000006,30.736 +2020-04-09 10:00:00,105.34,139.96200000000002,49.034,30.736 +2020-04-09 10:15:00,105.65,139.588,49.034,30.736 +2020-04-09 10:30:00,104.77,137.445,49.034,30.736 +2020-04-09 10:45:00,105.43,137.336,49.034,30.736 +2020-04-09 11:00:00,100.95,131.65,46.53,30.736 +2020-04-09 11:15:00,102.14,132.259,46.53,30.736 +2020-04-09 11:30:00,101.09,133.35299999999998,46.53,30.736 +2020-04-09 11:45:00,101.76,134.774,46.53,30.736 +2020-04-09 12:00:00,100.18,130.33100000000002,43.318000000000005,30.736 +2020-04-09 12:15:00,105.05,130.931,43.318000000000005,30.736 +2020-04-09 12:30:00,101.1,130.13,43.318000000000005,30.736 +2020-04-09 12:45:00,98.59,130.916,43.318000000000005,30.736 +2020-04-09 13:00:00,98.99,131.74,41.608000000000004,30.736 +2020-04-09 13:15:00,100.83,130.338,41.608000000000004,30.736 +2020-04-09 13:30:00,100.23,128.286,41.608000000000004,30.736 +2020-04-09 13:45:00,97.6,127.027,41.608000000000004,30.736 +2020-04-09 14:00:00,107.53,128.997,41.786,30.736 +2020-04-09 14:15:00,114.19,127.811,41.786,30.736 +2020-04-09 14:30:00,114.7,127.574,41.786,30.736 +2020-04-09 14:45:00,112.0,128.401,41.786,30.736 +2020-04-09 15:00:00,111.51,129.015,44.181999999999995,30.736 +2020-04-09 15:15:00,112.57,127.40299999999999,44.181999999999995,30.736 +2020-04-09 15:30:00,107.02,126.306,44.181999999999995,30.736 +2020-04-09 15:45:00,110.02,125.58,44.181999999999995,30.736 +2020-04-09 16:00:00,117.62,126.913,45.956,30.736 +2020-04-09 16:15:00,115.32,127.911,45.956,30.736 +2020-04-09 16:30:00,113.91,127.928,45.956,30.736 +2020-04-09 16:45:00,115.9,126.07,45.956,30.736 +2020-04-09 17:00:00,120.99,126.135,50.702,30.736 +2020-04-09 17:15:00,120.94,128.60299999999998,50.702,30.736 +2020-04-09 17:30:00,122.61,130.686,50.702,30.736 +2020-04-09 17:45:00,117.98,131.719,50.702,30.736 +2020-04-09 18:00:00,116.45,134.638,53.595,30.736 +2020-04-09 18:15:00,119.67,136.156,53.595,30.736 +2020-04-09 18:30:00,119.28,134.56,53.595,30.736 +2020-04-09 18:45:00,118.54,140.11,53.595,30.736 +2020-04-09 19:00:00,113.55,138.439,54.207,30.736 +2020-04-09 19:15:00,111.16,137.30700000000002,54.207,30.736 +2020-04-09 19:30:00,116.65,137.13,54.207,30.736 +2020-04-09 19:45:00,115.97,137.195,54.207,30.736 +2020-04-09 20:00:00,108.98,132.112,56.948,30.736 +2020-04-09 20:15:00,107.57,128.85299999999998,56.948,30.736 +2020-04-09 20:30:00,101.4,128.499,56.948,30.736 +2020-04-09 20:45:00,106.29,127.67299999999999,56.948,30.736 +2020-04-09 21:00:00,102.08,121.351,52.157,30.736 +2020-04-09 21:15:00,96.68,119.946,52.157,30.736 +2020-04-09 21:30:00,90.04,120.15100000000001,52.157,30.736 +2020-04-09 21:45:00,89.87,119.18700000000001,52.157,30.736 +2020-04-09 22:00:00,90.47,112.99700000000001,47.483000000000004,30.736 +2020-04-09 22:15:00,88.93,110.225,47.483000000000004,30.736 +2020-04-09 22:30:00,83.44,97.421,47.483000000000004,30.736 +2020-04-09 22:45:00,80.92,90.204,47.483000000000004,30.736 +2020-04-09 23:00:00,64.51,81.181,41.978,30.736 +2020-04-09 23:15:00,64.07,80.52199999999999,41.978,30.736 +2020-04-09 23:30:00,61.31,79.64699999999999,41.978,30.736 +2020-04-09 23:45:00,61.8,80.60300000000001,41.978,30.736 +2020-04-10 00:00:00,59.64,74.03399999999999,30.72,30.618000000000002 +2020-04-10 00:15:00,60.38,70.993,30.72,30.618000000000002 +2020-04-10 00:30:00,56.32,69.40899999999999,30.72,30.618000000000002 +2020-04-10 00:45:00,59.16,68.405,30.72,30.618000000000002 +2020-04-10 01:00:00,57.38,69.718,26.553,30.618000000000002 +2020-04-10 01:15:00,57.59,69.342,26.553,30.618000000000002 +2020-04-10 01:30:00,57.08,67.82300000000001,26.553,30.618000000000002 +2020-04-10 01:45:00,58.03,67.472,26.553,30.618000000000002 +2020-04-10 02:00:00,53.88,69.205,22.712,30.618000000000002 +2020-04-10 02:15:00,57.6,67.71,22.712,30.618000000000002 +2020-04-10 02:30:00,54.83,70.5,22.712,30.618000000000002 +2020-04-10 02:45:00,57.8,71.417,22.712,30.618000000000002 +2020-04-10 03:00:00,58.43,75.032,20.511999999999997,30.618000000000002 +2020-04-10 03:15:00,56.11,75.366,20.511999999999997,30.618000000000002 +2020-04-10 03:30:00,59.21,74.673,20.511999999999997,30.618000000000002 +2020-04-10 03:45:00,59.68,76.515,20.511999999999997,30.618000000000002 +2020-04-10 04:00:00,61.34,85.525,19.98,30.618000000000002 +2020-04-10 04:15:00,58.56,94.279,19.98,30.618000000000002 +2020-04-10 04:30:00,61.08,93.85799999999999,19.98,30.618000000000002 +2020-04-10 04:45:00,61.82,94.49600000000001,19.98,30.618000000000002 +2020-04-10 05:00:00,61.78,112.44,22.715,30.618000000000002 +2020-04-10 05:15:00,63.19,127.439,22.715,30.618000000000002 +2020-04-10 05:30:00,63.03,118.79899999999999,22.715,30.618000000000002 +2020-04-10 05:45:00,62.57,114.056,22.715,30.618000000000002 +2020-04-10 06:00:00,63.5,131.666,22.576999999999998,30.618000000000002 +2020-04-10 06:15:00,64.36,150.401,22.576999999999998,30.618000000000002 +2020-04-10 06:30:00,66.27,141.877,22.576999999999998,30.618000000000002 +2020-04-10 06:45:00,66.56,134.958,22.576999999999998,30.618000000000002 +2020-04-10 07:00:00,72.0,135.668,23.541999999999998,30.618000000000002 +2020-04-10 07:15:00,72.94,134.844,23.541999999999998,30.618000000000002 +2020-04-10 07:30:00,72.56,134.6,23.541999999999998,30.618000000000002 +2020-04-10 07:45:00,72.68,133.276,23.541999999999998,30.618000000000002 +2020-04-10 08:00:00,73.27,136.041,23.895,30.618000000000002 +2020-04-10 08:15:00,73.33,137.007,23.895,30.618000000000002 +2020-04-10 08:30:00,72.0,135.178,23.895,30.618000000000002 +2020-04-10 08:45:00,71.59,134.93200000000002,23.895,30.618000000000002 +2020-04-10 09:00:00,63.96,130.424,24.239,30.618000000000002 +2020-04-10 09:15:00,70.6,130.297,24.239,30.618000000000002 +2020-04-10 09:30:00,70.79,132.411,24.239,30.618000000000002 +2020-04-10 09:45:00,73.79,132.059,24.239,30.618000000000002 +2020-04-10 10:00:00,71.11,129.239,21.985,30.618000000000002 +2020-04-10 10:15:00,71.88,130.02,21.985,30.618000000000002 +2020-04-10 10:30:00,72.52,128.593,21.985,30.618000000000002 +2020-04-10 10:45:00,68.48,128.07,21.985,30.618000000000002 +2020-04-10 11:00:00,65.66,122.911,22.093000000000004,30.618000000000002 +2020-04-10 11:15:00,62.4,122.189,22.093000000000004,30.618000000000002 +2020-04-10 11:30:00,60.95,123.34700000000001,22.093000000000004,30.618000000000002 +2020-04-10 11:45:00,61.95,124.551,22.093000000000004,30.618000000000002 +2020-04-10 12:00:00,55.24,120.56299999999999,19.041,30.618000000000002 +2020-04-10 12:15:00,54.22,121.271,19.041,30.618000000000002 +2020-04-10 12:30:00,54.57,119.821,19.041,30.618000000000002 +2020-04-10 12:45:00,58.91,119.413,19.041,30.618000000000002 +2020-04-10 13:00:00,55.07,119.819,12.672,30.618000000000002 +2020-04-10 13:15:00,56.02,119.266,12.672,30.618000000000002 +2020-04-10 13:30:00,52.03,116.676,12.672,30.618000000000002 +2020-04-10 13:45:00,51.25,115.288,12.672,30.618000000000002 +2020-04-10 14:00:00,51.01,117.75200000000001,10.321,30.618000000000002 +2020-04-10 14:15:00,54.91,116.723,10.321,30.618000000000002 +2020-04-10 14:30:00,54.25,116.304,10.321,30.618000000000002 +2020-04-10 14:45:00,50.76,116.64299999999999,10.321,30.618000000000002 +2020-04-10 15:00:00,52.9,116.525,13.478,30.618000000000002 +2020-04-10 15:15:00,55.88,115.546,13.478,30.618000000000002 +2020-04-10 15:30:00,52.88,114.59100000000001,13.478,30.618000000000002 +2020-04-10 15:45:00,57.97,114.529,13.478,30.618000000000002 +2020-04-10 16:00:00,62.6,115.57799999999999,17.623,30.618000000000002 +2020-04-10 16:15:00,64.61,116.676,17.623,30.618000000000002 +2020-04-10 16:30:00,67.2,117.40700000000001,17.623,30.618000000000002 +2020-04-10 16:45:00,69.59,115.771,17.623,30.618000000000002 +2020-04-10 17:00:00,76.4,116.23299999999999,22.64,30.618000000000002 +2020-04-10 17:15:00,75.58,118.92200000000001,22.64,30.618000000000002 +2020-04-10 17:30:00,77.36,121.331,22.64,30.618000000000002 +2020-04-10 17:45:00,75.89,123.815,22.64,30.618000000000002 +2020-04-10 18:00:00,81.02,127.62,29.147,30.618000000000002 +2020-04-10 18:15:00,79.22,131.172,29.147,30.618000000000002 +2020-04-10 18:30:00,80.59,129.843,29.147,30.618000000000002 +2020-04-10 18:45:00,80.5,132.89600000000002,29.147,30.618000000000002 +2020-04-10 19:00:00,84.47,133.018,34.491,30.618000000000002 +2020-04-10 19:15:00,83.92,132.095,34.491,30.618000000000002 +2020-04-10 19:30:00,81.85,132.35,34.491,30.618000000000002 +2020-04-10 19:45:00,81.31,133.11700000000002,34.491,30.618000000000002 +2020-04-10 20:00:00,76.54,129.928,41.368,30.618000000000002 +2020-04-10 20:15:00,76.86,128.756,41.368,30.618000000000002 +2020-04-10 20:30:00,73.31,128.704,41.368,30.618000000000002 +2020-04-10 20:45:00,75.27,126.87899999999999,41.368,30.618000000000002 +2020-04-10 21:00:00,70.24,120.53399999999999,37.605,30.618000000000002 +2020-04-10 21:15:00,71.86,119.846,37.605,30.618000000000002 +2020-04-10 21:30:00,65.09,120.75200000000001,37.605,30.618000000000002 +2020-04-10 21:45:00,68.77,120.009,37.605,30.618000000000002 +2020-04-10 22:00:00,65.32,115.573,36.472,30.618000000000002 +2020-04-10 22:15:00,64.47,113.001,36.472,30.618000000000002 +2020-04-10 22:30:00,62.29,108.45200000000001,36.472,30.618000000000002 +2020-04-10 22:45:00,61.86,104.22399999999999,36.472,30.618000000000002 +2020-04-10 23:00:00,78.85,95.059,31.816,30.618000000000002 +2020-04-10 23:15:00,77.86,93.334,31.816,30.618000000000002 +2020-04-10 23:30:00,74.26,90.906,31.816,30.618000000000002 +2020-04-10 23:45:00,72.92,90.618,31.816,30.618000000000002 +2020-04-11 00:00:00,73.14,72.59,39.184,30.618000000000002 +2020-04-11 00:15:00,74.22,70.405,39.184,30.618000000000002 +2020-04-11 00:30:00,72.87,69.128,39.184,30.618000000000002 +2020-04-11 00:45:00,67.49,67.756,39.184,30.618000000000002 +2020-04-11 01:00:00,70.97,68.992,34.692,30.618000000000002 +2020-04-11 01:15:00,73.16,68.01899999999999,34.692,30.618000000000002 +2020-04-11 01:30:00,71.68,66.232,34.692,30.618000000000002 +2020-04-11 01:45:00,67.54,66.294,34.692,30.618000000000002 +2020-04-11 02:00:00,71.64,68.439,32.919000000000004,30.618000000000002 +2020-04-11 02:15:00,73.62,67.054,32.919000000000004,30.618000000000002 +2020-04-11 02:30:00,69.77,69.232,32.919000000000004,30.618000000000002 +2020-04-11 02:45:00,69.74,70.032,32.919000000000004,30.618000000000002 +2020-04-11 03:00:00,65.86,73.10600000000001,32.024,30.618000000000002 +2020-04-11 03:15:00,64.59,73.584,32.024,30.618000000000002 +2020-04-11 03:30:00,65.11,72.45,32.024,30.618000000000002 +2020-04-11 03:45:00,64.5,74.812,32.024,30.618000000000002 +2020-04-11 04:00:00,65.68,83.98200000000001,31.958000000000002,30.618000000000002 +2020-04-11 04:15:00,66.71,93.564,31.958000000000002,30.618000000000002 +2020-04-11 04:30:00,65.87,92.339,31.958000000000002,30.618000000000002 +2020-04-11 04:45:00,66.99,93.068,31.958000000000002,30.618000000000002 +2020-04-11 05:00:00,67.85,112.939,32.75,30.618000000000002 +2020-04-11 05:15:00,67.86,129.761,32.75,30.618000000000002 +2020-04-11 05:30:00,69.08,121.459,32.75,30.618000000000002 +2020-04-11 05:45:00,71.14,116.765,32.75,30.618000000000002 +2020-04-11 06:00:00,72.48,135.846,34.461999999999996,30.618000000000002 +2020-04-11 06:15:00,75.1,155.151,34.461999999999996,30.618000000000002 +2020-04-11 06:30:00,77.55,147.668,34.461999999999996,30.618000000000002 +2020-04-11 06:45:00,80.71,141.945,34.461999999999996,30.618000000000002 +2020-04-11 07:00:00,82.56,141.024,37.736,30.618000000000002 +2020-04-11 07:15:00,83.76,141.615,37.736,30.618000000000002 +2020-04-11 07:30:00,85.57,141.409,37.736,30.618000000000002 +2020-04-11 07:45:00,88.05,140.57399999999998,37.736,30.618000000000002 +2020-04-11 08:00:00,89.53,141.81,42.34,30.618000000000002 +2020-04-11 08:15:00,90.05,142.239,42.34,30.618000000000002 +2020-04-11 08:30:00,88.35,138.835,42.34,30.618000000000002 +2020-04-11 08:45:00,88.99,137.342,42.34,30.618000000000002 +2020-04-11 09:00:00,80.42,133.192,43.571999999999996,30.618000000000002 +2020-04-11 09:15:00,83.26,132.951,43.571999999999996,30.618000000000002 +2020-04-11 09:30:00,81.39,134.967,43.571999999999996,30.618000000000002 +2020-04-11 09:45:00,76.24,134.215,43.571999999999996,30.618000000000002 +2020-04-11 10:00:00,74.6,129.458,40.514,30.618000000000002 +2020-04-11 10:15:00,75.54,129.858,40.514,30.618000000000002 +2020-04-11 10:30:00,76.68,127.932,40.514,30.618000000000002 +2020-04-11 10:45:00,76.58,128.249,40.514,30.618000000000002 +2020-04-11 11:00:00,73.64,122.635,36.388000000000005,30.618000000000002 +2020-04-11 11:15:00,73.67,122.054,36.388000000000005,30.618000000000002 +2020-04-11 11:30:00,76.9,123.58200000000001,36.388000000000005,30.618000000000002 +2020-04-11 11:45:00,74.29,124.258,36.388000000000005,30.618000000000002 +2020-04-11 12:00:00,69.92,120.24700000000001,35.217,30.618000000000002 +2020-04-11 12:15:00,66.27,119.885,35.217,30.618000000000002 +2020-04-11 12:30:00,67.34,119.337,35.217,30.618000000000002 +2020-04-11 12:45:00,67.73,119.915,35.217,30.618000000000002 +2020-04-11 13:00:00,63.46,121.039,32.001999999999995,30.618000000000002 +2020-04-11 13:15:00,65.05,118.553,32.001999999999995,30.618000000000002 +2020-04-11 13:30:00,61.84,116.615,32.001999999999995,30.618000000000002 +2020-04-11 13:45:00,62.81,115.204,32.001999999999995,30.618000000000002 +2020-04-11 14:00:00,62.8,117.021,31.304000000000002,30.618000000000002 +2020-04-11 14:15:00,63.27,114.906,31.304000000000002,30.618000000000002 +2020-04-11 14:30:00,63.28,114.103,31.304000000000002,30.618000000000002 +2020-04-11 14:45:00,63.54,115.226,31.304000000000002,30.618000000000002 +2020-04-11 15:00:00,63.73,116.178,34.731,30.618000000000002 +2020-04-11 15:15:00,64.31,114.98899999999999,34.731,30.618000000000002 +2020-04-11 15:30:00,66.0,113.639,34.731,30.618000000000002 +2020-04-11 15:45:00,68.73,112.928,34.731,30.618000000000002 +2020-04-11 16:00:00,71.32,113.571,38.769,30.618000000000002 +2020-04-11 16:15:00,71.76,115.17,38.769,30.618000000000002 +2020-04-11 16:30:00,74.69,115.245,38.769,30.618000000000002 +2020-04-11 16:45:00,79.72,113.42200000000001,38.769,30.618000000000002 +2020-04-11 17:00:00,82.56,113.86,44.928000000000004,30.618000000000002 +2020-04-11 17:15:00,80.55,116.00200000000001,44.928000000000004,30.618000000000002 +2020-04-11 17:30:00,82.25,117.81700000000001,44.928000000000004,30.618000000000002 +2020-04-11 17:45:00,84.04,118.53399999999999,44.928000000000004,30.618000000000002 +2020-04-11 18:00:00,86.25,122.461,47.786,30.618000000000002 +2020-04-11 18:15:00,84.53,125.369,47.786,30.618000000000002 +2020-04-11 18:30:00,84.88,125.551,47.786,30.618000000000002 +2020-04-11 18:45:00,85.38,127.315,47.786,30.618000000000002 +2020-04-11 19:00:00,86.2,126.56,47.463,30.618000000000002 +2020-04-11 19:15:00,85.71,125.825,47.463,30.618000000000002 +2020-04-11 19:30:00,86.11,126.334,47.463,30.618000000000002 +2020-04-11 19:45:00,84.4,126.412,47.463,30.618000000000002 +2020-04-11 20:00:00,81.75,123.16,43.735,30.618000000000002 +2020-04-11 20:15:00,80.33,121.42200000000001,43.735,30.618000000000002 +2020-04-11 20:30:00,77.95,120.17399999999999,43.735,30.618000000000002 +2020-04-11 20:45:00,76.55,120.03200000000001,43.735,30.618000000000002 +2020-04-11 21:00:00,71.3,115.48700000000001,40.346,30.618000000000002 +2020-04-11 21:15:00,70.85,115.375,40.346,30.618000000000002 +2020-04-11 21:30:00,68.16,116.42200000000001,40.346,30.618000000000002 +2020-04-11 21:45:00,67.66,115.43,40.346,30.618000000000002 +2020-04-11 22:00:00,63.94,110.7,39.323,30.618000000000002 +2020-04-11 22:15:00,64.14,109.53200000000001,39.323,30.618000000000002 +2020-04-11 22:30:00,61.85,107.27600000000001,39.323,30.618000000000002 +2020-04-11 22:45:00,61.25,104.26299999999999,39.323,30.618000000000002 +2020-04-11 23:00:00,60.08,97.06,33.716,30.618000000000002 +2020-04-11 23:15:00,58.06,93.493,33.716,30.618000000000002 +2020-04-11 23:30:00,55.51,90.814,33.716,30.618000000000002 +2020-04-11 23:45:00,56.45,89.876,33.716,30.618000000000002 +2020-04-12 00:00:00,54.53,73.203,30.72,30.618000000000002 +2020-04-12 00:15:00,52.88,70.192,30.72,30.618000000000002 +2020-04-12 00:30:00,52.07,68.59,30.72,30.618000000000002 +2020-04-12 00:45:00,53.98,67.596,30.72,30.618000000000002 +2020-04-12 01:00:00,48.89,68.872,26.553,30.618000000000002 +2020-04-12 01:15:00,53.69,68.45100000000001,26.553,30.618000000000002 +2020-04-12 01:30:00,51.28,66.889,26.553,30.618000000000002 +2020-04-12 01:45:00,53.95,66.54899999999999,26.553,30.618000000000002 +2020-04-12 02:00:00,50.72,68.258,22.712,30.618000000000002 +2020-04-12 02:15:00,54.24,66.73,22.712,30.618000000000002 +2020-04-12 02:30:00,51.64,69.561,22.712,30.618000000000002 +2020-04-12 02:45:00,51.01,70.486,22.712,30.618000000000002 +2020-04-12 03:00:00,50.48,74.13600000000001,20.511999999999997,30.618000000000002 +2020-04-12 03:15:00,51.91,74.418,20.511999999999997,30.618000000000002 +2020-04-12 03:30:00,51.98,73.71600000000001,20.511999999999997,30.618000000000002 +2020-04-12 03:45:00,52.83,75.598,20.511999999999997,30.618000000000002 +2020-04-12 04:00:00,51.89,84.565,19.98,30.618000000000002 +2020-04-12 04:15:00,52.74,93.25200000000001,19.98,30.618000000000002 +2020-04-12 04:30:00,52.54,92.84100000000001,19.98,30.618000000000002 +2020-04-12 04:45:00,54.38,93.456,19.98,30.618000000000002 +2020-04-12 05:00:00,55.46,111.241,22.715,30.618000000000002 +2020-04-12 05:15:00,53.48,126.101,22.715,30.618000000000002 +2020-04-12 05:30:00,55.9,117.47200000000001,22.715,30.618000000000002 +2020-04-12 05:45:00,53.71,112.805,22.715,30.618000000000002 +2020-04-12 06:00:00,55.53,130.452,22.576999999999998,30.618000000000002 +2020-04-12 06:15:00,59.67,149.158,22.576999999999998,30.618000000000002 +2020-04-12 06:30:00,62.15,140.576,22.576999999999998,30.618000000000002 +2020-04-12 06:45:00,61.32,133.628,22.576999999999998,30.618000000000002 +2020-04-12 07:00:00,67.05,134.34799999999998,23.541999999999998,30.618000000000002 +2020-04-12 07:15:00,67.97,133.483,23.541999999999998,30.618000000000002 +2020-04-12 07:30:00,69.68,133.157,23.541999999999998,30.618000000000002 +2020-04-12 07:45:00,69.61,131.813,23.541999999999998,30.618000000000002 +2020-04-12 08:00:00,74.12,134.54,27.568,30.618000000000002 +2020-04-12 08:15:00,75.03,135.55200000000002,27.568,30.618000000000002 +2020-04-12 08:30:00,74.98,133.65,27.568,30.618000000000002 +2020-04-12 08:45:00,73.91,133.46200000000002,27.568,30.618000000000002 +2020-04-12 09:00:00,76.48,128.969,27.965,30.618000000000002 +2020-04-12 09:15:00,73.74,128.851,27.965,30.618000000000002 +2020-04-12 09:30:00,75.72,130.999,27.965,30.618000000000002 +2020-04-12 09:45:00,74.12,130.707,27.965,30.618000000000002 +2020-04-12 10:00:00,72.15,127.902,25.365,30.618000000000002 +2020-04-12 10:15:00,77.67,128.786,25.365,30.618000000000002 +2020-04-12 10:30:00,77.66,127.40899999999999,25.365,30.618000000000002 +2020-04-12 10:45:00,82.07,126.928,25.365,30.618000000000002 +2020-04-12 11:00:00,72.99,121.75399999999999,25.489,30.618000000000002 +2020-04-12 11:15:00,75.01,121.08,25.489,30.618000000000002 +2020-04-12 11:30:00,71.52,122.244,25.489,30.618000000000002 +2020-04-12 11:45:00,69.58,123.48700000000001,25.489,30.618000000000002 +2020-04-12 12:00:00,68.21,119.55,21.968000000000004,30.618000000000002 +2020-04-12 12:15:00,67.52,120.275,21.968000000000004,30.618000000000002 +2020-04-12 12:30:00,66.81,118.738,21.968000000000004,30.618000000000002 +2020-04-12 12:45:00,67.38,118.333,21.968000000000004,30.618000000000002 +2020-04-12 13:00:00,60.59,118.82600000000001,14.62,30.618000000000002 +2020-04-12 13:15:00,59.39,118.251,14.62,30.618000000000002 +2020-04-12 13:30:00,58.18,115.65799999999999,14.62,30.618000000000002 +2020-04-12 13:45:00,60.55,114.27600000000001,14.62,30.618000000000002 +2020-04-12 14:00:00,59.16,116.87700000000001,11.908,30.618000000000002 +2020-04-12 14:15:00,60.01,115.804,11.908,30.618000000000002 +2020-04-12 14:30:00,56.92,115.295,11.908,30.618000000000002 +2020-04-12 14:45:00,58.07,115.64200000000001,11.908,30.618000000000002 +2020-04-12 15:00:00,61.44,115.57700000000001,15.55,30.618000000000002 +2020-04-12 15:15:00,62.16,114.54899999999999,15.55,30.618000000000002 +2020-04-12 15:30:00,62.09,113.494,15.55,30.618000000000002 +2020-04-12 15:45:00,63.69,113.395,15.55,30.618000000000002 +2020-04-12 16:00:00,63.44,114.516,20.332,30.618000000000002 +2020-04-12 16:15:00,65.92,115.56,20.332,30.618000000000002 +2020-04-12 16:30:00,69.07,116.295,20.332,30.618000000000002 +2020-04-12 16:45:00,69.42,114.53200000000001,20.332,30.618000000000002 +2020-04-12 17:00:00,77.2,115.094,26.121,30.618000000000002 +2020-04-12 17:15:00,75.08,117.742,26.121,30.618000000000002 +2020-04-12 17:30:00,76.8,120.152,26.121,30.618000000000002 +2020-04-12 17:45:00,76.23,122.604,26.121,30.618000000000002 +2020-04-12 18:00:00,79.58,126.429,33.626999999999995,30.618000000000002 +2020-04-12 18:15:00,77.87,130.05700000000002,33.626999999999995,30.618000000000002 +2020-04-12 18:30:00,78.53,128.704,33.626999999999995,30.618000000000002 +2020-04-12 18:45:00,80.41,131.775,33.626999999999995,30.618000000000002 +2020-04-12 19:00:00,79.37,131.862,39.793,30.618000000000002 +2020-04-12 19:15:00,79.52,130.96200000000002,39.793,30.618000000000002 +2020-04-12 19:30:00,78.47,131.251,39.793,30.618000000000002 +2020-04-12 19:45:00,77.25,132.084,39.793,30.618000000000002 +2020-04-12 20:00:00,74.47,128.839,41.368,30.618000000000002 +2020-04-12 20:15:00,73.54,127.68799999999999,41.368,30.618000000000002 +2020-04-12 20:30:00,73.03,127.709,41.368,30.618000000000002 +2020-04-12 20:45:00,72.54,125.928,41.368,30.618000000000002 +2020-04-12 21:00:00,69.45,119.583,37.605,30.618000000000002 +2020-04-12 21:15:00,69.2,118.919,37.605,30.618000000000002 +2020-04-12 21:30:00,64.59,119.805,37.605,30.618000000000002 +2020-04-12 21:45:00,67.2,119.119,37.605,30.618000000000002 +2020-04-12 22:00:00,62.87,114.694,36.472,30.618000000000002 +2020-04-12 22:15:00,62.97,112.175,36.472,30.618000000000002 +2020-04-12 22:30:00,58.12,107.554,36.472,30.618000000000002 +2020-04-12 22:45:00,60.08,103.31299999999999,36.472,30.618000000000002 +2020-04-12 23:00:00,57.22,94.10700000000001,31.816,30.618000000000002 +2020-04-12 23:15:00,57.26,92.449,31.816,30.618000000000002 +2020-04-12 23:30:00,53.07,90.01700000000001,31.816,30.618000000000002 +2020-04-12 23:45:00,55.28,89.759,31.816,30.618000000000002 +2020-04-13 00:00:00,49.89,72.78699999999999,30.72,30.618000000000002 +2020-04-13 00:15:00,53.6,69.791,30.72,30.618000000000002 +2020-04-13 00:30:00,50.52,68.178,30.72,30.618000000000002 +2020-04-13 00:45:00,53.11,67.191,30.72,30.618000000000002 +2020-04-13 01:00:00,47.65,68.449,26.553,30.618000000000002 +2020-04-13 01:15:00,51.91,68.006,26.553,30.618000000000002 +2020-04-13 01:30:00,48.7,66.422,26.553,30.618000000000002 +2020-04-13 01:45:00,51.4,66.087,26.553,30.618000000000002 +2020-04-13 02:00:00,49.72,67.783,22.712,30.618000000000002 +2020-04-13 02:15:00,48.31,66.24,22.712,30.618000000000002 +2020-04-13 02:30:00,50.79,69.09100000000001,22.712,30.618000000000002 +2020-04-13 02:45:00,48.57,70.021,22.712,30.618000000000002 +2020-04-13 03:00:00,51.31,73.689,20.511999999999997,30.618000000000002 +2020-04-13 03:15:00,52.15,73.943,20.511999999999997,30.618000000000002 +2020-04-13 03:30:00,51.38,73.236,20.511999999999997,30.618000000000002 +2020-04-13 03:45:00,54.02,75.139,20.511999999999997,30.618000000000002 +2020-04-13 04:00:00,54.39,84.085,19.98,30.618000000000002 +2020-04-13 04:15:00,55.93,92.73899999999999,19.98,30.618000000000002 +2020-04-13 04:30:00,56.7,92.33200000000001,19.98,30.618000000000002 +2020-04-13 04:45:00,57.44,92.93700000000001,19.98,30.618000000000002 +2020-04-13 05:00:00,58.82,110.641,22.715,30.618000000000002 +2020-04-13 05:15:00,59.57,125.43,22.715,30.618000000000002 +2020-04-13 05:30:00,57.7,116.80799999999999,22.715,30.618000000000002 +2020-04-13 05:45:00,55.37,112.177,22.715,30.618000000000002 +2020-04-13 06:00:00,58.84,129.845,22.576999999999998,30.618000000000002 +2020-04-13 06:15:00,57.13,148.534,22.576999999999998,30.618000000000002 +2020-04-13 06:30:00,60.7,139.924,22.576999999999998,30.618000000000002 +2020-04-13 06:45:00,62.85,132.963,22.576999999999998,30.618000000000002 +2020-04-13 07:00:00,68.7,133.687,23.541999999999998,30.618000000000002 +2020-04-13 07:15:00,68.91,132.80200000000002,23.541999999999998,30.618000000000002 +2020-04-13 07:30:00,66.28,132.436,23.541999999999998,30.618000000000002 +2020-04-13 07:45:00,71.72,131.08,23.541999999999998,30.618000000000002 +2020-04-13 08:00:00,70.13,133.79,23.895,30.618000000000002 +2020-04-13 08:15:00,72.83,134.825,23.895,30.618000000000002 +2020-04-13 08:30:00,69.75,132.886,23.895,30.618000000000002 +2020-04-13 08:45:00,71.31,132.72899999999998,23.895,30.618000000000002 +2020-04-13 09:00:00,67.16,128.24200000000002,24.239,30.618000000000002 +2020-04-13 09:15:00,66.12,128.127,24.239,30.618000000000002 +2020-04-13 09:30:00,64.1,130.295,24.239,30.618000000000002 +2020-04-13 09:45:00,66.42,130.032,24.239,30.618000000000002 +2020-04-13 10:00:00,67.4,127.23700000000001,21.985,30.618000000000002 +2020-04-13 10:15:00,66.6,128.17,21.985,30.618000000000002 +2020-04-13 10:30:00,70.58,126.81700000000001,21.985,30.618000000000002 +2020-04-13 10:45:00,69.07,126.359,21.985,30.618000000000002 +2020-04-13 11:00:00,63.2,121.177,22.093000000000004,30.618000000000002 +2020-04-13 11:15:00,62.5,120.52799999999999,22.093000000000004,30.618000000000002 +2020-04-13 11:30:00,63.4,121.693,22.093000000000004,30.618000000000002 +2020-04-13 11:45:00,71.72,122.95700000000001,22.093000000000004,30.618000000000002 +2020-04-13 12:00:00,63.81,119.045,19.041,30.618000000000002 +2020-04-13 12:15:00,54.06,119.77799999999999,19.041,30.618000000000002 +2020-04-13 12:30:00,54.23,118.197,19.041,30.618000000000002 +2020-04-13 12:45:00,56.47,117.792,19.041,30.618000000000002 +2020-04-13 13:00:00,56.38,118.329,12.672,30.618000000000002 +2020-04-13 13:15:00,51.32,117.744,12.672,30.618000000000002 +2020-04-13 13:30:00,50.09,115.15,12.672,30.618000000000002 +2020-04-13 13:45:00,52.76,113.772,12.672,30.618000000000002 +2020-04-13 14:00:00,50.0,116.441,10.321,30.618000000000002 +2020-04-13 14:15:00,47.98,115.345,10.321,30.618000000000002 +2020-04-13 14:30:00,46.88,114.791,10.321,30.618000000000002 +2020-04-13 14:45:00,51.58,115.14200000000001,10.321,30.618000000000002 +2020-04-13 15:00:00,52.85,115.103,13.478,30.618000000000002 +2020-04-13 15:15:00,53.15,114.051,13.478,30.618000000000002 +2020-04-13 15:30:00,51.27,112.945,13.478,30.618000000000002 +2020-04-13 15:45:00,52.98,112.829,13.478,30.618000000000002 +2020-04-13 16:00:00,57.21,113.985,17.623,30.618000000000002 +2020-04-13 16:15:00,61.89,115.00399999999999,17.623,30.618000000000002 +2020-04-13 16:30:00,63.48,115.741,17.623,30.618000000000002 +2020-04-13 16:45:00,65.1,113.913,17.623,30.618000000000002 +2020-04-13 17:00:00,72.1,114.525,22.64,30.618000000000002 +2020-04-13 17:15:00,73.56,117.152,22.64,30.618000000000002 +2020-04-13 17:30:00,76.1,119.56299999999999,22.64,30.618000000000002 +2020-04-13 17:45:00,77.85,121.99799999999999,22.64,30.618000000000002 +2020-04-13 18:00:00,78.34,125.833,29.147,30.618000000000002 +2020-04-13 18:15:00,78.86,129.499,29.147,30.618000000000002 +2020-04-13 18:30:00,81.38,128.134,29.147,30.618000000000002 +2020-04-13 18:45:00,82.16,131.215,29.147,30.618000000000002 +2020-04-13 19:00:00,86.48,131.284,34.491,30.618000000000002 +2020-04-13 19:15:00,87.39,130.394,34.491,30.618000000000002 +2020-04-13 19:30:00,85.34,130.702,34.491,30.618000000000002 +2020-04-13 19:45:00,84.46,131.567,34.491,30.618000000000002 +2020-04-13 20:00:00,81.57,128.29399999999998,41.368,30.618000000000002 +2020-04-13 20:15:00,80.87,127.15299999999999,41.368,30.618000000000002 +2020-04-13 20:30:00,77.82,127.21,41.368,30.618000000000002 +2020-04-13 20:45:00,80.82,125.45200000000001,41.368,30.618000000000002 +2020-04-13 21:00:00,80.42,119.10700000000001,37.605,30.618000000000002 +2020-04-13 21:15:00,79.52,118.454,37.605,30.618000000000002 +2020-04-13 21:30:00,74.22,119.331,37.605,30.618000000000002 +2020-04-13 21:45:00,76.26,118.67399999999999,37.605,30.618000000000002 +2020-04-13 22:00:00,68.73,114.255,36.472,30.618000000000002 +2020-04-13 22:15:00,70.32,111.76100000000001,36.472,30.618000000000002 +2020-04-13 22:30:00,68.58,107.104,36.472,30.618000000000002 +2020-04-13 22:45:00,70.36,102.85700000000001,36.472,30.618000000000002 +2020-04-13 23:00:00,81.66,93.63,31.816,30.618000000000002 +2020-04-13 23:15:00,81.85,92.005,31.816,30.618000000000002 +2020-04-13 23:30:00,75.45,89.572,31.816,30.618000000000002 +2020-04-13 23:45:00,76.28,89.32799999999999,31.816,30.618000000000002 +2020-04-14 00:00:00,77.57,74.116,39.857,30.736 +2020-04-14 00:15:00,78.7,74.781,39.857,30.736 +2020-04-14 00:30:00,77.84,72.835,39.857,30.736 +2020-04-14 00:45:00,74.5,71.13600000000001,39.857,30.736 +2020-04-14 01:00:00,78.14,72.16,37.233000000000004,30.736 +2020-04-14 01:15:00,79.78,71.166,37.233000000000004,30.736 +2020-04-14 01:30:00,79.36,69.831,37.233000000000004,30.736 +2020-04-14 01:45:00,76.71,69.417,37.233000000000004,30.736 +2020-04-14 02:00:00,79.63,71.053,35.856,30.736 +2020-04-14 02:15:00,80.53,70.399,35.856,30.736 +2020-04-14 02:30:00,77.64,72.979,35.856,30.736 +2020-04-14 02:45:00,77.36,73.646,35.856,30.736 +2020-04-14 03:00:00,80.42,77.32,34.766999999999996,30.736 +2020-04-14 03:15:00,77.51,78.97,34.766999999999996,30.736 +2020-04-14 03:30:00,78.75,78.759,34.766999999999996,30.736 +2020-04-14 03:45:00,85.63,79.569,34.766999999999996,30.736 +2020-04-14 04:00:00,90.63,92.03,35.468,30.736 +2020-04-14 04:15:00,89.42,104.62,35.468,30.736 +2020-04-14 04:30:00,89.05,105.006,35.468,30.736 +2020-04-14 04:45:00,91.92,106.92,35.468,30.736 +2020-04-14 05:00:00,100.81,141.918,40.399,30.736 +2020-04-14 05:15:00,102.84,174.856,40.399,30.736 +2020-04-14 05:30:00,105.47,164.701,40.399,30.736 +2020-04-14 05:45:00,107.4,154.543,40.399,30.736 +2020-04-14 06:00:00,112.59,155.791,54.105,30.736 +2020-04-14 06:15:00,113.37,161.384,54.105,30.736 +2020-04-14 06:30:00,116.15,159.305,54.105,30.736 +2020-04-14 06:45:00,117.75,159.95,54.105,30.736 +2020-04-14 07:00:00,122.9,162.8,63.083,30.736 +2020-04-14 07:15:00,119.48,163.681,63.083,30.736 +2020-04-14 07:30:00,122.96,161.981,63.083,30.736 +2020-04-14 07:45:00,119.45,158.767,63.083,30.736 +2020-04-14 08:00:00,120.93,157.66,57.254,30.736 +2020-04-14 08:15:00,123.65,155.789,57.254,30.736 +2020-04-14 08:30:00,125.42,150.616,57.254,30.736 +2020-04-14 08:45:00,126.12,147.766,57.254,30.736 +2020-04-14 09:00:00,121.6,141.899,51.395,30.736 +2020-04-14 09:15:00,123.45,139.561,51.395,30.736 +2020-04-14 09:30:00,123.95,141.412,51.395,30.736 +2020-04-14 09:45:00,124.96,140.746,51.395,30.736 +2020-04-14 10:00:00,122.16,136.626,48.201,30.736 +2020-04-14 10:15:00,123.54,136.503,48.201,30.736 +2020-04-14 10:30:00,121.52,134.487,48.201,30.736 +2020-04-14 10:45:00,120.31,134.483,48.201,30.736 +2020-04-14 11:00:00,116.44,128.762,46.133,30.736 +2020-04-14 11:15:00,111.12,129.491,46.133,30.736 +2020-04-14 11:30:00,108.43,130.6,46.133,30.736 +2020-04-14 11:45:00,109.42,132.118,46.133,30.736 +2020-04-14 12:00:00,104.93,127.803,44.243,30.736 +2020-04-14 12:15:00,109.73,128.44299999999998,44.243,30.736 +2020-04-14 12:30:00,108.63,127.42299999999999,44.243,30.736 +2020-04-14 12:45:00,105.87,128.214,44.243,30.736 +2020-04-14 13:00:00,99.12,129.259,45.042,30.736 +2020-04-14 13:15:00,98.79,127.802,45.042,30.736 +2020-04-14 13:30:00,104.88,125.742,45.042,30.736 +2020-04-14 13:45:00,105.06,124.499,45.042,30.736 +2020-04-14 14:00:00,104.5,126.81,44.062,30.736 +2020-04-14 14:15:00,101.57,125.516,44.062,30.736 +2020-04-14 14:30:00,96.19,125.055,44.062,30.736 +2020-04-14 14:45:00,96.11,125.898,44.062,30.736 +2020-04-14 15:00:00,107.14,126.646,46.461999999999996,30.736 +2020-04-14 15:15:00,106.37,124.911,46.461999999999996,30.736 +2020-04-14 15:30:00,104.16,123.564,46.461999999999996,30.736 +2020-04-14 15:45:00,104.55,122.74700000000001,46.461999999999996,30.736 +2020-04-14 16:00:00,107.58,124.26,48.802,30.736 +2020-04-14 16:15:00,113.1,125.12299999999999,48.802,30.736 +2020-04-14 16:30:00,111.12,125.152,48.802,30.736 +2020-04-14 16:45:00,110.6,122.97399999999999,48.802,30.736 +2020-04-14 17:00:00,112.54,123.29,55.672,30.736 +2020-04-14 17:15:00,115.47,125.654,55.672,30.736 +2020-04-14 17:30:00,119.06,127.742,55.672,30.736 +2020-04-14 17:45:00,115.7,128.691,55.672,30.736 +2020-04-14 18:00:00,112.52,131.662,57.006,30.736 +2020-04-14 18:15:00,117.42,133.36700000000002,57.006,30.736 +2020-04-14 18:30:00,119.58,131.71200000000002,57.006,30.736 +2020-04-14 18:45:00,117.04,137.30700000000002,57.006,30.736 +2020-04-14 19:00:00,110.56,135.55100000000002,57.148,30.736 +2020-04-14 19:15:00,107.17,134.471,57.148,30.736 +2020-04-14 19:30:00,115.88,134.385,57.148,30.736 +2020-04-14 19:45:00,113.92,134.611,57.148,30.736 +2020-04-14 20:00:00,110.45,129.387,61.895,30.736 +2020-04-14 20:15:00,109.71,126.18299999999999,61.895,30.736 +2020-04-14 20:30:00,100.85,126.009,61.895,30.736 +2020-04-14 20:45:00,108.91,125.295,61.895,30.736 +2020-04-14 21:00:00,103.46,118.976,54.78,30.736 +2020-04-14 21:15:00,102.44,117.626,54.78,30.736 +2020-04-14 21:30:00,93.5,117.785,54.78,30.736 +2020-04-14 21:45:00,87.39,116.962,54.78,30.736 +2020-04-14 22:00:00,87.83,110.8,50.76,30.736 +2020-04-14 22:15:00,90.13,108.15799999999999,50.76,30.736 +2020-04-14 22:30:00,87.92,95.17399999999999,50.76,30.736 +2020-04-14 22:45:00,84.46,87.926,50.76,30.736 +2020-04-14 23:00:00,74.36,78.79899999999999,44.162,30.736 +2020-04-14 23:15:00,76.0,78.307,44.162,30.736 +2020-04-14 23:30:00,74.94,77.425,44.162,30.736 +2020-04-14 23:45:00,73.87,78.453,44.162,30.736 +2020-04-15 00:00:00,70.64,73.699,39.061,30.736 +2020-04-15 00:15:00,72.45,74.37899999999999,39.061,30.736 +2020-04-15 00:30:00,71.26,72.425,39.061,30.736 +2020-04-15 00:45:00,73.44,70.73100000000001,39.061,30.736 +2020-04-15 01:00:00,71.82,71.737,35.795,30.736 +2020-04-15 01:15:00,71.14,70.721,35.795,30.736 +2020-04-15 01:30:00,71.08,69.36399999999999,35.795,30.736 +2020-04-15 01:45:00,72.01,68.956,35.795,30.736 +2020-04-15 02:00:00,70.74,70.58,33.316,30.736 +2020-04-15 02:15:00,70.1,69.90899999999999,33.316,30.736 +2020-04-15 02:30:00,72.27,72.509,33.316,30.736 +2020-04-15 02:45:00,78.4,73.181,33.316,30.736 +2020-04-15 03:00:00,80.61,76.872,32.803000000000004,30.736 +2020-04-15 03:15:00,83.75,78.49600000000001,32.803000000000004,30.736 +2020-04-15 03:30:00,82.05,78.28,32.803000000000004,30.736 +2020-04-15 03:45:00,86.07,79.111,32.803000000000004,30.736 +2020-04-15 04:00:00,88.31,91.54899999999999,34.235,30.736 +2020-04-15 04:15:00,89.49,104.10700000000001,34.235,30.736 +2020-04-15 04:30:00,87.96,104.49600000000001,34.235,30.736 +2020-04-15 04:45:00,93.45,106.40100000000001,34.235,30.736 +2020-04-15 05:00:00,98.18,141.31799999999998,38.65,30.736 +2020-04-15 05:15:00,101.94,174.18400000000003,38.65,30.736 +2020-04-15 05:30:00,105.86,164.03599999999997,38.65,30.736 +2020-04-15 05:45:00,109.69,153.917,38.65,30.736 +2020-04-15 06:00:00,114.65,155.183,54.951,30.736 +2020-04-15 06:15:00,114.16,160.76,54.951,30.736 +2020-04-15 06:30:00,116.39,158.65200000000002,54.951,30.736 +2020-04-15 06:45:00,116.53,159.284,54.951,30.736 +2020-04-15 07:00:00,117.39,162.137,67.328,30.736 +2020-04-15 07:15:00,117.4,162.999,67.328,30.736 +2020-04-15 07:30:00,115.55,161.259,67.328,30.736 +2020-04-15 07:45:00,112.28,158.037,67.328,30.736 +2020-04-15 08:00:00,111.52,156.912,60.23,30.736 +2020-04-15 08:15:00,111.85,155.064,60.23,30.736 +2020-04-15 08:30:00,112.04,149.856,60.23,30.736 +2020-04-15 08:45:00,110.65,147.036,60.23,30.736 +2020-04-15 09:00:00,106.92,141.17600000000002,56.845,30.736 +2020-04-15 09:15:00,107.34,138.842,56.845,30.736 +2020-04-15 09:30:00,106.27,140.71,56.845,30.736 +2020-04-15 09:45:00,106.1,140.07299999999998,56.845,30.736 +2020-04-15 10:00:00,105.75,135.961,53.832,30.736 +2020-04-15 10:15:00,106.27,135.888,53.832,30.736 +2020-04-15 10:30:00,106.67,133.898,53.832,30.736 +2020-04-15 10:45:00,105.02,133.914,53.832,30.736 +2020-04-15 11:00:00,103.38,128.187,53.225,30.736 +2020-04-15 11:15:00,103.27,128.941,53.225,30.736 +2020-04-15 11:30:00,104.28,130.05100000000002,53.225,30.736 +2020-04-15 11:45:00,102.15,131.589,53.225,30.736 +2020-04-15 12:00:00,100.29,127.3,50.676,30.736 +2020-04-15 12:15:00,98.13,127.947,50.676,30.736 +2020-04-15 12:30:00,104.5,126.884,50.676,30.736 +2020-04-15 12:45:00,105.18,127.676,50.676,30.736 +2020-04-15 13:00:00,101.11,128.764,50.646,30.736 +2020-04-15 13:15:00,100.65,127.29700000000001,50.646,30.736 +2020-04-15 13:30:00,102.83,125.236,50.646,30.736 +2020-04-15 13:45:00,103.75,123.99700000000001,50.646,30.736 +2020-04-15 14:00:00,104.03,126.375,50.786,30.736 +2020-04-15 14:15:00,97.55,125.059,50.786,30.736 +2020-04-15 14:30:00,97.21,124.553,50.786,30.736 +2020-04-15 14:45:00,102.82,125.4,50.786,30.736 +2020-04-15 15:00:00,103.26,126.17299999999999,51.535,30.736 +2020-04-15 15:15:00,100.4,124.414,51.535,30.736 +2020-04-15 15:30:00,99.53,123.01799999999999,51.535,30.736 +2020-04-15 15:45:00,104.12,122.182,51.535,30.736 +2020-04-15 16:00:00,104.01,123.73200000000001,53.157,30.736 +2020-04-15 16:15:00,108.96,124.56700000000001,53.157,30.736 +2020-04-15 16:30:00,107.57,124.6,53.157,30.736 +2020-04-15 16:45:00,115.32,122.35799999999999,53.157,30.736 +2020-04-15 17:00:00,116.1,122.72399999999999,57.793,30.736 +2020-04-15 17:15:00,113.72,125.06700000000001,57.793,30.736 +2020-04-15 17:30:00,111.49,127.154,57.793,30.736 +2020-04-15 17:45:00,115.74,128.088,57.793,30.736 +2020-04-15 18:00:00,119.64,131.067,59.872,30.736 +2020-04-15 18:15:00,116.25,132.809,59.872,30.736 +2020-04-15 18:30:00,111.49,131.142,59.872,30.736 +2020-04-15 18:45:00,111.02,136.745,59.872,30.736 +2020-04-15 19:00:00,115.27,134.97299999999998,60.17100000000001,30.736 +2020-04-15 19:15:00,112.47,133.905,60.17100000000001,30.736 +2020-04-15 19:30:00,112.99,133.835,60.17100000000001,30.736 +2020-04-15 19:45:00,112.24,134.093,60.17100000000001,30.736 +2020-04-15 20:00:00,110.1,128.842,65.015,30.736 +2020-04-15 20:15:00,111.51,125.648,65.015,30.736 +2020-04-15 20:30:00,106.08,125.51100000000001,65.015,30.736 +2020-04-15 20:45:00,109.31,124.819,65.015,30.736 +2020-04-15 21:00:00,104.34,118.501,57.805,30.736 +2020-04-15 21:15:00,101.95,117.163,57.805,30.736 +2020-04-15 21:30:00,93.9,117.31200000000001,57.805,30.736 +2020-04-15 21:45:00,86.0,116.51700000000001,57.805,30.736 +2020-04-15 22:00:00,78.84,110.359,52.115,30.736 +2020-04-15 22:15:00,87.14,107.744,52.115,30.736 +2020-04-15 22:30:00,85.74,94.72200000000001,52.115,30.736 +2020-04-15 22:45:00,86.73,87.46799999999999,52.115,30.736 +2020-04-15 23:00:00,77.18,78.321,42.871,30.736 +2020-04-15 23:15:00,76.65,77.863,42.871,30.736 +2020-04-15 23:30:00,76.51,76.979,42.871,30.736 +2020-04-15 23:45:00,79.0,78.02199999999999,42.871,30.736 +2020-04-16 00:00:00,76.59,66.062,39.203,30.736 +2020-04-16 00:15:00,76.53,66.53699999999999,39.203,30.736 +2020-04-16 00:30:00,74.45,64.943,39.203,30.736 +2020-04-16 00:45:00,72.38,63.372,39.203,30.736 +2020-04-16 01:00:00,73.63,63.707,37.118,30.736 +2020-04-16 01:15:00,77.98,63.019,37.118,30.736 +2020-04-16 01:30:00,75.57,61.729,37.118,30.736 +2020-04-16 01:45:00,78.14,61.175,37.118,30.736 +2020-04-16 02:00:00,69.37,62.033,35.647,30.736 +2020-04-16 02:15:00,73.86,61.263000000000005,35.647,30.736 +2020-04-16 02:30:00,69.08,63.718999999999994,35.647,30.736 +2020-04-16 02:45:00,73.63,64.268,35.647,30.736 +2020-04-16 03:00:00,78.41,67.297,34.585,30.736 +2020-04-16 03:15:00,81.88,68.52,34.585,30.736 +2020-04-16 03:30:00,81.3,67.967,34.585,30.736 +2020-04-16 03:45:00,77.39,68.23100000000001,34.585,30.736 +2020-04-16 04:00:00,81.22,80.163,36.184,30.736 +2020-04-16 04:15:00,82.62,92.54,36.184,30.736 +2020-04-16 04:30:00,86.64,92.275,36.184,30.736 +2020-04-16 04:45:00,90.81,94.12799999999999,36.184,30.736 +2020-04-16 05:00:00,97.5,127.575,41.019,30.736 +2020-04-16 05:15:00,101.04,159.359,41.019,30.736 +2020-04-16 05:30:00,104.32,148.222,41.019,30.736 +2020-04-16 05:45:00,104.7,137.95,41.019,30.736 +2020-04-16 06:00:00,111.42,139.596,53.963,30.736 +2020-04-16 06:15:00,111.14,144.635,53.963,30.736 +2020-04-16 06:30:00,112.46,141.951,53.963,30.736 +2020-04-16 06:45:00,111.75,142.252,53.963,30.736 +2020-04-16 07:00:00,113.92,144.352,66.512,30.736 +2020-04-16 07:15:00,113.61,144.842,66.512,30.736 +2020-04-16 07:30:00,113.32,143.05100000000002,66.512,30.736 +2020-04-16 07:45:00,111.01,139.931,66.512,30.736 +2020-04-16 08:00:00,109.42,137.489,58.86,30.736 +2020-04-16 08:15:00,108.68,135.996,58.86,30.736 +2020-04-16 08:30:00,109.43,131.921,58.86,30.736 +2020-04-16 08:45:00,108.55,130.05200000000002,58.86,30.736 +2020-04-16 09:00:00,107.62,123.76799999999999,52.156000000000006,30.736 +2020-04-16 09:15:00,105.64,121.553,52.156000000000006,30.736 +2020-04-16 09:30:00,105.44,123.573,52.156000000000006,30.736 +2020-04-16 09:45:00,105.36,123.007,52.156000000000006,30.736 +2020-04-16 10:00:00,104.88,118.589,49.034,30.736 +2020-04-16 10:15:00,105.52,118.499,49.034,30.736 +2020-04-16 10:30:00,104.66,116.969,49.034,30.736 +2020-04-16 10:45:00,103.7,117.022,49.034,30.736 +2020-04-16 11:00:00,101.79,111.735,46.53,30.736 +2020-04-16 11:15:00,100.62,112.494,46.53,30.736 +2020-04-16 11:30:00,102.26,113.68,46.53,30.736 +2020-04-16 11:45:00,100.98,114.271,46.53,30.736 +2020-04-16 12:00:00,101.74,109.26899999999999,43.318000000000005,30.736 +2020-04-16 12:15:00,100.04,110.089,43.318000000000005,30.736 +2020-04-16 12:30:00,100.71,109.241,43.318000000000005,30.736 +2020-04-16 12:45:00,98.75,109.965,43.318000000000005,30.736 +2020-04-16 13:00:00,96.55,110.53399999999999,41.608000000000004,30.736 +2020-04-16 13:15:00,101.33,109.822,41.608000000000004,30.736 +2020-04-16 13:30:00,105.43,107.68799999999999,41.608000000000004,30.736 +2020-04-16 13:45:00,99.87,106.266,41.608000000000004,30.736 +2020-04-16 14:00:00,104.65,107.289,41.786,30.736 +2020-04-16 14:15:00,103.74,106.805,41.786,30.736 +2020-04-16 14:30:00,99.2,106.686,41.786,30.736 +2020-04-16 14:45:00,96.98,107.146,41.786,30.736 +2020-04-16 15:00:00,97.32,108.044,44.181999999999995,30.736 +2020-04-16 15:15:00,97.34,106.54299999999999,44.181999999999995,30.736 +2020-04-16 15:30:00,96.99,106.125,44.181999999999995,30.736 +2020-04-16 15:45:00,105.87,105.76,44.181999999999995,30.736 +2020-04-16 16:00:00,108.15,105.584,45.956,30.736 +2020-04-16 16:15:00,109.03,105.436,45.956,30.736 +2020-04-16 16:30:00,102.46,105.414,45.956,30.736 +2020-04-16 16:45:00,107.51,103.3,45.956,30.736 +2020-04-16 17:00:00,105.77,102.85,50.702,30.736 +2020-04-16 17:15:00,109.81,105.25399999999999,50.702,30.736 +2020-04-16 17:30:00,116.22,106.64299999999999,50.702,30.736 +2020-04-16 17:45:00,115.39,108.116,50.702,30.736 +2020-04-16 18:00:00,117.78,108.929,53.595,30.736 +2020-04-16 18:15:00,112.22,110.46799999999999,53.595,30.736 +2020-04-16 18:30:00,111.55,109.307,53.595,30.736 +2020-04-16 18:45:00,116.31,114.927,53.595,30.736 +2020-04-16 19:00:00,115.59,113.14,54.207,30.736 +2020-04-16 19:15:00,112.76,112.333,54.207,30.736 +2020-04-16 19:30:00,111.41,112.33,54.207,30.736 +2020-04-16 19:45:00,109.87,112.74,54.207,30.736 +2020-04-16 20:00:00,110.54,110.89399999999999,56.948,30.736 +2020-04-16 20:15:00,108.79,108.249,56.948,30.736 +2020-04-16 20:30:00,106.12,107.178,56.948,30.736 +2020-04-16 20:45:00,100.7,106.319,56.948,30.736 +2020-04-16 21:00:00,100.63,102.01,52.157,30.736 +2020-04-16 21:15:00,99.93,101.508,52.157,30.736 +2020-04-16 21:30:00,96.78,101.87899999999999,52.157,30.736 +2020-04-16 21:45:00,91.01,101.338,52.157,30.736 +2020-04-16 22:00:00,88.24,96.85600000000001,47.483000000000004,30.736 +2020-04-16 22:15:00,90.64,94.991,47.483000000000004,30.736 +2020-04-16 22:30:00,87.26,83.77600000000001,47.483000000000004,30.736 +2020-04-16 22:45:00,83.96,78.098,47.483000000000004,30.736 +2020-04-16 23:00:00,79.47,70.275,41.978,30.736 +2020-04-16 23:15:00,82.32,68.947,41.978,30.736 +2020-04-16 23:30:00,82.51,68.09100000000001,41.978,30.736 +2020-04-16 23:45:00,79.47,68.702,41.978,30.736 +2020-04-17 00:00:00,75.69,63.902,39.301,30.736 +2020-04-17 00:15:00,80.52,64.638,39.301,30.736 +2020-04-17 00:30:00,79.11,63.152,39.301,30.736 +2020-04-17 00:45:00,74.97,61.931000000000004,39.301,30.736 +2020-04-17 01:00:00,76.44,61.839,37.976,30.736 +2020-04-17 01:15:00,78.63,61.181999999999995,37.976,30.736 +2020-04-17 01:30:00,78.54,60.246,37.976,30.736 +2020-04-17 01:45:00,75.92,59.578,37.976,30.736 +2020-04-17 02:00:00,76.67,61.106,37.041,30.736 +2020-04-17 02:15:00,78.96,60.225,37.041,30.736 +2020-04-17 02:30:00,73.71,63.532,37.041,30.736 +2020-04-17 02:45:00,72.47,63.641000000000005,37.041,30.736 +2020-04-17 03:00:00,72.74,66.71300000000001,37.575,30.736 +2020-04-17 03:15:00,77.67,67.558,37.575,30.736 +2020-04-17 03:30:00,81.93,66.829,37.575,30.736 +2020-04-17 03:45:00,82.98,67.883,37.575,30.736 +2020-04-17 04:00:00,85.63,79.98899999999999,39.058,30.736 +2020-04-17 04:15:00,81.63,91.12700000000001,39.058,30.736 +2020-04-17 04:30:00,87.46,91.634,39.058,30.736 +2020-04-17 04:45:00,88.26,92.454,39.058,30.736 +2020-04-17 05:00:00,94.18,124.83,43.256,30.736 +2020-04-17 05:15:00,97.98,157.922,43.256,30.736 +2020-04-17 05:30:00,101.19,147.47299999999998,43.256,30.736 +2020-04-17 05:45:00,104.3,136.907,43.256,30.736 +2020-04-17 06:00:00,109.57,138.972,56.093999999999994,30.736 +2020-04-17 06:15:00,110.1,143.366,56.093999999999994,30.736 +2020-04-17 06:30:00,111.6,140.194,56.093999999999994,30.736 +2020-04-17 06:45:00,112.04,141.218,56.093999999999994,30.736 +2020-04-17 07:00:00,113.63,143.342,66.92699999999999,30.736 +2020-04-17 07:15:00,113.48,144.96,66.92699999999999,30.736 +2020-04-17 07:30:00,114.3,141.72799999999998,66.92699999999999,30.736 +2020-04-17 07:45:00,111.17,138.004,66.92699999999999,30.736 +2020-04-17 08:00:00,109.06,135.482,60.332,30.736 +2020-04-17 08:15:00,108.75,134.251,60.332,30.736 +2020-04-17 08:30:00,108.96,130.61,60.332,30.736 +2020-04-17 08:45:00,111.11,127.825,60.332,30.736 +2020-04-17 09:00:00,107.04,120.291,56.085,30.736 +2020-04-17 09:15:00,107.43,119.624,56.085,30.736 +2020-04-17 09:30:00,106.4,120.985,56.085,30.736 +2020-04-17 09:45:00,106.21,120.639,56.085,30.736 +2020-04-17 10:00:00,104.57,115.45100000000001,52.91,30.736 +2020-04-17 10:15:00,105.96,115.67200000000001,52.91,30.736 +2020-04-17 10:30:00,107.27,114.46600000000001,52.91,30.736 +2020-04-17 10:45:00,103.6,114.225,52.91,30.736 +2020-04-17 11:00:00,104.04,109.054,52.278999999999996,30.736 +2020-04-17 11:15:00,101.25,108.65100000000001,52.278999999999996,30.736 +2020-04-17 11:30:00,100.24,110.637,52.278999999999996,30.736 +2020-04-17 11:45:00,100.36,110.69200000000001,52.278999999999996,30.736 +2020-04-17 12:00:00,98.31,106.641,49.023999999999994,30.736 +2020-04-17 12:15:00,98.3,105.836,49.023999999999994,30.736 +2020-04-17 12:30:00,98.9,105.109,49.023999999999994,30.736 +2020-04-17 12:45:00,96.92,105.68,49.023999999999994,30.736 +2020-04-17 13:00:00,95.35,107.199,46.82,30.736 +2020-04-17 13:15:00,94.15,107.081,46.82,30.736 +2020-04-17 13:30:00,95.45,105.429,46.82,30.736 +2020-04-17 13:45:00,95.7,104.15100000000001,46.82,30.736 +2020-04-17 14:00:00,96.68,104.079,45.756,30.736 +2020-04-17 14:15:00,94.93,103.727,45.756,30.736 +2020-04-17 14:30:00,95.22,104.76,45.756,30.736 +2020-04-17 14:45:00,96.43,104.99799999999999,45.756,30.736 +2020-04-17 15:00:00,98.56,105.598,47.56,30.736 +2020-04-17 15:15:00,96.36,103.66799999999999,47.56,30.736 +2020-04-17 15:30:00,98.86,101.962,47.56,30.736 +2020-04-17 15:45:00,99.49,102.11399999999999,47.56,30.736 +2020-04-17 16:00:00,100.98,100.829,49.581,30.736 +2020-04-17 16:15:00,102.21,101.12700000000001,49.581,30.736 +2020-04-17 16:30:00,103.54,101.053,49.581,30.736 +2020-04-17 16:45:00,105.31,98.324,49.581,30.736 +2020-04-17 17:00:00,109.87,99.141,53.918,30.736 +2020-04-17 17:15:00,105.24,101.18,53.918,30.736 +2020-04-17 17:30:00,108.79,102.488,53.918,30.736 +2020-04-17 17:45:00,108.09,103.699,53.918,30.736 +2020-04-17 18:00:00,109.81,104.992,54.266000000000005,30.736 +2020-04-17 18:15:00,106.31,105.728,54.266000000000005,30.736 +2020-04-17 18:30:00,105.75,104.70200000000001,54.266000000000005,30.736 +2020-04-17 18:45:00,105.56,110.596,54.266000000000005,30.736 +2020-04-17 19:00:00,104.01,109.889,54.092,30.736 +2020-04-17 19:15:00,101.85,110.20700000000001,54.092,30.736 +2020-04-17 19:30:00,103.45,110.035,54.092,30.736 +2020-04-17 19:45:00,104.61,109.553,54.092,30.736 +2020-04-17 20:00:00,101.81,107.604,59.038999999999994,30.736 +2020-04-17 20:15:00,104.72,105.49,59.038999999999994,30.736 +2020-04-17 20:30:00,104.95,104.11200000000001,59.038999999999994,30.736 +2020-04-17 20:45:00,104.9,103.052,59.038999999999994,30.736 +2020-04-17 21:00:00,92.54,99.882,53.346000000000004,30.736 +2020-04-17 21:15:00,89.07,100.73,53.346000000000004,30.736 +2020-04-17 21:30:00,86.36,101.012,53.346000000000004,30.736 +2020-04-17 21:45:00,91.79,100.928,53.346000000000004,30.736 +2020-04-17 22:00:00,87.88,96.88600000000001,47.938,30.736 +2020-04-17 22:15:00,87.19,94.811,47.938,30.736 +2020-04-17 22:30:00,79.25,90.098,47.938,30.736 +2020-04-17 22:45:00,78.07,86.851,47.938,30.736 +2020-04-17 23:00:00,78.74,79.878,40.266,30.736 +2020-04-17 23:15:00,79.25,76.51899999999999,40.266,30.736 +2020-04-17 23:30:00,79.44,73.683,40.266,30.736 +2020-04-17 23:45:00,72.93,73.869,40.266,30.736 +2020-04-18 00:00:00,72.24,62.902,39.184,30.618000000000002 +2020-04-18 00:15:00,74.92,61.023999999999994,39.184,30.618000000000002 +2020-04-18 00:30:00,73.65,59.887,39.184,30.618000000000002 +2020-04-18 00:45:00,71.07,58.537,39.184,30.618000000000002 +2020-04-18 01:00:00,64.33,58.955,34.692,30.618000000000002 +2020-04-18 01:15:00,65.67,58.178000000000004,34.692,30.618000000000002 +2020-04-18 01:30:00,64.17,56.406000000000006,34.692,30.618000000000002 +2020-04-18 01:45:00,68.34,56.482,34.692,30.618000000000002 +2020-04-18 02:00:00,71.5,57.685,32.919000000000004,30.618000000000002 +2020-04-18 02:15:00,72.43,56.059,32.919000000000004,30.618000000000002 +2020-04-18 02:30:00,69.43,58.281000000000006,32.919000000000004,30.618000000000002 +2020-04-18 02:45:00,69.21,58.986000000000004,32.919000000000004,30.618000000000002 +2020-04-18 03:00:00,72.51,61.458999999999996,32.024,30.618000000000002 +2020-04-18 03:15:00,68.08,61.188,32.024,30.618000000000002 +2020-04-18 03:30:00,66.16,59.88399999999999,32.024,30.618000000000002 +2020-04-18 03:45:00,64.76,62.033,32.024,30.618000000000002 +2020-04-18 04:00:00,66.57,70.758,31.958000000000002,30.618000000000002 +2020-04-18 04:15:00,67.33,79.971,31.958000000000002,30.618000000000002 +2020-04-18 04:30:00,66.23,78.243,31.958000000000002,30.618000000000002 +2020-04-18 04:45:00,64.3,78.995,31.958000000000002,30.618000000000002 +2020-04-18 05:00:00,64.93,97.959,32.75,30.618000000000002 +2020-04-18 05:15:00,66.66,114.07,32.75,30.618000000000002 +2020-04-18 05:30:00,67.03,104.89,32.75,30.618000000000002 +2020-04-18 05:45:00,70.54,100.15,32.75,30.618000000000002 +2020-04-18 06:00:00,71.71,118.912,34.461999999999996,30.618000000000002 +2020-04-18 06:15:00,73.46,136.754,34.461999999999996,30.618000000000002 +2020-04-18 06:30:00,75.72,128.946,34.461999999999996,30.618000000000002 +2020-04-18 06:45:00,76.77,123.31,34.461999999999996,30.618000000000002 +2020-04-18 07:00:00,78.94,122.14299999999999,37.736,30.618000000000002 +2020-04-18 07:15:00,78.58,122.23200000000001,37.736,30.618000000000002 +2020-04-18 07:30:00,79.45,121.365,37.736,30.618000000000002 +2020-04-18 07:45:00,80.73,120.3,37.736,30.618000000000002 +2020-04-18 08:00:00,80.41,120.022,42.34,30.618000000000002 +2020-04-18 08:15:00,80.62,120.61,42.34,30.618000000000002 +2020-04-18 08:30:00,79.58,117.786,42.34,30.618000000000002 +2020-04-18 08:45:00,79.67,117.322,42.34,30.618000000000002 +2020-04-18 09:00:00,77.81,112.633,43.571999999999996,30.618000000000002 +2020-04-18 09:15:00,76.73,112.691,43.571999999999996,30.618000000000002 +2020-04-18 09:30:00,75.68,114.87700000000001,43.571999999999996,30.618000000000002 +2020-04-18 09:45:00,75.15,114.365,43.571999999999996,30.618000000000002 +2020-04-18 10:00:00,74.84,109.54700000000001,40.514,30.618000000000002 +2020-04-18 10:15:00,76.04,110.12799999999999,40.514,30.618000000000002 +2020-04-18 10:30:00,76.37,108.82600000000001,40.514,30.618000000000002 +2020-04-18 10:45:00,75.02,109.126,40.514,30.618000000000002 +2020-04-18 11:00:00,73.78,103.92299999999999,36.388000000000005,30.618000000000002 +2020-04-18 11:15:00,72.94,103.676,36.388000000000005,30.618000000000002 +2020-04-18 11:30:00,70.92,105.191,36.388000000000005,30.618000000000002 +2020-04-18 11:45:00,69.83,105.12100000000001,36.388000000000005,30.618000000000002 +2020-04-18 12:00:00,67.19,100.64200000000001,35.217,30.618000000000002 +2020-04-18 12:15:00,67.12,100.725,35.217,30.618000000000002 +2020-04-18 12:30:00,65.58,100.066,35.217,30.618000000000002 +2020-04-18 12:45:00,65.28,100.61399999999999,35.217,30.618000000000002 +2020-04-18 13:00:00,61.25,101.42399999999999,32.001999999999995,30.618000000000002 +2020-04-18 13:15:00,62.76,99.764,32.001999999999995,30.618000000000002 +2020-04-18 13:30:00,62.74,97.959,32.001999999999995,30.618000000000002 +2020-04-18 13:45:00,62.56,96.169,32.001999999999995,30.618000000000002 +2020-04-18 14:00:00,63.31,96.9,31.304000000000002,30.618000000000002 +2020-04-18 14:15:00,63.14,95.477,31.304000000000002,30.618000000000002 +2020-04-18 14:30:00,63.14,95.135,31.304000000000002,30.618000000000002 +2020-04-18 14:45:00,64.8,95.787,31.304000000000002,30.618000000000002 +2020-04-18 15:00:00,65.13,97.071,34.731,30.618000000000002 +2020-04-18 15:15:00,65.05,96.014,34.731,30.618000000000002 +2020-04-18 15:30:00,66.87,95.29700000000001,34.731,30.618000000000002 +2020-04-18 15:45:00,69.39,94.9,34.731,30.618000000000002 +2020-04-18 16:00:00,71.14,94.525,38.769,30.618000000000002 +2020-04-18 16:15:00,72.55,94.76299999999999,38.769,30.618000000000002 +2020-04-18 16:30:00,74.77,94.803,38.769,30.618000000000002 +2020-04-18 16:45:00,76.84,92.49600000000001,38.769,30.618000000000002 +2020-04-18 17:00:00,79.92,92.44,44.928000000000004,30.618000000000002 +2020-04-18 17:15:00,79.83,94.03399999999999,44.928000000000004,30.618000000000002 +2020-04-18 17:30:00,81.4,95.20200000000001,44.928000000000004,30.618000000000002 +2020-04-18 17:45:00,81.53,96.46799999999999,44.928000000000004,30.618000000000002 +2020-04-18 18:00:00,85.84,98.512,47.786,30.618000000000002 +2020-04-18 18:15:00,83.87,101.316,47.786,30.618000000000002 +2020-04-18 18:30:00,81.87,101.869,47.786,30.618000000000002 +2020-04-18 18:45:00,81.6,103.727,47.786,30.618000000000002 +2020-04-18 19:00:00,82.11,102.561,47.463,30.618000000000002 +2020-04-18 19:15:00,80.38,101.963,47.463,30.618000000000002 +2020-04-18 19:30:00,82.61,102.70299999999999,47.463,30.618000000000002 +2020-04-18 19:45:00,81.66,103.302,47.463,30.618000000000002 +2020-04-18 20:00:00,78.44,103.012,43.735,30.618000000000002 +2020-04-18 20:15:00,77.86,101.625,43.735,30.618000000000002 +2020-04-18 20:30:00,76.06,99.522,43.735,30.618000000000002 +2020-04-18 20:45:00,75.04,99.48700000000001,43.735,30.618000000000002 +2020-04-18 21:00:00,69.71,96.571,40.346,30.618000000000002 +2020-04-18 21:15:00,69.85,97.436,40.346,30.618000000000002 +2020-04-18 21:30:00,66.1,98.477,40.346,30.618000000000002 +2020-04-18 21:45:00,66.61,97.851,40.346,30.618000000000002 +2020-04-18 22:00:00,63.91,94.52,39.323,30.618000000000002 +2020-04-18 22:15:00,63.83,93.944,39.323,30.618000000000002 +2020-04-18 22:30:00,61.62,92.242,39.323,30.618000000000002 +2020-04-18 22:45:00,61.98,90.219,39.323,30.618000000000002 +2020-04-18 23:00:00,56.73,84.055,33.716,30.618000000000002 +2020-04-18 23:15:00,56.39,80.14699999999999,33.716,30.618000000000002 +2020-04-18 23:30:00,56.57,78.0,33.716,30.618000000000002 +2020-04-18 23:45:00,57.7,77.148,33.716,30.618000000000002 +2020-04-19 00:00:00,47.91,63.659,28.703000000000003,30.618000000000002 +2020-04-19 00:15:00,49.58,60.85,28.703000000000003,30.618000000000002 +2020-04-19 00:30:00,48.9,59.416000000000004,28.703000000000003,30.618000000000002 +2020-04-19 00:45:00,51.89,58.347,28.703000000000003,30.618000000000002 +2020-04-19 01:00:00,48.88,58.867,26.171,30.618000000000002 +2020-04-19 01:15:00,49.69,58.504,26.171,30.618000000000002 +2020-04-19 01:30:00,47.4,56.879,26.171,30.618000000000002 +2020-04-19 01:45:00,50.6,56.542,26.171,30.618000000000002 +2020-04-19 02:00:00,48.49,57.409,25.326999999999998,30.618000000000002 +2020-04-19 02:15:00,49.54,55.821000000000005,25.326999999999998,30.618000000000002 +2020-04-19 02:30:00,49.42,58.62,25.326999999999998,30.618000000000002 +2020-04-19 02:45:00,49.28,59.363,25.326999999999998,30.618000000000002 +2020-04-19 03:00:00,49.27,62.45399999999999,24.311999999999998,30.618000000000002 +2020-04-19 03:15:00,50.12,62.077,24.311999999999998,30.618000000000002 +2020-04-19 03:30:00,50.56,60.961999999999996,24.311999999999998,30.618000000000002 +2020-04-19 03:45:00,51.01,62.55,24.311999999999998,30.618000000000002 +2020-04-19 04:00:00,52.33,71.09100000000001,25.33,30.618000000000002 +2020-04-19 04:15:00,53.42,79.464,25.33,30.618000000000002 +2020-04-19 04:30:00,54.02,78.689,25.33,30.618000000000002 +2020-04-19 04:45:00,52.87,79.245,25.33,30.618000000000002 +2020-04-19 05:00:00,53.79,96.527,25.309,30.618000000000002 +2020-04-19 05:15:00,55.01,110.838,25.309,30.618000000000002 +2020-04-19 05:30:00,54.77,101.319,25.309,30.618000000000002 +2020-04-19 05:45:00,56.87,96.553,25.309,30.618000000000002 +2020-04-19 06:00:00,58.42,113.615,25.945999999999998,30.618000000000002 +2020-04-19 06:15:00,58.63,131.168,25.945999999999998,30.618000000000002 +2020-04-19 06:30:00,59.65,122.322,25.945999999999998,30.618000000000002 +2020-04-19 06:45:00,61.3,115.475,25.945999999999998,30.618000000000002 +2020-04-19 07:00:00,62.91,115.682,27.87,30.618000000000002 +2020-04-19 07:15:00,63.9,114.219,27.87,30.618000000000002 +2020-04-19 07:30:00,64.4,113.54899999999999,27.87,30.618000000000002 +2020-04-19 07:45:00,63.96,112.075,27.87,30.618000000000002 +2020-04-19 08:00:00,62.93,113.177,32.114000000000004,30.618000000000002 +2020-04-19 08:15:00,62.87,114.525,32.114000000000004,30.618000000000002 +2020-04-19 08:30:00,62.0,113.11399999999999,32.114000000000004,30.618000000000002 +2020-04-19 08:45:00,61.46,113.685,32.114000000000004,30.618000000000002 +2020-04-19 09:00:00,60.12,108.691,34.222,30.618000000000002 +2020-04-19 09:15:00,60.81,108.734,34.222,30.618000000000002 +2020-04-19 09:30:00,59.84,111.131,34.222,30.618000000000002 +2020-04-19 09:45:00,60.52,111.23299999999999,34.222,30.618000000000002 +2020-04-19 10:00:00,60.88,108.11,34.544000000000004,30.618000000000002 +2020-04-19 10:15:00,63.47,109.119,34.544000000000004,30.618000000000002 +2020-04-19 10:30:00,64.28,108.31299999999999,34.544000000000004,30.618000000000002 +2020-04-19 10:45:00,64.13,108.21,34.544000000000004,30.618000000000002 +2020-04-19 11:00:00,60.96,103.27799999999999,36.368,30.618000000000002 +2020-04-19 11:15:00,60.33,102.855,36.368,30.618000000000002 +2020-04-19 11:30:00,57.6,104.197,36.368,30.618000000000002 +2020-04-19 11:45:00,56.64,104.645,36.368,30.618000000000002 +2020-04-19 12:00:00,54.74,100.47,32.433,30.618000000000002 +2020-04-19 12:15:00,53.47,101.291,32.433,30.618000000000002 +2020-04-19 12:30:00,53.55,99.87299999999999,32.433,30.618000000000002 +2020-04-19 12:45:00,53.72,99.48299999999999,32.433,30.618000000000002 +2020-04-19 13:00:00,52.34,99.73100000000001,28.971999999999998,30.618000000000002 +2020-04-19 13:15:00,52.36,99.473,28.971999999999998,30.618000000000002 +2020-04-19 13:30:00,52.22,96.885,28.971999999999998,30.618000000000002 +2020-04-19 13:45:00,53.3,95.381,28.971999999999998,30.618000000000002 +2020-04-19 14:00:00,52.13,97.029,25.531999999999996,30.618000000000002 +2020-04-19 14:15:00,52.55,96.542,25.531999999999996,30.618000000000002 +2020-04-19 14:30:00,52.58,96.13799999999999,25.531999999999996,30.618000000000002 +2020-04-19 14:45:00,53.01,95.916,25.531999999999996,30.618000000000002 +2020-04-19 15:00:00,55.7,96.435,25.766,30.618000000000002 +2020-04-19 15:15:00,59.0,95.31299999999999,25.766,30.618000000000002 +2020-04-19 15:30:00,60.62,94.788,25.766,30.618000000000002 +2020-04-19 15:45:00,65.31,94.945,25.766,30.618000000000002 +2020-04-19 16:00:00,66.6,94.571,29.232,30.618000000000002 +2020-04-19 16:15:00,66.2,94.41799999999999,29.232,30.618000000000002 +2020-04-19 16:30:00,70.78,95.229,29.232,30.618000000000002 +2020-04-19 16:45:00,71.98,92.97,29.232,30.618000000000002 +2020-04-19 17:00:00,74.6,93.13,37.431,30.618000000000002 +2020-04-19 17:15:00,75.63,95.47,37.431,30.618000000000002 +2020-04-19 17:30:00,77.0,97.29899999999999,37.431,30.618000000000002 +2020-04-19 17:45:00,78.99,100.04899999999999,37.431,30.618000000000002 +2020-04-19 18:00:00,82.25,102.135,41.251999999999995,30.618000000000002 +2020-04-19 18:15:00,79.87,105.406,41.251999999999995,30.618000000000002 +2020-04-19 18:30:00,79.78,104.67200000000001,41.251999999999995,30.618000000000002 +2020-04-19 18:45:00,79.99,107.59899999999999,41.251999999999995,30.618000000000002 +2020-04-19 19:00:00,81.9,107.616,41.784,30.618000000000002 +2020-04-19 19:15:00,82.8,106.60600000000001,41.784,30.618000000000002 +2020-04-19 19:30:00,87.07,107.10799999999999,41.784,30.618000000000002 +2020-04-19 19:45:00,85.32,108.184,41.784,30.618000000000002 +2020-04-19 20:00:00,87.59,107.975,40.804,30.618000000000002 +2020-04-19 20:15:00,90.05,107.021,40.804,30.618000000000002 +2020-04-19 20:30:00,90.49,106.132,40.804,30.618000000000002 +2020-04-19 20:45:00,89.62,104.4,40.804,30.618000000000002 +2020-04-19 21:00:00,81.38,100.014,38.379,30.618000000000002 +2020-04-19 21:15:00,84.66,100.369,38.379,30.618000000000002 +2020-04-19 21:30:00,77.57,101.12200000000001,38.379,30.618000000000002 +2020-04-19 21:45:00,78.05,100.834,38.379,30.618000000000002 +2020-04-19 22:00:00,77.86,98.241,37.87,30.618000000000002 +2020-04-19 22:15:00,82.24,96.18799999999999,37.87,30.618000000000002 +2020-04-19 22:30:00,80.13,92.486,37.87,30.618000000000002 +2020-04-19 22:45:00,76.14,89.17200000000001,37.87,30.618000000000002 +2020-04-19 23:00:00,67.84,81.33,33.332,30.618000000000002 +2020-04-19 23:15:00,72.28,79.26100000000001,33.332,30.618000000000002 +2020-04-19 23:30:00,75.58,77.17699999999999,33.332,30.618000000000002 +2020-04-19 23:45:00,73.37,76.899,33.332,30.618000000000002 +2020-04-20 00:00:00,69.74,66.392,34.698,30.736 +2020-04-20 00:15:00,72.46,65.615,34.698,30.736 +2020-04-20 00:30:00,70.41,63.977,34.698,30.736 +2020-04-20 00:45:00,69.96,62.376000000000005,34.698,30.736 +2020-04-20 01:00:00,66.93,63.148,32.889,30.736 +2020-04-20 01:15:00,72.98,62.507,32.889,30.736 +2020-04-20 01:30:00,73.29,61.136,32.889,30.736 +2020-04-20 01:45:00,71.28,60.791000000000004,32.889,30.736 +2020-04-20 02:00:00,68.66,61.93899999999999,32.06,30.736 +2020-04-20 02:15:00,73.7,60.363,32.06,30.736 +2020-04-20 02:30:00,73.14,63.458999999999996,32.06,30.736 +2020-04-20 02:45:00,73.33,63.801,32.06,30.736 +2020-04-20 03:00:00,67.47,67.866,30.515,30.736 +2020-04-20 03:15:00,68.31,68.77,30.515,30.736 +2020-04-20 03:30:00,68.12,67.986,30.515,30.736 +2020-04-20 03:45:00,69.11,69.008,30.515,30.736 +2020-04-20 04:00:00,74.41,81.767,31.436,30.736 +2020-04-20 04:15:00,77.05,94.147,31.436,30.736 +2020-04-20 04:30:00,80.21,94.115,31.436,30.736 +2020-04-20 04:45:00,83.85,94.995,31.436,30.736 +2020-04-20 05:00:00,92.02,124.523,38.997,30.736 +2020-04-20 05:15:00,94.95,155.561,38.997,30.736 +2020-04-20 05:30:00,97.01,145.115,38.997,30.736 +2020-04-20 05:45:00,100.5,135.55100000000002,38.997,30.736 +2020-04-20 06:00:00,105.42,137.136,54.97,30.736 +2020-04-20 06:15:00,107.37,141.161,54.97,30.736 +2020-04-20 06:30:00,110.05,138.94799999999998,54.97,30.736 +2020-04-20 06:45:00,111.02,140.03,54.97,30.736 +2020-04-20 07:00:00,115.16,142.121,66.032,30.736 +2020-04-20 07:15:00,112.2,142.808,66.032,30.736 +2020-04-20 07:30:00,115.55,141.128,66.032,30.736 +2020-04-20 07:45:00,114.85,138.559,66.032,30.736 +2020-04-20 08:00:00,113.82,136.049,59.941,30.736 +2020-04-20 08:15:00,117.57,135.525,59.941,30.736 +2020-04-20 08:30:00,120.95,131.31799999999998,59.941,30.736 +2020-04-20 08:45:00,120.25,130.308,59.941,30.736 +2020-04-20 09:00:00,121.46,124.26299999999999,54.016000000000005,30.736 +2020-04-20 09:15:00,120.31,121.29700000000001,54.016000000000005,30.736 +2020-04-20 09:30:00,119.35,122.581,54.016000000000005,30.736 +2020-04-20 09:45:00,116.25,121.405,54.016000000000005,30.736 +2020-04-20 10:00:00,114.59,118.214,50.63,30.736 +2020-04-20 10:15:00,118.18,118.99600000000001,50.63,30.736 +2020-04-20 10:30:00,111.25,117.441,50.63,30.736 +2020-04-20 10:45:00,112.04,116.792,50.63,30.736 +2020-04-20 11:00:00,118.8,110.635,49.951,30.736 +2020-04-20 11:15:00,119.3,111.428,49.951,30.736 +2020-04-20 11:30:00,123.96,113.979,49.951,30.736 +2020-04-20 11:45:00,126.42,114.51899999999999,49.951,30.736 +2020-04-20 12:00:00,124.57,110.541,46.913000000000004,30.736 +2020-04-20 12:15:00,122.52,111.43799999999999,46.913000000000004,30.736 +2020-04-20 12:30:00,115.69,109.486,46.913000000000004,30.736 +2020-04-20 12:45:00,118.2,109.985,46.913000000000004,30.736 +2020-04-20 13:00:00,113.18,111.163,47.093999999999994,30.736 +2020-04-20 13:15:00,112.18,109.56700000000001,47.093999999999994,30.736 +2020-04-20 13:30:00,112.03,106.774,47.093999999999994,30.736 +2020-04-20 13:45:00,106.19,105.821,47.093999999999994,30.736 +2020-04-20 14:00:00,99.72,106.662,46.678000000000004,30.736 +2020-04-20 14:15:00,107.85,106.13,46.678000000000004,30.736 +2020-04-20 14:30:00,105.99,105.26899999999999,46.678000000000004,30.736 +2020-04-20 14:45:00,95.87,106.25399999999999,46.678000000000004,30.736 +2020-04-20 15:00:00,100.13,107.649,47.715,30.736 +2020-04-20 15:15:00,102.74,105.315,47.715,30.736 +2020-04-20 15:30:00,106.75,104.714,47.715,30.736 +2020-04-20 15:45:00,109.06,104.306,47.715,30.736 +2020-04-20 16:00:00,111.0,104.484,49.81100000000001,30.736 +2020-04-20 16:15:00,110.49,103.917,49.81100000000001,30.736 +2020-04-20 16:30:00,109.82,103.781,49.81100000000001,30.736 +2020-04-20 16:45:00,113.31,100.8,49.81100000000001,30.736 +2020-04-20 17:00:00,114.39,100.116,55.591,30.736 +2020-04-20 17:15:00,113.67,102.141,55.591,30.736 +2020-04-20 17:30:00,113.09,103.419,55.591,30.736 +2020-04-20 17:45:00,114.97,105.07,55.591,30.736 +2020-04-20 18:00:00,116.55,106.62,56.523,30.736 +2020-04-20 18:15:00,113.53,107.551,56.523,30.736 +2020-04-20 18:30:00,111.92,106.67,56.523,30.736 +2020-04-20 18:45:00,111.5,112.01299999999999,56.523,30.736 +2020-04-20 19:00:00,109.89,110.913,56.044,30.736 +2020-04-20 19:15:00,115.06,110.16,56.044,30.736 +2020-04-20 19:30:00,113.63,110.69200000000001,56.044,30.736 +2020-04-20 19:45:00,112.78,110.954,56.044,30.736 +2020-04-20 20:00:00,101.75,108.655,61.715,30.736 +2020-04-20 20:15:00,100.7,107.319,61.715,30.736 +2020-04-20 20:30:00,100.68,105.82600000000001,61.715,30.736 +2020-04-20 20:45:00,100.06,105.12200000000001,61.715,30.736 +2020-04-20 21:00:00,99.18,100.616,56.24,30.736 +2020-04-20 21:15:00,98.71,100.664,56.24,30.736 +2020-04-20 21:30:00,94.69,101.24700000000001,56.24,30.736 +2020-04-20 21:45:00,91.68,100.58200000000001,56.24,30.736 +2020-04-20 22:00:00,88.84,95.15299999999999,50.437,30.736 +2020-04-20 22:15:00,88.96,93.74600000000001,50.437,30.736 +2020-04-20 22:30:00,84.92,82.199,50.437,30.736 +2020-04-20 22:45:00,85.41,76.347,50.437,30.736 +2020-04-20 23:00:00,81.6,68.904,42.756,30.736 +2020-04-20 23:15:00,81.0,67.168,42.756,30.736 +2020-04-20 23:30:00,80.1,66.495,42.756,30.736 +2020-04-20 23:45:00,81.46,67.245,42.756,30.736 +2020-04-21 00:00:00,77.51,64.109,39.857,30.736 +2020-04-21 00:15:00,75.11,64.649,39.857,30.736 +2020-04-21 00:30:00,73.12,63.016999999999996,39.857,30.736 +2020-04-21 00:45:00,78.89,61.471000000000004,39.857,30.736 +2020-04-21 01:00:00,78.67,61.772,37.233000000000004,30.736 +2020-04-21 01:15:00,77.8,60.961000000000006,37.233000000000004,30.736 +2020-04-21 01:30:00,72.87,59.566,37.233000000000004,30.736 +2020-04-21 01:45:00,74.1,59.026,37.233000000000004,30.736 +2020-04-21 02:00:00,78.95,59.831,35.856,30.736 +2020-04-21 02:15:00,78.47,58.976000000000006,35.856,30.736 +2020-04-21 02:30:00,72.53,61.526,35.856,30.736 +2020-04-21 02:45:00,71.94,62.106,35.856,30.736 +2020-04-21 03:00:00,74.86,65.212,34.766999999999996,30.736 +2020-04-21 03:15:00,80.59,66.313,34.766999999999996,30.736 +2020-04-21 03:30:00,83.48,65.743,34.766999999999996,30.736 +2020-04-21 03:45:00,83.58,66.119,34.766999999999996,30.736 +2020-04-21 04:00:00,79.94,77.88,35.468,30.736 +2020-04-21 04:15:00,82.73,90.056,35.468,30.736 +2020-04-21 04:30:00,86.26,89.789,35.468,30.736 +2020-04-21 04:45:00,87.7,91.59100000000001,35.468,30.736 +2020-04-21 05:00:00,95.23,124.53200000000001,40.399,30.736 +2020-04-21 05:15:00,98.1,155.803,40.399,30.736 +2020-04-21 05:30:00,100.01,144.778,40.399,30.736 +2020-04-21 05:45:00,102.09,134.755,40.399,30.736 +2020-04-21 06:00:00,107.66,136.52100000000002,54.105,30.736 +2020-04-21 06:15:00,108.52,141.458,54.105,30.736 +2020-04-21 06:30:00,110.71,138.69,54.105,30.736 +2020-04-21 06:45:00,111.29,138.968,54.105,30.736 +2020-04-21 07:00:00,110.64,141.067,63.083,30.736 +2020-04-21 07:15:00,112.61,141.486,63.083,30.736 +2020-04-21 07:30:00,117.23,139.503,63.083,30.736 +2020-04-21 07:45:00,111.61,136.388,63.083,30.736 +2020-04-21 08:00:00,109.22,133.88299999999998,57.254,30.736 +2020-04-21 08:15:00,106.0,132.562,57.254,30.736 +2020-04-21 08:30:00,108.93,128.343,57.254,30.736 +2020-04-21 08:45:00,111.02,126.616,57.254,30.736 +2020-04-21 09:00:00,108.14,120.348,51.395,30.736 +2020-04-21 09:15:00,111.76,118.16,51.395,30.736 +2020-04-21 09:30:00,112.64,120.26700000000001,51.395,30.736 +2020-04-21 09:45:00,108.02,119.867,51.395,30.736 +2020-04-21 10:00:00,106.08,115.491,48.201,30.736 +2020-04-21 10:15:00,104.55,115.639,48.201,30.736 +2020-04-21 10:30:00,104.69,114.223,48.201,30.736 +2020-04-21 10:45:00,104.67,114.375,48.201,30.736 +2020-04-21 11:00:00,103.29,109.04799999999999,46.133,30.736 +2020-04-21 11:15:00,102.35,109.921,46.133,30.736 +2020-04-21 11:30:00,98.0,111.10700000000001,46.133,30.736 +2020-04-21 11:45:00,100.05,111.792,46.133,30.736 +2020-04-21 12:00:00,95.93,106.93700000000001,44.243,30.736 +2020-04-21 12:15:00,104.04,107.79700000000001,44.243,30.736 +2020-04-21 12:30:00,105.24,106.734,44.243,30.736 +2020-04-21 12:45:00,107.04,107.476,44.243,30.736 +2020-04-21 13:00:00,100.69,108.23700000000001,45.042,30.736 +2020-04-21 13:15:00,99.99,107.499,45.042,30.736 +2020-04-21 13:30:00,104.95,105.37799999999999,45.042,30.736 +2020-04-21 13:45:00,111.42,103.964,45.042,30.736 +2020-04-21 14:00:00,114.25,105.295,44.062,30.736 +2020-04-21 14:15:00,112.57,104.719,44.062,30.736 +2020-04-21 14:30:00,109.07,104.374,44.062,30.736 +2020-04-21 14:45:00,106.59,104.85,44.062,30.736 +2020-04-21 15:00:00,113.59,105.935,46.461999999999996,30.736 +2020-04-21 15:15:00,116.36,104.32,46.461999999999996,30.736 +2020-04-21 15:30:00,115.99,103.68299999999999,46.461999999999996,30.736 +2020-04-21 15:45:00,113.69,103.225,46.461999999999996,30.736 +2020-04-21 16:00:00,109.01,103.26299999999999,48.802,30.736 +2020-04-21 16:15:00,111.19,102.992,48.802,30.736 +2020-04-21 16:30:00,117.84,102.99799999999999,48.802,30.736 +2020-04-21 16:45:00,116.61,100.555,48.802,30.736 +2020-04-21 17:00:00,119.91,100.361,55.672,30.736 +2020-04-21 17:15:00,117.41,102.63799999999999,55.672,30.736 +2020-04-21 17:30:00,119.66,104.00299999999999,55.672,30.736 +2020-04-21 17:45:00,120.44,105.34700000000001,55.672,30.736 +2020-04-21 18:00:00,118.14,106.23299999999999,57.006,30.736 +2020-04-21 18:15:00,114.94,107.87,57.006,30.736 +2020-04-21 18:30:00,117.32,106.64200000000001,57.006,30.736 +2020-04-21 18:45:00,117.13,112.29299999999999,57.006,30.736 +2020-04-21 19:00:00,110.96,110.45100000000001,57.148,30.736 +2020-04-21 19:15:00,108.42,109.675,57.148,30.736 +2020-04-21 19:30:00,113.5,109.726,57.148,30.736 +2020-04-21 19:45:00,116.0,110.249,57.148,30.736 +2020-04-21 20:00:00,107.84,108.26299999999999,61.895,30.736 +2020-04-21 20:15:00,108.53,105.655,61.895,30.736 +2020-04-21 20:30:00,108.46,104.756,61.895,30.736 +2020-04-21 20:45:00,106.57,104.056,61.895,30.736 +2020-04-21 21:00:00,95.96,99.756,54.78,30.736 +2020-04-21 21:15:00,98.48,99.322,54.78,30.736 +2020-04-21 21:30:00,95.81,99.62700000000001,54.78,30.736 +2020-04-21 21:45:00,93.29,99.24,54.78,30.736 +2020-04-21 22:00:00,84.54,94.82799999999999,50.76,30.736 +2020-04-21 22:15:00,88.53,93.089,50.76,30.736 +2020-04-21 22:30:00,86.77,81.759,50.76,30.736 +2020-04-21 22:45:00,86.04,76.04,50.76,30.736 +2020-04-21 23:00:00,75.63,68.074,44.162,30.736 +2020-04-21 23:15:00,72.93,66.936,44.162,30.736 +2020-04-21 23:30:00,77.07,66.072,44.162,30.736 +2020-04-21 23:45:00,80.79,66.721,44.162,30.736 +2020-04-22 00:00:00,77.42,63.72,39.061,30.736 +2020-04-22 00:15:00,75.61,64.27199999999999,39.061,30.736 +2020-04-22 00:30:00,73.58,62.63399999999999,39.061,30.736 +2020-04-22 00:45:00,72.45,61.093,39.061,30.736 +2020-04-22 01:00:00,69.55,61.388000000000005,35.795,30.736 +2020-04-22 01:15:00,76.2,60.552,35.795,30.736 +2020-04-22 01:30:00,70.64,59.136,35.795,30.736 +2020-04-22 01:45:00,71.82,58.599,35.795,30.736 +2020-04-22 02:00:00,71.59,59.394,33.316,30.736 +2020-04-22 02:15:00,78.92,58.523,33.316,30.736 +2020-04-22 02:30:00,78.64,61.091,33.316,30.736 +2020-04-22 02:45:00,78.89,61.676,33.316,30.736 +2020-04-22 03:00:00,80.34,64.79899999999999,32.803000000000004,30.736 +2020-04-22 03:15:00,75.97,65.874,32.803000000000004,30.736 +2020-04-22 03:30:00,72.33,65.30199999999999,32.803000000000004,30.736 +2020-04-22 03:45:00,75.91,65.7,32.803000000000004,30.736 +2020-04-22 04:00:00,78.96,77.42699999999999,34.235,30.736 +2020-04-22 04:15:00,78.93,89.56200000000001,34.235,30.736 +2020-04-22 04:30:00,83.97,89.294,34.235,30.736 +2020-04-22 04:45:00,84.72,91.086,34.235,30.736 +2020-04-22 05:00:00,92.29,123.926,38.65,30.736 +2020-04-22 05:15:00,96.41,155.092,38.65,30.736 +2020-04-22 05:30:00,96.57,144.094,38.65,30.736 +2020-04-22 05:45:00,103.85,134.12,38.65,30.736 +2020-04-22 06:00:00,107.64,135.908,54.951,30.736 +2020-04-22 06:15:00,108.62,140.82299999999998,54.951,30.736 +2020-04-22 06:30:00,111.0,138.041,54.951,30.736 +2020-04-22 06:45:00,110.88,138.314,54.951,30.736 +2020-04-22 07:00:00,112.83,140.412,67.328,30.736 +2020-04-22 07:15:00,112.0,140.819,67.328,30.736 +2020-04-22 07:30:00,110.25,138.798,67.328,30.736 +2020-04-22 07:45:00,108.56,135.684,67.328,30.736 +2020-04-22 08:00:00,105.32,133.16899999999998,60.23,30.736 +2020-04-22 08:15:00,105.49,131.882,60.23,30.736 +2020-04-22 08:30:00,110.55,127.635,60.23,30.736 +2020-04-22 08:45:00,113.42,125.93700000000001,60.23,30.736 +2020-04-22 09:00:00,107.8,119.671,56.845,30.736 +2020-04-22 09:15:00,107.23,117.49,56.845,30.736 +2020-04-22 09:30:00,106.86,119.613,56.845,30.736 +2020-04-22 09:45:00,108.88,119.24600000000001,56.845,30.736 +2020-04-22 10:00:00,117.52,114.87799999999999,53.832,30.736 +2020-04-22 10:15:00,121.69,115.073,53.832,30.736 +2020-04-22 10:30:00,118.62,113.68,53.832,30.736 +2020-04-22 10:45:00,118.16,113.852,53.832,30.736 +2020-04-22 11:00:00,108.04,108.51700000000001,53.225,30.736 +2020-04-22 11:15:00,106.35,109.412,53.225,30.736 +2020-04-22 11:30:00,113.65,110.598,53.225,30.736 +2020-04-22 11:45:00,119.82,111.302,53.225,30.736 +2020-04-22 12:00:00,111.54,106.476,50.676,30.736 +2020-04-22 12:15:00,112.94,107.34299999999999,50.676,30.736 +2020-04-22 12:30:00,106.75,106.238,50.676,30.736 +2020-04-22 12:45:00,107.24,106.98299999999999,50.676,30.736 +2020-04-22 13:00:00,106.99,107.78200000000001,50.646,30.736 +2020-04-22 13:15:00,103.94,107.039,50.646,30.736 +2020-04-22 13:30:00,111.47,104.92,50.646,30.736 +2020-04-22 13:45:00,121.34,103.508,50.646,30.736 +2020-04-22 14:00:00,117.59,104.902,50.786,30.736 +2020-04-22 14:15:00,115.99,104.306,50.786,30.736 +2020-04-22 14:30:00,118.6,103.917,50.786,30.736 +2020-04-22 14:45:00,123.1,104.39399999999999,50.786,30.736 +2020-04-22 15:00:00,122.69,105.51799999999999,51.535,30.736 +2020-04-22 15:15:00,113.87,103.881,51.535,30.736 +2020-04-22 15:30:00,107.0,103.198,51.535,30.736 +2020-04-22 15:45:00,105.01,102.72399999999999,51.535,30.736 +2020-04-22 16:00:00,115.45,102.803,53.157,30.736 +2020-04-22 16:15:00,119.01,102.509,53.157,30.736 +2020-04-22 16:30:00,121.41,102.52,53.157,30.736 +2020-04-22 16:45:00,118.86,100.01100000000001,53.157,30.736 +2020-04-22 17:00:00,126.78,99.868,57.793,30.736 +2020-04-22 17:15:00,124.76,102.12,57.793,30.736 +2020-04-22 17:30:00,124.73,103.48,57.793,30.736 +2020-04-22 17:45:00,117.96,104.79799999999999,57.793,30.736 +2020-04-22 18:00:00,119.79,105.698,59.872,30.736 +2020-04-22 18:15:00,119.56,107.354,59.872,30.736 +2020-04-22 18:30:00,118.02,106.11200000000001,59.872,30.736 +2020-04-22 18:45:00,114.89,111.771,59.872,30.736 +2020-04-22 19:00:00,117.2,109.917,60.17100000000001,30.736 +2020-04-22 19:15:00,115.46,109.146,60.17100000000001,30.736 +2020-04-22 19:30:00,111.46,109.208,60.17100000000001,30.736 +2020-04-22 19:45:00,114.24,109.75299999999999,60.17100000000001,30.736 +2020-04-22 20:00:00,110.05,107.742,65.015,30.736 +2020-04-22 20:15:00,112.02,105.141,65.015,30.736 +2020-04-22 20:30:00,103.59,104.27600000000001,65.015,30.736 +2020-04-22 20:45:00,101.23,103.60600000000001,65.015,30.736 +2020-04-22 21:00:00,101.65,99.30799999999999,57.805,30.736 +2020-04-22 21:15:00,100.48,98.88799999999999,57.805,30.736 +2020-04-22 21:30:00,91.45,99.18,57.805,30.736 +2020-04-22 21:45:00,90.16,98.823,57.805,30.736 +2020-04-22 22:00:00,83.23,94.425,52.115,30.736 +2020-04-22 22:15:00,86.17,92.711,52.115,30.736 +2020-04-22 22:30:00,88.07,81.357,52.115,30.736 +2020-04-22 22:45:00,85.72,75.62899999999999,52.115,30.736 +2020-04-22 23:00:00,77.3,67.635,42.871,30.736 +2020-04-22 23:15:00,76.06,66.535,42.871,30.736 +2020-04-22 23:30:00,81.31,65.67,42.871,30.736 +2020-04-22 23:45:00,81.61,66.325,42.871,30.736 +2020-04-23 00:00:00,77.46,63.331,39.203,30.736 +2020-04-23 00:15:00,73.8,63.897,39.203,30.736 +2020-04-23 00:30:00,72.13,62.251000000000005,39.203,30.736 +2020-04-23 00:45:00,78.28,60.717,39.203,30.736 +2020-04-23 01:00:00,77.24,61.004,37.118,30.736 +2020-04-23 01:15:00,76.9,60.145,37.118,30.736 +2020-04-23 01:30:00,73.05,58.708,37.118,30.736 +2020-04-23 01:45:00,74.2,58.173,37.118,30.736 +2020-04-23 02:00:00,77.66,58.958,35.647,30.736 +2020-04-23 02:15:00,74.79,58.07,35.647,30.736 +2020-04-23 02:30:00,73.24,60.656000000000006,35.647,30.736 +2020-04-23 02:45:00,74.37,61.248000000000005,35.647,30.736 +2020-04-23 03:00:00,74.42,64.38600000000001,34.585,30.736 +2020-04-23 03:15:00,83.44,65.436,34.585,30.736 +2020-04-23 03:30:00,82.78,64.861,34.585,30.736 +2020-04-23 03:45:00,85.04,65.282,34.585,30.736 +2020-04-23 04:00:00,81.36,76.975,36.184,30.736 +2020-04-23 04:15:00,83.4,89.069,36.184,30.736 +2020-04-23 04:30:00,86.65,88.79899999999999,36.184,30.736 +2020-04-23 04:45:00,90.42,90.58200000000001,36.184,30.736 +2020-04-23 05:00:00,96.73,123.321,41.019,30.736 +2020-04-23 05:15:00,99.72,154.384,41.019,30.736 +2020-04-23 05:30:00,104.3,143.409,41.019,30.736 +2020-04-23 05:45:00,108.09,133.485,41.019,30.736 +2020-04-23 06:00:00,115.69,135.297,53.963,30.736 +2020-04-23 06:15:00,112.99,140.192,53.963,30.736 +2020-04-23 06:30:00,117.65,137.392,53.963,30.736 +2020-04-23 06:45:00,114.98,137.661,53.963,30.736 +2020-04-23 07:00:00,122.88,139.759,66.512,30.736 +2020-04-23 07:15:00,119.72,140.15200000000002,66.512,30.736 +2020-04-23 07:30:00,116.23,138.096,66.512,30.736 +2020-04-23 07:45:00,110.91,134.984,66.512,30.736 +2020-04-23 08:00:00,109.73,132.45600000000002,58.86,30.736 +2020-04-23 08:15:00,111.4,131.204,58.86,30.736 +2020-04-23 08:30:00,115.66,126.929,58.86,30.736 +2020-04-23 08:45:00,116.9,125.26,58.86,30.736 +2020-04-23 09:00:00,117.14,118.99799999999999,52.156000000000006,30.736 +2020-04-23 09:15:00,117.82,116.822,52.156000000000006,30.736 +2020-04-23 09:30:00,125.16,118.961,52.156000000000006,30.736 +2020-04-23 09:45:00,126.64,118.62700000000001,52.156000000000006,30.736 +2020-04-23 10:00:00,115.39,114.26899999999999,49.034,30.736 +2020-04-23 10:15:00,107.22,114.51,49.034,30.736 +2020-04-23 10:30:00,110.97,113.139,49.034,30.736 +2020-04-23 10:45:00,105.98,113.331,49.034,30.736 +2020-04-23 11:00:00,110.48,107.988,46.53,30.736 +2020-04-23 11:15:00,109.12,108.90700000000001,46.53,30.736 +2020-04-23 11:30:00,104.52,110.09200000000001,46.53,30.736 +2020-04-23 11:45:00,118.1,110.81299999999999,46.53,30.736 +2020-04-23 12:00:00,100.86,106.01799999999999,43.318000000000005,30.736 +2020-04-23 12:15:00,107.42,106.89200000000001,43.318000000000005,30.736 +2020-04-23 12:30:00,119.08,105.743,43.318000000000005,30.736 +2020-04-23 12:45:00,113.59,106.492,43.318000000000005,30.736 +2020-04-23 13:00:00,105.64,107.32799999999999,41.608000000000004,30.736 +2020-04-23 13:15:00,109.66,106.58,41.608000000000004,30.736 +2020-04-23 13:30:00,107.3,104.464,41.608000000000004,30.736 +2020-04-23 13:45:00,109.96,103.055,41.608000000000004,30.736 +2020-04-23 14:00:00,108.12,104.509,41.786,30.736 +2020-04-23 14:15:00,98.99,103.896,41.786,30.736 +2020-04-23 14:30:00,100.33,103.461,41.786,30.736 +2020-04-23 14:45:00,105.57,103.94200000000001,41.786,30.736 +2020-04-23 15:00:00,105.07,105.101,44.181999999999995,30.736 +2020-04-23 15:15:00,107.35,103.44200000000001,44.181999999999995,30.736 +2020-04-23 15:30:00,99.98,102.71700000000001,44.181999999999995,30.736 +2020-04-23 15:45:00,100.25,102.22399999999999,44.181999999999995,30.736 +2020-04-23 16:00:00,98.86,102.346,45.956,30.736 +2020-04-23 16:15:00,107.63,102.027,45.956,30.736 +2020-04-23 16:30:00,113.08,102.045,45.956,30.736 +2020-04-23 16:45:00,113.6,99.471,45.956,30.736 +2020-04-23 17:00:00,112.41,99.37799999999999,50.702,30.736 +2020-04-23 17:15:00,112.21,101.604,50.702,30.736 +2020-04-23 17:30:00,118.08,102.958,50.702,30.736 +2020-04-23 17:45:00,120.54,104.251,50.702,30.736 +2020-04-23 18:00:00,119.87,105.165,53.595,30.736 +2020-04-23 18:15:00,109.3,106.839,53.595,30.736 +2020-04-23 18:30:00,109.67,105.585,53.595,30.736 +2020-04-23 18:45:00,108.25,111.24799999999999,53.595,30.736 +2020-04-23 19:00:00,107.8,109.385,54.207,30.736 +2020-04-23 19:15:00,105.82,108.62,54.207,30.736 +2020-04-23 19:30:00,107.51,108.69200000000001,54.207,30.736 +2020-04-23 19:45:00,112.01,109.26,54.207,30.736 +2020-04-23 20:00:00,110.19,107.22,56.948,30.736 +2020-04-23 20:15:00,109.28,104.626,56.948,30.736 +2020-04-23 20:30:00,106.75,103.795,56.948,30.736 +2020-04-23 20:45:00,105.41,103.15899999999999,56.948,30.736 +2020-04-23 21:00:00,103.61,98.861,52.157,30.736 +2020-04-23 21:15:00,98.19,98.456,52.157,30.736 +2020-04-23 21:30:00,87.51,98.734,52.157,30.736 +2020-04-23 21:45:00,93.02,98.40700000000001,52.157,30.736 +2020-04-23 22:00:00,90.12,94.023,47.483000000000004,30.736 +2020-04-23 22:15:00,89.7,92.33200000000001,47.483000000000004,30.736 +2020-04-23 22:30:00,83.45,80.955,47.483000000000004,30.736 +2020-04-23 22:45:00,79.56,75.218,47.483000000000004,30.736 +2020-04-23 23:00:00,78.7,67.197,41.978,30.736 +2020-04-23 23:15:00,82.28,66.135,41.978,30.736 +2020-04-23 23:30:00,81.32,65.26899999999999,41.978,30.736 +2020-04-23 23:45:00,80.34,65.931,41.978,30.736 +2020-04-24 00:00:00,72.2,61.173,39.301,30.736 +2020-04-24 00:15:00,76.53,62.001999999999995,39.301,30.736 +2020-04-24 00:30:00,78.65,60.465,39.301,30.736 +2020-04-24 00:45:00,78.84,59.282,39.301,30.736 +2020-04-24 01:00:00,72.93,59.141999999999996,37.976,30.736 +2020-04-24 01:15:00,76.27,58.315,37.976,30.736 +2020-04-24 01:30:00,79.2,57.232,37.976,30.736 +2020-04-24 01:45:00,79.29,56.583999999999996,37.976,30.736 +2020-04-24 02:00:00,75.93,58.038999999999994,37.041,30.736 +2020-04-24 02:15:00,76.43,57.04,37.041,30.736 +2020-04-24 02:30:00,79.9,60.477,37.041,30.736 +2020-04-24 02:45:00,79.92,60.628,37.041,30.736 +2020-04-24 03:00:00,78.47,63.809,37.575,30.736 +2020-04-24 03:15:00,78.54,64.48100000000001,37.575,30.736 +2020-04-24 03:30:00,83.02,63.73,37.575,30.736 +2020-04-24 03:45:00,86.25,64.94,37.575,30.736 +2020-04-24 04:00:00,84.35,76.809,39.058,30.736 +2020-04-24 04:15:00,84.36,87.662,39.058,30.736 +2020-04-24 04:30:00,84.53,88.164,39.058,30.736 +2020-04-24 04:45:00,87.97,88.916,39.058,30.736 +2020-04-24 05:00:00,94.74,120.58200000000001,43.256,30.736 +2020-04-24 05:15:00,98.06,152.952,43.256,30.736 +2020-04-24 05:30:00,101.99,142.67,43.256,30.736 +2020-04-24 05:45:00,104.32,132.45,43.256,30.736 +2020-04-24 06:00:00,110.14,134.679,56.093999999999994,30.736 +2020-04-24 06:15:00,110.92,138.92700000000002,56.093999999999994,30.736 +2020-04-24 06:30:00,115.01,135.642,56.093999999999994,30.736 +2020-04-24 06:45:00,113.33,136.634,56.093999999999994,30.736 +2020-04-24 07:00:00,114.79,138.756,66.92699999999999,30.736 +2020-04-24 07:15:00,114.94,140.278,66.92699999999999,30.736 +2020-04-24 07:30:00,111.87,136.784,66.92699999999999,30.736 +2020-04-24 07:45:00,113.65,133.072,66.92699999999999,30.736 +2020-04-24 08:00:00,111.88,130.465,60.332,30.736 +2020-04-24 08:15:00,111.97,129.475,60.332,30.736 +2020-04-24 08:30:00,116.04,125.63600000000001,60.332,30.736 +2020-04-24 08:45:00,116.96,123.051,60.332,30.736 +2020-04-24 09:00:00,113.47,115.54,56.085,30.736 +2020-04-24 09:15:00,109.61,114.911,56.085,30.736 +2020-04-24 09:30:00,104.73,116.389,56.085,30.736 +2020-04-24 09:45:00,106.39,116.274,56.085,30.736 +2020-04-24 10:00:00,104.8,111.147,52.91,30.736 +2020-04-24 10:15:00,110.96,111.698,52.91,30.736 +2020-04-24 10:30:00,108.8,110.649,52.91,30.736 +2020-04-24 10:45:00,112.42,110.54799999999999,52.91,30.736 +2020-04-24 11:00:00,105.01,105.321,52.278999999999996,30.736 +2020-04-24 11:15:00,106.04,105.079,52.278999999999996,30.736 +2020-04-24 11:30:00,106.23,107.06299999999999,52.278999999999996,30.736 +2020-04-24 11:45:00,105.8,107.24700000000001,52.278999999999996,30.736 +2020-04-24 12:00:00,101.15,103.402,49.023999999999994,30.736 +2020-04-24 12:15:00,100.58,102.65100000000001,49.023999999999994,30.736 +2020-04-24 12:30:00,96.07,101.62299999999999,49.023999999999994,30.736 +2020-04-24 12:45:00,90.58,102.21799999999999,49.023999999999994,30.736 +2020-04-24 13:00:00,91.3,104.00399999999999,46.82,30.736 +2020-04-24 13:15:00,91.72,103.84899999999999,46.82,30.736 +2020-04-24 13:30:00,89.96,102.21700000000001,46.82,30.736 +2020-04-24 13:45:00,90.43,100.95299999999999,46.82,30.736 +2020-04-24 14:00:00,91.03,101.309,45.756,30.736 +2020-04-24 14:15:00,90.38,100.82799999999999,45.756,30.736 +2020-04-24 14:30:00,90.13,101.54700000000001,45.756,30.736 +2020-04-24 14:45:00,87.82,101.804,45.756,30.736 +2020-04-24 15:00:00,88.57,102.664,47.56,30.736 +2020-04-24 15:15:00,89.54,100.579,47.56,30.736 +2020-04-24 15:30:00,90.6,98.565,47.56,30.736 +2020-04-24 15:45:00,93.53,98.59200000000001,47.56,30.736 +2020-04-24 16:00:00,96.08,97.604,49.581,30.736 +2020-04-24 16:15:00,98.58,97.73100000000001,49.581,30.736 +2020-04-24 16:30:00,103.04,97.697,49.581,30.736 +2020-04-24 16:45:00,102.74,94.509,49.581,30.736 +2020-04-24 17:00:00,106.18,95.684,53.918,30.736 +2020-04-24 17:15:00,105.16,97.54299999999999,53.918,30.736 +2020-04-24 17:30:00,105.91,98.814,53.918,30.736 +2020-04-24 17:45:00,107.32,99.845,53.918,30.736 +2020-04-24 18:00:00,107.44,101.23700000000001,54.266000000000005,30.736 +2020-04-24 18:15:00,104.49,102.10700000000001,54.266000000000005,30.736 +2020-04-24 18:30:00,104.57,100.98899999999999,54.266000000000005,30.736 +2020-04-24 18:45:00,103.28,106.92399999999999,54.266000000000005,30.736 +2020-04-24 19:00:00,105.31,106.14399999999999,54.092,30.736 +2020-04-24 19:15:00,99.77,106.50299999999999,54.092,30.736 +2020-04-24 19:30:00,104.65,106.406,54.092,30.736 +2020-04-24 19:45:00,102.74,106.08,54.092,30.736 +2020-04-24 20:00:00,99.02,103.93700000000001,59.038999999999994,30.736 +2020-04-24 20:15:00,105.31,101.875,59.038999999999994,30.736 +2020-04-24 20:30:00,106.75,100.73700000000001,59.038999999999994,30.736 +2020-04-24 20:45:00,103.82,99.896,59.038999999999994,30.736 +2020-04-24 21:00:00,92.72,96.742,53.346000000000004,30.736 +2020-04-24 21:15:00,89.77,97.68700000000001,53.346000000000004,30.736 +2020-04-24 21:30:00,92.29,97.876,53.346000000000004,30.736 +2020-04-24 21:45:00,91.53,98.0,53.346000000000004,30.736 +2020-04-24 22:00:00,86.77,94.05799999999999,47.938,30.736 +2020-04-24 22:15:00,82.96,92.156,47.938,30.736 +2020-04-24 22:30:00,85.15,87.279,47.938,30.736 +2020-04-24 22:45:00,83.5,83.97200000000001,47.938,30.736 +2020-04-24 23:00:00,78.39,76.804,40.266,30.736 +2020-04-24 23:15:00,76.03,73.711,40.266,30.736 +2020-04-24 23:30:00,79.22,70.86399999999999,40.266,30.736 +2020-04-24 23:45:00,77.46,71.1,40.266,30.736 +2020-04-25 00:00:00,72.92,60.177,39.184,30.618000000000002 +2020-04-25 00:15:00,67.06,58.391000000000005,39.184,30.618000000000002 +2020-04-25 00:30:00,65.85,57.206,39.184,30.618000000000002 +2020-04-25 00:45:00,69.65,55.895,39.184,30.618000000000002 +2020-04-25 01:00:00,71.38,56.265,34.692,30.618000000000002 +2020-04-25 01:15:00,72.76,55.318000000000005,34.692,30.618000000000002 +2020-04-25 01:30:00,69.42,53.401,34.692,30.618000000000002 +2020-04-25 01:45:00,71.88,53.498000000000005,34.692,30.618000000000002 +2020-04-25 02:00:00,71.15,54.626000000000005,32.919000000000004,30.618000000000002 +2020-04-25 02:15:00,71.09,52.883,32.919000000000004,30.618000000000002 +2020-04-25 02:30:00,65.69,55.233999999999995,32.919000000000004,30.618000000000002 +2020-04-25 02:45:00,61.4,55.98,32.919000000000004,30.618000000000002 +2020-04-25 03:00:00,64.48,58.56100000000001,32.024,30.618000000000002 +2020-04-25 03:15:00,63.49,58.118,32.024,30.618000000000002 +2020-04-25 03:30:00,63.7,56.793,32.024,30.618000000000002 +2020-04-25 03:45:00,64.2,59.101000000000006,32.024,30.618000000000002 +2020-04-25 04:00:00,64.64,67.586,31.958000000000002,30.618000000000002 +2020-04-25 04:15:00,64.45,76.515,31.958000000000002,30.618000000000002 +2020-04-25 04:30:00,62.58,74.78,31.958000000000002,30.618000000000002 +2020-04-25 04:45:00,61.79,75.464,31.958000000000002,30.618000000000002 +2020-04-25 05:00:00,62.67,93.719,32.75,30.618000000000002 +2020-04-25 05:15:00,63.83,109.10600000000001,32.75,30.618000000000002 +2020-04-25 05:30:00,66.59,100.096,32.75,30.618000000000002 +2020-04-25 05:45:00,66.17,95.70100000000001,32.75,30.618000000000002 +2020-04-25 06:00:00,69.06,114.625,34.461999999999996,30.618000000000002 +2020-04-25 06:15:00,71.02,132.321,34.461999999999996,30.618000000000002 +2020-04-25 06:30:00,71.93,124.402,34.461999999999996,30.618000000000002 +2020-04-25 06:45:00,73.64,118.735,34.461999999999996,30.618000000000002 +2020-04-25 07:00:00,76.52,117.56200000000001,37.736,30.618000000000002 +2020-04-25 07:15:00,76.86,117.56,37.736,30.618000000000002 +2020-04-25 07:30:00,77.45,116.432,37.736,30.618000000000002 +2020-04-25 07:45:00,75.16,115.383,37.736,30.618000000000002 +2020-04-25 08:00:00,75.87,115.021,42.34,30.618000000000002 +2020-04-25 08:15:00,75.01,115.853,42.34,30.618000000000002 +2020-04-25 08:30:00,76.19,112.833,42.34,30.618000000000002 +2020-04-25 08:45:00,75.0,112.568,42.34,30.618000000000002 +2020-04-25 09:00:00,77.22,107.902,43.571999999999996,30.618000000000002 +2020-04-25 09:15:00,74.1,107.99700000000001,43.571999999999996,30.618000000000002 +2020-04-25 09:30:00,74.58,110.29799999999999,43.571999999999996,30.618000000000002 +2020-04-25 09:45:00,72.53,110.01700000000001,43.571999999999996,30.618000000000002 +2020-04-25 10:00:00,70.38,105.26,40.514,30.618000000000002 +2020-04-25 10:15:00,71.83,106.17,40.514,30.618000000000002 +2020-04-25 10:30:00,72.86,105.02600000000001,40.514,30.618000000000002 +2020-04-25 10:45:00,76.31,105.464,40.514,30.618000000000002 +2020-04-25 11:00:00,75.64,100.208,36.388000000000005,30.618000000000002 +2020-04-25 11:15:00,72.22,100.119,36.388000000000005,30.618000000000002 +2020-04-25 11:30:00,70.44,101.631,36.388000000000005,30.618000000000002 +2020-04-25 11:45:00,66.44,101.69,36.388000000000005,30.618000000000002 +2020-04-25 12:00:00,62.4,97.416,35.217,30.618000000000002 +2020-04-25 12:15:00,63.37,97.552,35.217,30.618000000000002 +2020-04-25 12:30:00,62.6,96.59299999999999,35.217,30.618000000000002 +2020-04-25 12:45:00,62.97,97.165,35.217,30.618000000000002 +2020-04-25 13:00:00,66.68,98.23899999999999,32.001999999999995,30.618000000000002 +2020-04-25 13:15:00,66.46,96.544,32.001999999999995,30.618000000000002 +2020-04-25 13:30:00,68.38,94.76,32.001999999999995,30.618000000000002 +2020-04-25 13:45:00,63.86,92.984,32.001999999999995,30.618000000000002 +2020-04-25 14:00:00,65.79,94.14200000000001,31.304000000000002,30.618000000000002 +2020-04-25 14:15:00,65.45,92.59,31.304000000000002,30.618000000000002 +2020-04-25 14:30:00,67.98,91.934,31.304000000000002,30.618000000000002 +2020-04-25 14:45:00,70.14,92.603,31.304000000000002,30.618000000000002 +2020-04-25 15:00:00,73.95,94.147,34.731,30.618000000000002 +2020-04-25 15:15:00,68.45,92.935,34.731,30.618000000000002 +2020-04-25 15:30:00,69.17,91.913,34.731,30.618000000000002 +2020-04-25 15:45:00,71.96,91.391,34.731,30.618000000000002 +2020-04-25 16:00:00,74.03,91.31200000000001,38.769,30.618000000000002 +2020-04-25 16:15:00,73.54,91.38,38.769,30.618000000000002 +2020-04-25 16:30:00,77.94,91.461,38.769,30.618000000000002 +2020-04-25 16:45:00,77.97,88.696,38.769,30.618000000000002 +2020-04-25 17:00:00,79.95,88.99700000000001,44.928000000000004,30.618000000000002 +2020-04-25 17:15:00,79.81,90.412,44.928000000000004,30.618000000000002 +2020-04-25 17:30:00,81.88,91.541,44.928000000000004,30.618000000000002 +2020-04-25 17:45:00,79.84,92.62799999999999,44.928000000000004,30.618000000000002 +2020-04-25 18:00:00,81.95,94.76899999999999,47.786,30.618000000000002 +2020-04-25 18:15:00,80.8,97.704,47.786,30.618000000000002 +2020-04-25 18:30:00,80.62,98.166,47.786,30.618000000000002 +2020-04-25 18:45:00,81.58,100.06200000000001,47.786,30.618000000000002 +2020-04-25 19:00:00,82.25,98.82600000000001,47.463,30.618000000000002 +2020-04-25 19:15:00,81.6,98.26799999999999,47.463,30.618000000000002 +2020-04-25 19:30:00,78.79,99.083,47.463,30.618000000000002 +2020-04-25 19:45:00,81.11,99.837,47.463,30.618000000000002 +2020-04-25 20:00:00,79.47,99.35600000000001,43.735,30.618000000000002 +2020-04-25 20:15:00,80.74,98.01899999999999,43.735,30.618000000000002 +2020-04-25 20:30:00,77.33,96.155,43.735,30.618000000000002 +2020-04-25 20:45:00,77.88,96.339,43.735,30.618000000000002 +2020-04-25 21:00:00,76.72,93.439,40.346,30.618000000000002 +2020-04-25 21:15:00,73.54,94.40100000000001,40.346,30.618000000000002 +2020-04-25 21:30:00,70.17,95.348,40.346,30.618000000000002 +2020-04-25 21:45:00,69.94,94.93,40.346,30.618000000000002 +2020-04-25 22:00:00,66.61,91.697,39.323,30.618000000000002 +2020-04-25 22:15:00,65.99,91.294,39.323,30.618000000000002 +2020-04-25 22:30:00,63.67,89.425,39.323,30.618000000000002 +2020-04-25 22:45:00,62.7,87.34100000000001,39.323,30.618000000000002 +2020-04-25 23:00:00,58.03,80.985,33.716,30.618000000000002 +2020-04-25 23:15:00,58.89,77.345,33.716,30.618000000000002 +2020-04-25 23:30:00,57.34,75.185,33.716,30.618000000000002 +2020-04-25 23:45:00,57.63,74.384,33.716,30.618000000000002 +2020-04-26 00:00:00,53.94,60.93899999999999,28.703000000000003,30.618000000000002 +2020-04-26 00:15:00,54.77,58.223,28.703000000000003,30.618000000000002 +2020-04-26 00:30:00,54.11,56.742,28.703000000000003,30.618000000000002 +2020-04-26 00:45:00,54.48,55.713,28.703000000000003,30.618000000000002 +2020-04-26 01:00:00,53.07,56.185,26.171,30.618000000000002 +2020-04-26 01:15:00,53.77,55.653,26.171,30.618000000000002 +2020-04-26 01:30:00,53.29,53.88399999999999,26.171,30.618000000000002 +2020-04-26 01:45:00,53.5,53.567,26.171,30.618000000000002 +2020-04-26 02:00:00,52.56,54.358999999999995,25.326999999999998,30.618000000000002 +2020-04-26 02:15:00,53.08,52.657,25.326999999999998,30.618000000000002 +2020-04-26 02:30:00,53.0,55.581,25.326999999999998,30.618000000000002 +2020-04-26 02:45:00,53.59,56.367,25.326999999999998,30.618000000000002 +2020-04-26 03:00:00,53.57,59.565,24.311999999999998,30.618000000000002 +2020-04-26 03:15:00,54.44,59.016000000000005,24.311999999999998,30.618000000000002 +2020-04-26 03:30:00,54.58,57.88,24.311999999999998,30.618000000000002 +2020-04-26 03:45:00,54.59,59.626999999999995,24.311999999999998,30.618000000000002 +2020-04-26 04:00:00,55.25,67.928,25.33,30.618000000000002 +2020-04-26 04:15:00,56.11,76.015,25.33,30.618000000000002 +2020-04-26 04:30:00,54.4,75.235,25.33,30.618000000000002 +2020-04-26 04:45:00,54.42,75.722,25.33,30.618000000000002 +2020-04-26 05:00:00,53.23,92.29700000000001,25.309,30.618000000000002 +2020-04-26 05:15:00,54.13,105.882,25.309,30.618000000000002 +2020-04-26 05:30:00,53.73,96.536,25.309,30.618000000000002 +2020-04-26 05:45:00,55.13,92.115,25.309,30.618000000000002 +2020-04-26 06:00:00,56.83,109.336,25.945999999999998,30.618000000000002 +2020-04-26 06:15:00,59.16,126.744,25.945999999999998,30.618000000000002 +2020-04-26 06:30:00,59.67,117.788,25.945999999999998,30.618000000000002 +2020-04-26 06:45:00,60.03,110.90899999999999,25.945999999999998,30.618000000000002 +2020-04-26 07:00:00,62.06,111.11,27.87,30.618000000000002 +2020-04-26 07:15:00,63.4,109.559,27.87,30.618000000000002 +2020-04-26 07:30:00,62.84,108.62899999999999,27.87,30.618000000000002 +2020-04-26 07:45:00,61.39,107.17399999999999,27.87,30.618000000000002 +2020-04-26 08:00:00,61.91,108.194,32.114000000000004,30.618000000000002 +2020-04-26 08:15:00,60.92,109.787,32.114000000000004,30.618000000000002 +2020-04-26 08:30:00,59.14,108.181,32.114000000000004,30.618000000000002 +2020-04-26 08:45:00,58.58,108.95200000000001,32.114000000000004,30.618000000000002 +2020-04-26 09:00:00,57.18,103.98299999999999,34.222,30.618000000000002 +2020-04-26 09:15:00,58.03,104.06200000000001,34.222,30.618000000000002 +2020-04-26 09:30:00,58.37,106.571,34.222,30.618000000000002 +2020-04-26 09:45:00,60.08,106.904,34.222,30.618000000000002 +2020-04-26 10:00:00,59.48,103.84200000000001,34.544000000000004,30.618000000000002 +2020-04-26 10:15:00,60.83,105.178,34.544000000000004,30.618000000000002 +2020-04-26 10:30:00,61.23,104.53,34.544000000000004,30.618000000000002 +2020-04-26 10:45:00,61.1,104.56299999999999,34.544000000000004,30.618000000000002 +2020-04-26 11:00:00,62.69,99.581,36.368,30.618000000000002 +2020-04-26 11:15:00,58.41,99.315,36.368,30.618000000000002 +2020-04-26 11:30:00,56.55,100.654,36.368,30.618000000000002 +2020-04-26 11:45:00,57.98,101.229,36.368,30.618000000000002 +2020-04-26 12:00:00,52.84,97.259,32.433,30.618000000000002 +2020-04-26 12:15:00,51.83,98.132,32.433,30.618000000000002 +2020-04-26 12:30:00,49.53,96.414,32.433,30.618000000000002 +2020-04-26 12:45:00,50.89,96.04799999999999,32.433,30.618000000000002 +2020-04-26 13:00:00,45.96,96.559,28.971999999999998,30.618000000000002 +2020-04-26 13:15:00,46.59,96.266,28.971999999999998,30.618000000000002 +2020-04-26 13:30:00,46.54,93.7,28.971999999999998,30.618000000000002 +2020-04-26 13:45:00,49.1,92.21,28.971999999999998,30.618000000000002 +2020-04-26 14:00:00,49.36,94.28200000000001,25.531999999999996,30.618000000000002 +2020-04-26 14:15:00,47.82,93.669,25.531999999999996,30.618000000000002 +2020-04-26 14:30:00,46.91,92.95,25.531999999999996,30.618000000000002 +2020-04-26 14:45:00,50.24,92.745,25.531999999999996,30.618000000000002 +2020-04-26 15:00:00,48.5,93.521,25.766,30.618000000000002 +2020-04-26 15:15:00,49.38,92.24700000000001,25.766,30.618000000000002 +2020-04-26 15:30:00,52.0,91.41799999999999,25.766,30.618000000000002 +2020-04-26 15:45:00,56.72,91.45100000000001,25.766,30.618000000000002 +2020-04-26 16:00:00,58.03,91.37200000000001,29.232,30.618000000000002 +2020-04-26 16:15:00,58.12,91.04899999999999,29.232,30.618000000000002 +2020-04-26 16:30:00,61.98,91.90100000000001,29.232,30.618000000000002 +2020-04-26 16:45:00,66.32,89.185,29.232,30.618000000000002 +2020-04-26 17:00:00,69.92,89.704,37.431,30.618000000000002 +2020-04-26 17:15:00,72.25,91.865,37.431,30.618000000000002 +2020-04-26 17:30:00,71.68,93.652,37.431,30.618000000000002 +2020-04-26 17:45:00,72.65,96.22200000000001,37.431,30.618000000000002 +2020-04-26 18:00:00,74.1,98.405,41.251999999999995,30.618000000000002 +2020-04-26 18:15:00,73.58,101.804,41.251999999999995,30.618000000000002 +2020-04-26 18:30:00,79.83,100.978,41.251999999999995,30.618000000000002 +2020-04-26 18:45:00,81.73,103.944,41.251999999999995,30.618000000000002 +2020-04-26 19:00:00,82.6,103.89399999999999,41.784,30.618000000000002 +2020-04-26 19:15:00,74.77,102.92299999999999,41.784,30.618000000000002 +2020-04-26 19:30:00,78.18,103.49700000000001,41.784,30.618000000000002 +2020-04-26 19:45:00,82.92,104.728,41.784,30.618000000000002 +2020-04-26 20:00:00,80.12,104.32799999999999,40.804,30.618000000000002 +2020-04-26 20:15:00,79.67,103.42299999999999,40.804,30.618000000000002 +2020-04-26 20:30:00,83.74,102.774,40.804,30.618000000000002 +2020-04-26 20:45:00,87.56,101.26,40.804,30.618000000000002 +2020-04-26 21:00:00,86.73,96.89200000000001,38.379,30.618000000000002 +2020-04-26 21:15:00,77.37,97.345,38.379,30.618000000000002 +2020-04-26 21:30:00,78.96,98.001,38.379,30.618000000000002 +2020-04-26 21:45:00,72.49,97.919,38.379,30.618000000000002 +2020-04-26 22:00:00,71.02,95.426,37.87,30.618000000000002 +2020-04-26 22:15:00,74.6,93.542,37.87,30.618000000000002 +2020-04-26 22:30:00,74.46,89.67299999999999,37.87,30.618000000000002 +2020-04-26 22:45:00,77.49,86.29799999999999,37.87,30.618000000000002 +2020-04-26 23:00:00,67.34,78.266,33.332,30.618000000000002 +2020-04-26 23:15:00,67.41,76.46300000000001,33.332,30.618000000000002 +2020-04-26 23:30:00,65.25,74.368,33.332,30.618000000000002 +2020-04-26 23:45:00,71.84,74.14,33.332,30.618000000000002 +2020-04-27 00:00:00,65.34,63.676,34.698,30.736 +2020-04-27 00:15:00,68.11,62.993,34.698,30.736 +2020-04-27 00:30:00,62.17,61.31,34.698,30.736 +2020-04-27 00:45:00,65.5,59.75,34.698,30.736 +2020-04-27 01:00:00,67.94,60.476000000000006,32.889,30.736 +2020-04-27 01:15:00,68.09,59.667,32.889,30.736 +2020-04-27 01:30:00,64.87,58.151,32.889,30.736 +2020-04-27 01:45:00,62.39,57.826,32.889,30.736 +2020-04-27 02:00:00,60.51,58.898999999999994,32.06,30.736 +2020-04-27 02:15:00,61.52,57.211000000000006,32.06,30.736 +2020-04-27 02:30:00,63.11,60.428999999999995,32.06,30.736 +2020-04-27 02:45:00,65.0,60.816,32.06,30.736 +2020-04-27 03:00:00,70.94,64.986,30.515,30.736 +2020-04-27 03:15:00,71.15,65.718,30.515,30.736 +2020-04-27 03:30:00,66.39,64.915,30.515,30.736 +2020-04-27 03:45:00,68.04,66.095,30.515,30.736 +2020-04-27 04:00:00,69.68,78.61399999999999,31.436,30.736 +2020-04-27 04:15:00,70.3,90.708,31.436,30.736 +2020-04-27 04:30:00,70.44,90.67,31.436,30.736 +2020-04-27 04:45:00,74.91,91.48200000000001,31.436,30.736 +2020-04-27 05:00:00,81.04,120.303,38.997,30.736 +2020-04-27 05:15:00,85.59,150.614,38.997,30.736 +2020-04-27 05:30:00,88.32,140.344,38.997,30.736 +2020-04-27 05:45:00,87.62,131.126,38.997,30.736 +2020-04-27 06:00:00,93.16,132.86700000000002,54.97,30.736 +2020-04-27 06:15:00,90.44,136.747,54.97,30.736 +2020-04-27 06:30:00,94.92,134.424,54.97,30.736 +2020-04-27 06:45:00,93.91,135.476,54.97,30.736 +2020-04-27 07:00:00,97.72,137.559,66.032,30.736 +2020-04-27 07:15:00,94.82,138.159,66.032,30.736 +2020-04-27 07:30:00,94.52,136.225,66.032,30.736 +2020-04-27 07:45:00,94.9,133.67700000000002,66.032,30.736 +2020-04-27 08:00:00,92.49,131.08700000000002,59.941,30.736 +2020-04-27 08:15:00,91.85,130.80700000000002,59.941,30.736 +2020-04-27 08:30:00,93.31,126.40799999999999,59.941,30.736 +2020-04-27 08:45:00,92.94,125.59700000000001,59.941,30.736 +2020-04-27 09:00:00,92.84,119.57600000000001,54.016000000000005,30.736 +2020-04-27 09:15:00,91.53,116.648,54.016000000000005,30.736 +2020-04-27 09:30:00,89.3,118.041,54.016000000000005,30.736 +2020-04-27 09:45:00,89.57,117.095,54.016000000000005,30.736 +2020-04-27 10:00:00,89.14,113.96600000000001,50.63,30.736 +2020-04-27 10:15:00,98.83,115.074,50.63,30.736 +2020-04-27 10:30:00,93.52,113.675,50.63,30.736 +2020-04-27 10:45:00,89.12,113.164,50.63,30.736 +2020-04-27 11:00:00,89.55,106.956,49.951,30.736 +2020-04-27 11:15:00,91.6,107.90700000000001,49.951,30.736 +2020-04-27 11:30:00,86.24,110.45299999999999,49.951,30.736 +2020-04-27 11:45:00,87.15,111.12,49.951,30.736 +2020-04-27 12:00:00,88.45,107.34700000000001,46.913000000000004,30.736 +2020-04-27 12:15:00,99.89,108.29299999999999,46.913000000000004,30.736 +2020-04-27 12:30:00,92.92,106.042,46.913000000000004,30.736 +2020-04-27 12:45:00,89.09,106.566,46.913000000000004,30.736 +2020-04-27 13:00:00,96.03,108.00299999999999,47.093999999999994,30.736 +2020-04-27 13:15:00,84.25,106.37299999999999,47.093999999999994,30.736 +2020-04-27 13:30:00,77.31,103.604,47.093999999999994,30.736 +2020-04-27 13:45:00,88.26,102.666,47.093999999999994,30.736 +2020-04-27 14:00:00,85.89,103.928,46.678000000000004,30.736 +2020-04-27 14:15:00,80.66,103.271,46.678000000000004,30.736 +2020-04-27 14:30:00,80.46,102.094,46.678000000000004,30.736 +2020-04-27 14:45:00,81.74,103.096,46.678000000000004,30.736 +2020-04-27 15:00:00,82.97,104.74700000000001,47.715,30.736 +2020-04-27 15:15:00,81.62,102.262,47.715,30.736 +2020-04-27 15:30:00,82.55,101.359,47.715,30.736 +2020-04-27 15:45:00,85.7,100.82799999999999,47.715,30.736 +2020-04-27 16:00:00,85.53,101.301,49.81100000000001,30.736 +2020-04-27 16:15:00,85.86,100.56299999999999,49.81100000000001,30.736 +2020-04-27 16:30:00,88.12,100.47,49.81100000000001,30.736 +2020-04-27 16:45:00,89.52,97.03399999999999,49.81100000000001,30.736 +2020-04-27 17:00:00,93.73,96.706,55.591,30.736 +2020-04-27 17:15:00,92.69,98.552,55.591,30.736 +2020-04-27 17:30:00,95.38,99.787,55.591,30.736 +2020-04-27 17:45:00,94.15,101.258,55.591,30.736 +2020-04-27 18:00:00,95.38,102.904,56.523,30.736 +2020-04-27 18:15:00,92.93,103.962,56.523,30.736 +2020-04-27 18:30:00,92.0,102.98899999999999,56.523,30.736 +2020-04-27 18:45:00,90.2,108.369,56.523,30.736 +2020-04-27 19:00:00,90.92,107.20299999999999,56.044,30.736 +2020-04-27 19:15:00,86.2,106.48899999999999,56.044,30.736 +2020-04-27 19:30:00,85.27,107.094,56.044,30.736 +2020-04-27 19:45:00,86.87,107.507,56.044,30.736 +2020-04-27 20:00:00,84.36,105.01899999999999,61.715,30.736 +2020-04-27 20:15:00,83.51,103.73299999999999,61.715,30.736 +2020-04-27 20:30:00,80.49,102.479,61.715,30.736 +2020-04-27 20:45:00,78.45,101.991,61.715,30.736 +2020-04-27 21:00:00,72.63,97.50399999999999,56.24,30.736 +2020-04-27 21:15:00,73.27,97.652,56.24,30.736 +2020-04-27 21:30:00,68.74,98.13600000000001,56.24,30.736 +2020-04-27 21:45:00,67.7,97.67399999999999,56.24,30.736 +2020-04-27 22:00:00,63.52,92.344,50.437,30.736 +2020-04-27 22:15:00,63.05,91.10600000000001,50.437,30.736 +2020-04-27 22:30:00,60.88,79.39,50.437,30.736 +2020-04-27 22:45:00,60.19,73.476,50.437,30.736 +2020-04-27 23:00:00,81.41,65.845,42.756,30.736 +2020-04-27 23:15:00,80.65,64.377,42.756,30.736 +2020-04-27 23:30:00,78.41,63.692,42.756,30.736 +2020-04-27 23:45:00,78.52,64.492,42.756,30.736 +2020-04-28 00:00:00,77.46,61.4,39.857,30.736 +2020-04-28 00:15:00,76.93,62.035,39.857,30.736 +2020-04-28 00:30:00,76.12,60.358000000000004,39.857,30.736 +2020-04-28 00:45:00,76.92,58.856,39.857,30.736 +2020-04-28 01:00:00,78.02,59.108999999999995,37.233000000000004,30.736 +2020-04-28 01:15:00,78.26,58.13,37.233000000000004,30.736 +2020-04-28 01:30:00,75.49,56.593,37.233000000000004,30.736 +2020-04-28 01:45:00,75.85,56.073,37.233000000000004,30.736 +2020-04-28 02:00:00,78.67,56.803000000000004,35.856,30.736 +2020-04-28 02:15:00,78.88,55.836999999999996,35.856,30.736 +2020-04-28 02:30:00,73.42,58.508,35.856,30.736 +2020-04-28 02:45:00,73.56,59.13,35.856,30.736 +2020-04-28 03:00:00,74.82,62.342,34.766999999999996,30.736 +2020-04-28 03:15:00,76.05,63.271,34.766999999999996,30.736 +2020-04-28 03:30:00,79.51,62.683,34.766999999999996,30.736 +2020-04-28 03:45:00,83.12,63.217,34.766999999999996,30.736 +2020-04-28 04:00:00,84.91,74.73899999999999,35.468,30.736 +2020-04-28 04:15:00,84.94,86.62700000000001,35.468,30.736 +2020-04-28 04:30:00,87.14,86.353,35.468,30.736 +2020-04-28 04:45:00,88.32,88.088,35.468,30.736 +2020-04-28 05:00:00,95.45,120.323,40.399,30.736 +2020-04-28 05:15:00,98.01,150.866,40.399,30.736 +2020-04-28 05:30:00,100.42,140.02200000000002,40.399,30.736 +2020-04-28 05:45:00,102.69,130.343,40.399,30.736 +2020-04-28 06:00:00,107.5,132.263,54.105,30.736 +2020-04-28 06:15:00,108.18,137.054,54.105,30.736 +2020-04-28 06:30:00,110.19,134.178,54.105,30.736 +2020-04-28 06:45:00,110.95,134.42600000000002,54.105,30.736 +2020-04-28 07:00:00,112.62,136.516,63.083,30.736 +2020-04-28 07:15:00,112.53,136.851,63.083,30.736 +2020-04-28 07:30:00,113.87,134.61700000000002,63.083,30.736 +2020-04-28 07:45:00,112.32,131.526,63.083,30.736 +2020-04-28 08:00:00,110.21,128.942,57.254,30.736 +2020-04-28 08:15:00,108.73,127.866,57.254,30.736 +2020-04-28 08:30:00,109.99,123.456,57.254,30.736 +2020-04-28 08:45:00,111.24,121.929,57.254,30.736 +2020-04-28 09:00:00,110.59,115.686,51.395,30.736 +2020-04-28 09:15:00,110.8,113.535,51.395,30.736 +2020-04-28 09:30:00,110.96,115.749,51.395,30.736 +2020-04-28 09:45:00,110.3,115.579,51.395,30.736 +2020-04-28 10:00:00,108.23,111.265,48.201,30.736 +2020-04-28 10:15:00,110.0,111.735,48.201,30.736 +2020-04-28 10:30:00,108.55,110.477,48.201,30.736 +2020-04-28 10:45:00,108.8,110.765,48.201,30.736 +2020-04-28 11:00:00,106.28,105.389,46.133,30.736 +2020-04-28 11:15:00,105.09,106.42,46.133,30.736 +2020-04-28 11:30:00,103.75,107.59899999999999,46.133,30.736 +2020-04-28 11:45:00,106.98,108.41,46.133,30.736 +2020-04-28 12:00:00,106.37,103.759,44.243,30.736 +2020-04-28 12:15:00,109.77,104.667,44.243,30.736 +2020-04-28 12:30:00,108.48,103.306,44.243,30.736 +2020-04-28 12:45:00,108.19,104.073,44.243,30.736 +2020-04-28 13:00:00,104.13,105.09,45.042,30.736 +2020-04-28 13:15:00,103.69,104.32,45.042,30.736 +2020-04-28 13:30:00,104.39,102.223,45.042,30.736 +2020-04-28 13:45:00,106.18,100.82600000000001,45.042,30.736 +2020-04-28 14:00:00,111.13,102.575,44.062,30.736 +2020-04-28 14:15:00,109.93,101.874,44.062,30.736 +2020-04-28 14:30:00,107.35,101.215,44.062,30.736 +2020-04-28 14:45:00,104.4,101.706,44.062,30.736 +2020-04-28 15:00:00,109.65,103.045,46.461999999999996,30.736 +2020-04-28 15:15:00,109.95,101.281,46.461999999999996,30.736 +2020-04-28 15:30:00,109.44,100.34299999999999,46.461999999999996,30.736 +2020-04-28 15:45:00,108.73,99.764,46.461999999999996,30.736 +2020-04-28 16:00:00,108.15,100.094,48.802,30.736 +2020-04-28 16:15:00,109.08,99.656,48.802,30.736 +2020-04-28 16:30:00,111.59,99.704,48.802,30.736 +2020-04-28 16:45:00,112.11,96.806,48.802,30.736 +2020-04-28 17:00:00,114.23,96.969,55.672,30.736 +2020-04-28 17:15:00,114.12,99.066,55.672,30.736 +2020-04-28 17:30:00,116.32,100.38799999999999,55.672,30.736 +2020-04-28 17:45:00,115.06,101.552,55.672,30.736 +2020-04-28 18:00:00,116.52,102.531,57.006,30.736 +2020-04-28 18:15:00,116.37,104.29299999999999,57.006,30.736 +2020-04-28 18:30:00,116.22,102.975,57.006,30.736 +2020-04-28 18:45:00,113.05,108.661,57.006,30.736 +2020-04-28 19:00:00,109.57,106.756,57.148,30.736 +2020-04-28 19:15:00,107.41,106.01799999999999,57.148,30.736 +2020-04-28 19:30:00,111.56,106.139,57.148,30.736 +2020-04-28 19:45:00,110.69,106.81299999999999,57.148,30.736 +2020-04-28 20:00:00,106.84,104.641,61.895,30.736 +2020-04-28 20:15:00,104.83,102.081,61.895,30.736 +2020-04-28 20:30:00,102.09,101.42,61.895,30.736 +2020-04-28 20:45:00,105.29,100.936,61.895,30.736 +2020-04-28 21:00:00,99.4,96.655,54.78,30.736 +2020-04-28 21:15:00,99.25,96.321,54.78,30.736 +2020-04-28 21:30:00,93.24,96.527,54.78,30.736 +2020-04-28 21:45:00,89.26,96.34100000000001,54.78,30.736 +2020-04-28 22:00:00,87.28,92.029,50.76,30.736 +2020-04-28 22:15:00,89.13,90.45700000000001,50.76,30.736 +2020-04-28 22:30:00,86.27,78.956,50.76,30.736 +2020-04-28 22:45:00,83.49,73.17399999999999,50.76,30.736 +2020-04-28 23:00:00,71.4,65.023,44.162,30.736 +2020-04-28 23:15:00,70.29,64.152,44.162,30.736 +2020-04-28 23:30:00,75.32,63.277,44.162,30.736 +2020-04-28 23:45:00,78.15,63.973,44.162,30.736 +2020-04-29 00:00:00,73.93,61.016999999999996,39.061,30.736 +2020-04-29 00:15:00,68.61,61.667,39.061,30.736 +2020-04-29 00:30:00,69.83,59.983999999999995,39.061,30.736 +2020-04-29 00:45:00,74.41,58.489,39.061,30.736 +2020-04-29 01:00:00,73.44,58.735,35.795,30.736 +2020-04-29 01:15:00,71.44,57.733000000000004,35.795,30.736 +2020-04-29 01:30:00,73.21,56.176,35.795,30.736 +2020-04-29 01:45:00,74.77,55.658,35.795,30.736 +2020-04-29 02:00:00,72.45,56.379,33.316,30.736 +2020-04-29 02:15:00,65.54,55.397,33.316,30.736 +2020-04-29 02:30:00,66.56,58.082,33.316,30.736 +2020-04-29 02:45:00,70.87,58.713,33.316,30.736 +2020-04-29 03:00:00,70.07,61.93899999999999,32.803000000000004,30.736 +2020-04-29 03:15:00,76.58,62.843,32.803000000000004,30.736 +2020-04-29 03:30:00,78.46,62.253,32.803000000000004,30.736 +2020-04-29 03:45:00,77.53,62.809,32.803000000000004,30.736 +2020-04-29 04:00:00,79.53,74.297,34.235,30.736 +2020-04-29 04:15:00,80.34,86.14399999999999,34.235,30.736 +2020-04-29 04:30:00,82.42,85.869,34.235,30.736 +2020-04-29 04:45:00,86.48,87.594,34.235,30.736 +2020-04-29 05:00:00,93.77,119.73,38.65,30.736 +2020-04-29 05:15:00,97.73,150.167,38.65,30.736 +2020-04-29 05:30:00,101.06,139.352,38.65,30.736 +2020-04-29 05:45:00,102.22,129.722,38.65,30.736 +2020-04-29 06:00:00,110.99,131.662,54.951,30.736 +2020-04-29 06:15:00,110.65,136.43200000000002,54.951,30.736 +2020-04-29 06:30:00,113.7,133.542,54.951,30.736 +2020-04-29 06:45:00,116.43,133.786,54.951,30.736 +2020-04-29 07:00:00,121.5,135.873,67.328,30.736 +2020-04-29 07:15:00,121.34,136.19899999999998,67.328,30.736 +2020-04-29 07:30:00,121.78,133.929,67.328,30.736 +2020-04-29 07:45:00,122.25,130.845,67.328,30.736 +2020-04-29 08:00:00,122.93,128.249,60.23,30.736 +2020-04-29 08:15:00,125.82,127.21,60.23,30.736 +2020-04-29 08:30:00,127.65,122.774,60.23,30.736 +2020-04-29 08:45:00,128.25,121.274,60.23,30.736 +2020-04-29 09:00:00,129.04,115.035,56.845,30.736 +2020-04-29 09:15:00,129.51,112.889,56.845,30.736 +2020-04-29 09:30:00,126.37,115.118,56.845,30.736 +2020-04-29 09:45:00,124.86,114.979,56.845,30.736 +2020-04-29 10:00:00,121.63,110.675,53.832,30.736 +2020-04-29 10:15:00,121.03,111.19,53.832,30.736 +2020-04-29 10:30:00,122.36,109.954,53.832,30.736 +2020-04-29 10:45:00,116.08,110.26100000000001,53.832,30.736 +2020-04-29 11:00:00,109.95,104.87799999999999,53.225,30.736 +2020-04-29 11:15:00,114.24,105.931,53.225,30.736 +2020-04-29 11:30:00,109.62,107.11,53.225,30.736 +2020-04-29 11:45:00,101.59,107.93799999999999,53.225,30.736 +2020-04-29 12:00:00,99.09,103.316,50.676,30.736 +2020-04-29 12:15:00,97.93,104.23,50.676,30.736 +2020-04-29 12:30:00,97.46,102.82700000000001,50.676,30.736 +2020-04-29 12:45:00,105.95,103.596,50.676,30.736 +2020-04-29 13:00:00,105.96,104.65100000000001,50.646,30.736 +2020-04-29 13:15:00,108.82,103.876,50.646,30.736 +2020-04-29 13:30:00,106.36,101.78200000000001,50.646,30.736 +2020-04-29 13:45:00,107.35,100.38799999999999,50.646,30.736 +2020-04-29 14:00:00,108.22,102.194,50.786,30.736 +2020-04-29 14:15:00,105.88,101.476,50.786,30.736 +2020-04-29 14:30:00,100.87,100.773,50.786,30.736 +2020-04-29 14:45:00,97.81,101.265,50.786,30.736 +2020-04-29 15:00:00,99.63,102.64,51.535,30.736 +2020-04-29 15:15:00,95.3,100.855,51.535,30.736 +2020-04-29 15:30:00,104.15,99.876,51.535,30.736 +2020-04-29 15:45:00,108.0,99.28,51.535,30.736 +2020-04-29 16:00:00,107.58,99.65299999999999,53.157,30.736 +2020-04-29 16:15:00,105.42,99.19,53.157,30.736 +2020-04-29 16:30:00,106.46,99.243,53.157,30.736 +2020-04-29 16:45:00,108.12,96.28399999999999,53.157,30.736 +2020-04-29 17:00:00,109.38,96.49600000000001,57.793,30.736 +2020-04-29 17:15:00,106.94,98.56700000000001,57.793,30.736 +2020-04-29 17:30:00,107.97,99.882,57.793,30.736 +2020-04-29 17:45:00,107.1,101.01899999999999,57.793,30.736 +2020-04-29 18:00:00,108.18,102.01299999999999,59.872,30.736 +2020-04-29 18:15:00,106.95,103.79,59.872,30.736 +2020-04-29 18:30:00,114.64,102.459,59.872,30.736 +2020-04-29 18:45:00,114.76,108.149,59.872,30.736 +2020-04-29 19:00:00,112.55,106.23700000000001,60.17100000000001,30.736 +2020-04-29 19:15:00,104.09,105.50299999999999,60.17100000000001,30.736 +2020-04-29 19:30:00,105.95,105.635,60.17100000000001,30.736 +2020-04-29 19:45:00,108.76,106.331,60.17100000000001,30.736 +2020-04-29 20:00:00,102.11,104.131,65.015,30.736 +2020-04-29 20:15:00,102.79,101.57799999999999,65.015,30.736 +2020-04-29 20:30:00,101.29,100.95100000000001,65.015,30.736 +2020-04-29 20:45:00,104.92,100.49700000000001,65.015,30.736 +2020-04-29 21:00:00,99.96,96.219,57.805,30.736 +2020-04-29 21:15:00,99.95,95.9,57.805,30.736 +2020-04-29 21:30:00,91.66,96.09100000000001,57.805,30.736 +2020-04-29 21:45:00,92.16,95.932,57.805,30.736 +2020-04-29 22:00:00,89.14,91.635,52.115,30.736 +2020-04-29 22:15:00,89.74,90.085,52.115,30.736 +2020-04-29 22:30:00,84.06,78.56,52.115,30.736 +2020-04-29 22:45:00,79.26,72.768,52.115,30.736 +2020-04-29 23:00:00,72.0,64.59100000000001,42.871,30.736 +2020-04-29 23:15:00,77.84,63.75899999999999,42.871,30.736 +2020-04-29 23:30:00,81.54,62.883,42.871,30.736 +2020-04-29 23:45:00,81.39,63.585,42.871,30.736 +2020-04-30 00:00:00,75.37,60.636,39.203,30.736 +2020-04-30 00:15:00,69.88,61.299,39.203,30.736 +2020-04-30 00:30:00,76.11,59.611999999999995,39.203,30.736 +2020-04-30 00:45:00,78.46,58.123000000000005,39.203,30.736 +2020-04-30 01:00:00,76.07,58.363,37.118,30.736 +2020-04-30 01:15:00,74.99,57.338,37.118,30.736 +2020-04-30 01:30:00,69.98,55.761,37.118,30.736 +2020-04-30 01:45:00,72.88,55.246,37.118,30.736 +2020-04-30 02:00:00,77.43,55.956,35.647,30.736 +2020-04-30 02:15:00,77.87,54.958999999999996,35.647,30.736 +2020-04-30 02:30:00,70.93,57.66,35.647,30.736 +2020-04-30 02:45:00,72.1,58.297,35.647,30.736 +2020-04-30 03:00:00,71.36,61.538000000000004,34.585,30.736 +2020-04-30 03:15:00,72.29,62.419,34.585,30.736 +2020-04-30 03:30:00,75.56,61.825,34.585,30.736 +2020-04-30 03:45:00,75.33,62.405,34.585,30.736 +2020-04-30 04:00:00,79.58,73.857,36.184,30.736 +2020-04-30 04:15:00,81.32,85.664,36.184,30.736 +2020-04-30 04:30:00,83.16,85.387,36.184,30.736 +2020-04-30 04:45:00,86.22,87.103,36.184,30.736 +2020-04-30 05:00:00,94.0,119.139,41.019,30.736 +2020-04-30 05:15:00,94.93,149.472,41.019,30.736 +2020-04-30 05:30:00,98.47,138.685,41.019,30.736 +2020-04-30 05:45:00,103.17,129.10299999999998,41.019,30.736 +2020-04-30 06:00:00,110.4,131.064,53.963,30.736 +2020-04-30 06:15:00,111.54,135.813,53.963,30.736 +2020-04-30 06:30:00,114.87,132.909,53.963,30.736 +2020-04-30 06:45:00,116.61,133.149,53.963,30.736 +2020-04-30 07:00:00,121.28,135.233,66.512,30.736 +2020-04-30 07:15:00,121.03,135.549,66.512,30.736 +2020-04-30 07:30:00,122.18,133.245,66.512,30.736 +2020-04-30 07:45:00,121.34,130.166,66.512,30.736 +2020-04-30 08:00:00,120.47,127.561,58.86,30.736 +2020-04-30 08:15:00,121.39,126.557,58.86,30.736 +2020-04-30 08:30:00,122.76,122.095,58.86,30.736 +2020-04-30 08:45:00,121.08,120.624,58.86,30.736 +2020-04-30 09:00:00,121.88,114.389,52.156000000000006,30.736 +2020-04-30 09:15:00,123.85,112.24799999999999,52.156000000000006,30.736 +2020-04-30 09:30:00,124.67,114.49,52.156000000000006,30.736 +2020-04-30 09:45:00,124.74,114.385,52.156000000000006,30.736 +2020-04-30 10:00:00,125.1,110.089,49.034,30.736 +2020-04-30 10:15:00,125.31,110.649,49.034,30.736 +2020-04-30 10:30:00,126.21,109.434,49.034,30.736 +2020-04-30 10:45:00,124.71,109.76,49.034,30.736 +2020-04-30 11:00:00,121.6,104.37200000000001,46.53,30.736 +2020-04-30 11:15:00,123.88,105.447,46.53,30.736 +2020-04-30 11:30:00,120.2,106.624,46.53,30.736 +2020-04-30 11:45:00,119.8,107.46799999999999,46.53,30.736 +2020-04-30 12:00:00,110.03,102.876,43.318000000000005,30.736 +2020-04-30 12:15:00,106.44,103.795,43.318000000000005,30.736 +2020-04-30 12:30:00,100.78,102.351,43.318000000000005,30.736 +2020-04-30 12:45:00,99.22,103.12299999999999,43.318000000000005,30.736 +2020-04-30 13:00:00,106.85,104.212,41.608000000000004,30.736 +2020-04-30 13:15:00,112.1,103.434,41.608000000000004,30.736 +2020-04-30 13:30:00,101.38,101.345,41.608000000000004,30.736 +2020-04-30 13:45:00,100.96,99.954,41.608000000000004,30.736 +2020-04-30 14:00:00,101.38,101.816,41.786,30.736 +2020-04-30 14:15:00,104.57,101.083,41.786,30.736 +2020-04-30 14:30:00,102.6,100.335,41.786,30.736 +2020-04-30 14:45:00,105.7,100.82799999999999,41.786,30.736 +2020-04-30 15:00:00,101.95,102.238,44.181999999999995,30.736 +2020-04-30 15:15:00,104.28,100.432,44.181999999999995,30.736 +2020-04-30 15:30:00,101.87,99.412,44.181999999999995,30.736 +2020-04-30 15:45:00,103.04,98.79899999999999,44.181999999999995,30.736 +2020-04-30 16:00:00,109.28,99.213,45.956,30.736 +2020-04-30 16:15:00,109.2,98.727,45.956,30.736 +2020-04-30 16:30:00,110.26,98.787,45.956,30.736 +2020-04-30 16:45:00,111.89,95.76299999999999,45.956,30.736 +2020-04-30 17:00:00,113.27,96.027,50.702,30.736 +2020-04-30 17:15:00,113.08,98.072,50.702,30.736 +2020-04-30 17:30:00,112.36,99.37899999999999,50.702,30.736 +2020-04-30 17:45:00,111.24,100.491,50.702,30.736 +2020-04-30 18:00:00,111.37,101.49600000000001,53.595,30.736 +2020-04-30 18:15:00,110.86,103.29,53.595,30.736 +2020-04-30 18:30:00,118.75,101.945,53.595,30.736 +2020-04-30 18:45:00,118.09,107.64,53.595,30.736 +2020-04-30 19:00:00,113.08,105.721,54.207,30.736 +2020-04-30 19:15:00,103.48,104.993,54.207,30.736 +2020-04-30 19:30:00,104.39,105.134,54.207,30.736 +2020-04-30 19:45:00,104.48,105.85,54.207,30.736 +2020-04-30 20:00:00,102.44,103.625,56.948,30.736 +2020-04-30 20:15:00,99.4,101.079,56.948,30.736 +2020-04-30 20:30:00,98.53,100.484,56.948,30.736 +2020-04-30 20:45:00,100.91,100.059,56.948,30.736 +2020-04-30 21:00:00,92.46,95.787,52.157,30.736 +2020-04-30 21:15:00,95.11,95.48200000000001,52.157,30.736 +2020-04-30 21:30:00,94.9,95.65799999999999,52.157,30.736 +2020-04-30 21:45:00,93.18,95.525,52.157,30.736 +2020-04-30 22:00:00,90.2,91.242,47.483000000000004,30.736 +2020-04-30 22:15:00,83.38,89.715,47.483000000000004,30.736 +2020-04-30 22:30:00,79.85,78.164,47.483000000000004,30.736 +2020-04-30 22:45:00,83.09,72.363,47.483000000000004,30.736 +2020-04-30 23:00:00,58.59,64.16199999999999,41.978,30.736 +2020-04-30 23:15:00,56.65,63.367,41.978,30.736 +2020-04-30 23:30:00,56.38,62.49100000000001,41.978,30.736 +2020-04-30 23:45:00,55.11,63.199,41.978,30.736 +2020-05-01 00:00:00,50.49,50.163000000000004,18.527,29.662 +2020-05-01 00:15:00,53.64,47.663999999999994,18.527,29.662 +2020-05-01 00:30:00,52.6,46.467,18.527,29.662 +2020-05-01 00:45:00,52.95,45.35,18.527,29.662 +2020-05-01 01:00:00,48.0,45.506,16.348,29.662 +2020-05-01 01:15:00,51.98,45.135,16.348,29.662 +2020-05-01 01:30:00,48.94,43.453,16.348,29.662 +2020-05-01 01:45:00,52.32,43.106,16.348,29.662 +2020-05-01 02:00:00,51.36,43.566,12.581,29.662 +2020-05-01 02:15:00,51.11,41.968,12.581,29.662 +2020-05-01 02:30:00,48.05,44.556999999999995,12.581,29.662 +2020-05-01 02:45:00,47.72,45.224,12.581,29.662 +2020-05-01 03:00:00,48.95,48.13399999999999,10.712,29.662 +2020-05-01 03:15:00,50.28,46.619,10.712,29.662 +2020-05-01 03:30:00,50.66,45.36600000000001,10.712,29.662 +2020-05-01 03:45:00,51.81,46.526,10.712,29.662 +2020-05-01 04:00:00,53.03,54.876000000000005,9.084,29.662 +2020-05-01 04:15:00,54.27,62.676,9.084,29.662 +2020-05-01 04:30:00,51.73,61.516999999999996,9.084,29.662 +2020-05-01 04:45:00,51.01,61.666000000000004,9.084,29.662 +2020-05-01 05:00:00,49.69,76.642,9.388,29.662 +2020-05-01 05:15:00,52.41,87.50299999999999,9.388,29.662 +2020-05-01 05:30:00,52.41,77.738,9.388,29.662 +2020-05-01 05:45:00,50.98,74.282,9.388,29.662 +2020-05-01 06:00:00,55.06,89.3,11.109000000000002,29.662 +2020-05-01 06:15:00,53.68,104.529,11.109000000000002,29.662 +2020-05-01 06:30:00,58.38,96.056,11.109000000000002,29.662 +2020-05-01 06:45:00,60.23,89.587,11.109000000000002,29.662 +2020-05-01 07:00:00,61.8,89.125,13.77,29.662 +2020-05-01 07:15:00,63.86,86.988,13.77,29.662 +2020-05-01 07:30:00,65.25,85.859,13.77,29.662 +2020-05-01 07:45:00,66.17,84.554,13.77,29.662 +2020-05-01 08:00:00,67.11,83.42200000000001,12.868,29.662 +2020-05-01 08:15:00,66.63,85.50299999999999,12.868,29.662 +2020-05-01 08:30:00,67.33,84.87100000000001,12.868,29.662 +2020-05-01 08:45:00,66.74,86.586,12.868,29.662 +2020-05-01 09:00:00,67.45,81.148,12.804,29.662 +2020-05-01 09:15:00,68.21,81.42699999999999,12.804,29.662 +2020-05-01 09:30:00,67.2,84.065,12.804,29.662 +2020-05-01 09:45:00,68.2,84.78399999999999,12.804,29.662 +2020-05-01 10:00:00,69.04,81.47,11.029000000000002,29.662 +2020-05-01 10:15:00,69.41,82.72200000000001,11.029000000000002,29.662 +2020-05-01 10:30:00,70.12,82.583,11.029000000000002,29.662 +2020-05-01 10:45:00,66.61,82.947,11.029000000000002,29.662 +2020-05-01 11:00:00,57.19,79.26,11.681,29.662 +2020-05-01 11:15:00,57.35,79.181,11.681,29.662 +2020-05-01 11:30:00,54.68,80.666,11.681,29.662 +2020-05-01 11:45:00,54.35,80.57,11.681,29.662 +2020-05-01 12:00:00,52.36,78.047,8.915,29.662 +2020-05-01 12:15:00,56.13,78.738,8.915,29.662 +2020-05-01 12:30:00,51.09,77.492,8.915,29.662 +2020-05-01 12:45:00,50.81,77.333,8.915,29.662 +2020-05-01 13:00:00,49.83,78.273,5.4639999999999995,29.662 +2020-05-01 13:15:00,45.08,78.259,5.4639999999999995,29.662 +2020-05-01 13:30:00,46.19,75.832,5.4639999999999995,29.662 +2020-05-01 13:45:00,47.46,74.252,5.4639999999999995,29.662 +2020-05-01 14:00:00,49.01,76.367,3.2939999999999996,29.662 +2020-05-01 14:15:00,48.35,75.575,3.2939999999999996,29.662 +2020-05-01 14:30:00,45.56,75.003,3.2939999999999996,29.662 +2020-05-01 14:45:00,48.43,74.546,3.2939999999999996,29.662 +2020-05-01 15:00:00,49.06,75.21,4.689,29.662 +2020-05-01 15:15:00,49.67,73.595,4.689,29.662 +2020-05-01 15:30:00,51.56,72.592,4.689,29.662 +2020-05-01 15:45:00,53.04,71.998,4.689,29.662 +2020-05-01 16:00:00,55.11,72.08,7.732,29.662 +2020-05-01 16:15:00,59.46,71.52,7.732,29.662 +2020-05-01 16:30:00,62.16,72.118,7.732,29.662 +2020-05-01 16:45:00,62.3,69.263,7.732,29.662 +2020-05-01 17:00:00,67.83,70.095,17.558,29.662 +2020-05-01 17:15:00,72.29,71.555,17.558,29.662 +2020-05-01 17:30:00,72.68,72.794,17.558,29.662 +2020-05-01 17:45:00,73.94,74.653,17.558,29.662 +2020-05-01 18:00:00,75.38,75.821,24.763,29.662 +2020-05-01 18:15:00,74.58,78.516,24.763,29.662 +2020-05-01 18:30:00,75.18,77.906,24.763,29.662 +2020-05-01 18:45:00,75.67,80.471,24.763,29.662 +2020-05-01 19:00:00,79.11,79.535,29.633000000000003,29.662 +2020-05-01 19:15:00,84.51,78.283,29.633000000000003,29.662 +2020-05-01 19:30:00,85.92,78.82,29.633000000000003,29.662 +2020-05-01 19:45:00,82.48,79.994,29.633000000000003,29.662 +2020-05-01 20:00:00,83.91,80.059,38.826,29.662 +2020-05-01 20:15:00,81.16,79.312,38.826,29.662 +2020-05-01 20:30:00,79.77,77.578,38.826,29.662 +2020-05-01 20:45:00,88.48,76.283,38.826,29.662 +2020-05-01 21:00:00,88.98,74.173,37.751,29.662 +2020-05-01 21:15:00,88.97,75.696,37.751,29.662 +2020-05-01 21:30:00,78.96,76.152,37.751,29.662 +2020-05-01 21:45:00,81.17,76.479,37.751,29.662 +2020-05-01 22:00:00,73.75,76.163,39.799,29.662 +2020-05-01 22:15:00,80.27,74.783,39.799,29.662 +2020-05-01 22:30:00,81.01,72.372,39.799,29.662 +2020-05-01 22:45:00,80.12,70.10600000000001,39.799,29.662 +2020-05-01 23:00:00,80.75,64.072,33.686,29.662 +2020-05-01 23:15:00,77.21,61.755,33.686,29.662 +2020-05-01 23:30:00,72.86,60.244,33.686,29.662 +2020-05-01 23:45:00,74.22,59.806999999999995,33.686,29.662 +2020-05-02 00:00:00,67.4,48.595,42.833999999999996,29.662 +2020-05-02 00:15:00,71.63,47.126000000000005,42.833999999999996,29.662 +2020-05-02 00:30:00,75.66,46.176,42.833999999999996,29.662 +2020-05-02 00:45:00,76.11,44.898999999999994,42.833999999999996,29.662 +2020-05-02 01:00:00,72.31,44.903999999999996,37.859,29.662 +2020-05-02 01:15:00,70.22,44.23,37.859,29.662 +2020-05-02 01:30:00,73.08,42.45,37.859,29.662 +2020-05-02 01:45:00,73.7,42.513999999999996,37.859,29.662 +2020-05-02 02:00:00,66.85,43.174,35.327,29.662 +2020-05-02 02:15:00,66.49,41.31100000000001,35.327,29.662 +2020-05-02 02:30:00,68.26,43.461000000000006,35.327,29.662 +2020-05-02 02:45:00,66.14,44.196999999999996,35.327,29.662 +2020-05-02 03:00:00,65.71,46.493,34.908,29.662 +2020-05-02 03:15:00,66.15,44.941,34.908,29.662 +2020-05-02 03:30:00,67.61,43.762,34.908,29.662 +2020-05-02 03:45:00,68.7,45.599,34.908,29.662 +2020-05-02 04:00:00,69.79,54.015,34.84,29.662 +2020-05-02 04:15:00,67.03,62.473,34.84,29.662 +2020-05-02 04:30:00,66.17,60.231,34.84,29.662 +2020-05-02 04:45:00,67.69,60.641999999999996,34.84,29.662 +2020-05-02 05:00:00,69.51,76.563,34.222,29.662 +2020-05-02 05:15:00,70.18,88.72,34.222,29.662 +2020-05-02 05:30:00,69.35,79.377,34.222,29.662 +2020-05-02 05:45:00,71.72,76.12100000000001,34.222,29.662 +2020-05-02 06:00:00,73.67,93.12,35.515,29.662 +2020-05-02 06:15:00,73.73,108.242,35.515,29.662 +2020-05-02 06:30:00,75.39,100.70299999999999,35.515,29.662 +2020-05-02 06:45:00,76.42,95.399,35.515,29.662 +2020-05-02 07:00:00,79.28,93.88799999999999,39.687,29.662 +2020-05-02 07:15:00,77.96,93.337,39.687,29.662 +2020-05-02 07:30:00,78.31,91.61399999999999,39.687,29.662 +2020-05-02 07:45:00,79.86,90.62100000000001,39.687,29.662 +2020-05-02 08:00:00,80.34,88.26799999999999,44.9,29.662 +2020-05-02 08:15:00,78.9,89.515,44.9,29.662 +2020-05-02 08:30:00,78.61,87.58,44.9,29.662 +2020-05-02 08:45:00,79.87,88.637,44.9,29.662 +2020-05-02 09:00:00,76.21,83.447,45.724,29.662 +2020-05-02 09:15:00,75.76,83.902,45.724,29.662 +2020-05-02 09:30:00,75.2,86.28399999999999,45.724,29.662 +2020-05-02 09:45:00,75.61,86.314,45.724,29.662 +2020-05-02 10:00:00,75.42,81.66199999999999,43.123999999999995,29.662 +2020-05-02 10:15:00,76.95,82.645,43.123999999999995,29.662 +2020-05-02 10:30:00,77.79,82.12200000000001,43.123999999999995,29.662 +2020-05-02 10:45:00,76.44,82.48899999999999,43.123999999999995,29.662 +2020-05-02 11:00:00,72.26,78.703,40.255,29.662 +2020-05-02 11:15:00,71.56,78.921,40.255,29.662 +2020-05-02 11:30:00,69.64,80.362,40.255,29.662 +2020-05-02 11:45:00,69.91,79.851,40.255,29.662 +2020-05-02 12:00:00,65.59,76.855,38.582,29.662 +2020-05-02 12:15:00,66.22,77.22399999999999,38.582,29.662 +2020-05-02 12:30:00,64.72,76.383,38.582,29.662 +2020-05-02 12:45:00,64.95,77.09,38.582,29.662 +2020-05-02 13:00:00,62.78,78.554,36.043,29.662 +2020-05-02 13:15:00,62.25,77.737,36.043,29.662 +2020-05-02 13:30:00,62.59,76.217,36.043,29.662 +2020-05-02 13:45:00,62.56,74.087,36.043,29.662 +2020-05-02 14:00:00,61.85,75.279,35.216,29.662 +2020-05-02 14:15:00,62.08,73.669,35.216,29.662 +2020-05-02 14:30:00,62.24,73.452,35.216,29.662 +2020-05-02 14:45:00,62.4,73.947,35.216,29.662 +2020-05-02 15:00:00,62.97,75.17399999999999,36.759,29.662 +2020-05-02 15:15:00,63.02,73.821,36.759,29.662 +2020-05-02 15:30:00,63.12,72.676,36.759,29.662 +2020-05-02 15:45:00,64.76,71.574,36.759,29.662 +2020-05-02 16:00:00,68.0,72.26,40.086,29.662 +2020-05-02 16:15:00,68.86,71.86399999999999,40.086,29.662 +2020-05-02 16:30:00,71.22,71.62,40.086,29.662 +2020-05-02 16:45:00,74.4,68.60300000000001,40.086,29.662 +2020-05-02 17:00:00,79.85,69.22399999999999,44.876999999999995,29.662 +2020-05-02 17:15:00,79.81,69.649,44.876999999999995,29.662 +2020-05-02 17:30:00,80.79,70.16199999999999,44.876999999999995,29.662 +2020-05-02 17:45:00,81.12,70.793,44.876999999999995,29.662 +2020-05-02 18:00:00,83.13,71.803,47.056000000000004,29.662 +2020-05-02 18:15:00,81.4,74.316,47.056000000000004,29.662 +2020-05-02 18:30:00,81.48,74.656,47.056000000000004,29.662 +2020-05-02 18:45:00,82.35,76.45,47.056000000000004,29.662 +2020-05-02 19:00:00,81.48,74.001,45.57,29.662 +2020-05-02 19:15:00,79.11,73.41,45.57,29.662 +2020-05-02 19:30:00,78.9,74.202,45.57,29.662 +2020-05-02 19:45:00,79.86,75.236,45.57,29.662 +2020-05-02 20:00:00,81.06,75.092,41.685,29.662 +2020-05-02 20:15:00,80.73,74.097,41.685,29.662 +2020-05-02 20:30:00,80.18,71.314,41.685,29.662 +2020-05-02 20:45:00,80.23,71.797,41.685,29.662 +2020-05-02 21:00:00,75.92,70.756,39.576,29.662 +2020-05-02 21:15:00,76.58,72.755,39.576,29.662 +2020-05-02 21:30:00,73.98,73.593,39.576,29.662 +2020-05-02 21:45:00,73.06,73.62,39.576,29.662 +2020-05-02 22:00:00,69.29,72.173,39.068000000000005,29.662 +2020-05-02 22:15:00,69.76,72.399,39.068000000000005,29.662 +2020-05-02 22:30:00,67.03,71.508,39.068000000000005,29.662 +2020-05-02 22:45:00,66.09,70.529,39.068000000000005,29.662 +2020-05-02 23:00:00,64.59,65.696,32.06,29.662 +2020-05-02 23:15:00,62.92,61.768,32.06,29.662 +2020-05-02 23:30:00,62.13,60.387,32.06,29.662 +2020-05-02 23:45:00,61.12,59.508,32.06,29.662 +2020-05-03 00:00:00,56.25,49.497,28.825,29.662 +2020-05-03 00:15:00,59.42,47.018,28.825,29.662 +2020-05-03 00:30:00,55.48,45.813,28.825,29.662 +2020-05-03 00:45:00,57.9,44.705,28.825,29.662 +2020-05-03 01:00:00,55.98,44.872,25.995,29.662 +2020-05-03 01:15:00,56.5,44.451,25.995,29.662 +2020-05-03 01:30:00,53.94,42.732,25.995,29.662 +2020-05-03 01:45:00,55.87,42.38399999999999,25.995,29.662 +2020-05-03 02:00:00,55.81,42.83,24.394000000000002,29.662 +2020-05-03 02:15:00,56.25,41.198,24.394000000000002,29.662 +2020-05-03 02:30:00,55.72,43.818000000000005,24.394000000000002,29.662 +2020-05-03 02:45:00,55.33,44.498999999999995,24.394000000000002,29.662 +2020-05-03 03:00:00,51.94,47.433,22.916999999999998,29.662 +2020-05-03 03:15:00,55.79,45.878,22.916999999999998,29.662 +2020-05-03 03:30:00,56.34,44.623999999999995,22.916999999999998,29.662 +2020-05-03 03:45:00,56.91,45.833,22.916999999999998,29.662 +2020-05-03 04:00:00,57.32,54.089,23.576999999999998,29.662 +2020-05-03 04:15:00,55.6,61.795,23.576999999999998,29.662 +2020-05-03 04:30:00,55.41,60.622,23.576999999999998,29.662 +2020-05-03 04:45:00,55.04,60.756,23.576999999999998,29.662 +2020-05-03 05:00:00,56.02,75.493,22.730999999999998,29.662 +2020-05-03 05:15:00,56.44,86.085,22.730999999999998,29.662 +2020-05-03 05:30:00,56.1,76.407,22.730999999999998,29.662 +2020-05-03 05:45:00,56.79,73.071,22.730999999999998,29.662 +2020-05-03 06:00:00,57.69,88.141,22.34,29.662 +2020-05-03 06:15:00,57.57,103.32,22.34,29.662 +2020-05-03 06:30:00,58.58,94.846,22.34,29.662 +2020-05-03 06:45:00,59.95,88.39299999999999,22.34,29.662 +2020-05-03 07:00:00,61.08,87.919,24.691999999999997,29.662 +2020-05-03 07:15:00,61.55,85.772,24.691999999999997,29.662 +2020-05-03 07:30:00,61.04,84.58,24.691999999999997,29.662 +2020-05-03 07:45:00,61.04,83.305,24.691999999999997,29.662 +2020-05-03 08:00:00,61.08,82.164,29.340999999999998,29.662 +2020-05-03 08:15:00,62.23,84.333,29.340999999999998,29.662 +2020-05-03 08:30:00,61.79,83.662,29.340999999999998,29.662 +2020-05-03 08:45:00,60.97,85.425,29.340999999999998,29.662 +2020-05-03 09:00:00,59.6,79.984,30.788,29.662 +2020-05-03 09:15:00,59.67,80.277,30.788,29.662 +2020-05-03 09:30:00,59.03,82.943,30.788,29.662 +2020-05-03 09:45:00,59.85,83.73,30.788,29.662 +2020-05-03 10:00:00,60.2,80.434,30.158,29.662 +2020-05-03 10:15:00,61.9,81.768,30.158,29.662 +2020-05-03 10:30:00,63.99,81.663,30.158,29.662 +2020-05-03 10:45:00,64.7,82.06200000000001,30.158,29.662 +2020-05-03 11:00:00,62.24,78.36,32.056,29.662 +2020-05-03 11:15:00,59.36,78.319,32.056,29.662 +2020-05-03 11:30:00,54.19,79.796,32.056,29.662 +2020-05-03 11:45:00,55.34,79.73100000000001,32.056,29.662 +2020-05-03 12:00:00,52.93,77.274,28.671999999999997,29.662 +2020-05-03 12:15:00,52.8,77.979,28.671999999999997,29.662 +2020-05-03 12:30:00,49.69,76.653,28.671999999999997,29.662 +2020-05-03 12:45:00,47.47,76.506,28.671999999999997,29.662 +2020-05-03 13:00:00,45.23,77.5,23.171,29.662 +2020-05-03 13:15:00,43.21,77.488,23.171,29.662 +2020-05-03 13:30:00,45.06,75.075,23.171,29.662 +2020-05-03 13:45:00,46.0,73.49600000000001,23.171,29.662 +2020-05-03 14:00:00,46.8,75.712,19.11,29.662 +2020-05-03 14:15:00,46.46,74.891,19.11,29.662 +2020-05-03 14:30:00,45.78,74.235,19.11,29.662 +2020-05-03 14:45:00,46.36,73.783,19.11,29.662 +2020-05-03 15:00:00,48.14,74.539,19.689,29.662 +2020-05-03 15:15:00,48.74,72.88600000000001,19.689,29.662 +2020-05-03 15:30:00,49.3,71.814,19.689,29.662 +2020-05-03 15:45:00,51.69,71.186,19.689,29.662 +2020-05-03 16:00:00,55.94,71.361,22.875,29.662 +2020-05-03 16:15:00,57.49,70.76100000000001,22.875,29.662 +2020-05-03 16:30:00,59.74,71.37899999999999,22.875,29.662 +2020-05-03 16:45:00,64.35,68.4,22.875,29.662 +2020-05-03 17:00:00,68.62,69.329,33.884,29.662 +2020-05-03 17:15:00,69.55,70.735,33.884,29.662 +2020-05-03 17:30:00,71.62,71.954,33.884,29.662 +2020-05-03 17:45:00,72.87,73.748,33.884,29.662 +2020-05-03 18:00:00,76.29,74.952,38.453,29.662 +2020-05-03 18:15:00,74.34,77.643,38.453,29.662 +2020-05-03 18:30:00,74.52,77.007,38.453,29.662 +2020-05-03 18:45:00,74.5,79.578,38.453,29.662 +2020-05-03 19:00:00,80.75,78.635,39.221,29.662 +2020-05-03 19:15:00,82.66,77.384,39.221,29.662 +2020-05-03 19:30:00,83.29,77.925,39.221,29.662 +2020-05-03 19:45:00,75.88,79.12100000000001,39.221,29.662 +2020-05-03 20:00:00,79.39,79.132,37.871,29.662 +2020-05-03 20:15:00,79.33,78.392,37.871,29.662 +2020-05-03 20:30:00,79.68,76.717,37.871,29.662 +2020-05-03 20:45:00,81.36,75.5,37.871,29.662 +2020-05-03 21:00:00,87.04,73.398,36.465,29.662 +2020-05-03 21:15:00,90.9,74.95,36.465,29.662 +2020-05-03 21:30:00,84.7,75.37,36.465,29.662 +2020-05-03 21:45:00,81.23,75.756,36.465,29.662 +2020-05-03 22:00:00,78.55,75.484,36.092,29.662 +2020-05-03 22:15:00,82.23,74.148,36.092,29.662 +2020-05-03 22:30:00,81.02,71.717,36.092,29.662 +2020-05-03 22:45:00,76.17,69.429,36.092,29.662 +2020-05-03 23:00:00,72.51,63.32899999999999,31.013,29.662 +2020-05-03 23:15:00,74.86,61.097,31.013,29.662 +2020-05-03 23:30:00,75.21,59.583999999999996,31.013,29.662 +2020-05-03 23:45:00,74.48,59.146,31.013,29.662 +2020-05-04 00:00:00,69.6,51.836999999999996,31.174,29.775 +2020-05-04 00:15:00,72.1,51.037,31.174,29.775 +2020-05-04 00:30:00,73.55,49.571999999999996,31.174,29.775 +2020-05-04 00:45:00,72.62,47.968999999999994,31.174,29.775 +2020-05-04 01:00:00,67.74,48.449,29.663,29.775 +2020-05-04 01:15:00,66.22,47.828,29.663,29.775 +2020-05-04 01:30:00,70.56,46.394,29.663,29.775 +2020-05-04 01:45:00,73.57,46.01,29.663,29.775 +2020-05-04 02:00:00,73.5,46.79600000000001,28.793000000000003,29.775 +2020-05-04 02:15:00,69.89,44.87,28.793000000000003,29.775 +2020-05-04 02:30:00,68.85,47.751000000000005,28.793000000000003,29.775 +2020-05-04 02:45:00,73.7,48.104,28.793000000000003,29.775 +2020-05-04 03:00:00,73.78,51.895,27.728,29.775 +2020-05-04 03:15:00,70.78,51.468,27.728,29.775 +2020-05-04 03:30:00,74.92,50.652,27.728,29.775 +2020-05-04 03:45:00,71.42,51.328,27.728,29.775 +2020-05-04 04:00:00,75.17,63.512,29.266,29.775 +2020-05-04 04:15:00,76.56,74.935,29.266,29.775 +2020-05-04 04:30:00,80.37,74.154,29.266,29.775 +2020-05-04 04:45:00,84.87,74.626,29.266,29.775 +2020-05-04 05:00:00,91.28,99.954,37.889,29.775 +2020-05-04 05:15:00,95.44,125.398,37.889,29.775 +2020-05-04 05:30:00,98.76,114.611,37.889,29.775 +2020-05-04 05:45:00,100.67,106.984,37.889,29.775 +2020-05-04 06:00:00,103.98,108.009,55.485,29.775 +2020-05-04 06:15:00,105.43,110.87700000000001,55.485,29.775 +2020-05-04 06:30:00,106.87,108.288,55.485,29.775 +2020-05-04 06:45:00,108.09,109.04,55.485,29.775 +2020-05-04 07:00:00,108.57,110.086,65.765,29.775 +2020-05-04 07:15:00,107.31,110.166,65.765,29.775 +2020-05-04 07:30:00,106.72,107.994,65.765,29.775 +2020-05-04 07:45:00,105.4,106.05799999999999,65.765,29.775 +2020-05-04 08:00:00,104.99,101.705,56.745,29.775 +2020-05-04 08:15:00,103.93,102.205,56.745,29.775 +2020-05-04 08:30:00,104.33,99.277,56.745,29.775 +2020-05-04 08:45:00,103.33,100.006,56.745,29.775 +2020-05-04 09:00:00,102.16,93.492,53.321999999999996,29.775 +2020-05-04 09:15:00,101.67,91.12200000000001,53.321999999999996,29.775 +2020-05-04 09:30:00,101.56,92.71799999999999,53.321999999999996,29.775 +2020-05-04 09:45:00,101.91,91.898,53.321999999999996,29.775 +2020-05-04 10:00:00,101.39,88.725,51.309,29.775 +2020-05-04 10:15:00,101.65,89.86200000000001,51.309,29.775 +2020-05-04 10:30:00,101.66,89.089,51.309,29.775 +2020-05-04 10:45:00,101.33,88.617,51.309,29.775 +2020-05-04 11:00:00,101.22,84.113,50.415,29.775 +2020-05-04 11:15:00,98.86,85.01899999999999,50.415,29.775 +2020-05-04 11:30:00,98.41,87.557,50.415,29.775 +2020-05-04 11:45:00,97.54,87.715,50.415,29.775 +2020-05-04 12:00:00,97.03,84.945,48.273,29.775 +2020-05-04 12:15:00,95.22,85.738,48.273,29.775 +2020-05-04 12:30:00,95.04,83.7,48.273,29.775 +2020-05-04 12:45:00,94.65,84.18700000000001,48.273,29.775 +2020-05-04 13:00:00,93.91,86.016,48.452,29.775 +2020-05-04 13:15:00,94.72,84.79299999999999,48.452,29.775 +2020-05-04 13:30:00,93.9,82.296,48.452,29.775 +2020-05-04 13:45:00,94.22,81.383,48.452,29.775 +2020-05-04 14:00:00,96.6,82.76899999999999,48.35,29.775 +2020-05-04 14:15:00,94.21,82.103,48.35,29.775 +2020-05-04 14:30:00,94.03,81.062,48.35,29.775 +2020-05-04 14:45:00,93.5,82.105,48.35,29.775 +2020-05-04 15:00:00,92.87,83.38600000000001,48.838,29.775 +2020-05-04 15:15:00,93.09,80.695,48.838,29.775 +2020-05-04 15:30:00,94.12,79.807,48.838,29.775 +2020-05-04 15:45:00,96.9,78.626,48.838,29.775 +2020-05-04 16:00:00,97.74,79.515,50.873000000000005,29.775 +2020-05-04 16:15:00,97.79,78.64699999999999,50.873000000000005,29.775 +2020-05-04 16:30:00,102.74,78.398,50.873000000000005,29.775 +2020-05-04 16:45:00,102.67,74.907,50.873000000000005,29.775 +2020-05-04 17:00:00,105.47,74.95100000000001,56.637,29.775 +2020-05-04 17:15:00,105.44,76.24,56.637,29.775 +2020-05-04 17:30:00,106.23,76.945,56.637,29.775 +2020-05-04 17:45:00,107.54,77.82600000000001,56.637,29.775 +2020-05-04 18:00:00,108.37,78.27,56.35,29.775 +2020-05-04 18:15:00,107.47,78.734,56.35,29.775 +2020-05-04 18:30:00,113.16,77.749,56.35,29.775 +2020-05-04 18:45:00,114.56,83.012,56.35,29.775 +2020-05-04 19:00:00,111.01,81.166,56.023,29.775 +2020-05-04 19:15:00,98.89,80.538,56.023,29.775 +2020-05-04 19:30:00,101.05,80.969,56.023,29.775 +2020-05-04 19:45:00,98.85,81.399,56.023,29.775 +2020-05-04 20:00:00,97.88,79.572,62.372,29.775 +2020-05-04 20:15:00,103.11,79.016,62.372,29.775 +2020-05-04 20:30:00,108.62,77.098,62.372,29.775 +2020-05-04 20:45:00,107.25,76.679,62.372,29.775 +2020-05-04 21:00:00,93.21,74.293,57.516999999999996,29.775 +2020-05-04 21:15:00,90.29,75.783,57.516999999999996,29.775 +2020-05-04 21:30:00,87.29,76.21,57.516999999999996,29.775 +2020-05-04 21:45:00,93.2,76.271,57.516999999999996,29.775 +2020-05-04 22:00:00,88.85,73.393,51.823,29.775 +2020-05-04 22:15:00,88.88,73.15899999999999,51.823,29.775 +2020-05-04 22:30:00,83.03,63.908,51.823,29.775 +2020-05-04 22:45:00,81.21,59.86,51.823,29.775 +2020-05-04 23:00:00,80.18,54.06100000000001,43.832,29.775 +2020-05-04 23:15:00,81.63,51.527,43.832,29.775 +2020-05-04 23:30:00,78.05,50.97,43.832,29.775 +2020-05-04 23:45:00,75.22,51.051,43.832,29.775 +2020-05-05 00:00:00,76.0,49.474,42.371,29.775 +2020-05-05 00:15:00,79.2,49.869,42.371,29.775 +2020-05-05 00:30:00,77.68,48.63,42.371,29.775 +2020-05-05 00:45:00,75.02,47.31100000000001,42.371,29.775 +2020-05-05 01:00:00,77.48,47.3,39.597,29.775 +2020-05-05 01:15:00,78.55,46.585,39.597,29.775 +2020-05-05 01:30:00,78.09,45.091,39.597,29.775 +2020-05-05 01:45:00,74.26,44.409,39.597,29.775 +2020-05-05 02:00:00,77.89,44.806999999999995,38.298,29.775 +2020-05-05 02:15:00,78.97,43.738,38.298,29.775 +2020-05-05 02:30:00,73.18,46.115,38.298,29.775 +2020-05-05 02:45:00,74.65,46.74,38.298,29.775 +2020-05-05 03:00:00,72.05,49.676,37.884,29.775 +2020-05-05 03:15:00,72.97,49.666000000000004,37.884,29.775 +2020-05-05 03:30:00,81.18,48.996,37.884,29.775 +2020-05-05 03:45:00,86.82,48.886,37.884,29.775 +2020-05-05 04:00:00,86.35,59.956,39.442,29.775 +2020-05-05 04:15:00,80.15,71.218,39.442,29.775 +2020-05-05 04:30:00,82.91,70.225,39.442,29.775 +2020-05-05 04:45:00,86.29,71.503,39.442,29.775 +2020-05-05 05:00:00,93.14,99.67200000000001,43.608000000000004,29.775 +2020-05-05 05:15:00,96.1,125.415,43.608000000000004,29.775 +2020-05-05 05:30:00,99.08,114.31700000000001,43.608000000000004,29.775 +2020-05-05 05:45:00,102.21,106.15799999999999,43.608000000000004,29.775 +2020-05-05 06:00:00,105.73,107.665,54.99100000000001,29.775 +2020-05-05 06:15:00,105.97,111.215,54.99100000000001,29.775 +2020-05-05 06:30:00,106.51,108.149,54.99100000000001,29.775 +2020-05-05 06:45:00,106.79,108.066,54.99100000000001,29.775 +2020-05-05 07:00:00,108.05,109.16,66.217,29.775 +2020-05-05 07:15:00,107.67,108.979,66.217,29.775 +2020-05-05 07:30:00,106.25,106.595,66.217,29.775 +2020-05-05 07:45:00,104.55,103.98200000000001,66.217,29.775 +2020-05-05 08:00:00,102.47,99.61399999999999,60.151,29.775 +2020-05-05 08:15:00,101.53,99.43299999999999,60.151,29.775 +2020-05-05 08:30:00,101.82,96.552,60.151,29.775 +2020-05-05 08:45:00,102.85,96.48899999999999,60.151,29.775 +2020-05-05 09:00:00,103.32,89.926,53.873000000000005,29.775 +2020-05-05 09:15:00,101.69,88.057,53.873000000000005,29.775 +2020-05-05 09:30:00,99.95,90.448,53.873000000000005,29.775 +2020-05-05 09:45:00,102.43,90.609,53.873000000000005,29.775 +2020-05-05 10:00:00,101.34,86.17399999999999,51.417,29.775 +2020-05-05 10:15:00,102.24,86.82799999999999,51.417,29.775 +2020-05-05 10:30:00,102.25,86.163,51.417,29.775 +2020-05-05 10:45:00,102.01,86.572,51.417,29.775 +2020-05-05 11:00:00,98.68,82.649,50.43600000000001,29.775 +2020-05-05 11:15:00,99.53,83.743,50.43600000000001,29.775 +2020-05-05 11:30:00,98.28,84.958,50.43600000000001,29.775 +2020-05-05 11:45:00,97.89,85.07700000000001,50.43600000000001,29.775 +2020-05-05 12:00:00,97.44,81.642,47.468,29.775 +2020-05-05 12:15:00,100.02,82.514,47.468,29.775 +2020-05-05 12:30:00,104.48,81.36,47.468,29.775 +2020-05-05 12:45:00,103.25,82.234,47.468,29.775 +2020-05-05 13:00:00,98.15,83.67200000000001,48.453,29.775 +2020-05-05 13:15:00,97.89,83.62799999999999,48.453,29.775 +2020-05-05 13:30:00,98.63,81.594,48.453,29.775 +2020-05-05 13:45:00,96.77,80.05199999999999,48.453,29.775 +2020-05-05 14:00:00,101.19,81.928,48.435,29.775 +2020-05-05 14:15:00,102.3,81.167,48.435,29.775 +2020-05-05 14:30:00,106.02,80.582,48.435,29.775 +2020-05-05 14:45:00,96.9,81.017,48.435,29.775 +2020-05-05 15:00:00,96.46,82.051,49.966,29.775 +2020-05-05 15:15:00,96.66,80.153,49.966,29.775 +2020-05-05 15:30:00,101.42,79.169,49.966,29.775 +2020-05-05 15:45:00,102.58,78.059,49.966,29.775 +2020-05-05 16:00:00,102.74,78.64699999999999,51.184,29.775 +2020-05-05 16:15:00,101.99,78.009,51.184,29.775 +2020-05-05 16:30:00,104.08,77.745,51.184,29.775 +2020-05-05 16:45:00,110.91,74.844,51.184,29.775 +2020-05-05 17:00:00,111.11,75.308,56.138999999999996,29.775 +2020-05-05 17:15:00,110.09,76.896,56.138999999999996,29.775 +2020-05-05 17:30:00,109.87,77.51899999999999,56.138999999999996,29.775 +2020-05-05 17:45:00,109.83,78.067,56.138999999999996,29.775 +2020-05-05 18:00:00,116.18,77.743,57.038000000000004,29.775 +2020-05-05 18:15:00,114.49,79.166,57.038000000000004,29.775 +2020-05-05 18:30:00,112.35,77.851,57.038000000000004,29.775 +2020-05-05 18:45:00,106.65,83.26299999999999,57.038000000000004,29.775 +2020-05-05 19:00:00,104.1,80.51100000000001,56.492,29.775 +2020-05-05 19:15:00,101.34,79.92399999999999,56.492,29.775 +2020-05-05 19:30:00,108.09,79.952,56.492,29.775 +2020-05-05 19:45:00,108.89,80.669,56.492,29.775 +2020-05-05 20:00:00,106.25,79.186,62.534,29.775 +2020-05-05 20:15:00,101.8,77.277,62.534,29.775 +2020-05-05 20:30:00,98.86,75.78699999999999,62.534,29.775 +2020-05-05 20:45:00,99.69,75.52600000000001,62.534,29.775 +2020-05-05 21:00:00,99.28,73.567,55.506,29.775 +2020-05-05 21:15:00,98.12,74.248,55.506,29.775 +2020-05-05 21:30:00,92.99,74.533,55.506,29.775 +2020-05-05 21:45:00,88.48,74.859,55.506,29.775 +2020-05-05 22:00:00,87.66,72.74600000000001,51.472,29.775 +2020-05-05 22:15:00,89.2,72.176,51.472,29.775 +2020-05-05 22:30:00,85.37,63.177,51.472,29.775 +2020-05-05 22:45:00,84.36,59.214,51.472,29.775 +2020-05-05 23:00:00,81.88,52.781000000000006,44.593,29.775 +2020-05-05 23:15:00,80.29,51.196000000000005,44.593,29.775 +2020-05-05 23:30:00,81.46,50.50899999999999,44.593,29.775 +2020-05-05 23:45:00,77.25,50.575,44.593,29.775 +2020-05-06 00:00:00,71.85,49.147,41.978,29.775 +2020-05-06 00:15:00,78.55,49.553000000000004,41.978,29.775 +2020-05-06 00:30:00,77.81,48.31,41.978,29.775 +2020-05-06 00:45:00,77.29,46.997,41.978,29.775 +2020-05-06 01:00:00,73.94,46.992,38.59,29.775 +2020-05-06 01:15:00,78.49,46.251999999999995,38.59,29.775 +2020-05-06 01:30:00,78.56,44.74100000000001,38.59,29.775 +2020-05-06 01:45:00,76.62,44.058,38.59,29.775 +2020-05-06 02:00:00,73.33,44.446999999999996,36.23,29.775 +2020-05-06 02:15:00,78.33,43.364,36.23,29.775 +2020-05-06 02:30:00,77.82,45.753,36.23,29.775 +2020-05-06 02:45:00,72.36,46.386,36.23,29.775 +2020-05-06 03:00:00,70.81,49.335,35.867,29.775 +2020-05-06 03:15:00,72.19,49.305,35.867,29.775 +2020-05-06 03:30:00,73.52,48.63399999999999,35.867,29.775 +2020-05-06 03:45:00,75.1,48.548,35.867,29.775 +2020-05-06 04:00:00,78.39,59.571999999999996,36.75,29.775 +2020-05-06 04:15:00,78.83,70.78699999999999,36.75,29.775 +2020-05-06 04:30:00,81.3,69.78699999999999,36.75,29.775 +2020-05-06 04:45:00,84.96,71.058,36.75,29.775 +2020-05-06 05:00:00,93.15,99.10799999999999,40.461,29.775 +2020-05-06 05:15:00,95.53,124.71799999999999,40.461,29.775 +2020-05-06 05:30:00,97.79,113.664,40.461,29.775 +2020-05-06 05:45:00,101.14,105.565,40.461,29.775 +2020-05-06 06:00:00,106.4,107.095,55.481,29.775 +2020-05-06 06:15:00,106.6,110.62100000000001,55.481,29.775 +2020-05-06 06:30:00,108.89,107.556,55.481,29.775 +2020-05-06 06:45:00,109.94,107.48100000000001,55.481,29.775 +2020-05-06 07:00:00,114.39,108.568,68.45,29.775 +2020-05-06 07:15:00,107.66,108.384,68.45,29.775 +2020-05-06 07:30:00,105.69,105.969,68.45,29.775 +2020-05-06 07:45:00,106.74,103.374,68.45,29.775 +2020-05-06 08:00:00,103.47,99.00200000000001,60.885,29.775 +2020-05-06 08:15:00,101.98,98.86399999999999,60.885,29.775 +2020-05-06 08:30:00,104.01,95.965,60.885,29.775 +2020-05-06 08:45:00,102.48,95.925,60.885,29.775 +2020-05-06 09:00:00,101.52,89.361,56.887,29.775 +2020-05-06 09:15:00,101.65,87.49799999999999,56.887,29.775 +2020-05-06 09:30:00,101.44,89.904,56.887,29.775 +2020-05-06 09:45:00,104.08,90.09700000000001,56.887,29.775 +2020-05-06 10:00:00,109.12,85.67200000000001,54.401,29.775 +2020-05-06 10:15:00,110.04,86.36399999999999,54.401,29.775 +2020-05-06 10:30:00,109.63,85.71600000000001,54.401,29.775 +2020-05-06 10:45:00,105.76,86.14200000000001,54.401,29.775 +2020-05-06 11:00:00,105.77,82.212,53.678000000000004,29.775 +2020-05-06 11:15:00,105.27,83.325,53.678000000000004,29.775 +2020-05-06 11:30:00,108.22,84.535,53.678000000000004,29.775 +2020-05-06 11:45:00,101.7,84.671,53.678000000000004,29.775 +2020-05-06 12:00:00,98.23,81.267,51.68,29.775 +2020-05-06 12:15:00,98.72,82.146,51.68,29.775 +2020-05-06 12:30:00,99.41,80.953,51.68,29.775 +2020-05-06 12:45:00,104.66,81.832,51.68,29.775 +2020-05-06 13:00:00,100.13,83.296,51.263000000000005,29.775 +2020-05-06 13:15:00,102.95,83.25200000000001,51.263000000000005,29.775 +2020-05-06 13:30:00,97.84,81.226,51.263000000000005,29.775 +2020-05-06 13:45:00,96.57,79.685,51.263000000000005,29.775 +2020-05-06 14:00:00,96.82,81.609,51.107,29.775 +2020-05-06 14:15:00,95.84,80.835,51.107,29.775 +2020-05-06 14:30:00,96.81,80.208,51.107,29.775 +2020-05-06 14:45:00,93.98,80.645,51.107,29.775 +2020-05-06 15:00:00,94.2,81.723,51.498000000000005,29.775 +2020-05-06 15:15:00,93.38,79.808,51.498000000000005,29.775 +2020-05-06 15:30:00,95.59,78.79,51.498000000000005,29.775 +2020-05-06 15:45:00,98.49,77.665,51.498000000000005,29.775 +2020-05-06 16:00:00,101.69,78.296,53.376999999999995,29.775 +2020-05-06 16:15:00,101.79,77.641,53.376999999999995,29.775 +2020-05-06 16:30:00,102.02,77.387,53.376999999999995,29.775 +2020-05-06 16:45:00,100.4,74.425,53.376999999999995,29.775 +2020-05-06 17:00:00,102.39,74.937,56.965,29.775 +2020-05-06 17:15:00,101.21,76.498,56.965,29.775 +2020-05-06 17:30:00,100.01,77.111,56.965,29.775 +2020-05-06 17:45:00,102.1,77.626,56.965,29.775 +2020-05-06 18:00:00,104.0,77.32,58.231,29.775 +2020-05-06 18:15:00,104.34,78.742,58.231,29.775 +2020-05-06 18:30:00,105.4,77.414,58.231,29.775 +2020-05-06 18:45:00,103.72,82.82700000000001,58.231,29.775 +2020-05-06 19:00:00,102.23,80.072,58.865,29.775 +2020-05-06 19:15:00,97.93,79.486,58.865,29.775 +2020-05-06 19:30:00,95.7,79.515,58.865,29.775 +2020-05-06 19:45:00,96.66,80.242,58.865,29.775 +2020-05-06 20:00:00,95.25,78.734,65.605,29.775 +2020-05-06 20:15:00,98.04,76.829,65.605,29.775 +2020-05-06 20:30:00,91.55,75.367,65.605,29.775 +2020-05-06 20:45:00,91.56,75.143,65.605,29.775 +2020-05-06 21:00:00,82.99,73.189,58.083999999999996,29.775 +2020-05-06 21:15:00,81.12,73.88600000000001,58.083999999999996,29.775 +2020-05-06 21:30:00,78.05,74.15100000000001,58.083999999999996,29.775 +2020-05-06 21:45:00,75.34,74.505,58.083999999999996,29.775 +2020-05-06 22:00:00,70.55,72.414,53.243,29.775 +2020-05-06 22:15:00,70.4,71.865,53.243,29.775 +2020-05-06 22:30:00,68.32,62.855,53.243,29.775 +2020-05-06 22:45:00,66.71,58.88,53.243,29.775 +2020-05-06 23:00:00,76.44,52.417,44.283,29.775 +2020-05-06 23:15:00,77.93,50.872,44.283,29.775 +2020-05-06 23:30:00,80.4,50.187,44.283,29.775 +2020-05-06 23:45:00,79.81,50.251999999999995,44.283,29.775 +2020-05-07 00:00:00,73.78,48.823,40.219,29.775 +2020-05-07 00:15:00,72.71,49.239,40.219,29.775 +2020-05-07 00:30:00,73.87,47.994,40.219,29.775 +2020-05-07 00:45:00,77.01,46.685,40.219,29.775 +2020-05-07 01:00:00,74.22,46.685,37.959,29.775 +2020-05-07 01:15:00,73.3,45.92100000000001,37.959,29.775 +2020-05-07 01:30:00,72.97,44.391999999999996,37.959,29.775 +2020-05-07 01:45:00,74.22,43.708999999999996,37.959,29.775 +2020-05-07 02:00:00,75.92,44.092,36.113,29.775 +2020-05-07 02:15:00,74.19,42.993,36.113,29.775 +2020-05-07 02:30:00,72.07,45.396,36.113,29.775 +2020-05-07 02:45:00,74.15,46.036,36.113,29.775 +2020-05-07 03:00:00,74.4,48.995,35.546,29.775 +2020-05-07 03:15:00,77.16,48.946000000000005,35.546,29.775 +2020-05-07 03:30:00,76.61,48.275,35.546,29.775 +2020-05-07 03:45:00,78.26,48.214,35.546,29.775 +2020-05-07 04:00:00,79.9,59.19,37.169000000000004,29.775 +2020-05-07 04:15:00,80.7,70.359,37.169000000000004,29.775 +2020-05-07 04:30:00,83.75,69.352,37.169000000000004,29.775 +2020-05-07 04:45:00,87.24,70.615,37.169000000000004,29.775 +2020-05-07 05:00:00,95.0,98.54899999999999,41.233000000000004,29.775 +2020-05-07 05:15:00,97.6,124.024,41.233000000000004,29.775 +2020-05-07 05:30:00,100.93,113.01700000000001,41.233000000000004,29.775 +2020-05-07 05:45:00,104.36,104.975,41.233000000000004,29.775 +2020-05-07 06:00:00,111.07,106.531,52.57,29.775 +2020-05-07 06:15:00,111.79,110.03200000000001,52.57,29.775 +2020-05-07 06:30:00,115.27,106.96700000000001,52.57,29.775 +2020-05-07 06:45:00,116.41,106.9,52.57,29.775 +2020-05-07 07:00:00,121.35,107.98,64.53,29.775 +2020-05-07 07:15:00,120.69,107.794,64.53,29.775 +2020-05-07 07:30:00,120.64,105.34899999999999,64.53,29.775 +2020-05-07 07:45:00,119.58,102.771,64.53,29.775 +2020-05-07 08:00:00,118.88,98.395,55.911,29.775 +2020-05-07 08:15:00,119.04,98.3,55.911,29.775 +2020-05-07 08:30:00,120.65,95.383,55.911,29.775 +2020-05-07 08:45:00,121.31,95.367,55.911,29.775 +2020-05-07 09:00:00,120.75,88.802,50.949,29.775 +2020-05-07 09:15:00,121.94,86.946,50.949,29.775 +2020-05-07 09:30:00,124.2,89.363,50.949,29.775 +2020-05-07 09:45:00,125.86,89.59,50.949,29.775 +2020-05-07 10:00:00,123.35,85.17299999999999,48.136,29.775 +2020-05-07 10:15:00,121.67,85.905,48.136,29.775 +2020-05-07 10:30:00,122.7,85.274,48.136,29.775 +2020-05-07 10:45:00,121.37,85.71700000000001,48.136,29.775 +2020-05-07 11:00:00,120.01,81.78,46.643,29.775 +2020-05-07 11:15:00,119.7,82.912,46.643,29.775 +2020-05-07 11:30:00,116.44,84.117,46.643,29.775 +2020-05-07 11:45:00,120.55,84.266,46.643,29.775 +2020-05-07 12:00:00,114.26,80.895,44.098,29.775 +2020-05-07 12:15:00,115.33,81.78,44.098,29.775 +2020-05-07 12:30:00,115.25,80.548,44.098,29.775 +2020-05-07 12:45:00,111.95,81.433,44.098,29.775 +2020-05-07 13:00:00,111.66,82.92299999999999,43.717,29.775 +2020-05-07 13:15:00,115.79,82.87899999999999,43.717,29.775 +2020-05-07 13:30:00,112.84,80.861,43.717,29.775 +2020-05-07 13:45:00,113.94,79.32,43.717,29.775 +2020-05-07 14:00:00,112.73,81.293,44.218999999999994,29.775 +2020-05-07 14:15:00,110.34,80.506,44.218999999999994,29.775 +2020-05-07 14:30:00,109.63,79.837,44.218999999999994,29.775 +2020-05-07 14:45:00,110.46,80.277,44.218999999999994,29.775 +2020-05-07 15:00:00,109.7,81.399,46.159,29.775 +2020-05-07 15:15:00,111.05,79.46600000000001,46.159,29.775 +2020-05-07 15:30:00,108.64,78.415,46.159,29.775 +2020-05-07 15:45:00,109.14,77.273,46.159,29.775 +2020-05-07 16:00:00,109.78,77.95100000000001,47.115,29.775 +2020-05-07 16:15:00,111.93,77.275,47.115,29.775 +2020-05-07 16:30:00,114.03,77.032,47.115,29.775 +2020-05-07 16:45:00,114.16,74.009,47.115,29.775 +2020-05-07 17:00:00,114.59,74.568,50.827,29.775 +2020-05-07 17:15:00,113.02,76.104,50.827,29.775 +2020-05-07 17:30:00,114.7,76.706,50.827,29.775 +2020-05-07 17:45:00,114.23,77.19,50.827,29.775 +2020-05-07 18:00:00,114.07,76.90100000000001,52.586000000000006,29.775 +2020-05-07 18:15:00,110.01,78.32,52.586000000000006,29.775 +2020-05-07 18:30:00,114.3,76.979,52.586000000000006,29.775 +2020-05-07 18:45:00,113.58,82.39299999999999,52.586000000000006,29.775 +2020-05-07 19:00:00,112.14,79.639,51.886,29.775 +2020-05-07 19:15:00,106.35,79.051,51.886,29.775 +2020-05-07 19:30:00,105.41,79.083,51.886,29.775 +2020-05-07 19:45:00,106.24,79.819,51.886,29.775 +2020-05-07 20:00:00,105.1,78.285,56.162,29.775 +2020-05-07 20:15:00,103.7,76.383,56.162,29.775 +2020-05-07 20:30:00,103.6,74.949,56.162,29.775 +2020-05-07 20:45:00,103.67,74.764,56.162,29.775 +2020-05-07 21:00:00,98.44,72.814,53.023,29.775 +2020-05-07 21:15:00,98.45,73.525,53.023,29.775 +2020-05-07 21:30:00,93.13,73.773,53.023,29.775 +2020-05-07 21:45:00,94.44,74.152,53.023,29.775 +2020-05-07 22:00:00,88.82,72.084,49.303999999999995,29.775 +2020-05-07 22:15:00,86.68,71.556,49.303999999999995,29.775 +2020-05-07 22:30:00,83.38,62.534,49.303999999999995,29.775 +2020-05-07 22:45:00,84.4,58.549,49.303999999999995,29.775 +2020-05-07 23:00:00,58.37,52.053999999999995,43.409,29.775 +2020-05-07 23:15:00,57.74,50.552,43.409,29.775 +2020-05-07 23:30:00,55.82,49.86600000000001,43.409,29.775 +2020-05-07 23:45:00,54.77,49.93,43.409,29.775 +2020-05-08 00:00:00,53.24,46.693000000000005,39.884,29.775 +2020-05-08 00:15:00,54.19,47.361999999999995,39.884,29.775 +2020-05-08 00:30:00,54.32,46.278999999999996,39.884,29.775 +2020-05-08 00:45:00,55.58,45.352,39.884,29.775 +2020-05-08 01:00:00,52.76,44.941,37.658,29.775 +2020-05-08 01:15:00,53.95,44.012,37.658,29.775 +2020-05-08 01:30:00,54.34,42.949,37.658,29.775 +2020-05-08 01:45:00,53.53,42.105,37.658,29.775 +2020-05-08 02:00:00,52.96,43.25,36.707,29.775 +2020-05-08 02:15:00,54.39,42.055,36.707,29.775 +2020-05-08 02:30:00,54.97,45.31399999999999,36.707,29.775 +2020-05-08 02:45:00,54.2,45.437,36.707,29.775 +2020-05-08 03:00:00,55.31,48.655,37.025,29.775 +2020-05-08 03:15:00,56.15,47.978,37.025,29.775 +2020-05-08 03:30:00,57.39,47.113,37.025,29.775 +2020-05-08 03:45:00,59.16,47.886,37.025,29.775 +2020-05-08 04:00:00,61.87,59.01,38.349000000000004,29.775 +2020-05-08 04:15:00,63.45,68.811,38.349000000000004,29.775 +2020-05-08 04:30:00,66.08,68.631,38.349000000000004,29.775 +2020-05-08 04:45:00,68.24,68.958,38.349000000000004,29.775 +2020-05-08 05:00:00,72.24,95.93,41.565,29.775 +2020-05-08 05:15:00,73.47,122.565,41.565,29.775 +2020-05-08 05:30:00,76.45,112.135,41.565,29.775 +2020-05-08 05:45:00,79.96,103.76799999999999,41.565,29.775 +2020-05-08 06:00:00,84.65,105.696,53.861000000000004,29.775 +2020-05-08 06:15:00,86.18,108.787,53.861000000000004,29.775 +2020-05-08 06:30:00,88.73,105.376,53.861000000000004,29.775 +2020-05-08 06:45:00,89.53,105.786,53.861000000000004,29.775 +2020-05-08 07:00:00,93.58,107.083,63.497,29.775 +2020-05-08 07:15:00,92.57,107.986,63.497,29.775 +2020-05-08 07:30:00,92.28,103.915,63.497,29.775 +2020-05-08 07:45:00,92.57,100.859,63.497,29.775 +2020-05-08 08:00:00,92.12,96.67299999999999,55.43899999999999,29.775 +2020-05-08 08:15:00,93.47,96.991,55.43899999999999,29.775 +2020-05-08 08:30:00,95.97,94.354,55.43899999999999,29.775 +2020-05-08 08:45:00,98.27,93.648,55.43899999999999,29.775 +2020-05-08 09:00:00,97.16,85.48,52.132,29.775 +2020-05-08 09:15:00,97.44,85.31700000000001,52.132,29.775 +2020-05-08 09:30:00,96.52,87.052,52.132,29.775 +2020-05-08 09:45:00,93.66,87.565,52.132,29.775 +2020-05-08 10:00:00,90.57,82.54700000000001,49.881,29.775 +2020-05-08 10:15:00,90.43,83.43799999999999,49.881,29.775 +2020-05-08 10:30:00,92.89,83.205,49.881,29.775 +2020-05-08 10:45:00,92.64,83.412,49.881,29.775 +2020-05-08 11:00:00,87.14,79.635,49.396,29.775 +2020-05-08 11:15:00,84.45,79.61,49.396,29.775 +2020-05-08 11:30:00,82.3,81.271,49.396,29.775 +2020-05-08 11:45:00,84.56,80.74600000000001,49.396,29.775 +2020-05-08 12:00:00,85.34,78.204,46.7,29.775 +2020-05-08 12:15:00,87.59,77.72800000000001,46.7,29.775 +2020-05-08 12:30:00,82.79,76.611,46.7,29.775 +2020-05-08 12:45:00,84.03,77.154,46.7,29.775 +2020-05-08 13:00:00,86.69,79.491,44.05,29.775 +2020-05-08 13:15:00,90.34,79.928,44.05,29.775 +2020-05-08 13:30:00,92.17,78.503,44.05,29.775 +2020-05-08 13:45:00,91.91,77.16,44.05,29.775 +2020-05-08 14:00:00,91.0,78.111,42.805,29.775 +2020-05-08 14:15:00,87.13,77.55199999999999,42.805,29.775 +2020-05-08 14:30:00,85.29,78.155,42.805,29.775 +2020-05-08 14:45:00,85.31,78.226,42.805,29.775 +2020-05-08 15:00:00,81.82,79.128,44.36600000000001,29.775 +2020-05-08 15:15:00,80.11,76.816,44.36600000000001,29.775 +2020-05-08 15:30:00,79.41,74.672,44.36600000000001,29.775 +2020-05-08 15:45:00,79.63,74.131,44.36600000000001,29.775 +2020-05-08 16:00:00,80.31,73.779,46.928999999999995,29.775 +2020-05-08 16:15:00,83.58,73.572,46.928999999999995,29.775 +2020-05-08 16:30:00,85.81,73.242,46.928999999999995,29.775 +2020-05-08 16:45:00,88.21,69.52600000000001,46.928999999999995,29.775 +2020-05-08 17:00:00,90.9,71.516,51.468,29.775 +2020-05-08 17:15:00,90.29,72.729,51.468,29.775 +2020-05-08 17:30:00,91.83,73.315,51.468,29.775 +2020-05-08 17:45:00,93.13,73.54899999999999,51.468,29.775 +2020-05-08 18:00:00,95.38,73.646,52.58,29.775 +2020-05-08 18:15:00,93.36,74.184,52.58,29.775 +2020-05-08 18:30:00,94.59,72.903,52.58,29.775 +2020-05-08 18:45:00,91.5,78.64,52.58,29.775 +2020-05-08 19:00:00,88.14,76.949,52.183,29.775 +2020-05-08 19:15:00,85.24,77.32600000000001,52.183,29.775 +2020-05-08 19:30:00,84.68,77.257,52.183,29.775 +2020-05-08 19:45:00,85.55,77.033,52.183,29.775 +2020-05-08 20:00:00,86.18,75.357,58.497,29.775 +2020-05-08 20:15:00,86.8,74.078,58.497,29.775 +2020-05-08 20:30:00,85.42,72.279,58.497,29.775 +2020-05-08 20:45:00,84.39,71.708,58.497,29.775 +2020-05-08 21:00:00,79.14,70.98,54.731,29.775 +2020-05-08 21:15:00,78.08,73.17699999999999,54.731,29.775 +2020-05-08 21:30:00,75.44,73.305,54.731,29.775 +2020-05-08 21:45:00,73.8,74.07300000000001,54.731,29.775 +2020-05-08 22:00:00,69.96,72.279,51.386,29.775 +2020-05-08 22:15:00,70.53,71.528,51.386,29.775 +2020-05-08 22:30:00,67.66,68.579,51.386,29.775 +2020-05-08 22:45:00,66.61,66.559,51.386,29.775 +2020-05-08 23:00:00,62.1,61.192,44.463,29.775 +2020-05-08 23:15:00,61.96,57.783,44.463,29.775 +2020-05-08 23:30:00,61.24,55.128,44.463,29.775 +2020-05-08 23:45:00,61.9,54.85,44.463,29.775 +2020-05-09 00:00:00,59.19,46.31399999999999,42.833999999999996,29.662 +2020-05-09 00:15:00,59.48,44.915,42.833999999999996,29.662 +2020-05-09 00:30:00,58.27,43.941,42.833999999999996,29.662 +2020-05-09 00:45:00,58.41,42.702,42.833999999999996,29.662 +2020-05-09 01:00:00,56.44,42.744,37.859,29.662 +2020-05-09 01:15:00,57.02,41.898999999999994,37.859,29.662 +2020-05-09 01:30:00,54.39,39.994,37.859,29.662 +2020-05-09 01:45:00,57.37,40.054,37.859,29.662 +2020-05-09 02:00:00,56.57,40.662,35.327,29.662 +2020-05-09 02:15:00,57.32,38.695,35.327,29.662 +2020-05-09 02:30:00,56.09,40.938,35.327,29.662 +2020-05-09 02:45:00,56.5,41.725,35.327,29.662 +2020-05-09 03:00:00,56.74,44.101000000000006,34.908,29.662 +2020-05-09 03:15:00,57.04,42.413999999999994,34.908,29.662 +2020-05-09 03:30:00,57.73,41.23,34.908,29.662 +2020-05-09 03:45:00,58.65,43.236000000000004,34.908,29.662 +2020-05-09 04:00:00,56.1,51.325,34.84,29.662 +2020-05-09 04:15:00,58.39,59.455,34.84,29.662 +2020-05-09 04:30:00,58.62,57.167,34.84,29.662 +2020-05-09 04:45:00,58.93,57.523,34.84,29.662 +2020-05-09 05:00:00,59.13,72.622,34.222,29.662 +2020-05-09 05:15:00,58.29,83.84100000000001,34.222,29.662 +2020-05-09 05:30:00,61.65,74.814,34.222,29.662 +2020-05-09 05:45:00,64.49,71.96600000000001,34.222,29.662 +2020-05-09 06:00:00,67.16,89.139,35.515,29.662 +2020-05-09 06:15:00,68.9,104.09200000000001,35.515,29.662 +2020-05-09 06:30:00,67.17,96.552,35.515,29.662 +2020-05-09 06:45:00,72.7,91.306,35.515,29.662 +2020-05-09 07:00:00,71.59,89.74700000000001,39.687,29.662 +2020-05-09 07:15:00,75.04,89.178,39.687,29.662 +2020-05-09 07:30:00,77.56,87.242,39.687,29.662 +2020-05-09 07:45:00,81.67,86.36399999999999,39.687,29.662 +2020-05-09 08:00:00,83.62,83.985,44.9,29.662 +2020-05-09 08:15:00,84.5,85.53399999999999,44.9,29.662 +2020-05-09 08:30:00,84.0,83.471,44.9,29.662 +2020-05-09 08:45:00,81.6,84.695,44.9,29.662 +2020-05-09 09:00:00,79.98,79.499,45.724,29.662 +2020-05-09 09:15:00,82.69,79.999,45.724,29.662 +2020-05-09 09:30:00,76.88,82.469,45.724,29.662 +2020-05-09 09:45:00,80.32,82.73200000000001,45.724,29.662 +2020-05-09 10:00:00,74.68,78.143,43.123999999999995,29.662 +2020-05-09 10:15:00,75.87,79.402,43.123999999999995,29.662 +2020-05-09 10:30:00,75.83,78.999,43.123999999999995,29.662 +2020-05-09 10:45:00,76.14,79.484,43.123999999999995,29.662 +2020-05-09 11:00:00,72.08,75.645,40.255,29.662 +2020-05-09 11:15:00,70.62,75.997,40.255,29.662 +2020-05-09 11:30:00,73.05,77.406,40.255,29.662 +2020-05-09 11:45:00,75.89,76.999,40.255,29.662 +2020-05-09 12:00:00,76.78,74.229,38.582,29.662 +2020-05-09 12:15:00,71.51,74.643,38.582,29.662 +2020-05-09 12:30:00,73.06,73.529,38.582,29.662 +2020-05-09 12:45:00,72.57,74.273,38.582,29.662 +2020-05-09 13:00:00,70.0,75.922,36.043,29.662 +2020-05-09 13:15:00,68.54,75.10600000000001,36.043,29.662 +2020-05-09 13:30:00,62.42,73.637,36.043,29.662 +2020-05-09 13:45:00,66.38,71.518,36.043,29.662 +2020-05-09 14:00:00,70.51,73.04899999999999,35.216,29.662 +2020-05-09 14:15:00,77.12,71.347,35.216,29.662 +2020-05-09 14:30:00,70.75,70.837,35.216,29.662 +2020-05-09 14:45:00,67.87,71.348,35.216,29.662 +2020-05-09 15:00:00,69.68,72.88600000000001,36.759,29.662 +2020-05-09 15:15:00,71.0,71.407,36.759,29.662 +2020-05-09 15:30:00,71.64,70.027,36.759,29.662 +2020-05-09 15:45:00,74.81,68.809,36.759,29.662 +2020-05-09 16:00:00,75.54,69.814,40.086,29.662 +2020-05-09 16:15:00,71.72,69.282,40.086,29.662 +2020-05-09 16:30:00,75.34,69.111,40.086,29.662 +2020-05-09 16:45:00,76.17,65.67,40.086,29.662 +2020-05-09 17:00:00,77.72,66.625,44.876999999999995,29.662 +2020-05-09 17:15:00,79.16,66.865,44.876999999999995,29.662 +2020-05-09 17:30:00,79.96,67.304,44.876999999999995,29.662 +2020-05-09 17:45:00,80.99,67.714,44.876999999999995,29.662 +2020-05-09 18:00:00,80.0,68.844,47.056000000000004,29.662 +2020-05-09 18:15:00,82.8,71.342,47.056000000000004,29.662 +2020-05-09 18:30:00,81.66,71.592,47.056000000000004,29.662 +2020-05-09 18:45:00,82.41,73.399,47.056000000000004,29.662 +2020-05-09 19:00:00,80.39,70.936,45.57,29.662 +2020-05-09 19:15:00,77.08,70.343,45.57,29.662 +2020-05-09 19:30:00,74.71,71.15100000000001,45.57,29.662 +2020-05-09 19:45:00,78.23,72.253,45.57,29.662 +2020-05-09 20:00:00,78.77,71.929,41.685,29.662 +2020-05-09 20:15:00,79.72,70.954,41.685,29.662 +2020-05-09 20:30:00,78.87,68.374,41.685,29.662 +2020-05-09 20:45:00,78.23,69.122,41.685,29.662 +2020-05-09 21:00:00,76.02,68.109,39.576,29.662 +2020-05-09 21:15:00,74.79,70.21300000000001,39.576,29.662 +2020-05-09 21:30:00,70.34,70.922,39.576,29.662 +2020-05-09 21:45:00,72.16,71.14399999999999,39.576,29.662 +2020-05-09 22:00:00,69.44,69.848,39.068000000000005,29.662 +2020-05-09 22:15:00,66.81,70.223,39.068000000000005,29.662 +2020-05-09 22:30:00,65.84,69.253,39.068000000000005,29.662 +2020-05-09 22:45:00,65.64,68.2,39.068000000000005,29.662 +2020-05-09 23:00:00,58.1,63.147,32.06,29.662 +2020-05-09 23:15:00,61.56,59.51,32.06,29.662 +2020-05-09 23:30:00,59.59,58.13,32.06,29.662 +2020-05-09 23:45:00,61.11,57.242,32.06,29.662 +2020-05-10 00:00:00,56.26,47.231,28.825,29.662 +2020-05-10 00:15:00,56.19,44.821000000000005,28.825,29.662 +2020-05-10 00:30:00,56.2,43.593999999999994,28.825,29.662 +2020-05-10 00:45:00,56.93,42.525,28.825,29.662 +2020-05-10 01:00:00,54.42,42.729,25.995,29.662 +2020-05-10 01:15:00,55.57,42.137,25.995,29.662 +2020-05-10 01:30:00,54.8,40.296,25.995,29.662 +2020-05-10 01:45:00,54.72,39.944,25.995,29.662 +2020-05-10 02:00:00,53.8,40.336,24.394000000000002,29.662 +2020-05-10 02:15:00,53.93,38.604,24.394000000000002,29.662 +2020-05-10 02:30:00,54.27,41.316,24.394000000000002,29.662 +2020-05-10 02:45:00,53.34,42.047,24.394000000000002,29.662 +2020-05-10 03:00:00,53.35,45.059,22.916999999999998,29.662 +2020-05-10 03:15:00,53.77,43.37,22.916999999999998,29.662 +2020-05-10 03:30:00,55.05,42.111999999999995,22.916999999999998,29.662 +2020-05-10 03:45:00,55.75,43.489,22.916999999999998,29.662 +2020-05-10 04:00:00,53.43,51.42,23.576999999999998,29.662 +2020-05-10 04:15:00,53.2,58.799,23.576999999999998,29.662 +2020-05-10 04:30:00,52.95,57.58,23.576999999999998,29.662 +2020-05-10 04:45:00,53.86,57.659,23.576999999999998,29.662 +2020-05-10 05:00:00,53.48,71.578,22.730999999999998,29.662 +2020-05-10 05:15:00,54.32,81.235,22.730999999999998,29.662 +2020-05-10 05:30:00,53.89,71.875,22.730999999999998,29.662 +2020-05-10 05:45:00,54.74,68.944,22.730999999999998,29.662 +2020-05-10 06:00:00,56.84,84.185,22.34,29.662 +2020-05-10 06:15:00,56.42,99.195,22.34,29.662 +2020-05-10 06:30:00,57.71,90.72200000000001,22.34,29.662 +2020-05-10 06:45:00,58.61,84.32700000000001,22.34,29.662 +2020-05-10 07:00:00,59.27,83.804,24.691999999999997,29.662 +2020-05-10 07:15:00,59.67,81.642,24.691999999999997,29.662 +2020-05-10 07:30:00,59.68,80.24,24.691999999999997,29.662 +2020-05-10 07:45:00,60.06,79.082,24.691999999999997,29.662 +2020-05-10 08:00:00,59.38,77.918,29.340999999999998,29.662 +2020-05-10 08:15:00,59.29,80.388,29.340999999999998,29.662 +2020-05-10 08:30:00,59.52,79.59,29.340999999999998,29.662 +2020-05-10 08:45:00,58.7,81.51899999999999,29.340999999999998,29.662 +2020-05-10 09:00:00,56.6,76.072,30.788,29.662 +2020-05-10 09:15:00,57.08,76.41,30.788,29.662 +2020-05-10 09:30:00,57.77,79.16199999999999,30.788,29.662 +2020-05-10 09:45:00,60.07,80.18,30.788,29.662 +2020-05-10 10:00:00,59.67,76.94800000000001,30.158,29.662 +2020-05-10 10:15:00,60.32,78.554,30.158,29.662 +2020-05-10 10:30:00,60.97,78.569,30.158,29.662 +2020-05-10 10:45:00,61.22,79.084,30.158,29.662 +2020-05-10 11:00:00,58.46,75.331,32.056,29.662 +2020-05-10 11:15:00,58.18,75.423,32.056,29.662 +2020-05-10 11:30:00,55.24,76.867,32.056,29.662 +2020-05-10 11:45:00,55.54,76.905,32.056,29.662 +2020-05-10 12:00:00,53.33,74.672,28.671999999999997,29.662 +2020-05-10 12:15:00,54.51,75.421,28.671999999999997,29.662 +2020-05-10 12:30:00,50.53,73.825,28.671999999999997,29.662 +2020-05-10 12:45:00,50.54,73.71300000000001,28.671999999999997,29.662 +2020-05-10 13:00:00,47.4,74.889,23.171,29.662 +2020-05-10 13:15:00,45.4,74.87899999999999,23.171,29.662 +2020-05-10 13:30:00,46.65,72.51899999999999,23.171,29.662 +2020-05-10 13:45:00,48.47,70.95,23.171,29.662 +2020-05-10 14:00:00,49.27,73.501,19.11,29.662 +2020-05-10 14:15:00,48.56,72.59100000000001,19.11,29.662 +2020-05-10 14:30:00,47.46,71.642,19.11,29.662 +2020-05-10 14:45:00,47.02,71.206,19.11,29.662 +2020-05-10 15:00:00,46.47,72.26899999999999,19.689,29.662 +2020-05-10 15:15:00,45.86,70.492,19.689,29.662 +2020-05-10 15:30:00,47.51,69.187,19.689,29.662 +2020-05-10 15:45:00,49.12,68.445,19.689,29.662 +2020-05-10 16:00:00,54.26,68.936,22.875,29.662 +2020-05-10 16:15:00,59.43,68.202,22.875,29.662 +2020-05-10 16:30:00,61.12,68.893,22.875,29.662 +2020-05-10 16:45:00,64.01,65.495,22.875,29.662 +2020-05-10 17:00:00,66.15,66.755,33.884,29.662 +2020-05-10 17:15:00,65.55,67.977,33.884,29.662 +2020-05-10 17:30:00,68.01,69.122,33.884,29.662 +2020-05-10 17:45:00,69.14,70.696,33.884,29.662 +2020-05-10 18:00:00,71.22,72.01899999999999,38.453,29.662 +2020-05-10 18:15:00,71.38,74.692,38.453,29.662 +2020-05-10 18:30:00,71.35,73.967,38.453,29.662 +2020-05-10 18:45:00,72.02,76.55,38.453,29.662 +2020-05-10 19:00:00,71.05,75.596,39.221,29.662 +2020-05-10 19:15:00,71.32,74.34100000000001,39.221,29.662 +2020-05-10 19:30:00,76.1,74.89699999999999,39.221,29.662 +2020-05-10 19:45:00,79.65,76.161,39.221,29.662 +2020-05-10 20:00:00,78.39,75.993,37.871,29.662 +2020-05-10 20:15:00,77.6,75.27199999999999,37.871,29.662 +2020-05-10 20:30:00,79.29,73.798,37.871,29.662 +2020-05-10 20:45:00,79.19,72.845,37.871,29.662 +2020-05-10 21:00:00,79.14,70.771,36.465,29.662 +2020-05-10 21:15:00,79.16,72.43,36.465,29.662 +2020-05-10 21:30:00,76.86,72.72,36.465,29.662 +2020-05-10 21:45:00,77.71,73.296,36.465,29.662 +2020-05-10 22:00:00,72.23,73.175,36.092,29.662 +2020-05-10 22:15:00,73.28,71.986,36.092,29.662 +2020-05-10 22:30:00,71.29,69.47399999999999,36.092,29.662 +2020-05-10 22:45:00,72.19,67.111,36.092,29.662 +2020-05-10 23:00:00,66.3,60.795,31.013,29.662 +2020-05-10 23:15:00,67.7,58.853,31.013,29.662 +2020-05-10 23:30:00,66.26,57.341,31.013,29.662 +2020-05-10 23:45:00,66.87,56.893,31.013,29.662 +2020-05-11 00:00:00,68.36,49.586999999999996,31.174,29.775 +2020-05-11 00:15:00,71.71,48.857,31.174,29.775 +2020-05-11 00:30:00,71.93,47.37,31.174,29.775 +2020-05-11 00:45:00,68.14,45.806999999999995,31.174,29.775 +2020-05-11 01:00:00,62.71,46.325,29.663,29.775 +2020-05-11 01:15:00,63.59,45.534,29.663,29.775 +2020-05-11 01:30:00,63.09,43.979,29.663,29.775 +2020-05-11 01:45:00,66.91,43.59,29.663,29.775 +2020-05-11 02:00:00,71.05,44.325,28.793000000000003,29.775 +2020-05-11 02:15:00,71.22,42.299,28.793000000000003,29.775 +2020-05-11 02:30:00,67.29,45.268,28.793000000000003,29.775 +2020-05-11 02:45:00,64.47,45.673,28.793000000000003,29.775 +2020-05-11 03:00:00,65.55,49.538999999999994,27.728,29.775 +2020-05-11 03:15:00,66.79,48.979,27.728,29.775 +2020-05-11 03:30:00,67.95,48.162,27.728,29.775 +2020-05-11 03:45:00,74.12,49.006,27.728,29.775 +2020-05-11 04:00:00,80.04,60.865,29.266,29.775 +2020-05-11 04:15:00,81.67,71.962,29.266,29.775 +2020-05-11 04:30:00,80.33,71.131,29.266,29.775 +2020-05-11 04:45:00,81.18,71.55199999999999,29.266,29.775 +2020-05-11 05:00:00,89.03,96.06299999999999,37.889,29.775 +2020-05-11 05:15:00,93.69,120.575,37.889,29.775 +2020-05-11 05:30:00,95.84,110.10700000000001,37.889,29.775 +2020-05-11 05:45:00,103.12,102.885,37.889,29.775 +2020-05-11 06:00:00,109.88,104.07799999999999,55.485,29.775 +2020-05-11 06:15:00,110.72,106.779,55.485,29.775 +2020-05-11 06:30:00,107.23,104.19200000000001,55.485,29.775 +2020-05-11 06:45:00,105.05,105.00299999999999,55.485,29.775 +2020-05-11 07:00:00,107.77,106.0,65.765,29.775 +2020-05-11 07:15:00,109.31,106.066,65.765,29.775 +2020-05-11 07:30:00,106.64,103.68700000000001,65.765,29.775 +2020-05-11 07:45:00,108.08,101.87200000000001,65.765,29.775 +2020-05-11 08:00:00,112.88,97.49700000000001,56.745,29.775 +2020-05-11 08:15:00,110.39,98.29700000000001,56.745,29.775 +2020-05-11 08:30:00,108.45,95.244,56.745,29.775 +2020-05-11 08:45:00,105.26,96.137,56.745,29.775 +2020-05-11 09:00:00,105.56,89.617,53.321999999999996,29.775 +2020-05-11 09:15:00,103.65,87.292,53.321999999999996,29.775 +2020-05-11 09:30:00,103.56,88.97200000000001,53.321999999999996,29.775 +2020-05-11 09:45:00,106.22,88.381,53.321999999999996,29.775 +2020-05-11 10:00:00,102.45,85.273,51.309,29.775 +2020-05-11 10:15:00,103.67,86.678,51.309,29.775 +2020-05-11 10:30:00,103.32,86.024,51.309,29.775 +2020-05-11 10:45:00,101.26,85.666,51.309,29.775 +2020-05-11 11:00:00,99.43,81.113,50.415,29.775 +2020-05-11 11:15:00,98.15,82.15100000000001,50.415,29.775 +2020-05-11 11:30:00,98.64,84.656,50.415,29.775 +2020-05-11 11:45:00,96.79,84.916,50.415,29.775 +2020-05-11 12:00:00,96.52,82.369,48.273,29.775 +2020-05-11 12:15:00,96.68,83.20299999999999,48.273,29.775 +2020-05-11 12:30:00,95.66,80.89699999999999,48.273,29.775 +2020-05-11 12:45:00,95.11,81.42,48.273,29.775 +2020-05-11 13:00:00,94.53,83.427,48.452,29.775 +2020-05-11 13:15:00,101.79,82.205,48.452,29.775 +2020-05-11 13:30:00,101.81,79.762,48.452,29.775 +2020-05-11 13:45:00,100.1,78.861,48.452,29.775 +2020-05-11 14:00:00,93.14,80.579,48.35,29.775 +2020-05-11 14:15:00,93.8,79.82300000000001,48.35,29.775 +2020-05-11 14:30:00,92.72,78.492,48.35,29.775 +2020-05-11 14:45:00,95.44,79.55,48.35,29.775 +2020-05-11 15:00:00,93.18,81.13600000000001,48.838,29.775 +2020-05-11 15:15:00,91.76,78.321,48.838,29.775 +2020-05-11 15:30:00,93.36,77.204,48.838,29.775 +2020-05-11 15:45:00,94.72,75.90899999999999,48.838,29.775 +2020-05-11 16:00:00,95.88,77.11399999999999,50.873000000000005,29.775 +2020-05-11 16:15:00,96.96,76.11,50.873000000000005,29.775 +2020-05-11 16:30:00,99.03,75.937,50.873000000000005,29.775 +2020-05-11 16:45:00,101.19,72.03,50.873000000000005,29.775 +2020-05-11 17:00:00,103.98,72.402,56.637,29.775 +2020-05-11 17:15:00,104.09,73.51,56.637,29.775 +2020-05-11 17:30:00,105.88,74.139,56.637,29.775 +2020-05-11 17:45:00,105.66,74.80199999999999,56.637,29.775 +2020-05-11 18:00:00,106.65,75.36399999999999,56.35,29.775 +2020-05-11 18:15:00,105.32,75.806,56.35,29.775 +2020-05-11 18:30:00,112.4,74.733,56.35,29.775 +2020-05-11 18:45:00,113.9,80.008,56.35,29.775 +2020-05-11 19:00:00,103.24,78.153,56.023,29.775 +2020-05-11 19:15:00,99.4,77.52,56.023,29.775 +2020-05-11 19:30:00,100.03,77.965,56.023,29.775 +2020-05-11 19:45:00,99.47,78.46300000000001,56.023,29.775 +2020-05-11 20:00:00,100.76,76.457,62.372,29.775 +2020-05-11 20:15:00,98.44,75.921,62.372,29.775 +2020-05-11 20:30:00,97.74,74.20100000000001,62.372,29.775 +2020-05-11 20:45:00,96.87,74.044,62.372,29.775 +2020-05-11 21:00:00,95.03,71.687,57.516999999999996,29.775 +2020-05-11 21:15:00,93.92,73.28399999999999,57.516999999999996,29.775 +2020-05-11 21:30:00,95.99,73.58,57.516999999999996,29.775 +2020-05-11 21:45:00,94.93,73.828,57.516999999999996,29.775 +2020-05-11 22:00:00,89.61,71.101,51.823,29.775 +2020-05-11 22:15:00,82.35,71.01100000000001,51.823,29.775 +2020-05-11 22:30:00,85.45,61.678999999999995,51.823,29.775 +2020-05-11 22:45:00,85.91,57.555,51.823,29.775 +2020-05-11 23:00:00,82.2,51.543,43.832,29.775 +2020-05-11 23:15:00,78.11,49.298,43.832,29.775 +2020-05-11 23:30:00,80.04,48.743,43.832,29.775 +2020-05-11 23:45:00,81.09,48.815,43.832,29.775 +2020-05-12 00:00:00,78.75,47.239,42.371,29.775 +2020-05-12 00:15:00,74.73,47.705,42.371,29.775 +2020-05-12 00:30:00,75.86,46.446000000000005,42.371,29.775 +2020-05-12 00:45:00,79.25,45.168,42.371,29.775 +2020-05-12 01:00:00,77.81,45.195,39.597,29.775 +2020-05-12 01:15:00,76.91,44.31100000000001,39.597,29.775 +2020-05-12 01:30:00,71.67,42.696999999999996,39.597,29.775 +2020-05-12 01:45:00,78.35,42.011,39.597,29.775 +2020-05-12 02:00:00,78.21,42.357,38.298,29.775 +2020-05-12 02:15:00,77.57,41.191,38.298,29.775 +2020-05-12 02:30:00,70.31,43.653,38.298,29.775 +2020-05-12 02:45:00,70.29,44.328,38.298,29.775 +2020-05-12 03:00:00,71.96,47.341,37.884,29.775 +2020-05-12 03:15:00,73.12,47.2,37.884,29.775 +2020-05-12 03:30:00,74.65,46.527,37.884,29.775 +2020-05-12 03:45:00,76.43,46.586000000000006,37.884,29.775 +2020-05-12 04:00:00,82.71,57.331,39.442,29.775 +2020-05-12 04:15:00,87.45,68.267,39.442,29.775 +2020-05-12 04:30:00,90.35,67.226,39.442,29.775 +2020-05-12 04:45:00,87.36,68.453,39.442,29.775 +2020-05-12 05:00:00,92.61,95.80799999999999,43.608000000000004,29.775 +2020-05-12 05:15:00,95.01,120.62100000000001,43.608000000000004,29.775 +2020-05-12 05:30:00,97.1,109.846,43.608000000000004,29.775 +2020-05-12 05:45:00,99.06,102.089,43.608000000000004,29.775 +2020-05-12 06:00:00,104.0,103.76,54.99100000000001,29.775 +2020-05-12 06:15:00,104.83,107.14399999999999,54.99100000000001,29.775 +2020-05-12 06:30:00,107.38,104.08200000000001,54.99100000000001,29.775 +2020-05-12 06:45:00,107.97,104.059,54.99100000000001,29.775 +2020-05-12 07:00:00,108.87,105.101,66.217,29.775 +2020-05-12 07:15:00,110.38,104.91,66.217,29.775 +2020-05-12 07:30:00,109.05,102.322,66.217,29.775 +2020-05-12 07:45:00,113.65,99.833,66.217,29.775 +2020-05-12 08:00:00,108.2,95.444,60.151,29.775 +2020-05-12 08:15:00,103.72,95.56299999999999,60.151,29.775 +2020-05-12 08:30:00,103.77,92.559,60.151,29.775 +2020-05-12 08:45:00,104.97,92.65899999999999,60.151,29.775 +2020-05-12 09:00:00,109.3,86.09100000000001,53.873000000000005,29.775 +2020-05-12 09:15:00,106.31,84.264,53.873000000000005,29.775 +2020-05-12 09:30:00,107.3,86.738,53.873000000000005,29.775 +2020-05-12 09:45:00,105.8,87.12700000000001,53.873000000000005,29.775 +2020-05-12 10:00:00,103.34,82.756,51.417,29.775 +2020-05-12 10:15:00,109.67,83.676,51.417,29.775 +2020-05-12 10:30:00,105.75,83.12700000000001,51.417,29.775 +2020-05-12 10:45:00,108.2,83.65,51.417,29.775 +2020-05-12 11:00:00,106.71,79.682,50.43600000000001,29.775 +2020-05-12 11:15:00,107.35,80.905,50.43600000000001,29.775 +2020-05-12 11:30:00,110.29,82.086,50.43600000000001,29.775 +2020-05-12 11:45:00,120.56,82.305,50.43600000000001,29.775 +2020-05-12 12:00:00,127.19,79.093,47.468,29.775 +2020-05-12 12:15:00,125.28,80.00399999999999,47.468,29.775 +2020-05-12 12:30:00,115.59,78.583,47.468,29.775 +2020-05-12 12:45:00,116.61,79.493,47.468,29.775 +2020-05-12 13:00:00,118.57,81.10600000000001,48.453,29.775 +2020-05-12 13:15:00,118.1,81.064,48.453,29.775 +2020-05-12 13:30:00,109.12,79.085,48.453,29.775 +2020-05-12 13:45:00,111.48,77.554,48.453,29.775 +2020-05-12 14:00:00,124.56,79.757,48.435,29.775 +2020-05-12 14:15:00,123.29,78.90899999999999,48.435,29.775 +2020-05-12 14:30:00,118.12,78.03399999999999,48.435,29.775 +2020-05-12 14:45:00,118.02,78.485,48.435,29.775 +2020-05-12 15:00:00,121.42,79.819,49.966,29.775 +2020-05-12 15:15:00,120.43,77.8,49.966,29.775 +2020-05-12 15:30:00,116.21,76.589,49.966,29.775 +2020-05-12 15:45:00,114.12,75.368,49.966,29.775 +2020-05-12 16:00:00,113.27,76.267,51.184,29.775 +2020-05-12 16:15:00,110.03,75.498,51.184,29.775 +2020-05-12 16:30:00,113.31,75.309,51.184,29.775 +2020-05-12 16:45:00,110.3,71.995,51.184,29.775 +2020-05-12 17:00:00,113.37,72.785,56.138999999999996,29.775 +2020-05-12 17:15:00,113.49,74.193,56.138999999999996,29.775 +2020-05-12 17:30:00,116.69,74.74,56.138999999999996,29.775 +2020-05-12 17:45:00,114.28,75.071,56.138999999999996,29.775 +2020-05-12 18:00:00,119.23,74.863,57.038000000000004,29.775 +2020-05-12 18:15:00,118.62,76.265,57.038000000000004,29.775 +2020-05-12 18:30:00,115.63,74.863,57.038000000000004,29.775 +2020-05-12 18:45:00,111.37,80.283,57.038000000000004,29.775 +2020-05-12 19:00:00,112.68,77.525,56.492,29.775 +2020-05-12 19:15:00,112.7,76.933,56.492,29.775 +2020-05-12 19:30:00,108.78,76.973,56.492,29.775 +2020-05-12 19:45:00,103.96,77.756,56.492,29.775 +2020-05-12 20:00:00,102.83,76.097,62.534,29.775 +2020-05-12 20:15:00,99.27,74.208,62.534,29.775 +2020-05-12 20:30:00,100.18,72.914,62.534,29.775 +2020-05-12 20:45:00,106.01,72.91199999999999,62.534,29.775 +2020-05-12 21:00:00,98.91,70.986,55.506,29.775 +2020-05-12 21:15:00,97.97,71.77199999999999,55.506,29.775 +2020-05-12 21:30:00,90.65,71.925,55.506,29.775 +2020-05-12 21:45:00,87.9,72.434,55.506,29.775 +2020-05-12 22:00:00,89.08,70.472,51.472,29.775 +2020-05-12 22:15:00,88.65,70.043,51.472,29.775 +2020-05-12 22:30:00,85.02,60.961000000000006,51.472,29.775 +2020-05-12 22:45:00,79.11,56.92100000000001,51.472,29.775 +2020-05-12 23:00:00,74.3,50.28,44.593,29.775 +2020-05-12 23:15:00,77.56,48.982,44.593,29.775 +2020-05-12 23:30:00,74.7,48.299,44.593,29.775 +2020-05-12 23:45:00,74.02,48.355,44.593,29.775 +2020-05-13 00:00:00,77.65,46.93,41.978,29.775 +2020-05-13 00:15:00,79.78,47.406000000000006,41.978,29.775 +2020-05-13 00:30:00,79.02,46.145,41.978,29.775 +2020-05-13 00:45:00,74.67,44.873000000000005,41.978,29.775 +2020-05-13 01:00:00,70.45,44.906000000000006,38.59,29.775 +2020-05-13 01:15:00,75.07,43.998999999999995,38.59,29.775 +2020-05-13 01:30:00,76.59,42.369,38.59,29.775 +2020-05-13 01:45:00,78.3,41.681000000000004,38.59,29.775 +2020-05-13 02:00:00,73.41,42.021,36.23,29.775 +2020-05-13 02:15:00,70.67,40.841,36.23,29.775 +2020-05-13 02:30:00,69.26,43.31399999999999,36.23,29.775 +2020-05-13 02:45:00,70.7,43.997,36.23,29.775 +2020-05-13 03:00:00,72.64,47.019,35.867,29.775 +2020-05-13 03:15:00,72.13,46.858999999999995,35.867,29.775 +2020-05-13 03:30:00,73.68,46.188,35.867,29.775 +2020-05-13 03:45:00,77.49,46.271,35.867,29.775 +2020-05-13 04:00:00,80.21,56.97,36.75,29.775 +2020-05-13 04:15:00,87.8,67.86,36.75,29.775 +2020-05-13 04:30:00,91.75,66.811,36.75,29.775 +2020-05-13 04:45:00,96.22,68.03,36.75,29.775 +2020-05-13 05:00:00,96.43,95.272,40.461,29.775 +2020-05-13 05:15:00,98.98,119.954,40.461,29.775 +2020-05-13 05:30:00,102.79,109.227,40.461,29.775 +2020-05-13 05:45:00,105.22,101.527,40.461,29.775 +2020-05-13 06:00:00,111.07,103.219,55.481,29.775 +2020-05-13 06:15:00,111.37,106.58,55.481,29.775 +2020-05-13 06:30:00,115.97,103.52,55.481,29.775 +2020-05-13 06:45:00,119.83,103.505,55.481,29.775 +2020-05-13 07:00:00,123.06,104.54,68.45,29.775 +2020-05-13 07:15:00,124.82,104.348,68.45,29.775 +2020-05-13 07:30:00,124.87,101.734,68.45,29.775 +2020-05-13 07:45:00,126.18,99.264,68.45,29.775 +2020-05-13 08:00:00,124.84,94.87200000000001,60.885,29.775 +2020-05-13 08:15:00,125.61,95.03299999999999,60.885,29.775 +2020-05-13 08:30:00,127.05,92.01299999999999,60.885,29.775 +2020-05-13 08:45:00,130.24,92.135,60.885,29.775 +2020-05-13 09:00:00,129.22,85.56700000000001,56.887,29.775 +2020-05-13 09:15:00,132.69,83.745,56.887,29.775 +2020-05-13 09:30:00,132.0,86.23,56.887,29.775 +2020-05-13 09:45:00,133.26,86.65,56.887,29.775 +2020-05-13 10:00:00,132.15,82.288,54.401,29.775 +2020-05-13 10:15:00,133.61,83.245,54.401,29.775 +2020-05-13 10:30:00,132.7,82.713,54.401,29.775 +2020-05-13 10:45:00,129.86,83.25200000000001,54.401,29.775 +2020-05-13 11:00:00,125.77,79.27600000000001,53.678000000000004,29.775 +2020-05-13 11:15:00,124.22,80.518,53.678000000000004,29.775 +2020-05-13 11:30:00,132.15,81.693,53.678000000000004,29.775 +2020-05-13 11:45:00,134.23,81.925,53.678000000000004,29.775 +2020-05-13 12:00:00,133.92,78.745,51.68,29.775 +2020-05-13 12:15:00,126.49,79.66,51.68,29.775 +2020-05-13 12:30:00,126.47,78.203,51.68,29.775 +2020-05-13 12:45:00,126.1,79.117,51.68,29.775 +2020-05-13 13:00:00,122.35,80.75399999999999,51.263000000000005,29.775 +2020-05-13 13:15:00,121.6,80.712,51.263000000000005,29.775 +2020-05-13 13:30:00,115.7,78.74,51.263000000000005,29.775 +2020-05-13 13:45:00,116.2,77.212,51.263000000000005,29.775 +2020-05-13 14:00:00,122.46,79.46,51.107,29.775 +2020-05-13 14:15:00,118.59,78.6,51.107,29.775 +2020-05-13 14:30:00,107.28,77.684,51.107,29.775 +2020-05-13 14:45:00,98.18,78.138,51.107,29.775 +2020-05-13 15:00:00,97.87,79.513,51.498000000000005,29.775 +2020-05-13 15:15:00,105.19,77.479,51.498000000000005,29.775 +2020-05-13 15:30:00,108.25,76.236,51.498000000000005,29.775 +2020-05-13 15:45:00,112.75,74.999,51.498000000000005,29.775 +2020-05-13 16:00:00,115.28,75.942,53.376999999999995,29.775 +2020-05-13 16:15:00,121.08,75.155,53.376999999999995,29.775 +2020-05-13 16:30:00,127.19,74.976,53.376999999999995,29.775 +2020-05-13 16:45:00,124.65,71.605,53.376999999999995,29.775 +2020-05-13 17:00:00,116.0,72.441,56.965,29.775 +2020-05-13 17:15:00,118.64,73.824,56.965,29.775 +2020-05-13 17:30:00,119.77,74.36,56.965,29.775 +2020-05-13 17:45:00,121.03,74.661,56.965,29.775 +2020-05-13 18:00:00,116.2,74.469,58.231,29.775 +2020-05-13 18:15:00,110.61,75.866,58.231,29.775 +2020-05-13 18:30:00,111.68,74.452,58.231,29.775 +2020-05-13 18:45:00,115.74,79.872,58.231,29.775 +2020-05-13 19:00:00,115.72,77.115,58.865,29.775 +2020-05-13 19:15:00,108.85,76.52199999999999,58.865,29.775 +2020-05-13 19:30:00,105.3,76.563,58.865,29.775 +2020-05-13 19:45:00,108.65,77.355,58.865,29.775 +2020-05-13 20:00:00,108.52,75.672,65.605,29.775 +2020-05-13 20:15:00,107.64,73.78399999999999,65.605,29.775 +2020-05-13 20:30:00,103.12,72.518,65.605,29.775 +2020-05-13 20:45:00,101.58,72.55199999999999,65.605,29.775 +2020-05-13 21:00:00,99.72,70.63,58.083999999999996,29.775 +2020-05-13 21:15:00,98.92,71.432,58.083999999999996,29.775 +2020-05-13 21:30:00,96.57,71.565,58.083999999999996,29.775 +2020-05-13 21:45:00,89.44,72.09899999999999,58.083999999999996,29.775 +2020-05-13 22:00:00,85.38,70.156,53.243,29.775 +2020-05-13 22:15:00,89.96,69.749,53.243,29.775 +2020-05-13 22:30:00,86.78,60.653,53.243,29.775 +2020-05-13 22:45:00,85.75,56.601000000000006,53.243,29.775 +2020-05-13 23:00:00,75.39,49.934,44.283,29.775 +2020-05-13 23:15:00,74.31,48.676,44.283,29.775 +2020-05-13 23:30:00,74.53,47.994,44.283,29.775 +2020-05-13 23:45:00,72.86,48.048,44.283,29.775 +2020-05-14 00:00:00,69.32,46.623000000000005,40.219,29.775 +2020-05-14 00:15:00,70.84,47.11,40.219,29.775 +2020-05-14 00:30:00,69.79,45.847,40.219,29.775 +2020-05-14 00:45:00,70.68,44.582,40.219,29.775 +2020-05-14 01:00:00,70.16,44.619,37.959,29.775 +2020-05-14 01:15:00,69.63,43.68899999999999,37.959,29.775 +2020-05-14 01:30:00,70.15,42.043,37.959,29.775 +2020-05-14 01:45:00,70.69,41.355,37.959,29.775 +2020-05-14 02:00:00,69.83,41.687,36.113,29.775 +2020-05-14 02:15:00,70.46,40.495,36.113,29.775 +2020-05-14 02:30:00,71.03,42.979,36.113,29.775 +2020-05-14 02:45:00,70.72,43.668,36.113,29.775 +2020-05-14 03:00:00,71.24,46.7,35.546,29.775 +2020-05-14 03:15:00,72.5,46.523999999999994,35.546,29.775 +2020-05-14 03:30:00,73.71,45.853,35.546,29.775 +2020-05-14 03:45:00,75.65,45.958999999999996,35.546,29.775 +2020-05-14 04:00:00,79.12,56.613,37.169000000000004,29.775 +2020-05-14 04:15:00,79.46,67.456,37.169000000000004,29.775 +2020-05-14 04:30:00,82.89,66.40100000000001,37.169000000000004,29.775 +2020-05-14 04:45:00,85.82,67.613,37.169000000000004,29.775 +2020-05-14 05:00:00,95.94,94.743,41.233000000000004,29.775 +2020-05-14 05:15:00,98.55,119.294,41.233000000000004,29.775 +2020-05-14 05:30:00,99.72,108.61399999999999,41.233000000000004,29.775 +2020-05-14 05:45:00,102.92,100.969,41.233000000000004,29.775 +2020-05-14 06:00:00,110.05,102.68299999999999,52.57,29.775 +2020-05-14 06:15:00,112.59,106.021,52.57,29.775 +2020-05-14 06:30:00,116.11,102.962,52.57,29.775 +2020-05-14 06:45:00,118.57,102.956,52.57,29.775 +2020-05-14 07:00:00,121.86,103.98200000000001,64.53,29.775 +2020-05-14 07:15:00,122.71,103.792,64.53,29.775 +2020-05-14 07:30:00,123.27,101.15100000000001,64.53,29.775 +2020-05-14 07:45:00,125.08,98.7,64.53,29.775 +2020-05-14 08:00:00,124.66,94.306,55.911,29.775 +2020-05-14 08:15:00,124.04,94.51,55.911,29.775 +2020-05-14 08:30:00,126.3,91.473,55.911,29.775 +2020-05-14 08:45:00,127.75,91.618,55.911,29.775 +2020-05-14 09:00:00,125.81,85.04799999999999,50.949,29.775 +2020-05-14 09:15:00,127.78,83.234,50.949,29.775 +2020-05-14 09:30:00,127.51,85.727,50.949,29.775 +2020-05-14 09:45:00,129.58,86.179,50.949,29.775 +2020-05-14 10:00:00,129.11,81.82600000000001,48.136,29.775 +2020-05-14 10:15:00,129.29,82.819,48.136,29.775 +2020-05-14 10:30:00,129.5,82.303,48.136,29.775 +2020-05-14 10:45:00,130.85,82.85600000000001,48.136,29.775 +2020-05-14 11:00:00,127.65,78.875,46.643,29.775 +2020-05-14 11:15:00,126.84,80.135,46.643,29.775 +2020-05-14 11:30:00,124.7,81.304,46.643,29.775 +2020-05-14 11:45:00,126.76,81.54899999999999,46.643,29.775 +2020-05-14 12:00:00,124.85,78.4,44.098,29.775 +2020-05-14 12:15:00,122.87,79.321,44.098,29.775 +2020-05-14 12:30:00,122.4,77.82600000000001,44.098,29.775 +2020-05-14 12:45:00,118.93,78.745,44.098,29.775 +2020-05-14 13:00:00,117.21,80.405,43.717,29.775 +2020-05-14 13:15:00,120.11,80.363,43.717,29.775 +2020-05-14 13:30:00,119.68,78.4,43.717,29.775 +2020-05-14 13:45:00,123.45,76.874,43.717,29.775 +2020-05-14 14:00:00,125.18,79.166,44.218999999999994,29.775 +2020-05-14 14:15:00,124.9,78.294,44.218999999999994,29.775 +2020-05-14 14:30:00,118.8,77.33800000000001,44.218999999999994,29.775 +2020-05-14 14:45:00,111.7,77.794,44.218999999999994,29.775 +2020-05-14 15:00:00,111.77,79.21,46.159,29.775 +2020-05-14 15:15:00,115.84,77.15899999999999,46.159,29.775 +2020-05-14 15:30:00,116.18,75.887,46.159,29.775 +2020-05-14 15:45:00,115.11,74.634,46.159,29.775 +2020-05-14 16:00:00,111.0,75.619,47.115,29.775 +2020-05-14 16:15:00,111.54,74.814,47.115,29.775 +2020-05-14 16:30:00,116.27,74.64699999999999,47.115,29.775 +2020-05-14 16:45:00,116.72,71.22,47.115,29.775 +2020-05-14 17:00:00,114.96,72.101,50.827,29.775 +2020-05-14 17:15:00,108.11,73.459,50.827,29.775 +2020-05-14 17:30:00,108.02,73.984,50.827,29.775 +2020-05-14 17:45:00,108.53,74.25399999999999,50.827,29.775 +2020-05-14 18:00:00,109.63,74.078,52.586000000000006,29.775 +2020-05-14 18:15:00,108.24,75.471,52.586000000000006,29.775 +2020-05-14 18:30:00,113.52,74.045,52.586000000000006,29.775 +2020-05-14 18:45:00,109.72,79.467,52.586000000000006,29.775 +2020-05-14 19:00:00,112.3,76.709,51.886,29.775 +2020-05-14 19:15:00,106.96,76.115,51.886,29.775 +2020-05-14 19:30:00,106.72,76.158,51.886,29.775 +2020-05-14 19:45:00,107.78,76.959,51.886,29.775 +2020-05-14 20:00:00,105.58,75.251,56.162,29.775 +2020-05-14 20:15:00,99.35,73.365,56.162,29.775 +2020-05-14 20:30:00,102.93,72.126,56.162,29.775 +2020-05-14 20:45:00,105.28,72.195,56.162,29.775 +2020-05-14 21:00:00,103.12,70.278,53.023,29.775 +2020-05-14 21:15:00,99.97,71.095,53.023,29.775 +2020-05-14 21:30:00,93.22,71.209,53.023,29.775 +2020-05-14 21:45:00,93.97,71.766,53.023,29.775 +2020-05-14 22:00:00,90.52,69.846,49.303999999999995,29.775 +2020-05-14 22:15:00,90.9,69.455,49.303999999999995,29.775 +2020-05-14 22:30:00,84.63,60.348,49.303999999999995,29.775 +2020-05-14 22:45:00,88.26,56.285,49.303999999999995,29.775 +2020-05-14 23:00:00,82.53,49.589,43.409,29.775 +2020-05-14 23:15:00,81.52,48.373000000000005,43.409,29.775 +2020-05-14 23:30:00,75.59,47.691,43.409,29.775 +2020-05-14 23:45:00,81.48,47.743,43.409,29.775 +2020-05-15 00:00:00,78.17,44.512,39.884,29.775 +2020-05-15 00:15:00,78.44,45.251999999999995,39.884,29.775 +2020-05-15 00:30:00,73.49,44.153,39.884,29.775 +2020-05-15 00:45:00,78.17,43.27,39.884,29.775 +2020-05-15 01:00:00,78.18,42.896,37.658,29.775 +2020-05-15 01:15:00,78.15,41.803000000000004,37.658,29.775 +2020-05-15 01:30:00,71.38,40.624,37.658,29.775 +2020-05-15 01:45:00,77.87,39.775999999999996,37.658,29.775 +2020-05-15 02:00:00,77.41,40.869,36.707,29.775 +2020-05-15 02:15:00,78.35,39.583,36.707,29.775 +2020-05-15 02:30:00,73.66,42.92,36.707,29.775 +2020-05-15 02:45:00,70.89,43.092,36.707,29.775 +2020-05-15 03:00:00,72.47,46.383,37.025,29.775 +2020-05-15 03:15:00,72.41,45.57899999999999,37.025,29.775 +2020-05-15 03:30:00,73.92,44.714,37.025,29.775 +2020-05-15 03:45:00,77.06,45.655,37.025,29.775 +2020-05-15 04:00:00,81.51,56.457,38.349000000000004,29.775 +2020-05-15 04:15:00,79.37,65.932,38.349000000000004,29.775 +2020-05-15 04:30:00,82.59,65.705,38.349000000000004,29.775 +2020-05-15 04:45:00,85.66,65.982,38.349000000000004,29.775 +2020-05-15 05:00:00,95.76,92.154,41.565,29.775 +2020-05-15 05:15:00,97.85,117.869,41.565,29.775 +2020-05-15 05:30:00,101.38,107.76899999999999,41.565,29.775 +2020-05-15 05:45:00,103.44,99.796,41.565,29.775 +2020-05-15 06:00:00,108.63,101.87700000000001,53.861000000000004,29.775 +2020-05-15 06:15:00,111.05,104.807,53.861000000000004,29.775 +2020-05-15 06:30:00,114.06,101.404,53.861000000000004,29.775 +2020-05-15 06:45:00,116.5,101.876,53.861000000000004,29.775 +2020-05-15 07:00:00,118.58,103.118,63.497,29.775 +2020-05-15 07:15:00,119.62,104.01899999999999,63.497,29.775 +2020-05-15 07:30:00,123.22,99.75399999999999,63.497,29.775 +2020-05-15 07:45:00,121.51,96.83,63.497,29.775 +2020-05-15 08:00:00,120.07,92.62799999999999,55.43899999999999,29.775 +2020-05-15 08:15:00,120.52,93.241,55.43899999999999,29.775 +2020-05-15 08:30:00,121.85,90.48700000000001,55.43899999999999,29.775 +2020-05-15 08:45:00,123.22,89.939,55.43899999999999,29.775 +2020-05-15 09:00:00,120.17,81.768,52.132,29.775 +2020-05-15 09:15:00,120.79,81.646,52.132,29.775 +2020-05-15 09:30:00,120.3,83.456,52.132,29.775 +2020-05-15 09:45:00,121.98,84.19,52.132,29.775 +2020-05-15 10:00:00,122.01,79.236,49.881,29.775 +2020-05-15 10:15:00,122.16,80.387,49.881,29.775 +2020-05-15 10:30:00,125.08,80.267,49.881,29.775 +2020-05-15 10:45:00,121.4,80.583,49.881,29.775 +2020-05-15 11:00:00,118.56,76.764,49.396,29.775 +2020-05-15 11:15:00,117.53,76.866,49.396,29.775 +2020-05-15 11:30:00,115.03,78.49,49.396,29.775 +2020-05-15 11:45:00,117.11,78.059,49.396,29.775 +2020-05-15 12:00:00,113.62,75.738,46.7,29.775 +2020-05-15 12:15:00,112.54,75.296,46.7,29.775 +2020-05-15 12:30:00,109.67,73.919,46.7,29.775 +2020-05-15 12:45:00,110.33,74.495,46.7,29.775 +2020-05-15 13:00:00,105.34,76.998,44.05,29.775 +2020-05-15 13:15:00,104.26,77.437,44.05,29.775 +2020-05-15 13:30:00,101.89,76.069,44.05,29.775 +2020-05-15 13:45:00,103.1,74.741,44.05,29.775 +2020-05-15 14:00:00,99.61,76.006,42.805,29.775 +2020-05-15 14:15:00,96.97,75.365,42.805,29.775 +2020-05-15 14:30:00,94.75,75.683,42.805,29.775 +2020-05-15 14:45:00,93.73,75.768,42.805,29.775 +2020-05-15 15:00:00,96.11,76.961,44.36600000000001,29.775 +2020-05-15 15:15:00,94.15,74.533,44.36600000000001,29.775 +2020-05-15 15:30:00,94.02,72.171,44.36600000000001,29.775 +2020-05-15 15:45:00,94.72,71.51899999999999,44.36600000000001,29.775 +2020-05-15 16:00:00,96.97,71.47399999999999,46.928999999999995,29.775 +2020-05-15 16:15:00,96.61,71.137,46.928999999999995,29.775 +2020-05-15 16:30:00,99.35,70.885,46.928999999999995,29.775 +2020-05-15 16:45:00,100.89,66.767,46.928999999999995,29.775 +2020-05-15 17:00:00,107.94,69.078,51.468,29.775 +2020-05-15 17:15:00,103.98,70.115,51.468,29.775 +2020-05-15 17:30:00,103.89,70.623,51.468,29.775 +2020-05-15 17:45:00,105.25,70.645,51.468,29.775 +2020-05-15 18:00:00,106.7,70.852,52.58,29.775 +2020-05-15 18:15:00,104.83,71.363,52.58,29.775 +2020-05-15 18:30:00,111.99,69.997,52.58,29.775 +2020-05-15 18:45:00,112.9,75.74,52.58,29.775 +2020-05-15 19:00:00,111.75,74.04899999999999,52.183,29.775 +2020-05-15 19:15:00,103.98,74.419,52.183,29.775 +2020-05-15 19:30:00,101.92,74.359,52.183,29.775 +2020-05-15 19:45:00,98.71,74.2,52.183,29.775 +2020-05-15 20:00:00,97.62,72.35,58.497,29.775 +2020-05-15 20:15:00,103.82,71.09,58.497,29.775 +2020-05-15 20:30:00,102.75,69.482,58.497,29.775 +2020-05-15 20:45:00,100.04,69.16199999999999,58.497,29.775 +2020-05-15 21:00:00,88.18,68.469,54.731,29.775 +2020-05-15 21:15:00,88.72,70.771,54.731,29.775 +2020-05-15 21:30:00,85.61,70.765,54.731,29.775 +2020-05-15 21:45:00,83.9,71.708,54.731,29.775 +2020-05-15 22:00:00,82.78,70.062,51.386,29.775 +2020-05-15 22:15:00,84.75,69.445,51.386,29.775 +2020-05-15 22:30:00,82.82,66.408,51.386,29.775 +2020-05-15 22:45:00,80.16,64.311,51.386,29.775 +2020-05-15 23:00:00,71.33,58.746,44.463,29.775 +2020-05-15 23:15:00,70.6,55.622,44.463,29.775 +2020-05-15 23:30:00,69.36,52.972,44.463,29.775 +2020-05-15 23:45:00,70.97,52.681000000000004,44.463,29.775 +2020-05-16 00:00:00,74.15,36.794000000000004,42.833999999999996,29.662 +2020-05-16 00:15:00,73.98,35.758,42.833999999999996,29.662 +2020-05-16 00:30:00,71.46,34.895,42.833999999999996,29.662 +2020-05-16 00:45:00,69.22,33.669000000000004,42.833999999999996,29.662 +2020-05-16 01:00:00,72.75,33.125,37.859,29.662 +2020-05-16 01:15:00,75.46,32.548,37.859,29.662 +2020-05-16 01:30:00,69.76,30.769000000000002,37.859,29.662 +2020-05-16 01:45:00,65.29,30.721,37.859,29.662 +2020-05-16 02:00:00,67.91,30.855,35.327,29.662 +2020-05-16 02:15:00,70.65,28.666,35.327,29.662 +2020-05-16 02:30:00,70.38,30.735,35.327,29.662 +2020-05-16 02:45:00,64.65,31.566,35.327,29.662 +2020-05-16 03:00:00,63.24,33.628,34.908,29.662 +2020-05-16 03:15:00,64.63,30.669,34.908,29.662 +2020-05-16 03:30:00,65.46,29.659000000000002,34.908,29.662 +2020-05-16 03:45:00,65.23,31.218000000000004,34.908,29.662 +2020-05-16 04:00:00,62.15,39.24,34.84,29.662 +2020-05-16 04:15:00,61.52,47.104,34.84,29.662 +2020-05-16 04:30:00,61.86,43.983999999999995,34.84,29.662 +2020-05-16 04:45:00,62.99,44.071999999999996,34.84,29.662 +2020-05-16 05:00:00,64.2,57.34,34.222,29.662 +2020-05-16 05:15:00,64.4,64.827,34.222,29.662 +2020-05-16 05:30:00,63.92,55.413999999999994,34.222,29.662 +2020-05-16 05:45:00,66.28,53.871,34.222,29.662 +2020-05-16 06:00:00,69.14,69.734,35.515,29.662 +2020-05-16 06:15:00,69.45,82.39299999999999,35.515,29.662 +2020-05-16 06:30:00,71.81,75.32300000000001,35.515,29.662 +2020-05-16 06:45:00,72.47,70.584,35.515,29.662 +2020-05-16 07:00:00,73.5,68.16199999999999,39.687,29.662 +2020-05-16 07:15:00,74.27,67.108,39.687,29.662 +2020-05-16 07:30:00,75.61,64.484,39.687,29.662 +2020-05-16 07:45:00,78.65,63.838,39.687,29.662 +2020-05-16 08:00:00,77.98,59.902,44.9,29.662 +2020-05-16 08:15:00,77.32,62.077,44.9,29.662 +2020-05-16 08:30:00,76.53,61.327,44.9,29.662 +2020-05-16 08:45:00,77.34,63.946000000000005,44.9,29.662 +2020-05-16 09:00:00,78.57,61.705,45.724,29.662 +2020-05-16 09:15:00,78.98,62.68899999999999,45.724,29.662 +2020-05-16 09:30:00,74.87,65.62899999999999,45.724,29.662 +2020-05-16 09:45:00,76.27,66.71600000000001,45.724,29.662 +2020-05-16 10:00:00,78.95,63.416000000000004,43.123999999999995,29.662 +2020-05-16 10:15:00,81.25,64.873,43.123999999999995,29.662 +2020-05-16 10:30:00,84.8,64.884,43.123999999999995,29.662 +2020-05-16 10:45:00,82.3,65.693,43.123999999999995,29.662 +2020-05-16 11:00:00,82.79,61.979,40.255,29.662 +2020-05-16 11:15:00,83.53,62.608000000000004,40.255,29.662 +2020-05-16 11:30:00,79.48,64.295,40.255,29.662 +2020-05-16 11:45:00,74.97,64.98,40.255,29.662 +2020-05-16 12:00:00,68.37,60.761,38.582,29.662 +2020-05-16 12:15:00,68.21,60.949,38.582,29.662 +2020-05-16 12:30:00,66.19,60.332,38.582,29.662 +2020-05-16 12:45:00,65.99,61.598,38.582,29.662 +2020-05-16 13:00:00,65.29,62.047,36.043,29.662 +2020-05-16 13:15:00,66.66,62.114,36.043,29.662 +2020-05-16 13:30:00,65.2,60.93,36.043,29.662 +2020-05-16 13:45:00,65.57,58.292,36.043,29.662 +2020-05-16 14:00:00,65.77,58.663999999999994,35.216,29.662 +2020-05-16 14:15:00,65.43,56.394,35.216,29.662 +2020-05-16 14:30:00,67.17,56.395,35.216,29.662 +2020-05-16 14:45:00,69.52,56.943000000000005,35.216,29.662 +2020-05-16 15:00:00,68.76,57.736000000000004,36.759,29.662 +2020-05-16 15:15:00,70.34,55.881,36.759,29.662 +2020-05-16 15:30:00,68.44,53.98,36.759,29.662 +2020-05-16 15:45:00,69.99,51.562,36.759,29.662 +2020-05-16 16:00:00,72.09,54.672,40.086,29.662 +2020-05-16 16:15:00,70.51,54.178000000000004,40.086,29.662 +2020-05-16 16:30:00,72.95,53.538000000000004,40.086,29.662 +2020-05-16 16:45:00,72.75,49.865,40.086,29.662 +2020-05-16 17:00:00,75.24,52.239,44.876999999999995,29.662 +2020-05-16 17:15:00,76.28,51.114,44.876999999999995,29.662 +2020-05-16 17:30:00,78.78,50.931000000000004,44.876999999999995,29.662 +2020-05-16 17:45:00,80.15,50.091,44.876999999999995,29.662 +2020-05-16 18:00:00,84.39,54.299,47.056000000000004,29.662 +2020-05-16 18:15:00,81.11,56.321000000000005,47.056000000000004,29.662 +2020-05-16 18:30:00,81.76,55.763000000000005,47.056000000000004,29.662 +2020-05-16 18:45:00,84.97,57.4,47.056000000000004,29.662 +2020-05-16 19:00:00,81.21,57.782,45.57,29.662 +2020-05-16 19:15:00,78.11,56.823,45.57,29.662 +2020-05-16 19:30:00,77.61,57.25899999999999,45.57,29.662 +2020-05-16 19:45:00,76.99,58.424,45.57,29.662 +2020-05-16 20:00:00,76.78,57.797,41.685,29.662 +2020-05-16 20:15:00,77.96,57.528,41.685,29.662 +2020-05-16 20:30:00,77.7,55.493,41.685,29.662 +2020-05-16 20:45:00,79.72,56.248000000000005,41.685,29.662 +2020-05-16 21:00:00,74.4,54.181999999999995,39.576,29.662 +2020-05-16 21:15:00,73.65,57.01,39.576,29.662 +2020-05-16 21:30:00,70.78,58.263000000000005,39.576,29.662 +2020-05-16 21:45:00,71.05,58.636,39.576,29.662 +2020-05-16 22:00:00,66.23,56.073,39.068000000000005,29.662 +2020-05-16 22:15:00,66.59,57.24100000000001,39.068000000000005,29.662 +2020-05-16 22:30:00,64.33,56.806000000000004,39.068000000000005,29.662 +2020-05-16 22:45:00,63.23,55.815,39.068000000000005,29.662 +2020-05-16 23:00:00,59.53,51.784,32.06,29.662 +2020-05-16 23:15:00,59.33,47.72,32.06,29.662 +2020-05-16 23:30:00,58.61,46.766000000000005,32.06,29.662 +2020-05-16 23:45:00,58.55,45.958,32.06,29.662 +2020-05-17 00:00:00,55.99,37.921,28.825,29.662 +2020-05-17 00:15:00,56.4,35.736999999999995,28.825,29.662 +2020-05-17 00:30:00,55.85,34.657,28.825,29.662 +2020-05-17 00:45:00,55.56,33.474000000000004,28.825,29.662 +2020-05-17 01:00:00,53.93,33.169000000000004,25.995,29.662 +2020-05-17 01:15:00,54.71,32.666,25.995,29.662 +2020-05-17 01:30:00,54.49,30.851,25.995,29.662 +2020-05-17 01:45:00,53.85,30.375,25.995,29.662 +2020-05-17 02:00:00,53.53,30.426,24.394000000000002,29.662 +2020-05-17 02:15:00,54.42,28.711,24.394000000000002,29.662 +2020-05-17 02:30:00,54.27,31.146,24.394000000000002,29.662 +2020-05-17 02:45:00,53.9,31.805999999999997,24.394000000000002,29.662 +2020-05-17 03:00:00,54.33,34.554,22.916999999999998,29.662 +2020-05-17 03:15:00,54.73,31.721,22.916999999999998,29.662 +2020-05-17 03:30:00,56.27,30.311,22.916999999999998,29.662 +2020-05-17 03:45:00,54.77,31.133000000000003,22.916999999999998,29.662 +2020-05-17 04:00:00,51.22,39.014,23.576999999999998,29.662 +2020-05-17 04:15:00,51.74,46.203,23.576999999999998,29.662 +2020-05-17 04:30:00,50.38,44.345,23.576999999999998,29.662 +2020-05-17 04:45:00,50.63,44.044,23.576999999999998,29.662 +2020-05-17 05:00:00,49.16,56.647,22.730999999999998,29.662 +2020-05-17 05:15:00,49.68,62.77,22.730999999999998,29.662 +2020-05-17 05:30:00,49.0,53.008,22.730999999999998,29.662 +2020-05-17 05:45:00,49.52,51.318999999999996,22.730999999999998,29.662 +2020-05-17 06:00:00,50.22,64.92699999999999,22.34,29.662 +2020-05-17 06:15:00,50.56,78.017,22.34,29.662 +2020-05-17 06:30:00,50.65,70.086,22.34,29.662 +2020-05-17 06:45:00,51.26,64.217,22.34,29.662 +2020-05-17 07:00:00,51.72,62.525,24.691999999999997,29.662 +2020-05-17 07:15:00,52.05,59.75899999999999,24.691999999999997,29.662 +2020-05-17 07:30:00,52.11,58.062,24.691999999999997,29.662 +2020-05-17 07:45:00,51.69,57.25899999999999,24.691999999999997,29.662 +2020-05-17 08:00:00,52.0,54.4,29.340999999999998,29.662 +2020-05-17 08:15:00,51.74,57.711000000000006,29.340999999999998,29.662 +2020-05-17 08:30:00,50.65,58.111000000000004,29.340999999999998,29.662 +2020-05-17 08:45:00,50.24,61.091,29.340999999999998,29.662 +2020-05-17 09:00:00,49.9,58.648999999999994,30.788,29.662 +2020-05-17 09:15:00,50.08,59.297,30.788,29.662 +2020-05-17 09:30:00,49.35,62.619,30.788,29.662 +2020-05-17 09:45:00,49.73,64.649,30.788,29.662 +2020-05-17 10:00:00,51.82,62.388000000000005,30.158,29.662 +2020-05-17 10:15:00,54.08,64.119,30.158,29.662 +2020-05-17 10:30:00,54.15,64.479,30.158,29.662 +2020-05-17 10:45:00,54.12,65.82300000000001,30.158,29.662 +2020-05-17 11:00:00,50.69,61.978,32.056,29.662 +2020-05-17 11:15:00,49.03,62.24,32.056,29.662 +2020-05-17 11:30:00,44.51,64.207,32.056,29.662 +2020-05-17 11:45:00,46.44,65.27199999999999,32.056,29.662 +2020-05-17 12:00:00,43.04,61.916000000000004,28.671999999999997,29.662 +2020-05-17 12:15:00,42.65,61.971000000000004,28.671999999999997,29.662 +2020-05-17 12:30:00,41.87,61.183,28.671999999999997,29.662 +2020-05-17 12:45:00,41.91,61.653999999999996,28.671999999999997,29.662 +2020-05-17 13:00:00,41.33,61.688,23.171,29.662 +2020-05-17 13:15:00,41.6,61.905,23.171,29.662 +2020-05-17 13:30:00,42.2,59.663000000000004,23.171,29.662 +2020-05-17 13:45:00,41.31,57.903999999999996,23.171,29.662 +2020-05-17 14:00:00,42.39,59.47,19.11,29.662 +2020-05-17 14:15:00,42.99,57.854,19.11,29.662 +2020-05-17 14:30:00,43.09,56.96,19.11,29.662 +2020-05-17 14:45:00,43.63,56.438,19.11,29.662 +2020-05-17 15:00:00,44.32,57.077,19.689,29.662 +2020-05-17 15:15:00,44.44,54.631,19.689,29.662 +2020-05-17 15:30:00,44.86,52.67100000000001,19.689,29.662 +2020-05-17 15:45:00,47.06,50.652,19.689,29.662 +2020-05-17 16:00:00,49.45,52.661,22.875,29.662 +2020-05-17 16:15:00,50.16,52.172,22.875,29.662 +2020-05-17 16:30:00,52.1,52.534,22.875,29.662 +2020-05-17 16:45:00,55.36,48.893,22.875,29.662 +2020-05-17 17:00:00,60.0,51.653,33.884,29.662 +2020-05-17 17:15:00,60.6,51.835,33.884,29.662 +2020-05-17 17:30:00,65.61,52.448,33.884,29.662 +2020-05-17 17:45:00,64.53,52.451,33.884,29.662 +2020-05-17 18:00:00,68.54,57.073,38.453,29.662 +2020-05-17 18:15:00,66.66,58.964,38.453,29.662 +2020-05-17 18:30:00,69.58,57.735,38.453,29.662 +2020-05-17 18:45:00,66.98,59.857,38.453,29.662 +2020-05-17 19:00:00,68.34,62.187,39.221,29.662 +2020-05-17 19:15:00,67.78,60.285,39.221,29.662 +2020-05-17 19:30:00,66.98,60.448,39.221,29.662 +2020-05-17 19:45:00,67.29,61.458999999999996,39.221,29.662 +2020-05-17 20:00:00,68.66,61.008,37.871,29.662 +2020-05-17 20:15:00,68.42,60.806000000000004,37.871,29.662 +2020-05-17 20:30:00,71.45,59.808,37.871,29.662 +2020-05-17 20:45:00,70.32,58.785,37.871,29.662 +2020-05-17 21:00:00,68.35,56.067,36.465,29.662 +2020-05-17 21:15:00,67.62,58.501999999999995,36.465,29.662 +2020-05-17 21:30:00,65.42,59.177,36.465,29.662 +2020-05-17 21:45:00,64.94,59.942,36.465,29.662 +2020-05-17 22:00:00,61.02,59.086000000000006,36.092,29.662 +2020-05-17 22:15:00,61.21,58.535,36.092,29.662 +2020-05-17 22:30:00,59.51,56.996,36.092,29.662 +2020-05-17 22:45:00,58.72,54.622,36.092,29.662 +2020-05-17 23:00:00,70.52,49.718,31.013,29.662 +2020-05-17 23:15:00,70.67,47.265,31.013,29.662 +2020-05-17 23:30:00,71.07,45.965,31.013,29.662 +2020-05-17 23:45:00,71.64,45.471000000000004,31.013,29.662 +2020-05-18 00:00:00,69.55,39.926,31.174,29.775 +2020-05-18 00:15:00,70.53,39.082,31.174,29.775 +2020-05-18 00:30:00,69.8,37.664,31.174,29.775 +2020-05-18 00:45:00,70.41,36.007,31.174,29.775 +2020-05-18 01:00:00,68.21,36.088,29.663,29.775 +2020-05-18 01:15:00,68.54,35.468,29.663,29.775 +2020-05-18 01:30:00,70.13,33.989000000000004,29.663,29.775 +2020-05-18 01:45:00,71.47,33.439,29.663,29.775 +2020-05-18 02:00:00,71.14,33.912,28.793000000000003,29.775 +2020-05-18 02:15:00,70.93,31.521,28.793000000000003,29.775 +2020-05-18 02:30:00,70.27,34.186,28.793000000000003,29.775 +2020-05-18 02:45:00,71.97,34.594,28.793000000000003,29.775 +2020-05-18 03:00:00,71.3,38.082,27.728,29.775 +2020-05-18 03:15:00,70.0,36.243,27.728,29.775 +2020-05-18 03:30:00,70.97,35.424,27.728,29.775 +2020-05-18 03:45:00,73.44,35.727,27.728,29.775 +2020-05-18 04:00:00,77.39,47.346000000000004,29.266,29.775 +2020-05-18 04:15:00,78.56,58.068999999999996,29.266,29.775 +2020-05-18 04:30:00,79.66,56.18600000000001,29.266,29.775 +2020-05-18 04:45:00,83.67,56.26,29.266,29.775 +2020-05-18 05:00:00,91.78,78.305,37.889,29.775 +2020-05-18 05:15:00,95.73,97.86399999999999,37.889,29.775 +2020-05-18 05:30:00,97.75,86.74700000000001,37.889,29.775 +2020-05-18 05:45:00,99.8,81.139,37.889,29.775 +2020-05-18 06:00:00,105.24,81.69,55.485,29.775 +2020-05-18 06:15:00,107.97,83.287,55.485,29.775 +2020-05-18 06:30:00,108.48,80.672,55.485,29.775 +2020-05-18 06:45:00,108.17,81.521,55.485,29.775 +2020-05-18 07:00:00,108.28,80.867,65.765,29.775 +2020-05-18 07:15:00,108.08,80.506,65.765,29.775 +2020-05-18 07:30:00,108.87,77.82,65.765,29.775 +2020-05-18 07:45:00,109.39,76.815,65.765,29.775 +2020-05-18 08:00:00,109.39,71.055,56.745,29.775 +2020-05-18 08:15:00,107.51,72.851,56.745,29.775 +2020-05-18 08:30:00,106.6,71.499,56.745,29.775 +2020-05-18 08:45:00,105.11,74.01100000000001,56.745,29.775 +2020-05-18 09:00:00,104.7,70.43,53.321999999999996,29.775 +2020-05-18 09:15:00,104.4,68.692,53.321999999999996,29.775 +2020-05-18 09:30:00,104.55,70.94800000000001,53.321999999999996,29.775 +2020-05-18 09:45:00,105.86,70.934,53.321999999999996,29.775 +2020-05-18 10:00:00,104.56,68.959,51.309,29.775 +2020-05-18 10:15:00,105.63,70.516,51.309,29.775 +2020-05-18 10:30:00,106.46,70.271,51.309,29.775 +2020-05-18 10:45:00,105.73,70.328,51.309,29.775 +2020-05-18 11:00:00,103.62,66.137,50.415,29.775 +2020-05-18 11:15:00,102.43,67.071,50.415,29.775 +2020-05-18 11:30:00,102.2,69.97800000000001,50.415,29.775 +2020-05-18 11:45:00,100.97,71.42699999999999,50.415,29.775 +2020-05-18 12:00:00,99.99,67.039,48.273,29.775 +2020-05-18 12:15:00,99.53,67.203,48.273,29.775 +2020-05-18 12:30:00,97.76,65.457,48.273,29.775 +2020-05-18 12:45:00,98.7,66.28,48.273,29.775 +2020-05-18 13:00:00,96.87,67.222,48.452,29.775 +2020-05-18 13:15:00,99.09,66.321,48.452,29.775 +2020-05-18 13:30:00,97.98,64.128,48.452,29.775 +2020-05-18 13:45:00,98.34,63.196999999999996,48.452,29.775 +2020-05-18 14:00:00,96.84,63.848,48.35,29.775 +2020-05-18 14:15:00,97.88,62.621,48.35,29.775 +2020-05-18 14:30:00,97.61,61.413999999999994,48.35,29.775 +2020-05-18 14:45:00,97.53,62.787,48.35,29.775 +2020-05-18 15:00:00,95.98,63.553000000000004,48.838,29.775 +2020-05-18 15:15:00,94.89,60.226000000000006,48.838,29.775 +2020-05-18 15:30:00,96.64,58.75899999999999,48.838,29.775 +2020-05-18 15:45:00,97.73,56.178000000000004,48.838,29.775 +2020-05-18 16:00:00,99.47,59.169,50.873000000000005,29.775 +2020-05-18 16:15:00,100.16,58.563,50.873000000000005,29.775 +2020-05-18 16:30:00,103.58,58.114,50.873000000000005,29.775 +2020-05-18 16:45:00,103.44,54.181999999999995,50.873000000000005,29.775 +2020-05-18 17:00:00,105.33,55.906000000000006,56.637,29.775 +2020-05-18 17:15:00,106.15,56.206,56.637,29.775 +2020-05-18 17:30:00,106.58,56.32899999999999,56.637,29.775 +2020-05-18 17:45:00,107.9,55.603,56.637,29.775 +2020-05-18 18:00:00,107.24,59.253,56.35,29.775 +2020-05-18 18:15:00,106.38,58.951,56.35,29.775 +2020-05-18 18:30:00,111.23,57.136,56.35,29.775 +2020-05-18 18:45:00,112.74,62.372,56.35,29.775 +2020-05-18 19:00:00,107.03,64.04899999999999,56.023,29.775 +2020-05-18 19:15:00,99.04,63.17100000000001,56.023,29.775 +2020-05-18 19:30:00,100.51,63.076,56.023,29.775 +2020-05-18 19:45:00,98.68,63.343999999999994,56.023,29.775 +2020-05-18 20:00:00,97.75,61.221000000000004,62.372,29.775 +2020-05-18 20:15:00,100.39,61.84,62.372,29.775 +2020-05-18 20:30:00,101.96,60.983000000000004,62.372,29.775 +2020-05-18 20:45:00,101.2,60.535,62.372,29.775 +2020-05-18 21:00:00,94.45,57.338,57.516999999999996,29.775 +2020-05-18 21:15:00,93.17,59.972,57.516999999999996,29.775 +2020-05-18 21:30:00,92.14,60.852,57.516999999999996,29.775 +2020-05-18 21:45:00,93.04,61.336000000000006,57.516999999999996,29.775 +2020-05-18 22:00:00,87.62,58.005,51.823,29.775 +2020-05-18 22:15:00,84.34,59.095,51.823,29.775 +2020-05-18 22:30:00,84.14,51.568999999999996,51.823,29.775 +2020-05-18 22:45:00,84.14,48.211000000000006,51.823,29.775 +2020-05-18 23:00:00,65.96,43.48,43.832,29.775 +2020-05-18 23:15:00,68.87,40.029,43.832,29.775 +2020-05-18 23:30:00,68.41,39.236,43.832,29.775 +2020-05-18 23:45:00,64.7,38.741,43.832,29.775 +2020-05-19 00:00:00,62.8,37.38,42.371,29.775 +2020-05-19 00:15:00,66.9,37.645,42.371,29.775 +2020-05-19 00:30:00,66.25,36.715,42.371,29.775 +2020-05-19 00:45:00,63.82,35.619,42.371,29.775 +2020-05-19 01:00:00,61.03,35.165,39.597,29.775 +2020-05-19 01:15:00,66.87,34.537,39.597,29.775 +2020-05-19 01:30:00,67.1,32.953,39.597,29.775 +2020-05-19 01:45:00,66.73,31.971999999999998,39.597,29.775 +2020-05-19 02:00:00,63.92,31.985,38.298,29.775 +2020-05-19 02:15:00,67.6,30.656,38.298,29.775 +2020-05-19 02:30:00,66.14,32.842,38.298,29.775 +2020-05-19 02:45:00,65.87,33.571999999999996,38.298,29.775 +2020-05-19 03:00:00,63.27,36.298,37.884,29.775 +2020-05-19 03:15:00,69.81,35.164,37.884,29.775 +2020-05-19 03:30:00,72.41,34.414,37.884,29.775 +2020-05-19 03:45:00,66.55,33.728,37.884,29.775 +2020-05-19 04:00:00,67.08,44.04600000000001,39.442,29.775 +2020-05-19 04:15:00,68.59,54.656000000000006,39.442,29.775 +2020-05-19 04:30:00,73.0,52.583,39.442,29.775 +2020-05-19 04:45:00,76.72,53.357,39.442,29.775 +2020-05-19 05:00:00,85.35,77.723,43.608000000000004,29.775 +2020-05-19 05:15:00,88.34,97.675,43.608000000000004,29.775 +2020-05-19 05:30:00,91.29,86.52,43.608000000000004,29.775 +2020-05-19 05:45:00,94.03,80.278,43.608000000000004,29.775 +2020-05-19 06:00:00,99.55,81.65100000000001,54.99100000000001,29.775 +2020-05-19 06:15:00,99.82,83.715,54.99100000000001,29.775 +2020-05-19 06:30:00,100.65,80.687,54.99100000000001,29.775 +2020-05-19 06:45:00,100.88,80.633,54.99100000000001,29.775 +2020-05-19 07:00:00,103.61,80.07600000000001,66.217,29.775 +2020-05-19 07:15:00,103.66,79.44800000000001,66.217,29.775 +2020-05-19 07:30:00,103.58,76.64399999999999,66.217,29.775 +2020-05-19 07:45:00,101.83,74.78399999999999,66.217,29.775 +2020-05-19 08:00:00,102.87,68.985,60.151,29.775 +2020-05-19 08:15:00,101.22,70.19800000000001,60.151,29.775 +2020-05-19 08:30:00,101.29,68.957,60.151,29.775 +2020-05-19 08:45:00,100.42,70.559,60.151,29.775 +2020-05-19 09:00:00,103.99,67.12100000000001,53.873000000000005,29.775 +2020-05-19 09:15:00,100.42,65.592,53.873000000000005,29.775 +2020-05-19 09:30:00,100.05,68.646,53.873000000000005,29.775 +2020-05-19 09:45:00,101.34,69.88,53.873000000000005,29.775 +2020-05-19 10:00:00,100.74,66.505,51.417,29.775 +2020-05-19 10:15:00,102.9,67.732,51.417,29.775 +2020-05-19 10:30:00,103.08,67.561,51.417,29.775 +2020-05-19 10:45:00,102.7,68.632,51.417,29.775 +2020-05-19 11:00:00,103.54,64.76,50.43600000000001,29.775 +2020-05-19 11:15:00,99.89,66.014,50.43600000000001,29.775 +2020-05-19 11:30:00,99.47,67.587,50.43600000000001,29.775 +2020-05-19 11:45:00,100.32,68.785,50.43600000000001,29.775 +2020-05-19 12:00:00,97.16,63.95399999999999,47.468,29.775 +2020-05-19 12:15:00,97.84,64.34,47.468,29.775 +2020-05-19 12:30:00,97.67,63.512,47.468,29.775 +2020-05-19 12:45:00,98.46,64.913,47.468,29.775 +2020-05-19 13:00:00,96.91,65.453,48.453,29.775 +2020-05-19 13:15:00,99.44,66.146,48.453,29.775 +2020-05-19 13:30:00,97.7,64.196,48.453,29.775 +2020-05-19 13:45:00,98.33,62.413000000000004,48.453,29.775 +2020-05-19 14:00:00,97.24,63.581,48.435,29.775 +2020-05-19 14:15:00,95.83,62.196000000000005,48.435,29.775 +2020-05-19 14:30:00,96.03,61.394,48.435,29.775 +2020-05-19 14:45:00,95.48,62.022,48.435,29.775 +2020-05-19 15:00:00,94.77,62.601000000000006,49.966,29.775 +2020-05-19 15:15:00,94.41,60.18600000000001,49.966,29.775 +2020-05-19 15:30:00,96.22,58.551,49.966,29.775 +2020-05-19 15:45:00,94.47,56.185,49.966,29.775 +2020-05-19 16:00:00,94.85,58.66,51.184,29.775 +2020-05-19 16:15:00,96.76,58.223,51.184,29.775 +2020-05-19 16:30:00,99.86,57.58,51.184,29.775 +2020-05-19 16:45:00,104.67,54.323,51.184,29.775 +2020-05-19 17:00:00,103.7,56.407,56.138999999999996,29.775 +2020-05-19 17:15:00,102.17,57.08,56.138999999999996,29.775 +2020-05-19 17:30:00,106.22,56.923,56.138999999999996,29.775 +2020-05-19 17:45:00,104.6,55.821999999999996,56.138999999999996,29.775 +2020-05-19 18:00:00,106.74,58.563,57.038000000000004,29.775 +2020-05-19 18:15:00,105.62,59.54600000000001,57.038000000000004,29.775 +2020-05-19 18:30:00,113.35,57.408,57.038000000000004,29.775 +2020-05-19 18:45:00,112.87,62.621,57.038000000000004,29.775 +2020-05-19 19:00:00,108.17,63.18899999999999,56.492,29.775 +2020-05-19 19:15:00,98.13,62.423,56.492,29.775 +2020-05-19 19:30:00,97.16,61.989,56.492,29.775 +2020-05-19 19:45:00,103.23,62.583999999999996,56.492,29.775 +2020-05-19 20:00:00,95.97,60.846000000000004,62.534,29.775 +2020-05-19 20:15:00,100.72,59.967,62.534,29.775 +2020-05-19 20:30:00,104.92,59.372,62.534,29.775 +2020-05-19 20:45:00,104.57,59.255,62.534,29.775 +2020-05-19 21:00:00,94.39,56.748999999999995,55.506,29.775 +2020-05-19 21:15:00,97.34,58.178999999999995,55.506,29.775 +2020-05-19 21:30:00,94.26,59.06,55.506,29.775 +2020-05-19 21:45:00,93.63,59.801,55.506,29.775 +2020-05-19 22:00:00,84.19,56.99,51.472,29.775 +2020-05-19 22:15:00,82.79,57.728,51.472,29.775 +2020-05-19 22:30:00,80.26,50.5,51.472,29.775 +2020-05-19 22:45:00,86.14,47.177,51.472,29.775 +2020-05-19 23:00:00,84.68,41.677,44.593,29.775 +2020-05-19 23:15:00,80.74,39.588,44.593,29.775 +2020-05-19 23:30:00,76.55,38.729,44.593,29.775 +2020-05-19 23:45:00,76.25,38.312,44.593,29.775 +2020-05-20 00:00:00,78.51,37.128,41.978,29.775 +2020-05-20 00:15:00,80.48,37.399,41.978,29.775 +2020-05-20 00:30:00,78.67,36.468,41.978,29.775 +2020-05-20 00:45:00,72.51,35.376,41.978,29.775 +2020-05-20 01:00:00,71.79,34.94,38.59,29.775 +2020-05-20 01:15:00,77.65,34.286,38.59,29.775 +2020-05-20 01:30:00,78.37,32.689,38.59,29.775 +2020-05-20 01:45:00,79.71,31.704,38.59,29.775 +2020-05-20 02:00:00,73.24,31.713,36.23,29.775 +2020-05-20 02:15:00,78.37,30.373,36.23,29.775 +2020-05-20 02:30:00,79.12,32.566,36.23,29.775 +2020-05-20 02:45:00,77.08,33.304,36.23,29.775 +2020-05-20 03:00:00,75.21,36.037,35.867,29.775 +2020-05-20 03:15:00,78.88,34.891999999999996,35.867,29.775 +2020-05-20 03:30:00,81.38,34.144,35.867,29.775 +2020-05-20 03:45:00,80.54,33.484,35.867,29.775 +2020-05-20 04:00:00,75.73,43.74100000000001,36.75,29.775 +2020-05-20 04:15:00,76.72,54.297,36.75,29.775 +2020-05-20 04:30:00,80.9,52.211000000000006,36.75,29.775 +2020-05-20 04:45:00,86.16,52.978,36.75,29.775 +2020-05-20 05:00:00,93.21,77.21,40.461,29.775 +2020-05-20 05:15:00,95.42,96.999,40.461,29.775 +2020-05-20 05:30:00,99.77,85.91,40.461,29.775 +2020-05-20 05:45:00,101.34,79.735,40.461,29.775 +2020-05-20 06:00:00,101.76,81.133,55.481,29.775 +2020-05-20 06:15:00,101.28,83.171,55.481,29.775 +2020-05-20 06:30:00,104.28,80.161,55.481,29.775 +2020-05-20 06:45:00,105.05,80.12899999999999,55.481,29.775 +2020-05-20 07:00:00,108.2,79.56,68.45,29.775 +2020-05-20 07:15:00,107.22,78.939,68.45,29.775 +2020-05-20 07:30:00,107.19,76.111,68.45,29.775 +2020-05-20 07:45:00,106.77,74.281,68.45,29.775 +2020-05-20 08:00:00,105.12,68.487,60.885,29.775 +2020-05-20 08:15:00,105.19,69.749,60.885,29.775 +2020-05-20 08:30:00,106.79,68.49600000000001,60.885,29.775 +2020-05-20 08:45:00,108.41,70.115,60.885,29.775 +2020-05-20 09:00:00,104.53,66.671,56.887,29.775 +2020-05-20 09:15:00,106.56,65.149,56.887,29.775 +2020-05-20 09:30:00,106.98,68.21300000000001,56.887,29.775 +2020-05-20 09:45:00,110.81,69.48,56.887,29.775 +2020-05-20 10:00:00,109.87,66.115,54.401,29.775 +2020-05-20 10:15:00,111.92,67.375,54.401,29.775 +2020-05-20 10:30:00,110.2,67.215,54.401,29.775 +2020-05-20 10:45:00,106.45,68.29899999999999,54.401,29.775 +2020-05-20 11:00:00,111.94,64.417,53.678000000000004,29.775 +2020-05-20 11:15:00,116.47,65.687,53.678000000000004,29.775 +2020-05-20 11:30:00,120.29,67.253,53.678000000000004,29.775 +2020-05-20 11:45:00,127.5,68.46,53.678000000000004,29.775 +2020-05-20 12:00:00,130.45,63.667,51.68,29.775 +2020-05-20 12:15:00,131.65,64.06,51.68,29.775 +2020-05-20 12:30:00,128.54,63.196000000000005,51.68,29.775 +2020-05-20 12:45:00,128.89,64.604,51.68,29.775 +2020-05-20 13:00:00,124.02,65.156,51.263000000000005,29.775 +2020-05-20 13:15:00,125.19,65.852,51.263000000000005,29.775 +2020-05-20 13:30:00,125.37,63.912,51.263000000000005,29.775 +2020-05-20 13:45:00,129.7,62.131,51.263000000000005,29.775 +2020-05-20 14:00:00,127.75,63.336000000000006,51.107,29.775 +2020-05-20 14:15:00,134.43,61.942,51.107,29.775 +2020-05-20 14:30:00,131.56,61.101000000000006,51.107,29.775 +2020-05-20 14:45:00,129.26,61.735,51.107,29.775 +2020-05-20 15:00:00,125.84,62.364,51.498000000000005,29.775 +2020-05-20 15:15:00,123.48,59.934,51.498000000000005,29.775 +2020-05-20 15:30:00,119.44,58.276,51.498000000000005,29.775 +2020-05-20 15:45:00,121.38,55.893,51.498000000000005,29.775 +2020-05-20 16:00:00,119.62,58.416000000000004,53.376999999999995,29.775 +2020-05-20 16:15:00,114.06,57.964,53.376999999999995,29.775 +2020-05-20 16:30:00,110.26,57.338,53.376999999999995,29.775 +2020-05-20 16:45:00,113.71,54.028,53.376999999999995,29.775 +2020-05-20 17:00:00,121.74,56.156000000000006,56.965,29.775 +2020-05-20 17:15:00,122.25,56.806999999999995,56.965,29.775 +2020-05-20 17:30:00,123.94,56.637,56.965,29.775 +2020-05-20 17:45:00,119.22,55.5,56.965,29.775 +2020-05-20 18:00:00,119.16,58.25899999999999,58.231,29.775 +2020-05-20 18:15:00,116.54,59.221000000000004,58.231,29.775 +2020-05-20 18:30:00,114.14,57.071000000000005,58.231,29.775 +2020-05-20 18:45:00,115.86,62.284,58.231,29.775 +2020-05-20 19:00:00,116.25,62.854,58.865,29.775 +2020-05-20 19:15:00,109.47,62.081,58.865,29.775 +2020-05-20 19:30:00,102.19,61.643,58.865,29.775 +2020-05-20 19:45:00,100.8,62.236000000000004,58.865,29.775 +2020-05-20 20:00:00,104.57,60.473,65.605,29.775 +2020-05-20 20:15:00,101.8,59.593999999999994,65.605,29.775 +2020-05-20 20:30:00,104.21,59.021,65.605,29.775 +2020-05-20 20:45:00,107.81,58.949,65.605,29.775 +2020-05-20 21:00:00,100.78,56.446999999999996,58.083999999999996,29.775 +2020-05-20 21:15:00,99.92,57.891999999999996,58.083999999999996,29.775 +2020-05-20 21:30:00,96.61,58.748000000000005,58.083999999999996,29.775 +2020-05-20 21:45:00,94.94,59.515,58.083999999999996,29.775 +2020-05-20 22:00:00,89.31,56.731,53.243,29.775 +2020-05-20 22:15:00,87.09,57.488,53.243,29.775 +2020-05-20 22:30:00,87.82,50.25899999999999,53.243,29.775 +2020-05-20 22:45:00,88.26,46.924,53.243,29.775 +2020-05-20 23:00:00,63.05,41.388999999999996,44.283,29.775 +2020-05-20 23:15:00,62.09,39.345,44.283,29.775 +2020-05-20 23:30:00,60.51,38.489000000000004,44.283,29.775 +2020-05-20 23:45:00,60.15,38.064,44.283,29.775 +2020-05-21 00:00:00,58.01,36.91,18.527,29.662 +2020-05-21 00:15:00,57.28,34.745,18.527,29.662 +2020-05-21 00:30:00,56.86,33.663000000000004,18.527,29.662 +2020-05-21 00:45:00,56.38,32.498000000000005,18.527,29.662 +2020-05-21 01:00:00,54.83,32.263000000000005,16.348,29.662 +2020-05-21 01:15:00,54.78,31.66,16.348,29.662 +2020-05-21 01:30:00,54.4,29.784000000000002,16.348,29.662 +2020-05-21 01:45:00,54.27,29.289,16.348,29.662 +2020-05-21 02:00:00,53.37,29.329,12.581,29.662 +2020-05-21 02:15:00,53.99,27.565,12.581,29.662 +2020-05-21 02:30:00,52.98,30.034000000000002,12.581,29.662 +2020-05-21 02:45:00,55.47,30.726999999999997,12.581,29.662 +2020-05-21 03:00:00,53.56,33.503,10.712,29.662 +2020-05-21 03:15:00,54.49,30.621,10.712,29.662 +2020-05-21 03:30:00,57.11,29.22,10.712,29.662 +2020-05-21 03:45:00,55.48,30.15,10.712,29.662 +2020-05-21 04:00:00,58.06,37.786,9.084,29.662 +2020-05-21 04:15:00,53.75,44.758,9.084,29.662 +2020-05-21 04:30:00,52.67,42.848,9.084,29.662 +2020-05-21 04:45:00,55.12,42.523,9.084,29.662 +2020-05-21 05:00:00,51.91,54.581,9.388,29.662 +2020-05-21 05:15:00,52.73,60.053000000000004,9.388,29.662 +2020-05-21 05:30:00,55.46,50.553999999999995,9.388,29.662 +2020-05-21 05:45:00,54.71,49.13399999999999,9.388,29.662 +2020-05-21 06:00:00,55.97,62.847,11.109000000000002,29.662 +2020-05-21 06:15:00,58.32,75.834,11.109000000000002,29.662 +2020-05-21 06:30:00,57.09,67.965,11.109000000000002,29.662 +2020-05-21 06:45:00,58.87,62.187,11.109000000000002,29.662 +2020-05-21 07:00:00,60.26,60.449,13.77,29.662 +2020-05-21 07:15:00,63.31,57.713,13.77,29.662 +2020-05-21 07:30:00,59.48,55.919,13.77,29.662 +2020-05-21 07:45:00,59.47,55.233000000000004,13.77,29.662 +2020-05-21 08:00:00,59.73,52.391999999999996,12.868,29.662 +2020-05-21 08:15:00,58.67,55.902,12.868,29.662 +2020-05-21 08:30:00,63.81,56.254,12.868,29.662 +2020-05-21 08:45:00,58.75,59.305,12.868,29.662 +2020-05-21 09:00:00,58.3,56.836000000000006,12.804,29.662 +2020-05-21 09:15:00,58.24,57.516999999999996,12.804,29.662 +2020-05-21 09:30:00,57.56,60.875,12.804,29.662 +2020-05-21 09:45:00,61.45,63.036,12.804,29.662 +2020-05-21 10:00:00,57.46,60.818000000000005,11.029000000000002,29.662 +2020-05-21 10:15:00,57.46,62.676,11.029000000000002,29.662 +2020-05-21 10:30:00,60.42,63.08,11.029000000000002,29.662 +2020-05-21 10:45:00,58.0,64.479,11.029000000000002,29.662 +2020-05-21 11:00:00,58.01,60.6,11.681,29.662 +2020-05-21 11:15:00,54.19,60.924,11.681,29.662 +2020-05-21 11:30:00,54.54,62.856,11.681,29.662 +2020-05-21 11:45:00,55.05,63.966,11.681,29.662 +2020-05-21 12:00:00,55.16,60.76,8.915,29.662 +2020-05-21 12:15:00,53.37,60.842,8.915,29.662 +2020-05-21 12:30:00,51.32,59.909,8.915,29.662 +2020-05-21 12:45:00,50.61,60.408,8.915,29.662 +2020-05-21 13:00:00,50.98,60.489,5.4639999999999995,29.662 +2020-05-21 13:15:00,52.85,60.721000000000004,5.4639999999999995,29.662 +2020-05-21 13:30:00,48.58,58.523,5.4639999999999995,29.662 +2020-05-21 13:45:00,49.76,56.766000000000005,5.4639999999999995,29.662 +2020-05-21 14:00:00,51.52,58.485,3.2939999999999996,29.662 +2020-05-21 14:15:00,57.07,56.833999999999996,3.2939999999999996,29.662 +2020-05-21 14:30:00,64.25,55.785,3.2939999999999996,29.662 +2020-05-21 14:45:00,65.85,55.278999999999996,3.2939999999999996,29.662 +2020-05-21 15:00:00,66.73,56.126999999999995,4.689,29.662 +2020-05-21 15:15:00,60.8,53.618,4.689,29.662 +2020-05-21 15:30:00,61.9,51.562,4.689,29.662 +2020-05-21 15:45:00,64.07,49.477,4.689,29.662 +2020-05-21 16:00:00,65.13,51.675,7.732,29.662 +2020-05-21 16:15:00,66.81,51.131,7.732,29.662 +2020-05-21 16:30:00,64.74,51.558,7.732,29.662 +2020-05-21 16:45:00,64.19,47.702,7.732,29.662 +2020-05-21 17:00:00,65.06,50.641999999999996,17.558,29.662 +2020-05-21 17:15:00,66.43,50.731,17.558,29.662 +2020-05-21 17:30:00,69.39,51.294,17.558,29.662 +2020-05-21 17:45:00,71.08,51.156000000000006,17.558,29.662 +2020-05-21 18:00:00,71.37,55.849,24.763,29.662 +2020-05-21 18:15:00,70.43,57.653999999999996,24.763,29.662 +2020-05-21 18:30:00,73.17,56.379,24.763,29.662 +2020-05-21 18:45:00,70.48,58.501000000000005,24.763,29.662 +2020-05-21 19:00:00,69.9,60.832,29.633000000000003,29.662 +2020-05-21 19:15:00,68.76,58.906000000000006,29.633000000000003,29.662 +2020-05-21 19:30:00,69.06,59.05,29.633000000000003,29.662 +2020-05-21 19:45:00,71.41,60.059,29.633000000000003,29.662 +2020-05-21 20:00:00,69.15,59.508,38.826,29.662 +2020-05-21 20:15:00,71.39,59.303999999999995,38.826,29.662 +2020-05-21 20:30:00,74.95,58.396,38.826,29.662 +2020-05-21 20:45:00,71.98,57.552,38.826,29.662 +2020-05-21 21:00:00,71.11,54.851000000000006,37.751,29.662 +2020-05-21 21:15:00,67.66,57.343999999999994,37.751,29.662 +2020-05-21 21:30:00,65.62,57.926,37.751,29.662 +2020-05-21 21:45:00,65.41,58.792,37.751,29.662 +2020-05-21 22:00:00,62.1,58.045,39.799,29.662 +2020-05-21 22:15:00,64.81,57.568999999999996,39.799,29.662 +2020-05-21 22:30:00,60.51,56.025,39.799,29.662 +2020-05-21 22:45:00,60.78,53.603,39.799,29.662 +2020-05-21 23:00:00,83.53,48.56,33.686,29.662 +2020-05-21 23:15:00,79.71,46.288999999999994,33.686,29.662 +2020-05-21 23:30:00,76.64,45.003,33.686,29.662 +2020-05-21 23:45:00,81.28,44.475,33.686,29.662 +2020-05-22 00:00:00,78.78,34.703,39.884,29.662 +2020-05-22 00:15:00,78.38,35.232,39.884,29.662 +2020-05-22 00:30:00,75.8,34.53,39.884,29.662 +2020-05-22 00:45:00,78.88,33.875,39.884,29.662 +2020-05-22 01:00:00,78.43,33.044000000000004,37.658,29.662 +2020-05-22 01:15:00,76.96,31.963,37.658,29.662 +2020-05-22 01:30:00,72.09,30.969,37.658,29.662 +2020-05-22 01:45:00,77.86,29.756999999999998,37.658,29.662 +2020-05-22 02:00:00,78.9,30.668000000000003,36.707,29.662 +2020-05-22 02:15:00,79.75,29.229,36.707,29.662 +2020-05-22 02:30:00,75.51,32.335,36.707,29.662 +2020-05-22 02:45:00,79.05,32.449,36.707,29.662 +2020-05-22 03:00:00,80.35,35.719,37.025,29.662 +2020-05-22 03:15:00,80.81,33.605,37.025,29.662 +2020-05-22 03:30:00,76.91,32.632,37.025,29.662 +2020-05-22 03:45:00,75.74,32.927,37.025,29.662 +2020-05-22 04:00:00,76.89,43.244,38.349000000000004,29.662 +2020-05-22 04:15:00,77.58,52.16,38.349000000000004,29.662 +2020-05-22 04:30:00,80.22,51.0,38.349000000000004,29.662 +2020-05-22 04:45:00,84.77,50.898,38.349000000000004,29.662 +2020-05-22 05:00:00,93.82,74.133,41.565,29.662 +2020-05-22 05:15:00,95.9,94.82,41.565,29.662 +2020-05-22 05:30:00,97.7,84.27,41.565,29.662 +2020-05-22 05:45:00,97.74,77.786,41.565,29.662 +2020-05-22 06:00:00,104.46,79.546,53.861000000000004,29.662 +2020-05-22 06:15:00,105.11,81.39,53.861000000000004,29.662 +2020-05-22 06:30:00,106.02,78.183,53.861000000000004,29.662 +2020-05-22 06:45:00,108.42,78.407,53.861000000000004,29.662 +2020-05-22 07:00:00,109.92,78.263,63.497,29.662 +2020-05-22 07:15:00,110.85,78.738,63.497,29.662 +2020-05-22 07:30:00,113.49,73.98100000000001,63.497,29.662 +2020-05-22 07:45:00,111.86,71.819,63.497,29.662 +2020-05-22 08:00:00,107.87,66.528,55.43899999999999,29.662 +2020-05-22 08:15:00,106.09,68.429,55.43899999999999,29.662 +2020-05-22 08:30:00,104.97,67.28,55.43899999999999,29.662 +2020-05-22 08:45:00,109.12,68.445,55.43899999999999,29.662 +2020-05-22 09:00:00,104.41,62.928000000000004,52.132,29.662 +2020-05-22 09:15:00,102.77,63.343,52.132,29.662 +2020-05-22 09:30:00,103.2,65.684,52.132,29.662 +2020-05-22 09:45:00,105.1,67.34899999999999,52.132,29.662 +2020-05-22 10:00:00,108.85,63.57,49.881,29.662 +2020-05-22 10:15:00,114.42,64.851,49.881,29.662 +2020-05-22 10:30:00,116.06,65.205,49.881,29.662 +2020-05-22 10:45:00,112.9,66.123,49.881,29.662 +2020-05-22 11:00:00,116.37,62.451,49.396,29.662 +2020-05-22 11:15:00,116.42,62.535,49.396,29.662 +2020-05-22 11:30:00,115.65,64.168,49.396,29.662 +2020-05-22 11:45:00,114.92,64.525,49.396,29.662 +2020-05-22 12:00:00,111.62,60.479,46.7,29.662 +2020-05-22 12:15:00,106.84,59.778,46.7,29.662 +2020-05-22 12:30:00,102.56,58.99100000000001,46.7,29.662 +2020-05-22 12:45:00,98.4,59.821000000000005,46.7,29.662 +2020-05-22 13:00:00,94.96,61.16,44.05,29.662 +2020-05-22 13:15:00,96.61,62.23,44.05,29.662 +2020-05-22 13:30:00,93.79,61.045,44.05,29.662 +2020-05-22 13:45:00,92.84,59.531000000000006,44.05,29.662 +2020-05-22 14:00:00,91.24,59.794,42.805,29.662 +2020-05-22 14:15:00,93.29,58.739,42.805,29.662 +2020-05-22 14:30:00,91.88,59.32899999999999,42.805,29.662 +2020-05-22 14:45:00,93.09,59.412,42.805,29.662 +2020-05-22 15:00:00,93.45,59.95399999999999,44.36600000000001,29.662 +2020-05-22 15:15:00,95.82,57.167,44.36600000000001,29.662 +2020-05-22 15:30:00,97.12,54.571000000000005,44.36600000000001,29.662 +2020-05-22 15:45:00,97.8,52.891000000000005,44.36600000000001,29.662 +2020-05-22 16:00:00,96.53,54.466,46.928999999999995,29.662 +2020-05-22 16:15:00,103.21,54.513999999999996,46.928999999999995,29.662 +2020-05-22 16:30:00,104.3,53.775,46.928999999999995,29.662 +2020-05-22 16:45:00,104.51,49.604,46.928999999999995,29.662 +2020-05-22 17:00:00,103.54,53.474,51.468,29.662 +2020-05-22 17:15:00,101.79,53.823,51.468,29.662 +2020-05-22 17:30:00,102.58,53.708,51.468,29.662 +2020-05-22 17:45:00,104.14,52.295,51.468,29.662 +2020-05-22 18:00:00,106.35,55.326,52.58,29.662 +2020-05-22 18:15:00,103.1,55.269,52.58,29.662 +2020-05-22 18:30:00,102.56,53.086000000000006,52.58,29.662 +2020-05-22 18:45:00,105.35,58.692,52.58,29.662 +2020-05-22 19:00:00,107.38,60.31399999999999,52.183,29.662 +2020-05-22 19:15:00,103.6,60.376000000000005,52.183,29.662 +2020-05-22 19:30:00,98.21,59.898999999999994,52.183,29.662 +2020-05-22 19:45:00,98.34,59.427,52.183,29.662 +2020-05-22 20:00:00,94.72,57.468999999999994,58.497,29.662 +2020-05-22 20:15:00,95.77,57.34,58.497,29.662 +2020-05-22 20:30:00,98.16,56.342,58.497,29.662 +2020-05-22 20:45:00,96.69,55.707,58.497,29.662 +2020-05-22 21:00:00,97.63,54.568000000000005,54.731,29.662 +2020-05-22 21:15:00,97.68,57.72,54.731,29.662 +2020-05-22 21:30:00,90.83,58.396,54.731,29.662 +2020-05-22 21:45:00,86.47,59.52,54.731,29.662 +2020-05-22 22:00:00,80.55,56.864,51.386,29.662 +2020-05-22 22:15:00,84.38,57.391000000000005,51.386,29.662 +2020-05-22 22:30:00,86.27,56.013000000000005,51.386,29.662 +2020-05-22 22:45:00,84.62,54.208999999999996,51.386,29.662 +2020-05-22 23:00:00,74.96,50.117,44.463,29.662 +2020-05-22 23:15:00,74.35,46.266000000000005,44.463,29.662 +2020-05-22 23:30:00,71.21,43.38,44.463,29.662 +2020-05-22 23:45:00,76.73,42.683,44.463,29.662 +2020-05-23 00:00:00,75.7,35.035,42.833999999999996,29.662 +2020-05-23 00:15:00,76.89,34.034,42.833999999999996,29.662 +2020-05-23 00:30:00,71.1,33.168,42.833999999999996,29.662 +2020-05-23 00:45:00,75.4,31.974,42.833999999999996,29.662 +2020-05-23 01:00:00,73.52,31.552,37.859,29.662 +2020-05-23 01:15:00,74.62,30.799,37.859,29.662 +2020-05-23 01:30:00,68.8,28.918000000000003,37.859,29.662 +2020-05-23 01:45:00,73.9,28.836,37.859,29.662 +2020-05-23 02:00:00,72.13,28.951,35.327,29.662 +2020-05-23 02:15:00,73.48,26.677,35.327,29.662 +2020-05-23 02:30:00,66.65,28.805,35.327,29.662 +2020-05-23 02:45:00,65.13,29.693,35.327,29.662 +2020-05-23 03:00:00,66.59,31.802,34.908,29.662 +2020-05-23 03:15:00,64.97,28.761,34.908,29.662 +2020-05-23 03:30:00,65.86,27.765,34.908,29.662 +2020-05-23 03:45:00,65.45,29.511999999999997,34.908,29.662 +2020-05-23 04:00:00,63.35,37.108000000000004,34.84,29.662 +2020-05-23 04:15:00,63.76,44.592,34.84,29.662 +2020-05-23 04:30:00,62.96,41.382,34.84,29.662 +2020-05-23 04:45:00,64.33,41.43,34.84,29.662 +2020-05-23 05:00:00,67.52,53.748000000000005,34.222,29.662 +2020-05-23 05:15:00,67.85,60.097,34.222,29.662 +2020-05-23 05:30:00,68.35,51.146,34.222,29.662 +2020-05-23 05:45:00,68.82,50.068999999999996,34.222,29.662 +2020-05-23 06:00:00,72.79,66.115,35.515,29.662 +2020-05-23 06:15:00,71.8,78.594,35.515,29.662 +2020-05-23 06:30:00,73.05,71.634,35.515,29.662 +2020-05-23 06:45:00,75.09,67.053,35.515,29.662 +2020-05-23 07:00:00,76.2,64.55199999999999,39.687,29.662 +2020-05-23 07:15:00,76.2,63.553000000000004,39.687,29.662 +2020-05-23 07:30:00,77.52,60.75899999999999,39.687,29.662 +2020-05-23 07:45:00,79.58,60.32,39.687,29.662 +2020-05-23 08:00:00,78.67,56.416000000000004,44.9,29.662 +2020-05-23 08:15:00,78.07,58.93600000000001,44.9,29.662 +2020-05-23 08:30:00,78.21,58.104,44.9,29.662 +2020-05-23 08:45:00,78.09,60.843,44.9,29.662 +2020-05-23 09:00:00,76.63,58.558,45.724,29.662 +2020-05-23 09:15:00,76.47,59.595,45.724,29.662 +2020-05-23 09:30:00,75.4,62.603,45.724,29.662 +2020-05-23 09:45:00,75.15,63.916000000000004,45.724,29.662 +2020-05-23 10:00:00,75.82,60.69,43.123999999999995,29.662 +2020-05-23 10:15:00,76.15,62.368,43.123999999999995,29.662 +2020-05-23 10:30:00,76.53,62.45399999999999,43.123999999999995,29.662 +2020-05-23 10:45:00,75.37,63.36,43.123999999999995,29.662 +2020-05-23 11:00:00,75.31,59.586999999999996,40.255,29.662 +2020-05-23 11:15:00,77.04,60.324,40.255,29.662 +2020-05-23 11:30:00,77.44,61.949,40.255,29.662 +2020-05-23 11:45:00,74.8,62.708999999999996,40.255,29.662 +2020-05-23 12:00:00,69.86,58.754,38.582,29.662 +2020-05-23 12:15:00,69.32,58.985,38.582,29.662 +2020-05-23 12:30:00,71.91,58.121,38.582,29.662 +2020-05-23 12:45:00,70.49,59.434,38.582,29.662 +2020-05-23 13:00:00,69.63,59.963,36.043,29.662 +2020-05-23 13:15:00,73.6,60.055,36.043,29.662 +2020-05-23 13:30:00,72.67,58.946999999999996,36.043,29.662 +2020-05-23 13:45:00,69.34,56.317,36.043,29.662 +2020-05-23 14:00:00,67.9,56.952,35.216,29.662 +2020-05-23 14:15:00,68.87,54.621,35.216,29.662 +2020-05-23 14:30:00,69.82,54.353,35.216,29.662 +2020-05-23 14:45:00,66.18,54.928999999999995,35.216,29.662 +2020-05-23 15:00:00,71.76,56.085,36.759,29.662 +2020-05-23 15:15:00,72.55,54.121,36.759,29.662 +2020-05-23 15:30:00,72.31,52.053000000000004,36.759,29.662 +2020-05-23 15:45:00,71.95,49.521,36.759,29.662 +2020-05-23 16:00:00,71.37,52.958,40.086,29.662 +2020-05-23 16:15:00,71.22,52.37,40.086,29.662 +2020-05-23 16:30:00,71.77,51.843999999999994,40.086,29.662 +2020-05-23 16:45:00,72.93,47.797,40.086,29.662 +2020-05-23 17:00:00,75.81,50.486000000000004,44.876999999999995,29.662 +2020-05-23 17:15:00,77.15,49.199,44.876999999999995,29.662 +2020-05-23 17:30:00,81.73,48.926,44.876999999999995,29.662 +2020-05-23 17:45:00,83.0,47.845,44.876999999999995,29.662 +2020-05-23 18:00:00,82.3,52.175,47.056000000000004,29.662 +2020-05-23 18:15:00,80.93,54.047,47.056000000000004,29.662 +2020-05-23 18:30:00,81.66,53.409,47.056000000000004,29.662 +2020-05-23 18:45:00,81.46,55.044,47.056000000000004,29.662 +2020-05-23 19:00:00,79.51,55.43,45.57,29.662 +2020-05-23 19:15:00,76.63,54.428999999999995,45.57,29.662 +2020-05-23 19:30:00,75.8,54.83,45.57,29.662 +2020-05-23 19:45:00,75.75,55.99,45.57,29.662 +2020-05-23 20:00:00,75.77,55.193000000000005,41.685,29.662 +2020-05-23 20:15:00,75.2,54.916000000000004,41.685,29.662 +2020-05-23 20:30:00,78.48,53.038999999999994,41.685,29.662 +2020-05-23 20:45:00,77.87,54.102,41.685,29.662 +2020-05-23 21:00:00,75.6,52.068999999999996,39.576,29.662 +2020-05-23 21:15:00,75.41,54.998999999999995,39.576,29.662 +2020-05-23 21:30:00,72.51,56.086999999999996,39.576,29.662 +2020-05-23 21:45:00,71.96,56.635,39.576,29.662 +2020-05-23 22:00:00,68.93,54.263000000000005,39.068000000000005,29.662 +2020-05-23 22:15:00,68.33,55.56,39.068000000000005,29.662 +2020-05-23 22:30:00,65.26,55.118,39.068000000000005,29.662 +2020-05-23 22:45:00,65.06,54.041000000000004,39.068000000000005,29.662 +2020-05-23 23:00:00,61.85,49.77,32.06,29.662 +2020-05-23 23:15:00,61.41,46.022,32.06,29.662 +2020-05-23 23:30:00,61.34,45.095,32.06,29.662 +2020-05-23 23:45:00,59.43,44.225,32.06,29.662 +2020-05-24 00:00:00,57.13,36.184,28.825,29.662 +2020-05-24 00:15:00,57.97,34.035,28.825,29.662 +2020-05-24 00:30:00,57.26,32.953,28.825,29.662 +2020-05-24 00:45:00,57.6,31.803,28.825,29.662 +2020-05-24 01:00:00,55.3,31.62,25.995,29.662 +2020-05-24 01:15:00,56.86,30.941999999999997,25.995,29.662 +2020-05-24 01:30:00,56.68,29.026999999999997,25.995,29.662 +2020-05-24 01:45:00,56.48,28.518,25.995,29.662 +2020-05-24 02:00:00,54.98,28.549,24.394000000000002,29.662 +2020-05-24 02:15:00,55.67,26.753,24.394000000000002,29.662 +2020-05-24 02:30:00,55.71,29.241999999999997,24.394000000000002,29.662 +2020-05-24 02:45:00,54.51,29.96,24.394000000000002,29.662 +2020-05-24 03:00:00,54.2,32.754,22.916999999999998,29.662 +2020-05-24 03:15:00,54.33,29.838,22.916999999999998,29.662 +2020-05-24 03:30:00,55.39,28.445999999999998,22.916999999999998,29.662 +2020-05-24 03:45:00,53.97,29.454,22.916999999999998,29.662 +2020-05-24 04:00:00,51.59,36.912,23.576999999999998,29.662 +2020-05-24 04:15:00,52.43,43.722,23.576999999999998,29.662 +2020-05-24 04:30:00,52.07,41.773999999999994,23.576999999999998,29.662 +2020-05-24 04:45:00,52.18,41.433,23.576999999999998,29.662 +2020-05-24 05:00:00,52.38,53.093999999999994,22.730999999999998,29.662 +2020-05-24 05:15:00,51.01,58.086999999999996,22.730999999999998,29.662 +2020-05-24 05:30:00,51.44,48.788000000000004,22.730999999999998,29.662 +2020-05-24 05:45:00,50.26,47.56100000000001,22.730999999999998,29.662 +2020-05-24 06:00:00,55.4,61.348,22.34,29.662 +2020-05-24 06:15:00,56.11,74.26,22.34,29.662 +2020-05-24 06:30:00,56.5,66.44,22.34,29.662 +2020-05-24 06:45:00,58.7,60.73,22.34,29.662 +2020-05-24 07:00:00,60.0,58.957,24.691999999999997,29.662 +2020-05-24 07:15:00,62.28,56.248999999999995,24.691999999999997,29.662 +2020-05-24 07:30:00,63.86,54.387,24.691999999999997,29.662 +2020-05-24 07:45:00,66.28,53.792,24.691999999999997,29.662 +2020-05-24 08:00:00,68.02,50.965,29.340999999999998,29.662 +2020-05-24 08:15:00,69.85,54.619,29.340999999999998,29.662 +2020-05-24 08:30:00,70.34,54.93600000000001,29.340999999999998,29.662 +2020-05-24 08:45:00,69.19,58.036,29.340999999999998,29.662 +2020-05-24 09:00:00,64.38,55.551,30.788,29.662 +2020-05-24 09:15:00,64.98,56.251999999999995,30.788,29.662 +2020-05-24 09:30:00,61.61,59.635,30.788,29.662 +2020-05-24 09:45:00,58.24,61.888999999999996,30.788,29.662 +2020-05-24 10:00:00,59.37,59.70399999999999,30.158,29.662 +2020-05-24 10:15:00,60.65,61.652,30.158,29.662 +2020-05-24 10:30:00,62.09,62.085,30.158,29.662 +2020-05-24 10:45:00,62.05,63.525,30.158,29.662 +2020-05-24 11:00:00,62.17,59.622,32.056,29.662 +2020-05-24 11:15:00,59.81,59.99100000000001,32.056,29.662 +2020-05-24 11:30:00,55.31,61.896,32.056,29.662 +2020-05-24 11:45:00,55.01,63.036,32.056,29.662 +2020-05-24 12:00:00,56.12,59.94,28.671999999999997,29.662 +2020-05-24 12:15:00,58.46,60.038000000000004,28.671999999999997,29.662 +2020-05-24 12:30:00,58.98,59.003,28.671999999999997,29.662 +2020-05-24 12:45:00,61.98,59.52,28.671999999999997,29.662 +2020-05-24 13:00:00,60.62,59.632,23.171,29.662 +2020-05-24 13:15:00,61.12,59.873000000000005,23.171,29.662 +2020-05-24 13:30:00,56.46,57.708999999999996,23.171,29.662 +2020-05-24 13:45:00,56.65,55.956,23.171,29.662 +2020-05-24 14:00:00,56.1,57.781000000000006,19.11,29.662 +2020-05-24 14:15:00,58.99,56.107,19.11,29.662 +2020-05-24 14:30:00,64.84,54.945,19.11,29.662 +2020-05-24 14:45:00,70.09,54.452,19.11,29.662 +2020-05-24 15:00:00,72.58,55.449,19.689,29.662 +2020-05-24 15:15:00,73.17,52.895,19.689,29.662 +2020-05-24 15:30:00,74.8,50.771,19.689,29.662 +2020-05-24 15:45:00,76.05,48.638999999999996,19.689,29.662 +2020-05-24 16:00:00,84.67,50.972,22.875,29.662 +2020-05-24 16:15:00,87.35,50.39,22.875,29.662 +2020-05-24 16:30:00,87.69,50.867,22.875,29.662 +2020-05-24 16:45:00,83.03,46.857,22.875,29.662 +2020-05-24 17:00:00,83.2,49.927,33.884,29.662 +2020-05-24 17:15:00,80.98,49.951,33.884,29.662 +2020-05-24 17:30:00,82.32,50.475,33.884,29.662 +2020-05-24 17:45:00,86.46,50.239,33.884,29.662 +2020-05-24 18:00:00,88.05,54.983000000000004,38.453,29.662 +2020-05-24 18:15:00,84.96,56.722,38.453,29.662 +2020-05-24 18:30:00,81.33,55.413999999999994,38.453,29.662 +2020-05-24 18:45:00,80.49,57.535,38.453,29.662 +2020-05-24 19:00:00,83.33,59.87,39.221,29.662 +2020-05-24 19:15:00,81.36,57.926,39.221,29.662 +2020-05-24 19:30:00,79.76,58.052,39.221,29.662 +2020-05-24 19:45:00,80.78,59.06,39.221,29.662 +2020-05-24 20:00:00,82.2,58.438,37.871,29.662 +2020-05-24 20:15:00,82.3,58.229,37.871,29.662 +2020-05-24 20:30:00,83.51,57.387,37.871,29.662 +2020-05-24 20:45:00,83.49,56.669,37.871,29.662 +2020-05-24 21:00:00,81.41,53.983999999999995,36.465,29.662 +2020-05-24 21:15:00,82.26,56.519,36.465,29.662 +2020-05-24 21:30:00,75.32,57.03,36.465,29.662 +2020-05-24 21:45:00,76.43,57.966,36.465,29.662 +2020-05-24 22:00:00,74.5,57.299,36.092,29.662 +2020-05-24 22:15:00,78.91,56.873999999999995,36.092,29.662 +2020-05-24 22:30:00,80.16,55.325,36.092,29.662 +2020-05-24 22:45:00,79.85,52.865,36.092,29.662 +2020-05-24 23:00:00,71.49,47.725,31.013,29.662 +2020-05-24 23:15:00,69.19,45.586999999999996,31.013,29.662 +2020-05-24 23:30:00,73.1,44.315,31.013,29.662 +2020-05-24 23:45:00,75.16,43.761,31.013,29.662 +2020-05-25 00:00:00,73.04,38.211999999999996,31.174,29.775 +2020-05-25 00:15:00,71.45,37.404,31.174,29.775 +2020-05-25 00:30:00,65.82,35.985,31.174,29.775 +2020-05-25 00:45:00,65.94,34.361,31.174,29.775 +2020-05-25 01:00:00,68.21,34.563,29.663,29.775 +2020-05-25 01:15:00,73.16,33.77,29.663,29.775 +2020-05-25 01:30:00,73.49,32.193000000000005,29.663,29.775 +2020-05-25 01:45:00,70.92,31.61,29.663,29.775 +2020-05-25 02:00:00,67.31,32.065,28.793000000000003,29.775 +2020-05-25 02:15:00,72.78,29.594,28.793000000000003,29.775 +2020-05-25 02:30:00,74.32,32.31,28.793000000000003,29.775 +2020-05-25 02:45:00,74.35,32.775999999999996,28.793000000000003,29.775 +2020-05-25 03:00:00,69.78,36.306999999999995,27.728,29.775 +2020-05-25 03:15:00,68.47,34.389,27.728,29.775 +2020-05-25 03:30:00,69.86,33.588,27.728,29.775 +2020-05-25 03:45:00,70.1,34.076,27.728,29.775 +2020-05-25 04:00:00,73.53,45.273999999999994,29.266,29.775 +2020-05-25 04:15:00,73.54,55.622,29.266,29.775 +2020-05-25 04:30:00,77.68,53.648,29.266,29.775 +2020-05-25 04:45:00,81.3,53.683,29.266,29.775 +2020-05-25 05:00:00,89.03,74.794,37.889,29.775 +2020-05-25 05:15:00,93.31,93.23,37.889,29.775 +2020-05-25 05:30:00,95.72,82.57700000000001,37.889,29.775 +2020-05-25 05:45:00,98.49,77.42699999999999,37.889,29.775 +2020-05-25 06:00:00,99.46,78.153,55.485,29.775 +2020-05-25 06:15:00,104.22,79.572,55.485,29.775 +2020-05-25 06:30:00,105.6,77.07,55.485,29.775 +2020-05-25 06:45:00,106.6,78.078,55.485,29.775 +2020-05-25 07:00:00,108.35,77.343,65.765,29.775 +2020-05-25 07:15:00,107.73,77.042,65.765,29.775 +2020-05-25 07:30:00,107.92,74.193,65.765,29.775 +2020-05-25 07:45:00,108.55,73.4,65.765,29.775 +2020-05-25 08:00:00,106.16,67.673,56.745,29.775 +2020-05-25 08:15:00,106.57,69.807,56.745,29.775 +2020-05-25 08:30:00,107.31,68.376,56.745,29.775 +2020-05-25 08:45:00,106.93,71.00399999999999,56.745,29.775 +2020-05-25 09:00:00,106.44,67.383,53.321999999999996,29.775 +2020-05-25 09:15:00,106.7,65.695,53.321999999999996,29.775 +2020-05-25 09:30:00,105.96,68.01100000000001,53.321999999999996,29.775 +2020-05-25 09:45:00,105.74,68.217,53.321999999999996,29.775 +2020-05-25 10:00:00,106.53,66.318,51.309,29.775 +2020-05-25 10:15:00,109.6,68.087,51.309,29.775 +2020-05-25 10:30:00,108.78,67.915,51.309,29.775 +2020-05-25 10:45:00,107.84,68.066,51.309,29.775 +2020-05-25 11:00:00,105.05,63.818000000000005,50.415,29.775 +2020-05-25 11:15:00,103.48,64.858,50.415,29.775 +2020-05-25 11:30:00,101.34,67.70100000000001,50.415,29.775 +2020-05-25 11:45:00,101.35,69.223,50.415,29.775 +2020-05-25 12:00:00,100.54,65.095,48.273,29.775 +2020-05-25 12:15:00,101.38,65.3,48.273,29.775 +2020-05-25 12:30:00,99.35,63.31,48.273,29.775 +2020-05-25 12:45:00,101.48,64.178,48.273,29.775 +2020-05-25 13:00:00,100.53,65.195,48.452,29.775 +2020-05-25 13:15:00,101.37,64.317,48.452,29.775 +2020-05-25 13:30:00,99.07,62.199,48.452,29.775 +2020-05-25 13:45:00,100.17,61.278999999999996,48.452,29.775 +2020-05-25 14:00:00,99.77,62.183,48.35,29.775 +2020-05-25 14:15:00,102.24,60.898999999999994,48.35,29.775 +2020-05-25 14:30:00,103.48,59.428000000000004,48.35,29.775 +2020-05-25 14:45:00,101.43,60.82899999999999,48.35,29.775 +2020-05-25 15:00:00,101.0,61.948,48.838,29.775 +2020-05-25 15:15:00,97.03,58.513000000000005,48.838,29.775 +2020-05-25 15:30:00,98.64,56.886,48.838,29.775 +2020-05-25 15:45:00,101.34,54.193999999999996,48.838,29.775 +2020-05-25 16:00:00,101.37,57.505,50.873000000000005,29.775 +2020-05-25 16:15:00,103.25,56.806999999999995,50.873000000000005,29.775 +2020-05-25 16:30:00,106.69,56.475,50.873000000000005,29.775 +2020-05-25 16:45:00,106.5,52.178000000000004,50.873000000000005,29.775 +2020-05-25 17:00:00,107.1,54.208999999999996,56.637,29.775 +2020-05-25 17:15:00,111.13,54.353,56.637,29.775 +2020-05-25 17:30:00,110.99,54.388000000000005,56.637,29.775 +2020-05-25 17:45:00,111.14,53.427,56.637,29.775 +2020-05-25 18:00:00,108.96,57.196999999999996,56.35,29.775 +2020-05-25 18:15:00,105.76,56.744,56.35,29.775 +2020-05-25 18:30:00,113.14,54.85,56.35,29.775 +2020-05-25 18:45:00,116.77,60.083,56.35,29.775 +2020-05-25 19:00:00,108.64,61.768,56.023,29.775 +2020-05-25 19:15:00,98.73,60.848,56.023,29.775 +2020-05-25 19:30:00,103.11,60.716,56.023,29.775 +2020-05-25 19:45:00,99.07,60.978,56.023,29.775 +2020-05-25 20:00:00,96.44,58.687,62.372,29.775 +2020-05-25 20:15:00,103.82,59.299,62.372,29.775 +2020-05-25 20:30:00,106.27,58.593999999999994,62.372,29.775 +2020-05-25 20:45:00,103.64,58.45,62.372,29.775 +2020-05-25 21:00:00,96.06,55.285,57.516999999999996,29.775 +2020-05-25 21:15:00,96.57,58.018,57.516999999999996,29.775 +2020-05-25 21:30:00,96.34,58.732,57.516999999999996,29.775 +2020-05-25 21:45:00,96.09,59.38399999999999,57.516999999999996,29.775 +2020-05-25 22:00:00,89.25,56.24,51.823,29.775 +2020-05-25 22:15:00,83.2,57.45399999999999,51.823,29.775 +2020-05-25 22:30:00,87.35,49.917,51.823,29.775 +2020-05-25 22:45:00,89.63,46.472,51.823,29.775 +2020-05-25 23:00:00,82.93,41.511,43.832,29.775 +2020-05-25 23:15:00,75.55,38.372,43.832,29.775 +2020-05-25 23:30:00,79.33,37.609,43.832,29.775 +2020-05-25 23:45:00,82.15,37.052,43.832,29.775 +2020-05-26 00:00:00,77.88,35.689,42.371,29.775 +2020-05-26 00:15:00,75.1,35.991,42.371,29.775 +2020-05-26 00:30:00,78.04,35.06,42.371,29.775 +2020-05-26 00:45:00,78.89,33.999,42.371,29.775 +2020-05-26 01:00:00,77.82,33.665,39.597,29.775 +2020-05-26 01:15:00,76.09,32.865,39.597,29.775 +2020-05-26 01:30:00,78.26,31.186,39.597,29.775 +2020-05-26 01:45:00,79.73,30.171999999999997,39.597,29.775 +2020-05-26 02:00:00,79.78,30.166999999999998,38.298,29.775 +2020-05-26 02:15:00,76.12,28.761999999999997,38.298,29.775 +2020-05-26 02:30:00,79.45,30.995,38.298,29.775 +2020-05-26 02:45:00,81.63,31.781999999999996,38.298,29.775 +2020-05-26 03:00:00,78.81,34.551,37.884,29.775 +2020-05-26 03:15:00,75.75,33.34,37.884,29.775 +2020-05-26 03:30:00,80.4,32.609,37.884,29.775 +2020-05-26 03:45:00,82.09,32.106,37.884,29.775 +2020-05-26 04:00:00,81.49,42.005,39.442,29.775 +2020-05-26 04:15:00,78.42,52.242,39.442,29.775 +2020-05-26 04:30:00,81.46,50.07899999999999,39.442,29.775 +2020-05-26 04:45:00,86.44,50.81399999999999,39.442,29.775 +2020-05-26 05:00:00,92.93,74.25399999999999,43.608000000000004,29.775 +2020-05-26 05:15:00,95.98,93.09100000000001,43.608000000000004,29.775 +2020-05-26 05:30:00,97.87,82.4,43.608000000000004,29.775 +2020-05-26 05:45:00,100.97,76.611,43.608000000000004,29.775 +2020-05-26 06:00:00,104.23,78.155,54.99100000000001,29.775 +2020-05-26 06:15:00,106.17,80.044,54.99100000000001,29.775 +2020-05-26 06:30:00,107.29,77.12899999999999,54.99100000000001,29.775 +2020-05-26 06:45:00,108.62,77.236,54.99100000000001,29.775 +2020-05-26 07:00:00,111.63,76.596,66.217,29.775 +2020-05-26 07:15:00,109.37,76.031,66.217,29.775 +2020-05-26 07:30:00,108.15,73.069,66.217,29.775 +2020-05-26 07:45:00,107.28,71.422,66.217,29.775 +2020-05-26 08:00:00,105.84,65.657,60.151,29.775 +2020-05-26 08:15:00,107.1,67.205,60.151,29.775 +2020-05-26 08:30:00,107.63,65.885,60.151,29.775 +2020-05-26 08:45:00,105.85,67.601,60.151,29.775 +2020-05-26 09:00:00,106.2,64.122,53.873000000000005,29.775 +2020-05-26 09:15:00,106.76,62.643,53.873000000000005,29.775 +2020-05-26 09:30:00,106.29,65.755,53.873000000000005,29.775 +2020-05-26 09:45:00,107.29,67.207,53.873000000000005,29.775 +2020-05-26 10:00:00,107.68,63.907,51.417,29.775 +2020-05-26 10:15:00,108.29,65.344,51.417,29.775 +2020-05-26 10:30:00,107.02,65.244,51.417,29.775 +2020-05-26 10:45:00,106.79,66.407,51.417,29.775 +2020-05-26 11:00:00,103.4,62.48,50.43600000000001,29.775 +2020-05-26 11:15:00,103.94,63.838,50.43600000000001,29.775 +2020-05-26 11:30:00,107.61,65.347,50.43600000000001,29.775 +2020-05-26 11:45:00,110.51,66.61399999999999,50.43600000000001,29.775 +2020-05-26 12:00:00,108.4,62.042,47.468,29.775 +2020-05-26 12:15:00,103.09,62.467,47.468,29.775 +2020-05-26 12:30:00,104.04,61.398,47.468,29.775 +2020-05-26 12:45:00,101.49,62.843,47.468,29.775 +2020-05-26 13:00:00,102.78,63.45399999999999,48.453,29.775 +2020-05-26 13:15:00,107.92,64.171,48.453,29.775 +2020-05-26 13:30:00,112.42,62.297,48.453,29.775 +2020-05-26 13:45:00,112.14,60.523999999999994,48.453,29.775 +2020-05-26 14:00:00,110.26,61.941,48.435,29.775 +2020-05-26 14:15:00,105.71,60.5,48.435,29.775 +2020-05-26 14:30:00,111.81,59.43600000000001,48.435,29.775 +2020-05-26 14:45:00,112.96,60.092,48.435,29.775 +2020-05-26 15:00:00,112.99,61.018,49.966,29.775 +2020-05-26 15:15:00,110.34,58.498000000000005,49.966,29.775 +2020-05-26 15:30:00,113.39,56.705,49.966,29.775 +2020-05-26 15:45:00,116.68,54.229,49.966,29.775 +2020-05-26 16:00:00,118.92,57.022,51.184,29.775 +2020-05-26 16:15:00,115.17,56.494,51.184,29.775 +2020-05-26 16:30:00,114.9,55.968,51.184,29.775 +2020-05-26 16:45:00,120.29,52.354,51.184,29.775 +2020-05-26 17:00:00,120.37,54.74,56.138999999999996,29.775 +2020-05-26 17:15:00,116.21,55.26,56.138999999999996,29.775 +2020-05-26 17:30:00,111.59,55.015,56.138999999999996,29.775 +2020-05-26 17:45:00,120.21,53.681000000000004,56.138999999999996,29.775 +2020-05-26 18:00:00,122.33,56.542,57.038000000000004,29.775 +2020-05-26 18:15:00,115.55,57.373000000000005,57.038000000000004,29.775 +2020-05-26 18:30:00,110.43,55.159,57.038000000000004,29.775 +2020-05-26 18:45:00,113.6,60.367,57.038000000000004,29.775 +2020-05-26 19:00:00,113.25,60.945,56.492,29.775 +2020-05-26 19:15:00,105.86,60.135,56.492,29.775 +2020-05-26 19:30:00,105.79,59.665,56.492,29.775 +2020-05-26 19:45:00,100.27,60.254,56.492,29.775 +2020-05-26 20:00:00,101.3,58.349,62.534,29.775 +2020-05-26 20:15:00,105.86,57.463,62.534,29.775 +2020-05-26 20:30:00,107.47,57.018,62.534,29.775 +2020-05-26 20:45:00,104.42,57.199,62.534,29.775 +2020-05-26 21:00:00,97.23,54.727,55.506,29.775 +2020-05-26 21:15:00,102.65,56.256,55.506,29.775 +2020-05-26 21:30:00,99.18,56.97,55.506,29.775 +2020-05-26 21:45:00,96.09,57.873999999999995,55.506,29.775 +2020-05-26 22:00:00,85.21,55.248000000000005,51.472,29.775 +2020-05-26 22:15:00,90.23,56.108999999999995,51.472,29.775 +2020-05-26 22:30:00,91.74,48.865,51.472,29.775 +2020-05-26 22:45:00,88.76,45.456,51.472,29.775 +2020-05-26 23:00:00,79.55,39.731,44.593,29.775 +2020-05-26 23:15:00,79.85,37.953,44.593,29.775 +2020-05-26 23:30:00,86.2,37.123000000000005,44.593,29.775 +2020-05-26 23:45:00,87.91,36.645,44.593,29.775 +2020-05-27 00:00:00,78.67,35.461999999999996,41.978,29.775 +2020-05-27 00:15:00,76.21,35.769,41.978,29.775 +2020-05-27 00:30:00,74.83,34.839,41.978,29.775 +2020-05-27 00:45:00,82.21,33.784,41.978,29.775 +2020-05-27 01:00:00,79.98,33.466,38.59,29.775 +2020-05-27 01:15:00,79.27,32.641999999999996,38.59,29.775 +2020-05-27 01:30:00,75.92,30.951,38.59,29.775 +2020-05-27 01:45:00,75.33,29.930999999999997,38.59,29.775 +2020-05-27 02:00:00,79.93,29.924,36.23,29.775 +2020-05-27 02:15:00,81.78,28.51,36.23,29.775 +2020-05-27 02:30:00,78.49,30.748,36.23,29.775 +2020-05-27 02:45:00,76.39,31.541999999999998,36.23,29.775 +2020-05-27 03:00:00,74.78,34.317,35.867,29.775 +2020-05-27 03:15:00,81.81,33.095,35.867,29.775 +2020-05-27 03:30:00,83.72,32.368,35.867,29.775 +2020-05-27 03:45:00,84.2,31.891,35.867,29.775 +2020-05-27 04:00:00,80.31,41.731,36.75,29.775 +2020-05-27 04:15:00,79.3,51.916000000000004,36.75,29.775 +2020-05-27 04:30:00,80.69,49.742,36.75,29.775 +2020-05-27 04:45:00,85.87,50.47,36.75,29.775 +2020-05-27 05:00:00,92.63,73.78399999999999,40.461,29.775 +2020-05-27 05:15:00,97.73,92.46600000000001,40.461,29.775 +2020-05-27 05:30:00,101.04,81.842,40.461,29.775 +2020-05-27 05:45:00,102.83,76.115,40.461,29.775 +2020-05-27 06:00:00,108.55,77.681,55.481,29.775 +2020-05-27 06:15:00,111.03,79.547,55.481,29.775 +2020-05-27 06:30:00,114.05,76.648,55.481,29.775 +2020-05-27 06:45:00,116.14,76.777,55.481,29.775 +2020-05-27 07:00:00,118.55,76.126,68.45,29.775 +2020-05-27 07:15:00,117.45,75.571,68.45,29.775 +2020-05-27 07:30:00,116.41,72.589,68.45,29.775 +2020-05-27 07:45:00,115.84,70.972,68.45,29.775 +2020-05-27 08:00:00,115.67,65.21300000000001,60.885,29.775 +2020-05-27 08:15:00,117.03,66.808,60.885,29.775 +2020-05-27 08:30:00,117.65,65.476,60.885,29.775 +2020-05-27 08:45:00,116.56,67.208,60.885,29.775 +2020-05-27 09:00:00,113.89,63.724,56.887,29.775 +2020-05-27 09:15:00,113.51,62.251000000000005,56.887,29.775 +2020-05-27 09:30:00,110.46,65.37,56.887,29.775 +2020-05-27 09:45:00,113.38,66.851,56.887,29.775 +2020-05-27 10:00:00,111.51,63.56100000000001,54.401,29.775 +2020-05-27 10:15:00,109.58,65.025,54.401,29.775 +2020-05-27 10:30:00,110.08,64.936,54.401,29.775 +2020-05-27 10:45:00,111.45,66.111,54.401,29.775 +2020-05-27 11:00:00,116.34,62.177,53.678000000000004,29.775 +2020-05-27 11:15:00,116.28,63.548,53.678000000000004,29.775 +2020-05-27 11:30:00,120.85,65.04899999999999,53.678000000000004,29.775 +2020-05-27 11:45:00,119.2,66.324,53.678000000000004,29.775 +2020-05-27 12:00:00,113.31,61.788000000000004,51.68,29.775 +2020-05-27 12:15:00,114.74,62.218,51.68,29.775 +2020-05-27 12:30:00,110.65,61.11600000000001,51.68,29.775 +2020-05-27 12:45:00,108.47,62.567,51.68,29.775 +2020-05-27 13:00:00,109.49,63.18600000000001,51.263000000000005,29.775 +2020-05-27 13:15:00,113.95,63.903999999999996,51.263000000000005,29.775 +2020-05-27 13:30:00,118.69,62.041000000000004,51.263000000000005,29.775 +2020-05-27 13:45:00,116.61,60.272,51.263000000000005,29.775 +2020-05-27 14:00:00,106.92,61.72,51.107,29.775 +2020-05-27 14:15:00,103.29,60.273,51.107,29.775 +2020-05-27 14:30:00,103.58,59.173,51.107,29.775 +2020-05-27 14:45:00,101.55,59.833999999999996,51.107,29.775 +2020-05-27 15:00:00,101.02,60.805,51.498000000000005,29.775 +2020-05-27 15:15:00,106.26,58.272,51.498000000000005,29.775 +2020-05-27 15:30:00,108.1,56.458,51.498000000000005,29.775 +2020-05-27 15:45:00,106.97,53.967,51.498000000000005,29.775 +2020-05-27 16:00:00,104.58,56.803000000000004,53.376999999999995,29.775 +2020-05-27 16:15:00,112.71,56.263000000000005,53.376999999999995,29.775 +2020-05-27 16:30:00,115.07,55.753,53.376999999999995,29.775 +2020-05-27 16:45:00,117.58,52.091,53.376999999999995,29.775 +2020-05-27 17:00:00,113.92,54.519,56.965,29.775 +2020-05-27 17:15:00,109.55,55.019,56.965,29.775 +2020-05-27 17:30:00,110.79,54.761,56.965,29.775 +2020-05-27 17:45:00,117.21,53.397,56.965,29.775 +2020-05-27 18:00:00,119.07,56.273,58.231,29.775 +2020-05-27 18:15:00,116.37,57.083,58.231,29.775 +2020-05-27 18:30:00,110.78,54.858000000000004,58.231,29.775 +2020-05-27 18:45:00,111.25,60.065,58.231,29.775 +2020-05-27 19:00:00,112.51,60.646,58.865,29.775 +2020-05-27 19:15:00,110.24,59.83,58.865,29.775 +2020-05-27 19:30:00,106.93,59.354,58.865,29.775 +2020-05-27 19:45:00,104.44,59.942,58.865,29.775 +2020-05-27 20:00:00,106.22,58.015,65.605,29.775 +2020-05-27 20:15:00,109.79,57.128,65.605,29.775 +2020-05-27 20:30:00,105.72,56.702,65.605,29.775 +2020-05-27 20:45:00,106.65,56.923,65.605,29.775 +2020-05-27 21:00:00,103.35,54.456,58.083999999999996,29.775 +2020-05-27 21:15:00,102.42,55.998999999999995,58.083999999999996,29.775 +2020-05-27 21:30:00,96.73,56.688,58.083999999999996,29.775 +2020-05-27 21:45:00,90.01,57.614,58.083999999999996,29.775 +2020-05-27 22:00:00,84.55,55.013999999999996,53.243,29.775 +2020-05-27 22:15:00,92.14,55.89,53.243,29.775 +2020-05-27 22:30:00,90.68,48.641999999999996,53.243,29.775 +2020-05-27 22:45:00,91.0,45.221000000000004,53.243,29.775 +2020-05-27 23:00:00,84.6,39.466,44.283,29.775 +2020-05-27 23:15:00,85.5,37.732,44.283,29.775 +2020-05-27 23:30:00,85.54,36.907,44.283,29.775 +2020-05-27 23:45:00,79.34,36.42,44.283,29.775 +2020-05-28 00:00:00,77.91,35.239000000000004,40.219,29.775 +2020-05-28 00:15:00,80.31,35.55,40.219,29.775 +2020-05-28 00:30:00,82.35,34.621,40.219,29.775 +2020-05-28 00:45:00,82.17,33.571,40.219,29.775 +2020-05-28 01:00:00,73.42,33.269,37.959,29.775 +2020-05-28 01:15:00,73.91,32.423,37.959,29.775 +2020-05-28 01:30:00,80.25,30.72,37.959,29.775 +2020-05-28 01:45:00,80.75,29.695,37.959,29.775 +2020-05-28 02:00:00,78.89,29.686,36.113,29.775 +2020-05-28 02:15:00,76.48,28.261999999999997,36.113,29.775 +2020-05-28 02:30:00,73.17,30.505,36.113,29.775 +2020-05-28 02:45:00,71.76,31.307,36.113,29.775 +2020-05-28 03:00:00,82.48,34.086,35.546,29.775 +2020-05-28 03:15:00,81.95,32.857,35.546,29.775 +2020-05-28 03:30:00,83.28,32.132,35.546,29.775 +2020-05-28 03:45:00,79.02,31.68,35.546,29.775 +2020-05-28 04:00:00,76.6,41.464,37.169000000000004,29.775 +2020-05-28 04:15:00,77.69,51.595,37.169000000000004,29.775 +2020-05-28 04:30:00,81.45,49.409,37.169000000000004,29.775 +2020-05-28 04:45:00,85.59,50.132,37.169000000000004,29.775 +2020-05-28 05:00:00,91.9,73.321,41.233000000000004,29.775 +2020-05-28 05:15:00,94.79,91.84899999999999,41.233000000000004,29.775 +2020-05-28 05:30:00,97.76,81.291,41.233000000000004,29.775 +2020-05-28 05:45:00,101.42,75.626,41.233000000000004,29.775 +2020-05-28 06:00:00,106.7,77.21300000000001,52.57,29.775 +2020-05-28 06:15:00,107.83,79.056,52.57,29.775 +2020-05-28 06:30:00,107.22,76.17399999999999,52.57,29.775 +2020-05-28 06:45:00,108.72,76.32600000000001,52.57,29.775 +2020-05-28 07:00:00,112.97,75.663,64.53,29.775 +2020-05-28 07:15:00,113.42,75.118,64.53,29.775 +2020-05-28 07:30:00,111.88,72.117,64.53,29.775 +2020-05-28 07:45:00,110.47,70.532,64.53,29.775 +2020-05-28 08:00:00,108.25,64.778,55.911,29.775 +2020-05-28 08:15:00,109.2,66.418,55.911,29.775 +2020-05-28 08:30:00,108.89,65.07600000000001,55.911,29.775 +2020-05-28 08:45:00,108.36,66.82300000000001,55.911,29.775 +2020-05-28 09:00:00,108.84,63.333999999999996,50.949,29.775 +2020-05-28 09:15:00,109.6,61.867,50.949,29.775 +2020-05-28 09:30:00,109.68,64.991,50.949,29.775 +2020-05-28 09:45:00,109.89,66.501,50.949,29.775 +2020-05-28 10:00:00,109.06,63.222,48.136,29.775 +2020-05-28 10:15:00,109.02,64.71300000000001,48.136,29.775 +2020-05-28 10:30:00,108.21,64.633,48.136,29.775 +2020-05-28 10:45:00,110.76,65.82,48.136,29.775 +2020-05-28 11:00:00,110.74,61.88,46.643,29.775 +2020-05-28 11:15:00,114.54,63.265,46.643,29.775 +2020-05-28 11:30:00,116.21,64.755,46.643,29.775 +2020-05-28 11:45:00,112.65,66.04,46.643,29.775 +2020-05-28 12:00:00,108.35,61.538999999999994,44.098,29.775 +2020-05-28 12:15:00,107.22,61.972,44.098,29.775 +2020-05-28 12:30:00,101.85,60.839,44.098,29.775 +2020-05-28 12:45:00,104.76,62.294,44.098,29.775 +2020-05-28 13:00:00,104.61,62.92100000000001,43.717,29.775 +2020-05-28 13:15:00,106.42,63.643,43.717,29.775 +2020-05-28 13:30:00,104.67,61.792,43.717,29.775 +2020-05-28 13:45:00,106.86,60.025,43.717,29.775 +2020-05-28 14:00:00,109.05,61.505,44.218999999999994,29.775 +2020-05-28 14:15:00,109.96,60.051,44.218999999999994,29.775 +2020-05-28 14:30:00,111.04,58.915,44.218999999999994,29.775 +2020-05-28 14:45:00,111.45,59.58,44.218999999999994,29.775 +2020-05-28 15:00:00,114.19,60.596000000000004,46.159,29.775 +2020-05-28 15:15:00,116.88,58.05,46.159,29.775 +2020-05-28 15:30:00,117.36,56.215,46.159,29.775 +2020-05-28 15:45:00,118.28,53.708999999999996,46.159,29.775 +2020-05-28 16:00:00,120.09,56.588,47.115,29.775 +2020-05-28 16:15:00,117.02,56.037,47.115,29.775 +2020-05-28 16:30:00,117.12,55.543,47.115,29.775 +2020-05-28 16:45:00,115.03,51.835,47.115,29.775 +2020-05-28 17:00:00,115.21,54.302,50.827,29.775 +2020-05-28 17:15:00,114.26,54.784,50.827,29.775 +2020-05-28 17:30:00,113.32,54.513999999999996,50.827,29.775 +2020-05-28 17:45:00,113.31,53.118,50.827,29.775 +2020-05-28 18:00:00,114.29,56.00899999999999,52.586000000000006,29.775 +2020-05-28 18:15:00,111.81,56.798,52.586000000000006,29.775 +2020-05-28 18:30:00,113.58,54.565,52.586000000000006,29.775 +2020-05-28 18:45:00,111.62,59.769,52.586000000000006,29.775 +2020-05-28 19:00:00,107.16,60.352,51.886,29.775 +2020-05-28 19:15:00,104.15,59.531000000000006,51.886,29.775 +2020-05-28 19:30:00,103.5,59.049,51.886,29.775 +2020-05-28 19:45:00,102.04,59.635,51.886,29.775 +2020-05-28 20:00:00,100.18,57.68600000000001,56.162,29.775 +2020-05-28 20:15:00,101.47,56.798,56.162,29.775 +2020-05-28 20:30:00,103.36,56.391000000000005,56.162,29.775 +2020-05-28 20:45:00,99.21,56.652,56.162,29.775 +2020-05-28 21:00:00,100.34,54.191,53.023,29.775 +2020-05-28 21:15:00,99.7,55.746,53.023,29.775 +2020-05-28 21:30:00,97.4,56.413000000000004,53.023,29.775 +2020-05-28 21:45:00,98.85,57.358000000000004,53.023,29.775 +2020-05-28 22:00:00,89.45,54.783,49.303999999999995,29.775 +2020-05-28 22:15:00,90.82,55.674,49.303999999999995,29.775 +2020-05-28 22:30:00,91.85,48.423,49.303999999999995,29.775 +2020-05-28 22:45:00,92.06,44.99,49.303999999999995,29.775 +2020-05-28 23:00:00,82.98,39.205999999999996,43.409,29.775 +2020-05-28 23:15:00,78.65,37.515,43.409,29.775 +2020-05-28 23:30:00,84.66,36.694,43.409,29.775 +2020-05-28 23:45:00,83.7,36.199,43.409,29.775 +2020-05-29 00:00:00,82.83,33.088,39.884,29.775 +2020-05-29 00:15:00,79.0,33.650999999999996,39.884,29.775 +2020-05-29 00:30:00,76.64,32.953,39.884,29.775 +2020-05-29 00:45:00,75.71,32.335,39.884,29.775 +2020-05-29 01:00:00,75.93,31.62,37.658,29.775 +2020-05-29 01:15:00,75.57,30.374000000000002,37.658,29.775 +2020-05-29 01:30:00,74.04,29.29,37.658,29.775 +2020-05-29 01:45:00,79.8,28.044,37.658,29.775 +2020-05-29 02:00:00,80.93,28.939,36.707,29.775 +2020-05-29 02:15:00,81.07,27.433000000000003,36.707,29.775 +2020-05-29 02:30:00,74.85,30.576,36.707,29.775 +2020-05-29 02:45:00,77.58,30.746,36.707,29.775 +2020-05-29 03:00:00,83.0,34.054,37.025,29.775 +2020-05-29 03:15:00,79.28,31.87,37.025,29.775 +2020-05-29 03:30:00,78.44,30.916999999999998,37.025,29.775 +2020-05-29 03:45:00,80.42,31.393,37.025,29.775 +2020-05-29 04:00:00,86.4,41.3,38.349000000000004,29.775 +2020-05-29 04:15:00,88.81,49.849,38.349000000000004,29.775 +2020-05-29 04:30:00,85.87,48.602,38.349000000000004,29.775 +2020-05-29 04:45:00,86.7,48.461000000000006,38.349000000000004,29.775 +2020-05-29 05:00:00,92.38,70.797,41.565,29.775 +2020-05-29 05:15:00,96.58,90.395,41.565,29.775 +2020-05-29 05:30:00,99.6,80.309,41.565,29.775 +2020-05-29 05:45:00,103.24,74.263,41.565,29.775 +2020-05-29 06:00:00,109.02,76.183,53.861000000000004,29.775 +2020-05-29 06:15:00,107.63,77.858,53.861000000000004,29.775 +2020-05-29 06:30:00,108.54,74.766,53.861000000000004,29.775 +2020-05-29 06:45:00,109.06,75.15100000000001,53.861000000000004,29.775 +2020-05-29 07:00:00,110.54,74.923,63.497,29.775 +2020-05-29 07:15:00,108.76,75.47,63.497,29.775 +2020-05-29 07:30:00,108.91,70.567,63.497,29.775 +2020-05-29 07:45:00,107.71,68.62,63.497,29.775 +2020-05-29 08:00:00,107.34,63.365,55.43899999999999,29.775 +2020-05-29 08:15:00,109.81,65.593,55.43899999999999,29.775 +2020-05-29 08:30:00,113.11,64.368,55.43899999999999,29.775 +2020-05-29 08:45:00,111.74,65.642,55.43899999999999,29.775 +2020-05-29 09:00:00,109.3,60.086999999999996,52.132,29.775 +2020-05-29 09:15:00,107.72,60.547,52.132,29.775 +2020-05-29 09:30:00,107.65,62.937,52.132,29.775 +2020-05-29 09:45:00,106.88,64.81,52.132,29.775 +2020-05-29 10:00:00,108.37,61.106,49.881,29.775 +2020-05-29 10:15:00,108.93,62.585,49.881,29.775 +2020-05-29 10:30:00,106.62,63.006,49.881,29.775 +2020-05-29 10:45:00,106.59,64.012,49.881,29.775 +2020-05-29 11:00:00,105.28,60.29,49.396,29.775 +2020-05-29 11:15:00,105.09,60.472,49.396,29.775 +2020-05-29 11:30:00,105.21,62.038999999999994,49.396,29.775 +2020-05-29 11:45:00,107.75,62.458999999999996,49.396,29.775 +2020-05-29 12:00:00,107.33,58.667,46.7,29.775 +2020-05-29 12:15:00,107.68,57.998999999999995,46.7,29.775 +2020-05-29 12:30:00,104.99,56.981,46.7,29.775 +2020-05-29 12:45:00,108.45,57.851000000000006,46.7,29.775 +2020-05-29 13:00:00,103.8,59.25,44.05,29.775 +2020-05-29 13:15:00,104.04,60.341,44.05,29.775 +2020-05-29 13:30:00,98.47,59.233000000000004,44.05,29.775 +2020-05-29 13:45:00,98.7,57.733999999999995,44.05,29.775 +2020-05-29 14:00:00,101.02,58.231,42.805,29.775 +2020-05-29 14:15:00,107.75,57.125,42.805,29.775 +2020-05-29 14:30:00,112.27,57.461000000000006,42.805,29.775 +2020-05-29 14:45:00,112.96,57.571000000000005,42.805,29.775 +2020-05-29 15:00:00,112.92,58.443000000000005,44.36600000000001,29.775 +2020-05-29 15:15:00,113.34,55.558,44.36600000000001,29.775 +2020-05-29 15:30:00,113.29,52.812,44.36600000000001,29.775 +2020-05-29 15:45:00,113.64,51.027,44.36600000000001,29.775 +2020-05-29 16:00:00,113.52,52.907,46.928999999999995,29.775 +2020-05-29 16:15:00,114.14,52.87,46.928999999999995,29.775 +2020-05-29 16:30:00,113.45,52.247,46.928999999999995,29.775 +2020-05-29 16:45:00,114.36,47.736000000000004,46.928999999999995,29.775 +2020-05-29 17:00:00,114.25,51.898,51.468,29.775 +2020-05-29 17:15:00,112.86,52.104,51.468,29.775 +2020-05-29 17:30:00,111.56,51.902,51.468,29.775 +2020-05-29 17:45:00,111.54,50.268,51.468,29.775 +2020-05-29 18:00:00,112.16,53.412,52.58,29.775 +2020-05-29 18:15:00,107.98,53.203,52.58,29.775 +2020-05-29 18:30:00,107.39,50.948,52.58,29.775 +2020-05-29 18:45:00,106.01,56.54600000000001,52.58,29.775 +2020-05-29 19:00:00,102.16,58.183,52.183,29.775 +2020-05-29 19:15:00,99.93,58.201,52.183,29.775 +2020-05-29 19:30:00,97.8,57.68600000000001,52.183,29.775 +2020-05-29 19:45:00,97.65,57.207,52.183,29.775 +2020-05-29 20:00:00,95.98,55.088,58.497,29.775 +2020-05-29 20:15:00,94.76,54.951,58.497,29.775 +2020-05-29 20:30:00,96.05,54.095,58.497,29.775 +2020-05-29 20:45:00,94.03,53.744,58.497,29.775 +2020-05-29 21:00:00,89.57,52.641999999999996,54.731,29.775 +2020-05-29 21:15:00,90.47,55.89,54.731,29.775 +2020-05-29 21:30:00,93.37,56.398999999999994,54.731,29.775 +2020-05-29 21:45:00,92.72,57.673,54.731,29.775 +2020-05-29 22:00:00,88.91,55.195,51.386,29.775 +2020-05-29 22:15:00,88.07,55.836999999999996,51.386,29.775 +2020-05-29 22:30:00,87.69,54.43600000000001,51.386,29.775 +2020-05-29 22:45:00,85.16,52.544,51.386,29.775 +2020-05-29 23:00:00,77.14,48.243,44.463,29.775 +2020-05-29 23:15:00,77.25,44.698,44.463,29.775 +2020-05-29 23:30:00,80.76,41.843999999999994,44.463,29.775 +2020-05-29 23:45:00,79.05,41.086000000000006,44.463,29.775 +2020-05-30 00:00:00,76.42,33.446,42.833999999999996,29.662 +2020-05-30 00:15:00,70.78,32.48,42.833999999999996,29.662 +2020-05-30 00:30:00,68.05,31.618000000000002,42.833999999999996,29.662 +2020-05-30 00:45:00,68.33,30.462,42.833999999999996,29.662 +2020-05-30 01:00:00,68.06,30.156,37.859,29.662 +2020-05-30 01:15:00,67.11,29.238000000000003,37.859,29.662 +2020-05-30 01:30:00,65.83,27.27,37.859,29.662 +2020-05-30 01:45:00,66.87,27.154,37.859,29.662 +2020-05-30 02:00:00,69.9,27.255,35.327,29.662 +2020-05-30 02:15:00,73.43,24.915,35.327,29.662 +2020-05-30 02:30:00,73.36,27.076999999999998,35.327,29.662 +2020-05-30 02:45:00,69.26,28.019000000000002,35.327,29.662 +2020-05-30 03:00:00,65.65,30.165,34.908,29.662 +2020-05-30 03:15:00,65.73,27.055,34.908,29.662 +2020-05-30 03:30:00,66.22,26.081999999999997,34.908,29.662 +2020-05-30 03:45:00,64.96,28.008000000000003,34.908,29.662 +2020-05-30 04:00:00,63.16,35.196999999999996,34.84,29.662 +2020-05-30 04:15:00,63.06,42.316,34.84,29.662 +2020-05-30 04:30:00,60.91,39.02,34.84,29.662 +2020-05-30 04:45:00,65.06,39.03,34.84,29.662 +2020-05-30 05:00:00,64.16,50.458,34.222,29.662 +2020-05-30 05:15:00,65.68,55.727,34.222,29.662 +2020-05-30 05:30:00,66.81,47.24,34.222,29.662 +2020-05-30 05:45:00,68.98,46.597,34.222,29.662 +2020-05-30 06:00:00,71.04,62.797,35.515,29.662 +2020-05-30 06:15:00,72.65,75.111,35.515,29.662 +2020-05-30 06:30:00,72.85,68.265,35.515,29.662 +2020-05-30 06:45:00,75.19,63.847,35.515,29.662 +2020-05-30 07:00:00,78.59,61.262,39.687,29.662 +2020-05-30 07:15:00,80.62,60.335,39.687,29.662 +2020-05-30 07:30:00,84.57,57.398999999999994,39.687,29.662 +2020-05-30 07:45:00,86.6,57.178000000000004,39.687,29.662 +2020-05-30 08:00:00,86.67,53.312,44.9,29.662 +2020-05-30 08:15:00,86.66,56.153999999999996,44.9,29.662 +2020-05-30 08:30:00,87.74,55.246,44.9,29.662 +2020-05-30 08:45:00,88.34,58.092,44.9,29.662 +2020-05-30 09:00:00,88.31,55.77,45.724,29.662 +2020-05-30 09:15:00,89.73,56.852,45.724,29.662 +2020-05-30 09:30:00,90.73,59.906000000000006,45.724,29.662 +2020-05-30 09:45:00,90.92,61.423,45.724,29.662 +2020-05-30 10:00:00,90.8,58.273999999999994,43.123999999999995,29.662 +2020-05-30 10:15:00,91.47,60.143,43.123999999999995,29.662 +2020-05-30 10:30:00,90.99,60.29600000000001,43.123999999999995,29.662 +2020-05-30 10:45:00,93.34,61.288000000000004,43.123999999999995,29.662 +2020-05-30 11:00:00,92.41,57.467,40.255,29.662 +2020-05-30 11:15:00,90.73,58.301,40.255,29.662 +2020-05-30 11:30:00,88.51,59.858999999999995,40.255,29.662 +2020-05-30 11:45:00,87.44,60.681000000000004,40.255,29.662 +2020-05-30 12:00:00,84.12,56.977,38.582,29.662 +2020-05-30 12:15:00,84.24,57.239,38.582,29.662 +2020-05-30 12:30:00,83.18,56.147,38.582,29.662 +2020-05-30 12:45:00,81.0,57.498999999999995,38.582,29.662 +2020-05-30 13:00:00,79.85,58.083999999999996,36.043,29.662 +2020-05-30 13:15:00,84.69,58.198,36.043,29.662 +2020-05-30 13:30:00,83.22,57.165,36.043,29.662 +2020-05-30 13:45:00,79.43,54.551,36.043,29.662 +2020-05-30 14:00:00,77.05,55.413999999999994,35.216,29.662 +2020-05-30 14:15:00,77.16,53.035,35.216,29.662 +2020-05-30 14:30:00,76.32,52.515,35.216,29.662 +2020-05-30 14:45:00,76.2,53.118,35.216,29.662 +2020-05-30 15:00:00,77.38,54.601000000000006,36.759,29.662 +2020-05-30 15:15:00,76.91,52.538000000000004,36.759,29.662 +2020-05-30 15:30:00,75.07,50.324,36.759,29.662 +2020-05-30 15:45:00,72.67,47.688,36.759,29.662 +2020-05-30 16:00:00,73.75,51.426,40.086,29.662 +2020-05-30 16:15:00,77.63,50.755,40.086,29.662 +2020-05-30 16:30:00,77.61,50.346000000000004,40.086,29.662 +2020-05-30 16:45:00,79.19,45.966,40.086,29.662 +2020-05-30 17:00:00,80.06,48.941,44.876999999999995,29.662 +2020-05-30 17:15:00,80.99,47.513999999999996,44.876999999999995,29.662 +2020-05-30 17:30:00,81.71,47.153999999999996,44.876999999999995,29.662 +2020-05-30 17:45:00,82.2,45.856,44.876999999999995,29.662 +2020-05-30 18:00:00,84.63,50.298,47.056000000000004,29.662 +2020-05-30 18:15:00,85.41,52.016999999999996,47.056000000000004,29.662 +2020-05-30 18:30:00,82.68,51.31,47.056000000000004,29.662 +2020-05-30 18:45:00,82.12,52.935,47.056000000000004,29.662 +2020-05-30 19:00:00,81.55,53.339,45.57,29.662 +2020-05-30 19:15:00,78.78,52.294,45.57,29.662 +2020-05-30 19:30:00,77.84,52.655,45.57,29.662 +2020-05-30 19:45:00,77.76,53.809,45.57,29.662 +2020-05-30 20:00:00,73.8,52.851000000000006,41.685,29.662 +2020-05-30 20:15:00,77.24,52.566,41.685,29.662 +2020-05-30 20:30:00,78.75,50.82899999999999,41.685,29.662 +2020-05-30 20:45:00,79.95,52.173,41.685,29.662 +2020-05-30 21:00:00,76.15,50.175,39.576,29.662 +2020-05-30 21:15:00,75.58,53.202,39.576,29.662 +2020-05-30 21:30:00,73.83,54.123000000000005,39.576,29.662 +2020-05-30 21:45:00,73.25,54.816,39.576,29.662 +2020-05-30 22:00:00,69.37,52.62,39.068000000000005,29.662 +2020-05-30 22:15:00,70.64,54.028999999999996,39.068000000000005,29.662 +2020-05-30 22:30:00,66.88,53.56100000000001,39.068000000000005,29.662 +2020-05-30 22:45:00,65.37,52.398999999999994,39.068000000000005,29.662 +2020-05-30 23:00:00,61.82,47.92100000000001,32.06,29.662 +2020-05-30 23:15:00,61.6,44.477,32.06,29.662 +2020-05-30 23:30:00,60.72,43.583999999999996,32.06,29.662 +2020-05-30 23:45:00,59.52,42.652,32.06,29.662 +2020-05-31 00:00:00,58.29,34.622,28.825,29.662 +2020-05-31 00:15:00,58.97,32.507,28.825,29.662 +2020-05-31 00:30:00,58.75,31.43,28.825,29.662 +2020-05-31 00:45:00,58.69,30.32,28.825,29.662 +2020-05-31 01:00:00,56.94,30.25,25.995,29.662 +2020-05-31 01:15:00,57.91,29.410999999999998,25.995,29.662 +2020-05-31 01:30:00,54.43,27.41,25.995,29.662 +2020-05-31 01:45:00,57.82,26.866,25.995,29.662 +2020-05-31 02:00:00,57.05,26.883000000000003,24.394000000000002,29.662 +2020-05-31 02:15:00,57.16,25.024,24.394000000000002,29.662 +2020-05-31 02:30:00,57.04,27.545,24.394000000000002,29.662 +2020-05-31 02:45:00,57.15,28.318,24.394000000000002,29.662 +2020-05-31 03:00:00,56.31,31.146,22.916999999999998,29.662 +2020-05-31 03:15:00,56.76,28.165,22.916999999999998,29.662 +2020-05-31 03:30:00,57.63,26.795,22.916999999999998,29.662 +2020-05-31 03:45:00,57.02,27.980999999999998,22.916999999999998,29.662 +2020-05-31 04:00:00,54.07,35.034,23.576999999999998,29.662 +2020-05-31 04:15:00,53.44,41.483000000000004,23.576999999999998,29.662 +2020-05-31 04:30:00,52.41,39.448,23.576999999999998,29.662 +2020-05-31 04:45:00,52.3,39.071999999999996,23.576999999999998,29.662 +2020-05-31 05:00:00,51.54,49.851000000000006,22.730999999999998,29.662 +2020-05-31 05:15:00,50.75,53.773999999999994,22.730999999999998,29.662 +2020-05-31 05:30:00,50.89,44.93899999999999,22.730999999999998,29.662 +2020-05-31 05:45:00,52.44,44.14,22.730999999999998,29.662 +2020-05-31 06:00:00,54.87,58.076,22.34,29.662 +2020-05-31 06:15:00,54.95,70.82600000000001,22.34,29.662 +2020-05-31 06:30:00,56.67,63.121,22.34,29.662 +2020-05-31 06:45:00,59.11,57.573,22.34,29.662 +2020-05-31 07:00:00,60.32,55.717,24.691999999999997,29.662 +2020-05-31 07:15:00,62.84,53.083,24.691999999999997,29.662 +2020-05-31 07:30:00,62.65,51.083999999999996,24.691999999999997,29.662 +2020-05-31 07:45:00,62.23,50.706,24.691999999999997,29.662 +2020-05-31 08:00:00,63.06,47.919,29.340999999999998,29.662 +2020-05-31 08:15:00,62.23,51.89,29.340999999999998,29.662 +2020-05-31 08:30:00,64.21,52.13399999999999,29.340999999999998,29.662 +2020-05-31 08:45:00,65.4,55.338,29.340999999999998,29.662 +2020-05-31 09:00:00,62.96,52.817,30.788,29.662 +2020-05-31 09:15:00,61.99,53.562,30.788,29.662 +2020-05-31 09:30:00,59.44,56.989,30.788,29.662 +2020-05-31 09:45:00,62.19,59.445,30.788,29.662 +2020-05-31 10:00:00,62.46,57.333999999999996,30.158,29.662 +2020-05-31 10:15:00,62.53,59.471000000000004,30.158,29.662 +2020-05-31 10:30:00,63.05,59.968999999999994,30.158,29.662 +2020-05-31 10:45:00,65.84,61.492,30.158,29.662 +2020-05-31 11:00:00,61.04,57.544,32.056,29.662 +2020-05-31 11:15:00,61.5,58.008,32.056,29.662 +2020-05-31 11:30:00,63.18,59.845,32.056,29.662 +2020-05-31 11:45:00,61.81,61.044,32.056,29.662 +2020-05-31 12:00:00,59.18,58.198,28.671999999999997,29.662 +2020-05-31 12:15:00,59.97,58.324,28.671999999999997,29.662 +2020-05-31 12:30:00,55.08,57.066,28.671999999999997,29.662 +2020-05-31 12:45:00,55.72,57.618,28.671999999999997,29.662 +2020-05-31 13:00:00,57.1,57.784,23.171,29.662 +2020-05-31 13:15:00,57.89,58.04600000000001,23.171,29.662 +2020-05-31 13:30:00,60.84,55.958,23.171,29.662 +2020-05-31 13:45:00,58.45,54.222,23.171,29.662 +2020-05-31 14:00:00,55.33,56.27,19.11,29.662 +2020-05-31 14:15:00,57.48,54.55,19.11,29.662 +2020-05-31 14:30:00,55.43,53.14,19.11,29.662 +2020-05-31 14:45:00,56.24,52.672,19.11,29.662 +2020-05-31 15:00:00,56.35,53.988,19.689,29.662 +2020-05-31 15:15:00,59.22,51.339,19.689,29.662 +2020-05-31 15:30:00,57.36,49.071999999999996,19.689,29.662 +2020-05-31 15:45:00,59.35,46.838,19.689,29.662 +2020-05-31 16:00:00,63.4,49.468,22.875,29.662 +2020-05-31 16:15:00,64.4,48.803999999999995,22.875,29.662 +2020-05-31 16:30:00,66.23,49.398999999999994,22.875,29.662 +2020-05-31 16:45:00,67.91,45.062,22.875,29.662 +2020-05-31 17:00:00,72.6,48.413999999999994,33.884,29.662 +2020-05-31 17:15:00,71.61,48.3,33.884,29.662 +2020-05-31 17:30:00,75.13,48.739,33.884,29.662 +2020-05-31 17:45:00,76.52,48.29,33.884,29.662 +2020-05-31 18:00:00,79.29,53.143,38.453,29.662 +2020-05-31 18:15:00,79.24,54.731,38.453,29.662 +2020-05-31 18:30:00,79.41,53.353,38.453,29.662 +2020-05-31 18:45:00,83.37,55.465,38.453,29.662 +2020-05-31 19:00:00,80.11,57.818000000000005,39.221,29.662 +2020-05-31 19:15:00,85.23,55.82899999999999,39.221,29.662 +2020-05-31 19:30:00,84.73,55.917,39.221,29.662 +2020-05-31 19:45:00,86.55,56.916000000000004,39.221,29.662 +2020-05-31 20:00:00,86.5,56.137,37.871,29.662 +2020-05-31 20:15:00,86.56,55.92,37.871,29.662 +2020-05-31 20:30:00,87.79,55.214,37.871,29.662 +2020-05-31 20:45:00,86.92,54.773999999999994,37.871,29.662 +2020-05-31 21:00:00,81.05,52.123999999999995,36.465,29.662 +2020-05-31 21:15:00,84.64,54.754,36.465,29.662 +2020-05-31 21:30:00,78.67,55.098,36.465,29.662 +2020-05-31 21:45:00,78.8,56.174,36.465,29.662 +2020-05-31 22:00:00,77.15,55.681999999999995,36.092,29.662 +2020-05-31 22:15:00,82.27,55.36600000000001,36.092,29.662 +2020-05-31 22:30:00,81.25,53.788000000000004,36.092,29.662 +2020-05-31 22:45:00,78.71,51.242,36.092,29.662 +2020-05-31 23:00:00,54.1,45.903999999999996,31.013,29.662 +2020-05-31 23:15:00,55.38,44.067,31.013,29.662 +2020-05-31 23:30:00,54.63,42.828,31.013,29.662 +2020-05-31 23:45:00,53.74,42.213,31.013,29.662 +2020-06-01 00:00:00,52.35,28.079,19.295,29.17 +2020-06-01 00:15:00,52.65,26.704,19.295,29.17 +2020-06-01 00:30:00,52.03,25.386999999999997,19.295,29.17 +2020-06-01 00:45:00,52.09,24.485,19.295,29.17 +2020-06-01 01:00:00,50.91,24.236,15.365,29.17 +2020-06-01 01:15:00,52.12,23.596,15.365,29.17 +2020-06-01 01:30:00,50.95,21.804000000000002,15.365,29.17 +2020-06-01 01:45:00,51.01,21.910999999999998,15.365,29.17 +2020-06-01 02:00:00,50.17,21.433000000000003,13.03,29.17 +2020-06-01 02:15:00,50.81,19.726,13.03,29.17 +2020-06-01 02:30:00,50.97,22.149,13.03,29.17 +2020-06-01 02:45:00,51.9,22.715999999999998,13.03,29.17 +2020-06-01 03:00:00,51.98,24.44,13.46,29.17 +2020-06-01 03:15:00,52.07,21.471,13.46,29.17 +2020-06-01 03:30:00,52.7,20.089000000000002,13.46,29.17 +2020-06-01 03:45:00,49.25,20.781,13.46,29.17 +2020-06-01 04:00:00,51.66,26.259,13.305,29.17 +2020-06-01 04:15:00,51.17,32.0,13.305,29.17 +2020-06-01 04:30:00,48.48,29.869,13.305,29.17 +2020-06-01 04:45:00,50.55,29.331999999999997,13.305,29.17 +2020-06-01 05:00:00,48.78,38.022,13.482000000000001,29.17 +2020-06-01 05:15:00,47.36,39.586,13.482000000000001,29.17 +2020-06-01 05:30:00,50.13,31.343000000000004,13.482000000000001,29.17 +2020-06-01 05:45:00,50.0,31.281,13.482000000000001,29.17 +2020-06-01 06:00:00,51.06,43.007,14.677999999999999,29.17 +2020-06-01 06:15:00,50.92,53.478,14.677999999999999,29.17 +2020-06-01 06:30:00,51.78,46.945,14.677999999999999,29.17 +2020-06-01 06:45:00,52.65,42.336999999999996,14.677999999999999,29.17 +2020-06-01 07:00:00,54.68,42.636,18.473,29.17 +2020-06-01 07:15:00,53.98,40.101,18.473,29.17 +2020-06-01 07:30:00,53.67,38.333,18.473,29.17 +2020-06-01 07:45:00,54.26,38.079,18.473,29.17 +2020-06-01 08:00:00,54.22,38.016,18.142,29.17 +2020-06-01 08:15:00,53.58,41.693999999999996,18.142,29.17 +2020-06-01 08:30:00,52.87,42.685,18.142,29.17 +2020-06-01 08:45:00,53.28,45.778999999999996,18.142,29.17 +2020-06-01 09:00:00,50.94,42.258,19.148,29.17 +2020-06-01 09:15:00,51.9,43.126999999999995,19.148,29.17 +2020-06-01 09:30:00,47.84,46.728,19.148,29.17 +2020-06-01 09:45:00,51.24,49.751999999999995,19.148,29.17 +2020-06-01 10:00:00,53.22,45.975,17.139,29.17 +2020-06-01 10:15:00,55.99,47.87,17.139,29.17 +2020-06-01 10:30:00,57.89,48.418,17.139,29.17 +2020-06-01 10:45:00,57.81,50.29600000000001,17.139,29.17 +2020-06-01 11:00:00,53.25,45.78,18.037,29.17 +2020-06-01 11:15:00,51.93,46.23,18.037,29.17 +2020-06-01 11:30:00,49.85,48.203,18.037,29.17 +2020-06-01 11:45:00,49.1,49.861000000000004,18.037,29.17 +2020-06-01 12:00:00,46.61,47.415,16.559,29.17 +2020-06-01 12:15:00,46.04,47.076,16.559,29.17 +2020-06-01 12:30:00,44.13,46.448,16.559,29.17 +2020-06-01 12:45:00,44.13,47.167,16.559,29.17 +2020-06-01 13:00:00,43.0,47.331,13.697000000000001,29.17 +2020-06-01 13:15:00,42.54,47.643,13.697000000000001,29.17 +2020-06-01 13:30:00,42.78,45.68899999999999,13.697000000000001,29.17 +2020-06-01 13:45:00,44.17,44.308,13.697000000000001,29.17 +2020-06-01 14:00:00,44.11,46.63399999999999,12.578,29.17 +2020-06-01 14:15:00,43.14,44.994,12.578,29.17 +2020-06-01 14:30:00,44.66,43.99,12.578,29.17 +2020-06-01 14:45:00,44.56,43.341,12.578,29.17 +2020-06-01 15:00:00,47.23,44.93899999999999,14.425999999999998,29.17 +2020-06-01 15:15:00,46.69,42.405,14.425999999999998,29.17 +2020-06-01 15:30:00,50.96,40.277,14.425999999999998,29.17 +2020-06-01 15:45:00,50.68,38.414,14.425999999999998,29.17 +2020-06-01 16:00:00,52.39,40.319,18.287,29.17 +2020-06-01 16:15:00,55.05,39.81,18.287,29.17 +2020-06-01 16:30:00,57.45,39.958,18.287,29.17 +2020-06-01 16:45:00,61.38,36.124,18.287,29.17 +2020-06-01 17:00:00,65.43,39.303000000000004,24.461,29.17 +2020-06-01 17:15:00,66.16,38.798,24.461,29.17 +2020-06-01 17:30:00,68.01,38.955999999999996,24.461,29.17 +2020-06-01 17:45:00,69.86,38.388000000000005,24.461,29.17 +2020-06-01 18:00:00,71.38,42.556000000000004,31.44,29.17 +2020-06-01 18:15:00,71.03,43.601000000000006,31.44,29.17 +2020-06-01 18:30:00,73.56,42.254,31.44,29.17 +2020-06-01 18:45:00,79.8,43.708999999999996,31.44,29.17 +2020-06-01 19:00:00,80.73,46.256,34.859,29.17 +2020-06-01 19:15:00,73.32,44.074,34.859,29.17 +2020-06-01 19:30:00,73.38,43.883,34.859,29.17 +2020-06-01 19:45:00,70.97,44.55,34.859,29.17 +2020-06-01 20:00:00,73.37,44.213,42.937,29.17 +2020-06-01 20:15:00,81.13,44.318999999999996,42.937,29.17 +2020-06-01 20:30:00,83.24,43.968,42.937,29.17 +2020-06-01 20:45:00,81.93,43.483999999999995,42.937,29.17 +2020-06-01 21:00:00,74.91,41.38399999999999,39.795,29.17 +2020-06-01 21:15:00,74.14,44.074,39.795,29.17 +2020-06-01 21:30:00,74.04,44.778999999999996,39.795,29.17 +2020-06-01 21:45:00,70.96,45.748999999999995,39.795,29.17 +2020-06-01 22:00:00,72.4,44.92100000000001,41.108000000000004,29.17 +2020-06-01 22:15:00,76.24,45.008,41.108000000000004,29.17 +2020-06-01 22:30:00,72.97,43.971000000000004,41.108000000000004,29.17 +2020-06-01 22:45:00,68.25,41.247,41.108000000000004,29.17 +2020-06-01 23:00:00,75.51,37.607,33.82,29.17 +2020-06-01 23:15:00,76.08,35.554,33.82,29.17 +2020-06-01 23:30:00,75.77,34.259,33.82,29.17 +2020-06-01 23:45:00,75.39,33.885,33.82,29.17 +2020-06-02 00:00:00,72.23,27.486,44.625,29.28 +2020-06-02 00:15:00,72.85,28.073,44.625,29.28 +2020-06-02 00:30:00,72.81,27.004,44.625,29.28 +2020-06-02 00:45:00,72.9,26.359,44.625,29.28 +2020-06-02 01:00:00,72.66,25.963,41.733000000000004,29.28 +2020-06-02 01:15:00,73.01,25.336,41.733000000000004,29.28 +2020-06-02 01:30:00,71.48,23.773000000000003,41.733000000000004,29.28 +2020-06-02 01:45:00,71.71,23.325,41.733000000000004,29.28 +2020-06-02 02:00:00,71.14,22.816,39.872,29.28 +2020-06-02 02:15:00,72.89,21.364,39.872,29.28 +2020-06-02 02:30:00,72.11,23.55,39.872,29.28 +2020-06-02 02:45:00,72.88,24.247,39.872,29.28 +2020-06-02 03:00:00,72.62,25.929000000000002,38.711,29.28 +2020-06-02 03:15:00,73.57,24.605999999999998,38.711,29.28 +2020-06-02 03:30:00,74.97,23.865,38.711,29.28 +2020-06-02 03:45:00,74.58,23.045,38.711,29.28 +2020-06-02 04:00:00,76.34,30.565,39.823,29.28 +2020-06-02 04:15:00,80.5,39.384,39.823,29.28 +2020-06-02 04:30:00,80.66,36.830999999999996,39.823,29.28 +2020-06-02 04:45:00,85.79,37.228,39.823,29.28 +2020-06-02 05:00:00,99.7,55.825,43.228,29.28 +2020-06-02 05:15:00,105.56,69.34,43.228,29.28 +2020-06-02 05:30:00,108.46,59.775,43.228,29.28 +2020-06-02 05:45:00,108.08,55.667,43.228,29.28 +2020-06-02 06:00:00,115.07,57.045,54.316,29.28 +2020-06-02 06:15:00,119.43,57.893,54.316,29.28 +2020-06-02 06:30:00,122.79,55.489,54.316,29.28 +2020-06-02 06:45:00,121.32,55.802,54.316,29.28 +2020-06-02 07:00:00,119.92,56.843,65.758,29.28 +2020-06-02 07:15:00,127.93,56.339,65.758,29.28 +2020-06-02 07:30:00,129.28,53.638999999999996,65.758,29.28 +2020-06-02 07:45:00,129.07,52.556999999999995,65.758,29.28 +2020-06-02 08:00:00,127.22,49.989,57.983000000000004,29.28 +2020-06-02 08:15:00,131.77,51.858999999999995,57.983000000000004,29.28 +2020-06-02 08:30:00,134.36,51.706,57.983000000000004,29.28 +2020-06-02 08:45:00,133.94,53.798,57.983000000000004,29.28 +2020-06-02 09:00:00,128.31,49.465,52.653,29.28 +2020-06-02 09:15:00,126.41,48.339,52.653,29.28 +2020-06-02 09:30:00,129.84,51.693999999999996,52.653,29.28 +2020-06-02 09:45:00,130.52,53.861000000000004,52.653,29.28 +2020-06-02 10:00:00,129.02,49.056999999999995,51.408,29.28 +2020-06-02 10:15:00,128.01,50.575,51.408,29.28 +2020-06-02 10:30:00,124.54,50.652,51.408,29.28 +2020-06-02 10:45:00,130.5,52.098,51.408,29.28 +2020-06-02 11:00:00,130.58,47.683,51.913000000000004,29.28 +2020-06-02 11:15:00,129.85,48.934,51.913000000000004,29.28 +2020-06-02 11:30:00,124.12,50.481,51.913000000000004,29.28 +2020-06-02 11:45:00,123.31,52.216,51.913000000000004,29.28 +2020-06-02 12:00:00,125.14,48.06100000000001,49.508,29.28 +2020-06-02 12:15:00,122.36,48.11600000000001,49.508,29.28 +2020-06-02 12:30:00,118.37,47.354,49.508,29.28 +2020-06-02 12:45:00,115.76,48.853,49.508,29.28 +2020-06-02 13:00:00,120.83,49.547,50.007,29.28 +2020-06-02 13:15:00,121.9,50.588,50.007,29.28 +2020-06-02 13:30:00,116.48,48.832,50.007,29.28 +2020-06-02 13:45:00,110.09,47.396,50.007,29.28 +2020-06-02 14:00:00,109.79,49.284,49.778999999999996,29.28 +2020-06-02 14:15:00,111.56,47.966,49.778999999999996,29.28 +2020-06-02 14:30:00,112.09,47.093999999999994,49.778999999999996,29.28 +2020-06-02 14:45:00,114.29,47.645,49.778999999999996,29.28 +2020-06-02 15:00:00,110.48,48.917,51.559,29.28 +2020-06-02 15:15:00,103.02,46.593999999999994,51.559,29.28 +2020-06-02 15:30:00,100.93,44.923,51.559,29.28 +2020-06-02 15:45:00,105.33,42.842,51.559,29.28 +2020-06-02 16:00:00,105.0,45.167,53.531000000000006,29.28 +2020-06-02 16:15:00,105.3,44.769,53.531000000000006,29.28 +2020-06-02 16:30:00,106.76,43.912,53.531000000000006,29.28 +2020-06-02 16:45:00,111.6,40.665,53.531000000000006,29.28 +2020-06-02 17:00:00,113.3,43.038999999999994,59.497,29.28 +2020-06-02 17:15:00,109.25,43.193000000000005,59.497,29.28 +2020-06-02 17:30:00,106.26,42.56,59.497,29.28 +2020-06-02 17:45:00,108.2,41.12,59.497,29.28 +2020-06-02 18:00:00,110.8,43.38,59.861999999999995,29.28 +2020-06-02 18:15:00,114.43,43.85,59.861999999999995,29.28 +2020-06-02 18:30:00,111.78,41.553999999999995,59.861999999999995,29.28 +2020-06-02 18:45:00,107.85,45.961000000000006,59.861999999999995,29.28 +2020-06-02 19:00:00,102.8,46.961999999999996,60.989,29.28 +2020-06-02 19:15:00,105.39,46.086000000000006,60.989,29.28 +2020-06-02 19:30:00,98.65,45.317,60.989,29.28 +2020-06-02 19:45:00,98.03,45.648,60.989,29.28 +2020-06-02 20:00:00,95.94,44.276,68.35600000000001,29.28 +2020-06-02 20:15:00,101.74,44.063,68.35600000000001,29.28 +2020-06-02 20:30:00,101.49,44.187,68.35600000000001,29.28 +2020-06-02 20:45:00,102.03,44.443000000000005,68.35600000000001,29.28 +2020-06-02 21:00:00,92.62,42.57,59.251000000000005,29.28 +2020-06-02 21:15:00,92.31,44.255,59.251000000000005,29.28 +2020-06-02 21:30:00,87.58,45.371,59.251000000000005,29.28 +2020-06-02 21:45:00,86.58,46.31399999999999,59.251000000000005,29.28 +2020-06-02 22:00:00,81.88,43.613,54.736999999999995,29.28 +2020-06-02 22:15:00,80.55,45.158,54.736999999999995,29.28 +2020-06-02 22:30:00,78.5,39.465,54.736999999999995,29.28 +2020-06-02 22:45:00,77.43,36.318000000000005,54.736999999999995,29.28 +2020-06-02 23:00:00,74.39,32.014,46.806999999999995,29.28 +2020-06-02 23:15:00,74.81,30.081,46.806999999999995,29.28 +2020-06-02 23:30:00,74.8,28.956999999999997,46.806999999999995,29.28 +2020-06-02 23:45:00,73.41,28.416999999999998,46.806999999999995,29.28 +2020-06-03 00:00:00,70.04,27.325,43.824,29.28 +2020-06-03 00:15:00,71.56,27.914,43.824,29.28 +2020-06-03 00:30:00,68.67,26.846,43.824,29.28 +2020-06-03 00:45:00,70.95,26.205,43.824,29.28 +2020-06-03 01:00:00,71.49,25.829,39.86,29.28 +2020-06-03 01:15:00,71.44,25.180999999999997,39.86,29.28 +2020-06-03 01:30:00,70.05,23.608,39.86,29.28 +2020-06-03 01:45:00,70.5,23.154,39.86,29.28 +2020-06-03 02:00:00,70.07,22.645,37.931999999999995,29.28 +2020-06-03 02:15:00,71.93,21.186999999999998,37.931999999999995,29.28 +2020-06-03 02:30:00,70.28,23.375,37.931999999999995,29.28 +2020-06-03 02:45:00,70.45,24.079,37.931999999999995,29.28 +2020-06-03 03:00:00,71.64,25.763,37.579,29.28 +2020-06-03 03:15:00,72.7,24.436999999999998,37.579,29.28 +2020-06-03 03:30:00,72.09,23.701,37.579,29.28 +2020-06-03 03:45:00,72.31,22.904,37.579,29.28 +2020-06-03 04:00:00,76.85,30.365,37.931999999999995,29.28 +2020-06-03 04:15:00,80.4,39.134,37.931999999999995,29.28 +2020-06-03 04:30:00,88.31,36.568000000000005,37.931999999999995,29.28 +2020-06-03 04:45:00,93.4,36.96,37.931999999999995,29.28 +2020-06-03 05:00:00,94.29,55.43600000000001,40.942,29.28 +2020-06-03 05:15:00,98.54,68.80199999999999,40.942,29.28 +2020-06-03 05:30:00,101.92,59.305,40.942,29.28 +2020-06-03 05:45:00,106.14,55.255,40.942,29.28 +2020-06-03 06:00:00,113.26,56.653,56.516999999999996,29.28 +2020-06-03 06:15:00,112.2,57.481,56.516999999999996,29.28 +2020-06-03 06:30:00,106.99,55.099,56.516999999999996,29.28 +2020-06-03 06:45:00,110.19,55.44,56.516999999999996,29.28 +2020-06-03 07:00:00,113.12,56.468999999999994,71.707,29.28 +2020-06-03 07:15:00,116.27,55.978,71.707,29.28 +2020-06-03 07:30:00,115.88,53.263000000000005,71.707,29.28 +2020-06-03 07:45:00,113.15,52.215,71.707,29.28 +2020-06-03 08:00:00,113.28,49.655,61.17,29.28 +2020-06-03 08:15:00,115.37,51.568000000000005,61.17,29.28 +2020-06-03 08:30:00,116.39,51.406000000000006,61.17,29.28 +2020-06-03 08:45:00,112.02,53.507,61.17,29.28 +2020-06-03 09:00:00,110.21,49.167,57.282,29.28 +2020-06-03 09:15:00,113.3,48.047,57.282,29.28 +2020-06-03 09:30:00,110.77,51.406000000000006,57.282,29.28 +2020-06-03 09:45:00,112.52,53.597,57.282,29.28 +2020-06-03 10:00:00,114.37,48.805,54.026,29.28 +2020-06-03 10:15:00,114.82,50.343,54.026,29.28 +2020-06-03 10:30:00,116.42,50.426,54.026,29.28 +2020-06-03 10:45:00,116.13,51.88,54.026,29.28 +2020-06-03 11:00:00,109.87,47.458,54.277,29.28 +2020-06-03 11:15:00,104.76,48.72,54.277,29.28 +2020-06-03 11:30:00,108.82,50.256,54.277,29.28 +2020-06-03 11:45:00,108.87,51.997,54.277,29.28 +2020-06-03 12:00:00,106.62,47.876000000000005,52.552,29.28 +2020-06-03 12:15:00,102.67,47.937,52.552,29.28 +2020-06-03 12:30:00,98.68,47.147,52.552,29.28 +2020-06-03 12:45:00,105.85,48.65,52.552,29.28 +2020-06-03 13:00:00,107.79,49.343999999999994,52.111999999999995,29.28 +2020-06-03 13:15:00,108.61,50.388000000000005,52.111999999999995,29.28 +2020-06-03 13:30:00,102.26,48.643,52.111999999999995,29.28 +2020-06-03 13:45:00,104.72,47.208,52.111999999999995,29.28 +2020-06-03 14:00:00,111.64,49.123000000000005,52.066,29.28 +2020-06-03 14:15:00,108.26,47.799,52.066,29.28 +2020-06-03 14:30:00,108.8,46.897,52.066,29.28 +2020-06-03 14:45:00,103.39,47.453,52.066,29.28 +2020-06-03 15:00:00,103.63,48.77,52.523999999999994,29.28 +2020-06-03 15:15:00,105.44,46.435,52.523999999999994,29.28 +2020-06-03 15:30:00,104.72,44.748999999999995,52.523999999999994,29.28 +2020-06-03 15:45:00,100.41,42.653999999999996,52.523999999999994,29.28 +2020-06-03 16:00:00,100.42,45.018,54.101000000000006,29.28 +2020-06-03 16:15:00,110.51,44.611000000000004,54.101000000000006,29.28 +2020-06-03 16:30:00,111.07,43.773999999999994,54.101000000000006,29.28 +2020-06-03 16:45:00,109.26,40.488,54.101000000000006,29.28 +2020-06-03 17:00:00,104.98,42.896,58.155,29.28 +2020-06-03 17:15:00,111.12,43.037,58.155,29.28 +2020-06-03 17:30:00,114.79,42.395,58.155,29.28 +2020-06-03 17:45:00,115.69,40.927,58.155,29.28 +2020-06-03 18:00:00,108.75,43.202,60.205,29.28 +2020-06-03 18:15:00,112.12,43.644,60.205,29.28 +2020-06-03 18:30:00,114.87,41.341,60.205,29.28 +2020-06-03 18:45:00,113.03,45.747,60.205,29.28 +2020-06-03 19:00:00,104.74,46.748999999999995,61.568999999999996,29.28 +2020-06-03 19:15:00,99.58,45.865,61.568999999999996,29.28 +2020-06-03 19:30:00,104.58,45.088,61.568999999999996,29.28 +2020-06-03 19:45:00,104.49,45.413999999999994,61.568999999999996,29.28 +2020-06-03 20:00:00,102.46,44.022,68.145,29.28 +2020-06-03 20:15:00,95.88,43.805,68.145,29.28 +2020-06-03 20:30:00,95.33,43.943999999999996,68.145,29.28 +2020-06-03 20:45:00,95.16,44.238,68.145,29.28 +2020-06-03 21:00:00,92.82,42.37,59.696000000000005,29.28 +2020-06-03 21:15:00,92.05,44.066,59.696000000000005,29.28 +2020-06-03 21:30:00,89.64,45.158,59.696000000000005,29.28 +2020-06-03 21:45:00,88.21,46.118,59.696000000000005,29.28 +2020-06-03 22:00:00,83.59,43.44,54.861999999999995,29.28 +2020-06-03 22:15:00,82.97,44.998000000000005,54.861999999999995,29.28 +2020-06-03 22:30:00,81.26,39.305,54.861999999999995,29.28 +2020-06-03 22:45:00,80.33,36.147,54.861999999999995,29.28 +2020-06-03 23:00:00,75.58,31.816999999999997,45.568000000000005,29.28 +2020-06-03 23:15:00,75.33,29.924,45.568000000000005,29.28 +2020-06-03 23:30:00,74.44,28.808000000000003,45.568000000000005,29.28 +2020-06-03 23:45:00,73.46,28.256999999999998,45.568000000000005,29.28 +2020-06-04 00:00:00,73.36,27.168000000000003,40.181,29.28 +2020-06-04 00:15:00,73.36,27.758000000000003,40.181,29.28 +2020-06-04 00:30:00,72.26,26.691999999999997,40.181,29.28 +2020-06-04 00:45:00,74.73,26.054000000000002,40.181,29.28 +2020-06-04 01:00:00,71.88,25.698,38.296,29.28 +2020-06-04 01:15:00,72.5,25.03,38.296,29.28 +2020-06-04 01:30:00,71.31,23.448,38.296,29.28 +2020-06-04 01:45:00,71.5,22.986,38.296,29.28 +2020-06-04 02:00:00,71.83,22.479,36.575,29.28 +2020-06-04 02:15:00,71.45,21.015,36.575,29.28 +2020-06-04 02:30:00,71.1,23.204,36.575,29.28 +2020-06-04 02:45:00,71.42,23.915,36.575,29.28 +2020-06-04 03:00:00,73.16,25.601,36.394,29.28 +2020-06-04 03:15:00,72.99,24.271,36.394,29.28 +2020-06-04 03:30:00,71.02,23.54,36.394,29.28 +2020-06-04 03:45:00,73.01,22.767,36.394,29.28 +2020-06-04 04:00:00,77.6,30.171,37.207,29.28 +2020-06-04 04:15:00,77.75,38.889,37.207,29.28 +2020-06-04 04:30:00,88.08,36.309,37.207,29.28 +2020-06-04 04:45:00,93.23,36.698,37.207,29.28 +2020-06-04 05:00:00,94.94,55.053999999999995,40.713,29.28 +2020-06-04 05:15:00,96.67,68.271,40.713,29.28 +2020-06-04 05:30:00,104.59,58.843999999999994,40.713,29.28 +2020-06-04 05:45:00,107.94,54.852,40.713,29.28 +2020-06-04 06:00:00,112.46,56.268,50.952,29.28 +2020-06-04 06:15:00,109.5,57.075,50.952,29.28 +2020-06-04 06:30:00,108.67,54.715,50.952,29.28 +2020-06-04 06:45:00,109.33,55.085,50.952,29.28 +2020-06-04 07:00:00,110.44,56.102,64.88,29.28 +2020-06-04 07:15:00,109.83,55.625,64.88,29.28 +2020-06-04 07:30:00,113.96,52.897,64.88,29.28 +2020-06-04 07:45:00,116.37,51.881,64.88,29.28 +2020-06-04 08:00:00,114.22,49.33,55.133,29.28 +2020-06-04 08:15:00,107.51,51.283,55.133,29.28 +2020-06-04 08:30:00,112.36,51.113,55.133,29.28 +2020-06-04 08:45:00,109.8,53.223,55.133,29.28 +2020-06-04 09:00:00,114.08,48.876000000000005,48.912,29.28 +2020-06-04 09:15:00,119.39,47.761,48.912,29.28 +2020-06-04 09:30:00,116.25,51.125,48.912,29.28 +2020-06-04 09:45:00,110.92,53.34,48.912,29.28 +2020-06-04 10:00:00,107.25,48.559,45.968999999999994,29.28 +2020-06-04 10:15:00,107.81,50.117,45.968999999999994,29.28 +2020-06-04 10:30:00,113.72,50.20399999999999,45.968999999999994,29.28 +2020-06-04 10:45:00,116.8,51.668,45.968999999999994,29.28 +2020-06-04 11:00:00,117.17,47.239,44.067,29.28 +2020-06-04 11:15:00,109.05,48.511,44.067,29.28 +2020-06-04 11:30:00,110.66,50.037,44.067,29.28 +2020-06-04 11:45:00,105.85,51.783,44.067,29.28 +2020-06-04 12:00:00,111.23,47.696999999999996,41.501000000000005,29.28 +2020-06-04 12:15:00,113.61,47.762,41.501000000000005,29.28 +2020-06-04 12:30:00,110.92,46.945,41.501000000000005,29.28 +2020-06-04 12:45:00,102.91,48.453,41.501000000000005,29.28 +2020-06-04 13:00:00,102.85,49.145,41.117,29.28 +2020-06-04 13:15:00,107.65,50.192,41.117,29.28 +2020-06-04 13:30:00,107.87,48.458,41.117,29.28 +2020-06-04 13:45:00,112.9,47.026,41.117,29.28 +2020-06-04 14:00:00,99.93,48.964,41.492,29.28 +2020-06-04 14:15:00,105.4,47.635,41.492,29.28 +2020-06-04 14:30:00,103.85,46.70399999999999,41.492,29.28 +2020-06-04 14:45:00,104.92,47.266000000000005,41.492,29.28 +2020-06-04 15:00:00,109.26,48.626000000000005,43.711999999999996,29.28 +2020-06-04 15:15:00,110.94,46.278,43.711999999999996,29.28 +2020-06-04 15:30:00,109.79,44.57899999999999,43.711999999999996,29.28 +2020-06-04 15:45:00,112.69,42.47,43.711999999999996,29.28 +2020-06-04 16:00:00,114.07,44.871,45.446000000000005,29.28 +2020-06-04 16:15:00,109.19,44.458,45.446000000000005,29.28 +2020-06-04 16:30:00,114.59,43.638999999999996,45.446000000000005,29.28 +2020-06-04 16:45:00,116.07,40.316,45.446000000000005,29.28 +2020-06-04 17:00:00,115.34,42.75899999999999,48.803000000000004,29.28 +2020-06-04 17:15:00,110.62,42.886,48.803000000000004,29.28 +2020-06-04 17:30:00,108.75,42.233999999999995,48.803000000000004,29.28 +2020-06-04 17:45:00,116.09,40.74,48.803000000000004,29.28 +2020-06-04 18:00:00,113.07,43.028,51.167,29.28 +2020-06-04 18:15:00,111.11,43.445,51.167,29.28 +2020-06-04 18:30:00,108.92,41.133,51.167,29.28 +2020-06-04 18:45:00,115.29,45.537,51.167,29.28 +2020-06-04 19:00:00,113.15,46.541000000000004,52.486000000000004,29.28 +2020-06-04 19:15:00,104.97,45.648999999999994,52.486000000000004,29.28 +2020-06-04 19:30:00,105.27,44.864,52.486000000000004,29.28 +2020-06-04 19:45:00,105.77,45.185,52.486000000000004,29.28 +2020-06-04 20:00:00,104.54,43.773999999999994,59.635,29.28 +2020-06-04 20:15:00,99.31,43.553999999999995,59.635,29.28 +2020-06-04 20:30:00,96.07,43.706,59.635,29.28 +2020-06-04 20:45:00,96.72,44.038000000000004,59.635,29.28 +2020-06-04 21:00:00,93.51,42.174,54.353,29.28 +2020-06-04 21:15:00,91.29,43.881,54.353,29.28 +2020-06-04 21:30:00,89.16,44.949,54.353,29.28 +2020-06-04 21:45:00,87.45,45.925,54.353,29.28 +2020-06-04 22:00:00,83.88,43.271,49.431999999999995,29.28 +2020-06-04 22:15:00,83.67,44.842,49.431999999999995,29.28 +2020-06-04 22:30:00,80.79,39.148,49.431999999999995,29.28 +2020-06-04 22:45:00,82.95,35.979,49.431999999999995,29.28 +2020-06-04 23:00:00,77.09,31.623,42.872,29.28 +2020-06-04 23:15:00,76.4,29.77,42.872,29.28 +2020-06-04 23:30:00,76.03,28.66,42.872,29.28 +2020-06-04 23:45:00,75.13,28.1,42.872,29.28 +2020-06-05 00:00:00,72.68,25.186999999999998,39.819,29.28 +2020-06-05 00:15:00,74.39,26.006,39.819,29.28 +2020-06-05 00:30:00,73.39,25.189,39.819,29.28 +2020-06-05 00:45:00,72.85,24.973000000000003,39.819,29.28 +2020-06-05 01:00:00,73.16,24.238000000000003,37.797,29.28 +2020-06-05 01:15:00,73.41,23.063000000000002,37.797,29.28 +2020-06-05 01:30:00,71.51,22.136999999999997,37.797,29.28 +2020-06-05 01:45:00,72.07,21.439,37.797,29.28 +2020-06-05 02:00:00,71.82,21.836,36.905,29.28 +2020-06-05 02:15:00,72.76,20.305,36.905,29.28 +2020-06-05 02:30:00,72.71,23.339000000000002,36.905,29.28 +2020-06-05 02:45:00,73.05,23.405,36.905,29.28 +2020-06-05 03:00:00,73.18,25.739,37.1,29.28 +2020-06-05 03:15:00,74.33,23.340999999999998,37.1,29.28 +2020-06-05 03:30:00,73.58,22.383000000000003,37.1,29.28 +2020-06-05 03:45:00,74.46,22.509,37.1,29.28 +2020-06-05 04:00:00,78.21,30.009,37.882,29.28 +2020-06-05 04:15:00,84.39,37.158,37.882,29.28 +2020-06-05 04:30:00,85.9,35.499,37.882,29.28 +2020-06-05 04:45:00,92.12,35.150999999999996,37.882,29.28 +2020-06-05 05:00:00,94.15,52.801,40.777,29.28 +2020-06-05 05:15:00,96.53,66.937,40.777,29.28 +2020-06-05 05:30:00,98.78,57.873000000000005,40.777,29.28 +2020-06-05 05:45:00,105.4,53.503,40.777,29.28 +2020-06-05 06:00:00,110.45,55.2,55.528,29.28 +2020-06-05 06:15:00,115.64,55.998999999999995,55.528,29.28 +2020-06-05 06:30:00,113.37,53.525,55.528,29.28 +2020-06-05 06:45:00,113.78,53.961000000000006,55.528,29.28 +2020-06-05 07:00:00,117.75,55.501999999999995,67.749,29.28 +2020-06-05 07:15:00,116.94,56.013000000000005,67.749,29.28 +2020-06-05 07:30:00,113.93,51.391999999999996,67.749,29.28 +2020-06-05 07:45:00,110.35,50.114,67.749,29.28 +2020-06-05 08:00:00,109.2,48.199,57.55,29.28 +2020-06-05 08:15:00,110.52,50.788000000000004,57.55,29.28 +2020-06-05 08:30:00,122.31,50.622,57.55,29.28 +2020-06-05 08:45:00,120.12,52.435,57.55,29.28 +2020-06-05 09:00:00,121.23,45.938,52.588,29.28 +2020-06-05 09:15:00,117.3,46.71,52.588,29.28 +2020-06-05 09:30:00,116.81,49.378,52.588,29.28 +2020-06-05 09:45:00,121.98,51.972,52.588,29.28 +2020-06-05 10:00:00,119.87,46.911,49.772,29.28 +2020-06-05 10:15:00,116.15,48.356,49.772,29.28 +2020-06-05 10:30:00,115.85,48.961999999999996,49.772,29.28 +2020-06-05 10:45:00,117.93,50.29600000000001,49.772,29.28 +2020-06-05 11:00:00,114.69,46.1,49.226000000000006,29.28 +2020-06-05 11:15:00,113.17,46.253,49.226000000000006,29.28 +2020-06-05 11:30:00,105.0,47.621,49.226000000000006,29.28 +2020-06-05 11:45:00,103.88,48.471000000000004,49.226000000000006,29.28 +2020-06-05 12:00:00,100.79,44.951,45.705,29.28 +2020-06-05 12:15:00,102.92,44.177,45.705,29.28 +2020-06-05 12:30:00,99.51,43.463,45.705,29.28 +2020-06-05 12:45:00,100.37,44.29,45.705,29.28 +2020-06-05 13:00:00,99.89,45.646,43.133,29.28 +2020-06-05 13:15:00,99.79,46.958,43.133,29.28 +2020-06-05 13:30:00,100.88,45.986999999999995,43.133,29.28 +2020-06-05 13:45:00,99.87,44.84,43.133,29.28 +2020-06-05 14:00:00,99.86,45.912,41.989,29.28 +2020-06-05 14:15:00,97.26,44.972,41.989,29.28 +2020-06-05 14:30:00,96.34,45.495,41.989,29.28 +2020-06-05 14:45:00,94.99,45.443999999999996,41.989,29.28 +2020-06-05 15:00:00,95.46,46.718,43.728,29.28 +2020-06-05 15:15:00,98.1,44.083999999999996,43.728,29.28 +2020-06-05 15:30:00,95.79,41.667,43.728,29.28 +2020-06-05 15:45:00,95.92,40.283,43.728,29.28 +2020-06-05 16:00:00,97.42,41.795,45.93899999999999,29.28 +2020-06-05 16:15:00,100.06,41.875,45.93899999999999,29.28 +2020-06-05 16:30:00,99.13,40.913000000000004,45.93899999999999,29.28 +2020-06-05 16:45:00,101.84,36.795,45.93899999999999,29.28 +2020-06-05 17:00:00,103.81,40.939,50.488,29.28 +2020-06-05 17:15:00,103.47,40.846,50.488,29.28 +2020-06-05 17:30:00,103.83,40.308,50.488,29.28 +2020-06-05 17:45:00,104.89,38.606,50.488,29.28 +2020-06-05 18:00:00,103.73,41.031000000000006,52.408,29.28 +2020-06-05 18:15:00,103.19,40.478,52.408,29.28 +2020-06-05 18:30:00,102.75,38.1,52.408,29.28 +2020-06-05 18:45:00,103.29,42.903999999999996,52.408,29.28 +2020-06-05 19:00:00,98.83,44.846000000000004,52.736000000000004,29.28 +2020-06-05 19:15:00,95.61,44.645,52.736000000000004,29.28 +2020-06-05 19:30:00,93.94,43.871,52.736000000000004,29.28 +2020-06-05 19:45:00,93.79,43.167,52.736000000000004,29.28 +2020-06-05 20:00:00,92.15,41.589,59.68,29.28 +2020-06-05 20:15:00,92.31,42.13,59.68,29.28 +2020-06-05 20:30:00,91.82,41.824,59.68,29.28 +2020-06-05 20:45:00,92.41,41.468999999999994,59.68,29.28 +2020-06-05 21:00:00,89.29,40.918,54.343999999999994,29.28 +2020-06-05 21:15:00,87.29,44.281000000000006,54.343999999999994,29.28 +2020-06-05 21:30:00,84.11,45.185,54.343999999999994,29.28 +2020-06-05 21:45:00,83.05,46.424,54.343999999999994,29.28 +2020-06-05 22:00:00,79.44,43.754,49.672,29.28 +2020-06-05 22:15:00,78.6,45.085,49.672,29.28 +2020-06-05 22:30:00,77.06,44.566,49.672,29.28 +2020-06-05 22:45:00,75.8,42.548,49.672,29.28 +2020-06-05 23:00:00,72.07,39.743,42.065,29.28 +2020-06-05 23:15:00,72.18,36.24,42.065,29.28 +2020-06-05 23:30:00,71.31,33.249,42.065,29.28 +2020-06-05 23:45:00,70.36,32.497,42.065,29.28 +2020-06-06 00:00:00,67.51,25.941999999999997,38.829,29.17 +2020-06-06 00:15:00,67.86,25.7,38.829,29.17 +2020-06-06 00:30:00,67.41,24.565,38.829,29.17 +2020-06-06 00:45:00,67.72,23.725,38.829,29.17 +2020-06-06 01:00:00,66.82,23.335,34.63,29.17 +2020-06-06 01:15:00,67.25,22.605999999999998,34.63,29.17 +2020-06-06 01:30:00,65.85,20.85,34.63,29.17 +2020-06-06 01:45:00,66.09,21.315,34.63,29.17 +2020-06-06 02:00:00,64.68,20.839000000000002,32.465,29.17 +2020-06-06 02:15:00,64.57,18.511,32.465,29.17 +2020-06-06 02:30:00,64.68,20.68,32.465,29.17 +2020-06-06 02:45:00,64.25,21.514,32.465,29.17 +2020-06-06 03:00:00,64.14,22.599,31.925,29.17 +2020-06-06 03:15:00,65.0,19.414,31.925,29.17 +2020-06-06 03:30:00,65.09,18.62,31.925,29.17 +2020-06-06 03:45:00,67.6,20.195,31.925,29.17 +2020-06-06 04:00:00,70.16,25.434,31.309,29.17 +2020-06-06 04:15:00,70.62,31.435,31.309,29.17 +2020-06-06 04:30:00,63.11,27.958000000000002,31.309,29.17 +2020-06-06 04:45:00,65.22,27.819000000000003,31.309,29.17 +2020-06-06 05:00:00,67.91,36.066,30.323,29.17 +2020-06-06 05:15:00,67.72,37.842,30.323,29.17 +2020-06-06 05:30:00,69.31,30.338,30.323,29.17 +2020-06-06 05:45:00,71.04,30.802,30.323,29.17 +2020-06-06 06:00:00,75.65,44.887,31.438000000000002,29.17 +2020-06-06 06:15:00,80.98,54.621,31.438000000000002,29.17 +2020-06-06 06:30:00,84.8,48.958999999999996,31.438000000000002,29.17 +2020-06-06 06:45:00,83.47,45.533,31.438000000000002,29.17 +2020-06-06 07:00:00,78.85,45.275,34.891999999999996,29.17 +2020-06-06 07:15:00,78.38,44.446999999999996,34.891999999999996,29.17 +2020-06-06 07:30:00,79.02,41.507,34.891999999999996,29.17 +2020-06-06 07:45:00,80.34,41.519,34.891999999999996,29.17 +2020-06-06 08:00:00,81.61,40.611999999999995,39.608000000000004,29.17 +2020-06-06 08:15:00,82.54,43.39,39.608000000000004,29.17 +2020-06-06 08:30:00,81.42,43.358000000000004,39.608000000000004,29.17 +2020-06-06 08:45:00,81.09,46.385,39.608000000000004,29.17 +2020-06-06 09:00:00,79.87,42.973,40.894,29.17 +2020-06-06 09:15:00,87.56,44.285,40.894,29.17 +2020-06-06 09:30:00,87.68,47.515,40.894,29.17 +2020-06-06 09:45:00,83.66,49.722,40.894,29.17 +2020-06-06 10:00:00,80.88,45.253,39.525,29.17 +2020-06-06 10:15:00,87.29,47.07,39.525,29.17 +2020-06-06 10:30:00,83.73,47.373999999999995,39.525,29.17 +2020-06-06 10:45:00,83.72,48.521,39.525,29.17 +2020-06-06 11:00:00,84.12,44.211000000000006,36.718,29.17 +2020-06-06 11:15:00,90.83,45.122,36.718,29.17 +2020-06-06 11:30:00,94.67,46.63,36.718,29.17 +2020-06-06 11:45:00,94.56,48.016999999999996,36.718,29.17 +2020-06-06 12:00:00,91.99,44.79600000000001,35.688,29.17 +2020-06-06 12:15:00,90.46,44.895,35.688,29.17 +2020-06-06 12:30:00,88.8,44.06399999999999,35.688,29.17 +2020-06-06 12:45:00,87.54,45.498999999999995,35.688,29.17 +2020-06-06 13:00:00,85.74,46.00899999999999,32.858000000000004,29.17 +2020-06-06 13:15:00,87.07,46.6,32.858000000000004,29.17 +2020-06-06 13:30:00,87.33,45.77,32.858000000000004,29.17 +2020-06-06 13:45:00,86.83,43.407,32.858000000000004,29.17 +2020-06-06 14:00:00,86.15,44.693999999999996,31.738000000000003,29.17 +2020-06-06 14:15:00,84.56,42.516000000000005,31.738000000000003,29.17 +2020-06-06 14:30:00,81.7,42.419,31.738000000000003,29.17 +2020-06-06 14:45:00,80.31,42.846000000000004,31.738000000000003,29.17 +2020-06-06 15:00:00,80.41,44.662,34.35,29.17 +2020-06-06 15:15:00,79.49,42.766000000000005,34.35,29.17 +2020-06-06 15:30:00,79.47,40.687,34.35,29.17 +2020-06-06 15:45:00,79.76,38.425,34.35,29.17 +2020-06-06 16:00:00,80.07,41.907,37.522,29.17 +2020-06-06 16:15:00,79.03,41.225,37.522,29.17 +2020-06-06 16:30:00,80.08,40.488,37.522,29.17 +2020-06-06 16:45:00,81.23,36.4,37.522,29.17 +2020-06-06 17:00:00,82.36,39.404,42.498000000000005,29.17 +2020-06-06 17:15:00,81.67,37.425,42.498000000000005,29.17 +2020-06-06 17:30:00,81.82,36.743,42.498000000000005,29.17 +2020-06-06 17:45:00,84.04,35.443000000000005,42.498000000000005,29.17 +2020-06-06 18:00:00,84.14,39.175,44.701,29.17 +2020-06-06 18:15:00,83.83,40.357,44.701,29.17 +2020-06-06 18:30:00,84.61,39.39,44.701,29.17 +2020-06-06 18:45:00,84.06,40.567,44.701,29.17 +2020-06-06 19:00:00,81.29,41.091,45.727,29.17 +2020-06-06 19:15:00,78.62,39.875,45.727,29.17 +2020-06-06 19:30:00,76.78,39.896,45.727,29.17 +2020-06-06 19:45:00,75.72,40.85,45.727,29.17 +2020-06-06 20:00:00,74.89,40.242,43.391000000000005,29.17 +2020-06-06 20:15:00,74.17,40.381,43.391000000000005,29.17 +2020-06-06 20:30:00,74.7,39.22,43.391000000000005,29.17 +2020-06-06 20:45:00,74.33,40.624,43.391000000000005,29.17 +2020-06-06 21:00:00,71.59,38.898,41.231,29.17 +2020-06-06 21:15:00,69.93,41.982,41.231,29.17 +2020-06-06 21:30:00,68.91,43.161,41.231,29.17 +2020-06-06 21:45:00,68.03,43.858999999999995,41.231,29.17 +2020-06-06 22:00:00,64.54,41.316,40.798,29.17 +2020-06-06 22:15:00,64.33,43.133,40.798,29.17 +2020-06-06 22:30:00,62.24,42.849,40.798,29.17 +2020-06-06 22:45:00,62.62,41.352,40.798,29.17 +2020-06-06 23:00:00,58.66,38.109,34.402,29.17 +2020-06-06 23:15:00,58.11,34.889,34.402,29.17 +2020-06-06 23:30:00,56.97,34.075,34.402,29.17 +2020-06-06 23:45:00,56.18,33.421,34.402,29.17 +2020-06-07 00:00:00,54.78,27.147,30.171,29.17 +2020-06-07 00:15:00,55.23,25.78,30.171,29.17 +2020-06-07 00:30:00,54.07,24.471999999999998,30.171,29.17 +2020-06-07 00:45:00,54.14,23.596999999999998,30.171,29.17 +2020-06-07 01:00:00,52.67,23.464000000000002,27.15,29.17 +2020-06-07 01:15:00,52.85,22.699,27.15,29.17 +2020-06-07 01:30:00,52.59,20.854,27.15,29.17 +2020-06-07 01:45:00,52.21,20.92,27.15,29.17 +2020-06-07 02:00:00,50.98,20.448,25.403000000000002,29.17 +2020-06-07 02:15:00,51.38,18.707,25.403000000000002,29.17 +2020-06-07 02:30:00,50.88,21.136,25.403000000000002,29.17 +2020-06-07 02:45:00,51.13,21.745,25.403000000000002,29.17 +2020-06-07 03:00:00,50.84,23.48,23.386999999999997,29.17 +2020-06-07 03:15:00,51.71,20.49,23.386999999999997,29.17 +2020-06-07 03:30:00,52.75,19.136,23.386999999999997,29.17 +2020-06-07 03:45:00,50.92,19.973,23.386999999999997,29.17 +2020-06-07 04:00:00,50.55,25.101,23.941999999999997,29.17 +2020-06-07 04:15:00,51.55,30.546,23.941999999999997,29.17 +2020-06-07 04:30:00,51.6,28.333000000000002,23.941999999999997,29.17 +2020-06-07 04:45:00,52.09,27.774,23.941999999999997,29.17 +2020-06-07 05:00:00,53.72,35.75,23.026,29.17 +2020-06-07 05:15:00,54.12,36.433,23.026,29.17 +2020-06-07 05:30:00,55.59,28.6,23.026,29.17 +2020-06-07 05:45:00,57.85,28.881,23.026,29.17 +2020-06-07 06:00:00,59.26,40.721,23.223000000000003,29.17 +2020-06-07 06:15:00,60.63,51.071999999999996,23.223000000000003,29.17 +2020-06-07 06:30:00,62.83,44.669,23.223000000000003,29.17 +2020-06-07 06:45:00,64.38,40.23,23.223000000000003,29.17 +2020-06-07 07:00:00,65.6,40.458,24.968000000000004,29.17 +2020-06-07 07:15:00,66.44,38.01,24.968000000000004,29.17 +2020-06-07 07:30:00,68.26,36.156,24.968000000000004,29.17 +2020-06-07 07:45:00,68.77,36.098,24.968000000000004,29.17 +2020-06-07 08:00:00,69.95,36.086,29.131,29.17 +2020-06-07 08:15:00,69.53,40.012,29.131,29.17 +2020-06-07 08:30:00,69.54,40.953,29.131,29.17 +2020-06-07 08:45:00,69.78,44.098,29.131,29.17 +2020-06-07 09:00:00,70.2,40.535,29.904,29.17 +2020-06-07 09:15:00,72.11,41.438,29.904,29.17 +2020-06-07 09:30:00,74.06,45.06399999999999,29.904,29.17 +2020-06-07 09:45:00,74.87,48.231,29.904,29.17 +2020-06-07 10:00:00,77.19,44.516000000000005,28.943,29.17 +2020-06-07 10:15:00,78.39,46.53,28.943,29.17 +2020-06-07 10:30:00,77.62,47.106,28.943,29.17 +2020-06-07 10:45:00,75.28,49.04,28.943,29.17 +2020-06-07 11:00:00,71.31,44.483999999999995,31.682,29.17 +2020-06-07 11:15:00,70.31,44.994,31.682,29.17 +2020-06-07 11:30:00,70.0,46.903,31.682,29.17 +2020-06-07 11:45:00,67.85,48.593,31.682,29.17 +2020-06-07 12:00:00,64.35,46.353,27.315,29.17 +2020-06-07 12:15:00,61.2,46.038000000000004,27.315,29.17 +2020-06-07 12:30:00,57.59,45.248999999999995,27.315,29.17 +2020-06-07 12:45:00,59.18,45.997,27.315,29.17 +2020-06-07 13:00:00,59.01,46.153,23.894000000000002,29.17 +2020-06-07 13:15:00,55.2,46.481,23.894000000000002,29.17 +2020-06-07 13:30:00,57.81,44.589,23.894000000000002,29.17 +2020-06-07 13:45:00,60.26,43.223,23.894000000000002,29.17 +2020-06-07 14:00:00,57.88,45.69,21.148000000000003,29.17 +2020-06-07 14:15:00,64.49,44.026,21.148000000000003,29.17 +2020-06-07 14:30:00,66.8,42.846000000000004,21.148000000000003,29.17 +2020-06-07 14:45:00,67.65,42.228,21.148000000000003,29.17 +2020-06-07 15:00:00,67.87,44.083,21.229,29.17 +2020-06-07 15:15:00,65.72,41.477,21.229,29.17 +2020-06-07 15:30:00,59.62,39.268,21.229,29.17 +2020-06-07 15:45:00,60.76,37.325,21.229,29.17 +2020-06-07 16:00:00,62.45,39.453,25.037,29.17 +2020-06-07 16:15:00,63.37,38.900999999999996,25.037,29.17 +2020-06-07 16:30:00,66.59,39.158,25.037,29.17 +2020-06-07 16:45:00,70.11,35.108000000000004,25.037,29.17 +2020-06-07 17:00:00,72.96,38.486,37.11,29.17 +2020-06-07 17:15:00,76.34,37.903,37.11,29.17 +2020-06-07 17:30:00,79.24,38.003,37.11,29.17 +2020-06-07 17:45:00,81.88,37.28,37.11,29.17 +2020-06-07 18:00:00,83.72,41.528999999999996,42.215,29.17 +2020-06-07 18:15:00,79.56,42.415,42.215,29.17 +2020-06-07 18:30:00,76.0,41.022,42.215,29.17 +2020-06-07 18:45:00,78.65,42.47,42.215,29.17 +2020-06-07 19:00:00,79.38,45.026,44.383,29.17 +2020-06-07 19:15:00,79.12,42.795,44.383,29.17 +2020-06-07 19:30:00,79.25,42.556999999999995,44.383,29.17 +2020-06-07 19:45:00,78.61,43.195,44.383,29.17 +2020-06-07 20:00:00,79.46,42.74,43.426,29.17 +2020-06-07 20:15:00,79.03,42.831,43.426,29.17 +2020-06-07 20:30:00,79.41,42.56100000000001,43.426,29.17 +2020-06-07 20:45:00,81.82,42.298,43.426,29.17 +2020-06-07 21:00:00,81.38,40.224000000000004,42.265,29.17 +2020-06-07 21:15:00,80.43,42.977,42.265,29.17 +2020-06-07 21:30:00,78.77,43.54,42.265,29.17 +2020-06-07 21:45:00,77.29,44.605,42.265,29.17 +2020-06-07 22:00:00,73.8,43.917,42.26,29.17 +2020-06-07 22:15:00,72.46,44.08,42.26,29.17 +2020-06-07 22:30:00,70.72,43.038999999999994,42.26,29.17 +2020-06-07 22:45:00,70.73,40.247,42.26,29.17 +2020-06-07 23:00:00,68.68,36.454,36.609,29.17 +2020-06-07 23:15:00,69.16,34.64,36.609,29.17 +2020-06-07 23:30:00,67.02,33.389,36.609,29.17 +2020-06-07 23:45:00,67.56,32.952,36.609,29.17 +2020-06-08 00:00:00,65.18,28.81,34.611,29.28 +2020-06-08 00:15:00,65.79,28.454,34.611,29.28 +2020-06-08 00:30:00,66.1,26.794,34.611,29.28 +2020-06-08 00:45:00,66.13,25.502,34.611,29.28 +2020-06-08 01:00:00,64.79,25.755,33.552,29.28 +2020-06-08 01:15:00,65.06,24.938000000000002,33.552,29.28 +2020-06-08 01:30:00,65.24,23.433000000000003,33.552,29.28 +2020-06-08 01:45:00,65.35,23.410999999999998,33.552,29.28 +2020-06-08 02:00:00,65.58,23.37,32.351,29.28 +2020-06-08 02:15:00,67.8,20.785999999999998,32.351,29.28 +2020-06-08 02:30:00,73.54,23.4,32.351,29.28 +2020-06-08 02:45:00,74.8,23.823,32.351,29.28 +2020-06-08 03:00:00,71.75,26.151999999999997,30.793000000000003,29.28 +2020-06-08 03:15:00,68.21,23.986,30.793000000000003,29.28 +2020-06-08 03:30:00,68.19,23.261999999999997,30.793000000000003,29.28 +2020-06-08 03:45:00,75.1,23.634,30.793000000000003,29.28 +2020-06-08 04:00:00,76.21,32.04,31.274,29.28 +2020-06-08 04:15:00,81.8,40.582,31.274,29.28 +2020-06-08 04:30:00,81.13,38.092,31.274,29.28 +2020-06-08 04:45:00,84.34,37.895,31.274,29.28 +2020-06-08 05:00:00,91.59,53.857,37.75,29.28 +2020-06-08 05:15:00,94.96,65.925,37.75,29.28 +2020-06-08 05:30:00,96.35,56.716,37.75,29.28 +2020-06-08 05:45:00,103.22,53.645,37.75,29.28 +2020-06-08 06:00:00,115.44,54.214,55.36,29.28 +2020-06-08 06:15:00,117.66,54.621,55.36,29.28 +2020-06-08 06:30:00,119.09,52.708999999999996,55.36,29.28 +2020-06-08 06:45:00,118.2,54.078,55.36,29.28 +2020-06-08 07:00:00,122.59,54.916000000000004,65.87,29.28 +2020-06-08 07:15:00,126.53,54.75899999999999,65.87,29.28 +2020-06-08 07:30:00,126.58,52.008,65.87,29.28 +2020-06-08 07:45:00,117.45,52.041000000000004,65.87,29.28 +2020-06-08 08:00:00,116.82,49.583,55.695,29.28 +2020-06-08 08:15:00,124.15,52.214,55.695,29.28 +2020-06-08 08:30:00,127.09,51.86600000000001,55.695,29.28 +2020-06-08 08:45:00,129.96,54.918,55.695,29.28 +2020-06-08 09:00:00,127.9,50.291000000000004,50.881,29.28 +2020-06-08 09:15:00,127.73,49.188,50.881,29.28 +2020-06-08 09:30:00,129.88,51.846000000000004,50.881,29.28 +2020-06-08 09:45:00,128.32,52.891000000000005,50.881,29.28 +2020-06-08 10:00:00,125.16,49.513999999999996,49.138000000000005,29.28 +2020-06-08 10:15:00,119.84,51.376000000000005,49.138000000000005,29.28 +2020-06-08 10:30:00,116.84,51.43899999999999,49.138000000000005,29.28 +2020-06-08 10:45:00,124.11,51.953,49.138000000000005,29.28 +2020-06-08 11:00:00,126.44,47.358000000000004,49.178000000000004,29.28 +2020-06-08 11:15:00,121.8,48.31100000000001,49.178000000000004,29.28 +2020-06-08 11:30:00,117.6,50.997,49.178000000000004,29.28 +2020-06-08 11:45:00,113.89,53.128,49.178000000000004,29.28 +2020-06-08 12:00:00,106.1,49.479,47.698,29.28 +2020-06-08 12:15:00,110.89,49.273999999999994,47.698,29.28 +2020-06-08 12:30:00,105.06,47.468999999999994,47.698,29.28 +2020-06-08 12:45:00,103.24,48.36,47.698,29.28 +2020-06-08 13:00:00,104.28,49.425,48.104,29.28 +2020-06-08 13:15:00,105.35,48.79,48.104,29.28 +2020-06-08 13:30:00,102.99,47.021,48.104,29.28 +2020-06-08 13:45:00,106.17,46.507,48.104,29.28 +2020-06-08 14:00:00,104.77,48.076,48.53,29.28 +2020-06-08 14:15:00,99.17,46.91,48.53,29.28 +2020-06-08 14:30:00,100.05,45.497,48.53,29.28 +2020-06-08 14:45:00,101.1,46.841,48.53,29.28 +2020-06-08 15:00:00,100.5,48.551,49.351000000000006,29.28 +2020-06-08 15:15:00,97.86,45.243,49.351000000000006,29.28 +2020-06-08 15:30:00,98.11,43.674,49.351000000000006,29.28 +2020-06-08 15:45:00,102.17,41.217,49.351000000000006,29.28 +2020-06-08 16:00:00,104.3,44.405,51.44,29.28 +2020-06-08 16:15:00,106.98,43.84,51.44,29.28 +2020-06-08 16:30:00,105.78,43.396,51.44,29.28 +2020-06-08 16:45:00,107.49,39.226,51.44,29.28 +2020-06-08 17:00:00,108.2,41.544,56.868,29.28 +2020-06-08 17:15:00,108.56,41.22,56.868,29.28 +2020-06-08 17:30:00,108.83,40.895,56.868,29.28 +2020-06-08 17:45:00,109.18,39.635,56.868,29.28 +2020-06-08 18:00:00,108.41,42.896,57.229,29.28 +2020-06-08 18:15:00,107.9,41.818999999999996,57.229,29.28 +2020-06-08 18:30:00,107.3,39.758,57.229,29.28 +2020-06-08 18:45:00,105.82,44.278,57.229,29.28 +2020-06-08 19:00:00,103.74,46.408,57.744,29.28 +2020-06-08 19:15:00,100.22,45.333999999999996,57.744,29.28 +2020-06-08 19:30:00,98.33,44.78,57.744,29.28 +2020-06-08 19:45:00,97.62,44.755,57.744,29.28 +2020-06-08 20:00:00,95.68,42.876000000000005,66.05199999999999,29.28 +2020-06-08 20:15:00,95.32,44.086999999999996,66.05199999999999,29.28 +2020-06-08 20:30:00,96.73,44.168,66.05199999999999,29.28 +2020-06-08 20:45:00,96.53,44.285,66.05199999999999,29.28 +2020-06-08 21:00:00,91.82,41.66,59.396,29.28 +2020-06-08 21:15:00,91.66,44.745,59.396,29.28 +2020-06-08 21:30:00,89.92,45.61,59.396,29.28 +2020-06-08 21:45:00,88.61,46.43600000000001,59.396,29.28 +2020-06-08 22:00:00,83.94,43.571000000000005,53.06,29.28 +2020-06-08 22:15:00,83.74,45.533,53.06,29.28 +2020-06-08 22:30:00,81.28,39.543,53.06,29.28 +2020-06-08 22:45:00,80.11,36.312,53.06,29.28 +2020-06-08 23:00:00,74.81,32.603,46.148,29.28 +2020-06-08 23:15:00,76.9,29.473000000000003,46.148,29.28 +2020-06-08 23:30:00,73.41,28.424,46.148,29.28 +2020-06-08 23:45:00,75.06,27.683000000000003,46.148,29.28 +2020-06-09 00:00:00,71.41,26.438000000000002,44.625,29.28 +2020-06-09 00:15:00,73.02,27.034000000000002,44.625,29.28 +2020-06-09 00:30:00,70.5,25.976999999999997,44.625,29.28 +2020-06-09 00:45:00,73.38,25.364,44.625,29.28 +2020-06-09 01:00:00,72.8,25.099,41.733000000000004,29.28 +2020-06-09 01:15:00,73.38,24.331999999999997,41.733000000000004,29.28 +2020-06-09 01:30:00,72.4,22.71,41.733000000000004,29.28 +2020-06-09 01:45:00,72.79,22.215,41.733000000000004,29.28 +2020-06-09 02:00:00,70.58,21.713,39.872,29.28 +2020-06-09 02:15:00,72.27,20.226,39.872,29.28 +2020-06-09 02:30:00,80.49,22.413,39.872,29.28 +2020-06-09 02:45:00,80.75,23.159000000000002,39.872,29.28 +2020-06-09 03:00:00,78.25,24.851,38.711,29.28 +2020-06-09 03:15:00,73.94,23.508000000000003,38.711,29.28 +2020-06-09 03:30:00,77.01,22.8,38.711,29.28 +2020-06-09 03:45:00,74.49,22.145,38.711,29.28 +2020-06-09 04:00:00,77.74,29.266,39.823,29.28 +2020-06-09 04:15:00,79.76,37.743,39.823,29.28 +2020-06-09 04:30:00,82.3,35.097,39.823,29.28 +2020-06-09 04:45:00,87.15,35.468,39.823,29.28 +2020-06-09 05:00:00,96.24,53.251000000000005,43.228,29.28 +2020-06-09 05:15:00,100.0,65.757,43.228,29.28 +2020-06-09 05:30:00,103.94,56.667,43.228,29.28 +2020-06-09 05:45:00,105.25,52.95,43.228,29.28 +2020-06-09 06:00:00,114.5,54.452,54.316,29.28 +2020-06-09 06:15:00,116.55,55.166000000000004,54.316,29.28 +2020-06-09 06:30:00,110.99,52.913000000000004,54.316,29.28 +2020-06-09 06:45:00,112.3,53.423,54.316,29.28 +2020-06-09 07:00:00,121.18,54.379,65.758,29.28 +2020-06-09 07:15:00,120.41,53.98,65.758,29.28 +2020-06-09 07:30:00,115.9,51.185,65.758,29.28 +2020-06-09 07:45:00,116.85,50.333999999999996,65.758,29.28 +2020-06-09 08:00:00,118.19,47.826,57.983000000000004,29.28 +2020-06-09 08:15:00,119.82,49.976000000000006,57.983000000000004,29.28 +2020-06-09 08:30:00,115.93,49.765,57.983000000000004,29.28 +2020-06-09 08:45:00,114.08,51.917,57.983000000000004,29.28 +2020-06-09 09:00:00,112.79,47.534,52.653,29.28 +2020-06-09 09:15:00,113.07,46.446999999999996,52.653,29.28 +2020-06-09 09:30:00,113.8,49.825,52.653,29.28 +2020-06-09 09:45:00,113.95,52.155,52.653,29.28 +2020-06-09 10:00:00,112.65,47.425,51.408,29.28 +2020-06-09 10:15:00,115.45,49.073,51.408,29.28 +2020-06-09 10:30:00,121.75,49.181999999999995,51.408,29.28 +2020-06-09 10:45:00,124.45,50.68899999999999,51.408,29.28 +2020-06-09 11:00:00,118.76,46.229,51.913000000000004,29.28 +2020-06-09 11:15:00,118.38,47.549,51.913000000000004,29.28 +2020-06-09 11:30:00,118.73,49.02,51.913000000000004,29.28 +2020-06-09 11:45:00,120.94,50.788999999999994,51.913000000000004,29.28 +2020-06-09 12:00:00,115.6,46.87,49.508,29.28 +2020-06-09 12:15:00,114.07,46.952,49.508,29.28 +2020-06-09 12:30:00,115.92,46.006,49.508,29.28 +2020-06-09 12:45:00,116.36,47.536,49.508,29.28 +2020-06-09 13:00:00,114.4,48.217,50.007,29.28 +2020-06-09 13:15:00,114.3,49.275,50.007,29.28 +2020-06-09 13:30:00,114.18,47.592,50.007,29.28 +2020-06-09 13:45:00,114.22,46.174,50.007,29.28 +2020-06-09 14:00:00,109.09,48.222,49.778999999999996,29.28 +2020-06-09 14:15:00,106.54,46.875,49.778999999999996,29.28 +2020-06-09 14:30:00,113.2,45.803999999999995,49.778999999999996,29.28 +2020-06-09 14:45:00,112.19,46.391000000000005,49.778999999999996,29.28 +2020-06-09 15:00:00,108.09,47.952,51.559,29.28 +2020-06-09 15:15:00,101.97,45.549,51.559,29.28 +2020-06-09 15:30:00,104.39,43.786,51.559,29.28 +2020-06-09 15:45:00,107.82,41.613,51.559,29.28 +2020-06-09 16:00:00,106.35,44.193000000000005,53.531000000000006,29.28 +2020-06-09 16:15:00,101.43,43.746,53.531000000000006,29.28 +2020-06-09 16:30:00,105.03,43.018,53.531000000000006,29.28 +2020-06-09 16:45:00,106.03,39.527,53.531000000000006,29.28 +2020-06-09 17:00:00,112.45,42.126999999999995,59.497,29.28 +2020-06-09 17:15:00,115.83,42.196000000000005,59.497,29.28 +2020-06-09 17:30:00,116.3,41.498000000000005,59.497,29.28 +2020-06-09 17:45:00,110.26,39.883,59.497,29.28 +2020-06-09 18:00:00,106.4,42.235,59.861999999999995,29.28 +2020-06-09 18:15:00,108.44,42.52,59.861999999999995,29.28 +2020-06-09 18:30:00,112.57,40.175,59.861999999999995,29.28 +2020-06-09 18:45:00,113.13,44.573,59.861999999999995,29.28 +2020-06-09 19:00:00,106.86,45.585,60.989,29.28 +2020-06-09 19:15:00,103.42,44.653,60.989,29.28 +2020-06-09 19:30:00,98.81,43.82899999999999,60.989,29.28 +2020-06-09 19:45:00,99.4,44.125,60.989,29.28 +2020-06-09 20:00:00,95.84,42.619,68.35600000000001,29.28 +2020-06-09 20:15:00,94.92,42.388000000000005,68.35600000000001,29.28 +2020-06-09 20:30:00,94.19,42.602,68.35600000000001,29.28 +2020-06-09 20:45:00,94.35,43.11,68.35600000000001,29.28 +2020-06-09 21:00:00,90.98,41.266999999999996,59.251000000000005,29.28 +2020-06-09 21:15:00,89.1,43.023999999999994,59.251000000000005,29.28 +2020-06-09 21:30:00,86.25,43.975,59.251000000000005,29.28 +2020-06-09 21:45:00,86.55,45.022,59.251000000000005,29.28 +2020-06-09 22:00:00,82.28,42.479,54.736999999999995,29.28 +2020-06-09 22:15:00,80.43,44.108999999999995,54.736999999999995,29.28 +2020-06-09 22:30:00,78.68,38.405,54.736999999999995,29.28 +2020-06-09 22:45:00,78.71,35.179,54.736999999999995,29.28 +2020-06-09 23:00:00,74.03,30.709,46.806999999999995,29.28 +2020-06-09 23:15:00,74.01,29.048000000000002,46.806999999999995,29.28 +2020-06-09 23:30:00,74.25,27.978,46.806999999999995,29.28 +2020-06-09 23:45:00,73.65,27.365,46.806999999999995,29.28 +2020-06-10 00:00:00,69.28,26.302,43.824,29.28 +2020-06-10 00:15:00,71.2,26.901,43.824,29.28 +2020-06-10 00:30:00,66.99,25.846,43.824,29.28 +2020-06-10 00:45:00,71.61,25.239,43.824,29.28 +2020-06-10 01:00:00,69.8,24.991,39.86,29.28 +2020-06-10 01:15:00,71.05,24.204,39.86,29.28 +2020-06-10 01:30:00,70.29,22.575,39.86,29.28 +2020-06-10 01:45:00,71.08,22.073,39.86,29.28 +2020-06-10 02:00:00,69.28,21.573,37.931999999999995,29.28 +2020-06-10 02:15:00,70.6,20.082,37.931999999999995,29.28 +2020-06-10 02:30:00,77.94,22.269000000000002,37.931999999999995,29.28 +2020-06-10 02:45:00,78.48,23.021,37.931999999999995,29.28 +2020-06-10 03:00:00,76.46,24.714000000000002,37.579,29.28 +2020-06-10 03:15:00,74.28,23.369,37.579,29.28 +2020-06-10 03:30:00,73.07,22.666,37.579,29.28 +2020-06-10 03:45:00,74.4,22.034000000000002,37.579,29.28 +2020-06-10 04:00:00,78.61,29.1,37.931999999999995,29.28 +2020-06-10 04:15:00,78.12,37.53,37.931999999999995,29.28 +2020-06-10 04:30:00,82.15,34.872,37.931999999999995,29.28 +2020-06-10 04:45:00,86.72,35.239000000000004,37.931999999999995,29.28 +2020-06-10 05:00:00,95.11,52.913000000000004,40.942,29.28 +2020-06-10 05:15:00,101.05,65.282,40.942,29.28 +2020-06-10 05:30:00,107.89,56.26,40.942,29.28 +2020-06-10 05:45:00,112.89,52.593,40.942,29.28 +2020-06-10 06:00:00,115.77,54.111999999999995,56.516999999999996,29.28 +2020-06-10 06:15:00,116.7,54.808,56.516999999999996,29.28 +2020-06-10 06:30:00,120.98,52.577,56.516999999999996,29.28 +2020-06-10 06:45:00,121.83,53.114,56.516999999999996,29.28 +2020-06-10 07:00:00,121.07,54.059,71.707,29.28 +2020-06-10 07:15:00,116.29,53.674,71.707,29.28 +2020-06-10 07:30:00,121.13,50.87,71.707,29.28 +2020-06-10 07:45:00,119.6,50.05,71.707,29.28 +2020-06-10 08:00:00,120.93,47.552,61.17,29.28 +2020-06-10 08:15:00,117.32,49.738,61.17,29.28 +2020-06-10 08:30:00,118.76,49.519,61.17,29.28 +2020-06-10 08:45:00,116.95,51.678000000000004,61.17,29.28 +2020-06-10 09:00:00,124.86,47.288999999999994,57.282,29.28 +2020-06-10 09:15:00,121.0,46.206,57.282,29.28 +2020-06-10 09:30:00,126.65,49.588,57.282,29.28 +2020-06-10 09:45:00,128.01,51.937,57.282,29.28 +2020-06-10 10:00:00,127.44,47.218,54.026,29.28 +2020-06-10 10:15:00,119.72,48.883,54.026,29.28 +2020-06-10 10:30:00,121.47,48.994,54.026,29.28 +2020-06-10 10:45:00,128.3,50.51,54.026,29.28 +2020-06-10 11:00:00,132.15,46.044,54.277,29.28 +2020-06-10 11:15:00,127.14,47.372,54.277,29.28 +2020-06-10 11:30:00,117.1,48.833999999999996,54.277,29.28 +2020-06-10 11:45:00,114.36,50.605,54.277,29.28 +2020-06-10 12:00:00,120.41,46.718999999999994,52.552,29.28 +2020-06-10 12:15:00,123.17,46.803999999999995,52.552,29.28 +2020-06-10 12:30:00,122.08,45.833999999999996,52.552,29.28 +2020-06-10 12:45:00,120.6,47.368,52.552,29.28 +2020-06-10 13:00:00,118.16,48.044,52.111999999999995,29.28 +2020-06-10 13:15:00,119.23,49.104,52.111999999999995,29.28 +2020-06-10 13:30:00,117.97,47.431000000000004,52.111999999999995,29.28 +2020-06-10 13:45:00,113.38,46.016999999999996,52.111999999999995,29.28 +2020-06-10 14:00:00,112.33,48.085,52.066,29.28 +2020-06-10 14:15:00,108.78,46.735,52.066,29.28 +2020-06-10 14:30:00,112.6,45.636,52.066,29.28 +2020-06-10 14:45:00,111.92,46.229,52.066,29.28 +2020-06-10 15:00:00,110.0,47.827,52.523999999999994,29.28 +2020-06-10 15:15:00,106.57,45.413999999999994,52.523999999999994,29.28 +2020-06-10 15:30:00,108.24,43.638999999999996,52.523999999999994,29.28 +2020-06-10 15:45:00,110.6,41.455,52.523999999999994,29.28 +2020-06-10 16:00:00,112.33,44.068000000000005,54.101000000000006,29.28 +2020-06-10 16:15:00,110.81,43.615,54.101000000000006,29.28 +2020-06-10 16:30:00,111.57,42.906000000000006,54.101000000000006,29.28 +2020-06-10 16:45:00,118.23,39.384,54.101000000000006,29.28 +2020-06-10 17:00:00,119.5,42.012,58.155,29.28 +2020-06-10 17:15:00,116.37,42.07,58.155,29.28 +2020-06-10 17:30:00,117.51,41.364,58.155,29.28 +2020-06-10 17:45:00,114.99,39.728,58.155,29.28 +2020-06-10 18:00:00,121.59,42.092,60.205,29.28 +2020-06-10 18:15:00,119.08,42.352,60.205,29.28 +2020-06-10 18:30:00,115.11,40.0,60.205,29.28 +2020-06-10 18:45:00,110.65,44.397,60.205,29.28 +2020-06-10 19:00:00,108.16,45.411,61.568999999999996,29.28 +2020-06-10 19:15:00,108.06,44.47,61.568999999999996,29.28 +2020-06-10 19:30:00,106.76,43.638999999999996,61.568999999999996,29.28 +2020-06-10 19:45:00,103.49,43.93,61.568999999999996,29.28 +2020-06-10 20:00:00,96.17,42.407,68.145,29.28 +2020-06-10 20:15:00,96.64,42.173,68.145,29.28 +2020-06-10 20:30:00,95.52,42.398999999999994,68.145,29.28 +2020-06-10 20:45:00,94.92,42.938,68.145,29.28 +2020-06-10 21:00:00,90.77,41.101000000000006,59.696000000000005,29.28 +2020-06-10 21:15:00,90.39,42.868,59.696000000000005,29.28 +2020-06-10 21:30:00,86.63,43.795,59.696000000000005,29.28 +2020-06-10 21:45:00,85.83,44.854,59.696000000000005,29.28 +2020-06-10 22:00:00,81.02,42.332,54.861999999999995,29.28 +2020-06-10 22:15:00,79.98,43.971000000000004,54.861999999999995,29.28 +2020-06-10 22:30:00,77.22,38.265,54.861999999999995,29.28 +2020-06-10 22:45:00,76.83,35.029,54.861999999999995,29.28 +2020-06-10 23:00:00,73.12,30.535999999999998,45.568000000000005,29.28 +2020-06-10 23:15:00,75.38,28.914,45.568000000000005,29.28 +2020-06-10 23:30:00,74.71,27.851999999999997,45.568000000000005,29.28 +2020-06-10 23:45:00,75.2,27.229,45.568000000000005,29.28 +2020-06-11 00:00:00,70.41,26.171999999999997,40.181,29.28 +2020-06-11 00:15:00,70.82,26.772,40.181,29.28 +2020-06-11 00:30:00,70.59,25.719,40.181,29.28 +2020-06-11 00:45:00,70.45,25.116999999999997,40.181,29.28 +2020-06-11 01:00:00,69.57,24.886,38.296,29.28 +2020-06-11 01:15:00,70.14,24.081,38.296,29.28 +2020-06-11 01:30:00,68.96,22.445,38.296,29.28 +2020-06-11 01:45:00,69.74,21.936999999999998,38.296,29.28 +2020-06-11 02:00:00,69.81,21.438000000000002,36.575,29.28 +2020-06-11 02:15:00,69.88,19.944000000000003,36.575,29.28 +2020-06-11 02:30:00,70.09,22.129,36.575,29.28 +2020-06-11 02:45:00,76.04,22.886999999999997,36.575,29.28 +2020-06-11 03:00:00,79.84,24.581,36.394,29.28 +2020-06-11 03:15:00,79.7,23.234,36.394,29.28 +2020-06-11 03:30:00,74.17,22.535,36.394,29.28 +2020-06-11 03:45:00,76.38,21.927,36.394,29.28 +2020-06-11 04:00:00,75.88,28.939,37.207,29.28 +2020-06-11 04:15:00,78.09,37.323,37.207,29.28 +2020-06-11 04:30:00,82.26,34.653,37.207,29.28 +2020-06-11 04:45:00,86.97,35.016,37.207,29.28 +2020-06-11 05:00:00,94.99,52.582,40.713,29.28 +2020-06-11 05:15:00,100.23,64.817,40.713,29.28 +2020-06-11 05:30:00,106.76,55.861000000000004,40.713,29.28 +2020-06-11 05:45:00,111.82,52.246,40.713,29.28 +2020-06-11 06:00:00,116.83,53.778999999999996,50.952,29.28 +2020-06-11 06:15:00,112.43,54.458,50.952,29.28 +2020-06-11 06:30:00,113.1,52.248000000000005,50.952,29.28 +2020-06-11 06:45:00,118.36,52.81399999999999,50.952,29.28 +2020-06-11 07:00:00,124.46,53.745,64.88,29.28 +2020-06-11 07:15:00,126.91,53.378,64.88,29.28 +2020-06-11 07:30:00,121.71,50.56100000000001,64.88,29.28 +2020-06-11 07:45:00,118.85,49.775,64.88,29.28 +2020-06-11 08:00:00,123.62,47.285,55.133,29.28 +2020-06-11 08:15:00,123.22,49.50899999999999,55.133,29.28 +2020-06-11 08:30:00,122.66,49.281000000000006,55.133,29.28 +2020-06-11 08:45:00,116.59,51.448,55.133,29.28 +2020-06-11 09:00:00,119.41,47.053000000000004,48.912,29.28 +2020-06-11 09:15:00,123.06,45.975,48.912,29.28 +2020-06-11 09:30:00,124.81,49.357,48.912,29.28 +2020-06-11 09:45:00,122.25,51.726000000000006,48.912,29.28 +2020-06-11 10:00:00,123.12,47.016999999999996,45.968999999999994,29.28 +2020-06-11 10:15:00,122.31,48.696999999999996,45.968999999999994,29.28 +2020-06-11 10:30:00,124.76,48.813,45.968999999999994,29.28 +2020-06-11 10:45:00,121.0,50.336999999999996,45.968999999999994,29.28 +2020-06-11 11:00:00,114.05,45.86600000000001,44.067,29.28 +2020-06-11 11:15:00,112.2,47.202,44.067,29.28 +2020-06-11 11:30:00,108.33,48.652,44.067,29.28 +2020-06-11 11:45:00,108.51,50.427,44.067,29.28 +2020-06-11 12:00:00,109.52,46.573,41.501000000000005,29.28 +2020-06-11 12:15:00,106.82,46.66,41.501000000000005,29.28 +2020-06-11 12:30:00,106.48,45.667,41.501000000000005,29.28 +2020-06-11 12:45:00,100.88,47.20399999999999,41.501000000000005,29.28 +2020-06-11 13:00:00,95.77,47.875,41.117,29.28 +2020-06-11 13:15:00,95.82,48.938,41.117,29.28 +2020-06-11 13:30:00,100.99,47.275,41.117,29.28 +2020-06-11 13:45:00,104.58,45.864,41.117,29.28 +2020-06-11 14:00:00,103.94,47.951,41.492,29.28 +2020-06-11 14:15:00,100.72,46.599,41.492,29.28 +2020-06-11 14:30:00,97.94,45.475,41.492,29.28 +2020-06-11 14:45:00,99.64,46.071000000000005,41.492,29.28 +2020-06-11 15:00:00,99.53,47.706,43.711999999999996,29.28 +2020-06-11 15:15:00,97.45,45.283,43.711999999999996,29.28 +2020-06-11 15:30:00,94.25,43.497,43.711999999999996,29.28 +2020-06-11 15:45:00,100.3,41.3,43.711999999999996,29.28 +2020-06-11 16:00:00,103.4,43.946000000000005,45.446000000000005,29.28 +2020-06-11 16:15:00,100.4,43.488,45.446000000000005,29.28 +2020-06-11 16:30:00,100.04,42.797,45.446000000000005,29.28 +2020-06-11 16:45:00,101.12,39.244,45.446000000000005,29.28 +2020-06-11 17:00:00,110.22,41.901,48.803000000000004,29.28 +2020-06-11 17:15:00,110.07,41.951,48.803000000000004,29.28 +2020-06-11 17:30:00,113.51,41.235,48.803000000000004,29.28 +2020-06-11 17:45:00,106.59,39.578,48.803000000000004,29.28 +2020-06-11 18:00:00,104.74,41.95399999999999,51.167,29.28 +2020-06-11 18:15:00,106.72,42.18899999999999,51.167,29.28 +2020-06-11 18:30:00,107.72,39.83,51.167,29.28 +2020-06-11 18:45:00,102.99,44.225,51.167,29.28 +2020-06-11 19:00:00,99.51,45.243,52.486000000000004,29.28 +2020-06-11 19:15:00,103.25,44.294,52.486000000000004,29.28 +2020-06-11 19:30:00,105.06,43.455,52.486000000000004,29.28 +2020-06-11 19:45:00,97.36,43.74100000000001,52.486000000000004,29.28 +2020-06-11 20:00:00,101.57,42.2,59.635,29.28 +2020-06-11 20:15:00,101.54,41.964,59.635,29.28 +2020-06-11 20:30:00,100.96,42.202,59.635,29.28 +2020-06-11 20:45:00,96.25,42.772,59.635,29.28 +2020-06-11 21:00:00,92.74,40.939,54.353,29.28 +2020-06-11 21:15:00,89.61,42.716,54.353,29.28 +2020-06-11 21:30:00,87.7,43.619,54.353,29.28 +2020-06-11 21:45:00,94.37,44.69,54.353,29.28 +2020-06-11 22:00:00,89.15,42.188,49.431999999999995,29.28 +2020-06-11 22:15:00,88.41,43.838,49.431999999999995,29.28 +2020-06-11 22:30:00,80.58,38.126999999999995,49.431999999999995,29.28 +2020-06-11 22:45:00,80.98,34.881,49.431999999999995,29.28 +2020-06-11 23:00:00,82.25,30.368000000000002,42.872,29.28 +2020-06-11 23:15:00,83.85,28.783,42.872,29.28 +2020-06-11 23:30:00,79.67,27.729,42.872,29.28 +2020-06-11 23:45:00,74.85,27.095,42.872,29.28 +2020-06-12 00:00:00,71.36,24.219,39.819,29.28 +2020-06-12 00:15:00,72.66,25.046999999999997,39.819,29.28 +2020-06-12 00:30:00,77.04,24.245,39.819,29.28 +2020-06-12 00:45:00,78.71,24.064,39.819,29.28 +2020-06-12 01:00:00,74.65,23.453000000000003,37.797,29.28 +2020-06-12 01:15:00,72.83,22.143,37.797,29.28 +2020-06-12 01:30:00,71.34,21.164,37.797,29.28 +2020-06-12 01:45:00,78.24,20.419,37.797,29.28 +2020-06-12 02:00:00,77.22,20.825,36.905,29.28 +2020-06-12 02:15:00,75.15,19.269000000000002,36.905,29.28 +2020-06-12 02:30:00,76.1,22.295,36.905,29.28 +2020-06-12 02:45:00,72.39,22.408,36.905,29.28 +2020-06-12 03:00:00,77.81,24.747,37.1,29.28 +2020-06-12 03:15:00,79.33,22.335,37.1,29.28 +2020-06-12 03:30:00,78.33,21.410999999999998,37.1,29.28 +2020-06-12 03:45:00,74.64,21.698,37.1,29.28 +2020-06-12 04:00:00,77.34,28.813000000000002,37.882,29.28 +2020-06-12 04:15:00,84.37,35.63,37.882,29.28 +2020-06-12 04:30:00,86.71,33.882,37.882,29.28 +2020-06-12 04:45:00,88.82,33.51,37.882,29.28 +2020-06-12 05:00:00,89.4,50.383,40.777,29.28 +2020-06-12 05:15:00,93.53,63.548,40.777,29.28 +2020-06-12 05:30:00,98.42,54.953,40.777,29.28 +2020-06-12 05:45:00,98.91,50.955,40.777,29.28 +2020-06-12 06:00:00,102.82,52.763000000000005,55.528,29.28 +2020-06-12 06:15:00,102.95,53.437,55.528,29.28 +2020-06-12 06:30:00,110.8,51.111999999999995,55.528,29.28 +2020-06-12 06:45:00,112.79,51.744,55.528,29.28 +2020-06-12 07:00:00,116.91,53.201,67.749,29.28 +2020-06-12 07:15:00,107.11,53.821000000000005,67.749,29.28 +2020-06-12 07:30:00,104.05,49.117,67.749,29.28 +2020-06-12 07:45:00,103.9,48.068999999999996,67.749,29.28 +2020-06-12 08:00:00,106.28,46.215,57.55,29.28 +2020-06-12 08:15:00,102.7,49.07,57.55,29.28 +2020-06-12 08:30:00,109.84,48.847,57.55,29.28 +2020-06-12 08:45:00,110.15,50.713,57.55,29.28 +2020-06-12 09:00:00,108.4,44.169,52.588,29.28 +2020-06-12 09:15:00,103.04,44.977,52.588,29.28 +2020-06-12 09:30:00,109.97,47.66,52.588,29.28 +2020-06-12 09:45:00,108.51,50.403999999999996,52.588,29.28 +2020-06-12 10:00:00,107.23,45.416000000000004,49.772,29.28 +2020-06-12 10:15:00,105.95,46.98,49.772,29.28 +2020-06-12 10:30:00,114.2,47.611999999999995,49.772,29.28 +2020-06-12 10:45:00,115.09,49.004,49.772,29.28 +2020-06-12 11:00:00,112.1,44.768,49.226000000000006,29.28 +2020-06-12 11:15:00,107.96,44.983999999999995,49.226000000000006,29.28 +2020-06-12 11:30:00,105.26,46.276,49.226000000000006,29.28 +2020-06-12 11:45:00,103.17,47.152,49.226000000000006,29.28 +2020-06-12 12:00:00,101.7,43.861000000000004,45.705,29.28 +2020-06-12 12:15:00,103.75,43.108999999999995,45.705,29.28 +2020-06-12 12:30:00,102.31,42.221000000000004,45.705,29.28 +2020-06-12 12:45:00,102.28,43.075,45.705,29.28 +2020-06-12 13:00:00,98.03,44.407,43.133,29.28 +2020-06-12 13:15:00,98.2,45.735,43.133,29.28 +2020-06-12 13:30:00,97.7,44.833999999999996,43.133,29.28 +2020-06-12 13:45:00,99.86,43.71,43.133,29.28 +2020-06-12 14:00:00,100.43,44.925,41.989,29.28 +2020-06-12 14:15:00,103.53,43.963,41.989,29.28 +2020-06-12 14:30:00,105.57,44.29600000000001,41.989,29.28 +2020-06-12 14:45:00,108.6,44.278999999999996,41.989,29.28 +2020-06-12 15:00:00,107.88,45.821999999999996,43.728,29.28 +2020-06-12 15:15:00,108.67,43.114,43.728,29.28 +2020-06-12 15:30:00,108.56,40.613,43.728,29.28 +2020-06-12 15:45:00,108.51,39.143,43.728,29.28 +2020-06-12 16:00:00,107.02,40.894,45.93899999999999,29.28 +2020-06-12 16:15:00,107.22,40.931,45.93899999999999,29.28 +2020-06-12 16:30:00,106.33,40.096,45.93899999999999,29.28 +2020-06-12 16:45:00,105.53,35.755,45.93899999999999,29.28 +2020-06-12 17:00:00,105.78,40.11,50.488,29.28 +2020-06-12 17:15:00,104.46,39.942,50.488,29.28 +2020-06-12 17:30:00,104.33,39.343,50.488,29.28 +2020-06-12 17:45:00,106.02,37.482,50.488,29.28 +2020-06-12 18:00:00,104.89,39.993,52.408,29.28 +2020-06-12 18:15:00,103.14,39.260999999999996,52.408,29.28 +2020-06-12 18:30:00,102.14,36.836999999999996,52.408,29.28 +2020-06-12 18:45:00,101.78,41.63,52.408,29.28 +2020-06-12 19:00:00,98.46,43.586999999999996,52.736000000000004,29.28 +2020-06-12 19:15:00,94.32,43.331,52.736000000000004,29.28 +2020-06-12 19:30:00,94.61,42.503,52.736000000000004,29.28 +2020-06-12 19:45:00,91.52,41.763999999999996,52.736000000000004,29.28 +2020-06-12 20:00:00,89.27,40.058,59.68,29.28 +2020-06-12 20:15:00,91.99,40.582,59.68,29.28 +2020-06-12 20:30:00,88.8,40.359,59.68,29.28 +2020-06-12 20:45:00,88.94,40.238,59.68,29.28 +2020-06-12 21:00:00,86.4,39.718,54.343999999999994,29.28 +2020-06-12 21:15:00,85.82,43.148,54.343999999999994,29.28 +2020-06-12 21:30:00,83.13,43.888999999999996,54.343999999999994,29.28 +2020-06-12 21:45:00,84.05,45.218,54.343999999999994,29.28 +2020-06-12 22:00:00,77.72,42.698,49.672,29.28 +2020-06-12 22:15:00,77.94,44.105,49.672,29.28 +2020-06-12 22:30:00,74.99,43.566,49.672,29.28 +2020-06-12 22:45:00,76.11,41.47,49.672,29.28 +2020-06-12 23:00:00,70.24,38.516,42.065,29.28 +2020-06-12 23:15:00,69.98,35.275999999999996,42.065,29.28 +2020-06-12 23:30:00,69.72,32.342,42.065,29.28 +2020-06-12 23:45:00,69.07,31.518,42.065,29.28 +2020-06-13 00:00:00,64.84,25.0,38.829,29.17 +2020-06-13 00:15:00,66.33,24.767,38.829,29.17 +2020-06-13 00:30:00,65.93,23.649,38.829,29.17 +2020-06-13 00:45:00,64.97,22.844,38.829,29.17 +2020-06-13 01:00:00,64.06,22.574,34.63,29.17 +2020-06-13 01:15:00,64.21,21.715,34.63,29.17 +2020-06-13 01:30:00,62.98,19.908,34.63,29.17 +2020-06-13 01:45:00,63.23,20.326,34.63,29.17 +2020-06-13 02:00:00,62.58,19.86,32.465,29.17 +2020-06-13 02:15:00,62.35,17.509,32.465,29.17 +2020-06-13 02:30:00,62.98,19.667,32.465,29.17 +2020-06-13 02:45:00,62.07,20.549,32.465,29.17 +2020-06-13 03:00:00,63.84,21.636999999999997,31.925,29.17 +2020-06-13 03:15:00,70.97,18.439,31.925,29.17 +2020-06-13 03:30:00,65.1,17.679000000000002,31.925,29.17 +2020-06-13 03:45:00,63.54,19.415,31.925,29.17 +2020-06-13 04:00:00,61.45,24.273000000000003,31.309,29.17 +2020-06-13 04:15:00,69.01,29.947,31.309,29.17 +2020-06-13 04:30:00,69.89,26.381,31.309,29.17 +2020-06-13 04:45:00,70.47,26.219,31.309,29.17 +2020-06-13 05:00:00,64.5,33.701,30.323,29.17 +2020-06-13 05:15:00,66.69,34.52,30.323,29.17 +2020-06-13 05:30:00,66.77,27.484,30.323,29.17 +2020-06-13 05:45:00,69.87,28.311999999999998,30.323,29.17 +2020-06-13 06:00:00,75.11,42.504,31.438000000000002,29.17 +2020-06-13 06:15:00,75.75,52.11600000000001,31.438000000000002,29.17 +2020-06-13 06:30:00,84.06,46.602,31.438000000000002,29.17 +2020-06-13 06:45:00,87.29,43.371,31.438000000000002,29.17 +2020-06-13 07:00:00,86.33,43.028,34.891999999999996,29.17 +2020-06-13 07:15:00,87.6,42.312,34.891999999999996,29.17 +2020-06-13 07:30:00,90.91,39.294000000000004,34.891999999999996,29.17 +2020-06-13 07:45:00,93.57,39.534,34.891999999999996,29.17 +2020-06-13 08:00:00,94.56,38.69,39.608000000000004,29.17 +2020-06-13 08:15:00,92.12,41.727,39.608000000000004,29.17 +2020-06-13 08:30:00,96.32,41.638999999999996,39.608000000000004,29.17 +2020-06-13 08:45:00,97.62,44.715,39.608000000000004,29.17 +2020-06-13 09:00:00,91.24,41.26,40.894,29.17 +2020-06-13 09:15:00,87.74,42.605,40.894,29.17 +2020-06-13 09:30:00,87.68,45.849,40.894,29.17 +2020-06-13 09:45:00,84.6,48.202,40.894,29.17 +2020-06-13 10:00:00,79.46,43.803999999999995,39.525,29.17 +2020-06-13 10:15:00,84.16,45.736000000000004,39.525,29.17 +2020-06-13 10:30:00,89.39,46.066,39.525,29.17 +2020-06-13 10:45:00,89.77,47.268,39.525,29.17 +2020-06-13 11:00:00,91.12,42.92,36.718,29.17 +2020-06-13 11:15:00,93.16,43.891999999999996,36.718,29.17 +2020-06-13 11:30:00,86.51,45.324,36.718,29.17 +2020-06-13 11:45:00,84.34,46.735,36.718,29.17 +2020-06-13 12:00:00,79.65,43.74,35.688,29.17 +2020-06-13 12:15:00,73.18,43.858000000000004,35.688,29.17 +2020-06-13 12:30:00,70.87,42.857,35.688,29.17 +2020-06-13 12:45:00,71.71,44.318999999999996,35.688,29.17 +2020-06-13 13:00:00,72.51,44.803000000000004,32.858000000000004,29.17 +2020-06-13 13:15:00,73.91,45.407,32.858000000000004,29.17 +2020-06-13 13:30:00,70.81,44.647,32.858000000000004,29.17 +2020-06-13 13:45:00,71.46,42.306000000000004,32.858000000000004,29.17 +2020-06-13 14:00:00,65.58,43.733000000000004,31.738000000000003,29.17 +2020-06-13 14:15:00,67.67,41.534,31.738000000000003,29.17 +2020-06-13 14:30:00,66.02,41.251000000000005,31.738000000000003,29.17 +2020-06-13 14:45:00,69.44,41.713,31.738000000000003,29.17 +2020-06-13 15:00:00,69.59,43.79,34.35,29.17 +2020-06-13 15:15:00,68.57,41.821000000000005,34.35,29.17 +2020-06-13 15:30:00,70.63,39.661,34.35,29.17 +2020-06-13 15:45:00,70.47,37.313,34.35,29.17 +2020-06-13 16:00:00,70.88,41.03,37.522,29.17 +2020-06-13 16:15:00,69.13,40.306999999999995,37.522,29.17 +2020-06-13 16:30:00,70.38,39.698,37.522,29.17 +2020-06-13 16:45:00,72.18,35.393,37.522,29.17 +2020-06-13 17:00:00,75.53,38.603,42.498000000000005,29.17 +2020-06-13 17:15:00,75.43,36.553000000000004,42.498000000000005,29.17 +2020-06-13 17:30:00,76.48,35.812,42.498000000000005,29.17 +2020-06-13 17:45:00,78.84,34.356,42.498000000000005,29.17 +2020-06-13 18:00:00,80.38,38.174,44.701,29.17 +2020-06-13 18:15:00,80.49,39.178000000000004,44.701,29.17 +2020-06-13 18:30:00,83.97,38.166,44.701,29.17 +2020-06-13 18:45:00,81.48,39.333,44.701,29.17 +2020-06-13 19:00:00,81.9,39.872,45.727,29.17 +2020-06-13 19:15:00,76.64,38.601,45.727,29.17 +2020-06-13 19:30:00,75.82,38.568000000000005,45.727,29.17 +2020-06-13 19:45:00,78.53,39.488,45.727,29.17 +2020-06-13 20:00:00,74.72,38.754,43.391000000000005,29.17 +2020-06-13 20:15:00,76.21,38.875,43.391000000000005,29.17 +2020-06-13 20:30:00,72.61,37.795,43.391000000000005,29.17 +2020-06-13 20:45:00,72.49,39.428000000000004,43.391000000000005,29.17 +2020-06-13 21:00:00,71.31,37.734,41.231,29.17 +2020-06-13 21:15:00,69.62,40.884,41.231,29.17 +2020-06-13 21:30:00,68.99,41.898999999999994,41.231,29.17 +2020-06-13 21:45:00,65.8,42.681000000000004,41.231,29.17 +2020-06-13 22:00:00,61.94,40.286,40.798,29.17 +2020-06-13 22:15:00,62.44,42.176,40.798,29.17 +2020-06-13 22:30:00,60.41,41.869,40.798,29.17 +2020-06-13 22:45:00,59.47,40.296,40.798,29.17 +2020-06-13 23:00:00,56.53,36.909,34.402,29.17 +2020-06-13 23:15:00,56.79,33.949,34.402,29.17 +2020-06-13 23:30:00,56.94,33.191,34.402,29.17 +2020-06-13 23:45:00,56.32,32.468,34.402,29.17 +2020-06-14 00:00:00,52.28,26.233,30.171,29.17 +2020-06-14 00:15:00,53.6,24.875,30.171,29.17 +2020-06-14 00:30:00,49.61,23.584,30.171,29.17 +2020-06-14 00:45:00,51.87,22.745,30.171,29.17 +2020-06-14 01:00:00,50.13,22.730999999999998,27.15,29.17 +2020-06-14 01:15:00,51.9,21.837,27.15,29.17 +2020-06-14 01:30:00,50.98,19.942999999999998,27.15,29.17 +2020-06-14 01:45:00,51.2,19.963,27.15,29.17 +2020-06-14 02:00:00,49.19,19.500999999999998,25.403000000000002,29.17 +2020-06-14 02:15:00,49.45,17.74,25.403000000000002,29.17 +2020-06-14 02:30:00,46.81,20.155,25.403000000000002,29.17 +2020-06-14 02:45:00,49.86,20.811,25.403000000000002,29.17 +2020-06-14 03:00:00,48.72,22.546999999999997,23.386999999999997,29.17 +2020-06-14 03:15:00,49.41,19.547,23.386999999999997,29.17 +2020-06-14 03:30:00,49.29,18.229,23.386999999999997,29.17 +2020-06-14 03:45:00,48.21,19.223,23.386999999999997,29.17 +2020-06-14 04:00:00,46.3,23.975,23.941999999999997,29.17 +2020-06-14 04:15:00,49.49,29.096999999999998,23.941999999999997,29.17 +2020-06-14 04:30:00,49.02,26.796999999999997,23.941999999999997,29.17 +2020-06-14 04:45:00,51.15,26.214000000000002,23.941999999999997,29.17 +2020-06-14 05:00:00,52.15,33.439,23.026,29.17 +2020-06-14 05:15:00,52.37,33.179,23.026,29.17 +2020-06-14 05:30:00,51.73,25.811,23.026,29.17 +2020-06-14 05:45:00,51.3,26.45,23.026,29.17 +2020-06-14 06:00:00,55.97,38.391,23.223000000000003,29.17 +2020-06-14 06:15:00,56.52,48.623999999999995,23.223000000000003,29.17 +2020-06-14 06:30:00,57.52,42.369,23.223000000000003,29.17 +2020-06-14 06:45:00,56.72,38.123000000000005,23.223000000000003,29.17 +2020-06-14 07:00:00,58.51,38.266999999999996,24.968000000000004,29.17 +2020-06-14 07:15:00,64.2,35.931999999999995,24.968000000000004,29.17 +2020-06-14 07:30:00,64.55,34.004,24.968000000000004,29.17 +2020-06-14 07:45:00,59.52,34.175,24.968000000000004,29.17 +2020-06-14 08:00:00,60.05,34.224000000000004,29.131,29.17 +2020-06-14 08:15:00,60.54,38.406,29.131,29.17 +2020-06-14 08:30:00,59.86,39.29,29.131,29.17 +2020-06-14 08:45:00,59.75,42.483999999999995,29.131,29.17 +2020-06-14 09:00:00,55.4,38.879,29.904,29.17 +2020-06-14 09:15:00,59.64,39.813,29.904,29.17 +2020-06-14 09:30:00,58.18,43.45,29.904,29.17 +2020-06-14 09:45:00,60.74,46.76,29.904,29.17 +2020-06-14 10:00:00,65.21,43.11600000000001,28.943,29.17 +2020-06-14 10:15:00,69.62,45.239,28.943,29.17 +2020-06-14 10:30:00,76.14,45.839,28.943,29.17 +2020-06-14 10:45:00,76.23,47.826,28.943,29.17 +2020-06-14 11:00:00,72.62,43.235,31.682,29.17 +2020-06-14 11:15:00,66.42,43.803999999999995,31.682,29.17 +2020-06-14 11:30:00,63.36,45.636,31.682,29.17 +2020-06-14 11:45:00,64.54,47.348,31.682,29.17 +2020-06-14 12:00:00,58.01,45.331,27.315,29.17 +2020-06-14 12:15:00,62.22,45.035,27.315,29.17 +2020-06-14 12:30:00,67.96,44.08,27.315,29.17 +2020-06-14 12:45:00,72.55,44.851000000000006,27.315,29.17 +2020-06-14 13:00:00,70.76,44.978,23.894000000000002,29.17 +2020-06-14 13:15:00,65.86,45.318000000000005,23.894000000000002,29.17 +2020-06-14 13:30:00,52.16,43.497,23.894000000000002,29.17 +2020-06-14 13:45:00,56.73,42.155,23.894000000000002,29.17 +2020-06-14 14:00:00,56.02,44.756,21.148000000000003,29.17 +2020-06-14 14:15:00,58.16,43.071999999999996,21.148000000000003,29.17 +2020-06-14 14:30:00,59.65,41.708999999999996,21.148000000000003,29.17 +2020-06-14 14:45:00,66.92,41.126999999999995,21.148000000000003,29.17 +2020-06-14 15:00:00,72.76,43.235,21.229,29.17 +2020-06-14 15:15:00,75.07,40.558,21.229,29.17 +2020-06-14 15:30:00,73.16,38.271,21.229,29.17 +2020-06-14 15:45:00,75.74,36.243,21.229,29.17 +2020-06-14 16:00:00,74.37,38.602,25.037,29.17 +2020-06-14 16:15:00,75.85,38.010999999999996,25.037,29.17 +2020-06-14 16:30:00,76.68,38.395,25.037,29.17 +2020-06-14 16:45:00,79.86,34.135,25.037,29.17 +2020-06-14 17:00:00,80.06,37.714,37.11,29.17 +2020-06-14 17:15:00,75.98,37.064,37.11,29.17 +2020-06-14 17:30:00,76.02,37.105,37.11,29.17 +2020-06-14 17:45:00,75.92,36.233000000000004,37.11,29.17 +2020-06-14 18:00:00,79.59,40.564,42.215,29.17 +2020-06-14 18:15:00,78.09,41.273999999999994,42.215,29.17 +2020-06-14 18:30:00,79.69,39.839,42.215,29.17 +2020-06-14 18:45:00,80.19,41.276,42.215,29.17 +2020-06-14 19:00:00,81.77,43.848,44.383,29.17 +2020-06-14 19:15:00,78.54,41.562,44.383,29.17 +2020-06-14 19:30:00,77.06,41.271,44.383,29.17 +2020-06-14 19:45:00,76.9,41.873999999999995,44.383,29.17 +2020-06-14 20:00:00,77.81,41.295,43.426,29.17 +2020-06-14 20:15:00,77.47,41.37,43.426,29.17 +2020-06-14 20:30:00,78.68,41.177,43.426,29.17 +2020-06-14 20:45:00,80.51,41.136,43.426,29.17 +2020-06-14 21:00:00,78.65,39.095,42.265,29.17 +2020-06-14 21:15:00,80.2,41.913999999999994,42.265,29.17 +2020-06-14 21:30:00,77.65,42.313,42.265,29.17 +2020-06-14 21:45:00,77.23,43.458,42.265,29.17 +2020-06-14 22:00:00,72.55,42.913999999999994,42.26,29.17 +2020-06-14 22:15:00,73.29,43.146,42.26,29.17 +2020-06-14 22:30:00,72.39,42.07899999999999,42.26,29.17 +2020-06-14 22:45:00,72.99,39.211,42.26,29.17 +2020-06-14 23:00:00,66.65,35.281,36.609,29.17 +2020-06-14 23:15:00,66.5,33.724000000000004,36.609,29.17 +2020-06-14 23:30:00,66.47,32.531,36.609,29.17 +2020-06-14 23:45:00,66.58,32.023,36.609,29.17 +2020-06-15 00:00:00,63.68,27.921999999999997,34.611,29.28 +2020-06-15 00:15:00,66.57,27.576,34.611,29.28 +2020-06-15 00:30:00,66.22,25.934,34.611,29.28 +2020-06-15 00:45:00,67.82,24.68,34.611,29.28 +2020-06-15 01:00:00,66.38,25.049,33.552,29.28 +2020-06-15 01:15:00,63.83,24.105,33.552,29.28 +2020-06-15 01:30:00,65.85,22.554000000000002,33.552,29.28 +2020-06-15 01:45:00,63.4,22.485,33.552,29.28 +2020-06-15 02:00:00,64.21,22.454,32.351,29.28 +2020-06-15 02:15:00,65.04,19.855,32.351,29.28 +2020-06-15 02:30:00,65.8,22.451,32.351,29.28 +2020-06-15 02:45:00,71.81,22.92,32.351,29.28 +2020-06-15 03:00:00,71.74,25.247,30.793000000000003,29.28 +2020-06-15 03:15:00,73.07,23.074,30.793000000000003,29.28 +2020-06-15 03:30:00,67.77,22.386999999999997,30.793000000000003,29.28 +2020-06-15 03:45:00,68.94,22.915,30.793000000000003,29.28 +2020-06-15 04:00:00,70.83,30.949,31.274,29.28 +2020-06-15 04:15:00,72.74,39.173,31.274,29.28 +2020-06-15 04:30:00,76.76,36.596,31.274,29.28 +2020-06-15 04:45:00,82.08,36.376999999999995,31.274,29.28 +2020-06-15 05:00:00,88.23,51.6,37.75,29.28 +2020-06-15 05:15:00,92.27,62.74100000000001,37.75,29.28 +2020-06-15 05:30:00,94.48,53.993,37.75,29.28 +2020-06-15 05:45:00,97.09,51.272,37.75,29.28 +2020-06-15 06:00:00,102.24,51.938,55.36,29.28 +2020-06-15 06:15:00,111.18,52.23,55.36,29.28 +2020-06-15 06:30:00,115.52,50.466,55.36,29.28 +2020-06-15 06:45:00,117.34,52.028,55.36,29.28 +2020-06-15 07:00:00,115.83,52.782,65.87,29.28 +2020-06-15 07:15:00,114.27,52.74100000000001,65.87,29.28 +2020-06-15 07:30:00,122.7,49.918,65.87,29.28 +2020-06-15 07:45:00,120.92,50.178999999999995,65.87,29.28 +2020-06-15 08:00:00,119.84,47.783,55.695,29.28 +2020-06-15 08:15:00,111.36,50.665,55.695,29.28 +2020-06-15 08:30:00,110.75,50.261,55.695,29.28 +2020-06-15 08:45:00,113.47,53.36,55.695,29.28 +2020-06-15 09:00:00,117.2,48.68899999999999,50.881,29.28 +2020-06-15 09:15:00,116.73,47.618,50.881,29.28 +2020-06-15 09:30:00,110.25,50.285,50.881,29.28 +2020-06-15 09:45:00,118.14,51.467,50.881,29.28 +2020-06-15 10:00:00,117.56,48.161,49.138000000000005,29.28 +2020-06-15 10:15:00,114.82,50.128,49.138000000000005,29.28 +2020-06-15 10:30:00,113.15,50.214,49.138000000000005,29.28 +2020-06-15 10:45:00,110.62,50.78,49.138000000000005,29.28 +2020-06-15 11:00:00,110.55,46.151,49.178000000000004,29.28 +2020-06-15 11:15:00,113.2,47.161,49.178000000000004,29.28 +2020-06-15 11:30:00,114.7,49.771,49.178000000000004,29.28 +2020-06-15 11:45:00,107.65,51.922,49.178000000000004,29.28 +2020-06-15 12:00:00,103.58,48.492,47.698,29.28 +2020-06-15 12:15:00,100.76,48.303000000000004,47.698,29.28 +2020-06-15 12:30:00,98.31,46.336000000000006,47.698,29.28 +2020-06-15 12:45:00,100.35,47.248000000000005,47.698,29.28 +2020-06-15 13:00:00,98.72,48.282,48.104,29.28 +2020-06-15 13:15:00,97.1,47.658,48.104,29.28 +2020-06-15 13:30:00,98.57,45.958,48.104,29.28 +2020-06-15 13:45:00,100.45,45.47,48.104,29.28 +2020-06-15 14:00:00,109.23,47.168,48.53,29.28 +2020-06-15 14:15:00,107.1,45.983999999999995,48.53,29.28 +2020-06-15 14:30:00,105.45,44.391000000000005,48.53,29.28 +2020-06-15 14:45:00,107.37,45.77,48.53,29.28 +2020-06-15 15:00:00,105.82,47.726000000000006,49.351000000000006,29.28 +2020-06-15 15:15:00,101.42,44.35,49.351000000000006,29.28 +2020-06-15 15:30:00,93.89,42.706,49.351000000000006,29.28 +2020-06-15 15:45:00,102.39,40.167,49.351000000000006,29.28 +2020-06-15 16:00:00,103.23,43.58,51.44,29.28 +2020-06-15 16:15:00,103.03,42.976000000000006,51.44,29.28 +2020-06-15 16:30:00,103.38,42.66,51.44,29.28 +2020-06-15 16:45:00,105.31,38.287,51.44,29.28 +2020-06-15 17:00:00,110.64,40.8,56.868,29.28 +2020-06-15 17:15:00,108.12,40.414,56.868,29.28 +2020-06-15 17:30:00,107.45,40.03,56.868,29.28 +2020-06-15 17:45:00,108.12,38.626,56.868,29.28 +2020-06-15 18:00:00,107.0,41.968,57.229,29.28 +2020-06-15 18:15:00,105.19,40.717,57.229,29.28 +2020-06-15 18:30:00,103.36,38.615,57.229,29.28 +2020-06-15 18:45:00,103.93,43.123999999999995,57.229,29.28 +2020-06-15 19:00:00,101.36,45.273,57.744,29.28 +2020-06-15 19:15:00,97.27,44.143,57.744,29.28 +2020-06-15 19:30:00,94.43,43.535,57.744,29.28 +2020-06-15 19:45:00,94.45,43.476000000000006,57.744,29.28 +2020-06-15 20:00:00,93.01,41.475,66.05199999999999,29.28 +2020-06-15 20:15:00,93.07,42.67,66.05199999999999,29.28 +2020-06-15 20:30:00,92.72,42.825,66.05199999999999,29.28 +2020-06-15 20:45:00,92.0,43.159,66.05199999999999,29.28 +2020-06-15 21:00:00,90.53,40.566,59.396,29.28 +2020-06-15 21:15:00,89.26,43.715,59.396,29.28 +2020-06-15 21:30:00,86.15,44.418,59.396,29.28 +2020-06-15 21:45:00,83.94,45.318999999999996,59.396,29.28 +2020-06-15 22:00:00,79.53,42.593999999999994,53.06,29.28 +2020-06-15 22:15:00,79.12,44.623999999999995,53.06,29.28 +2020-06-15 22:30:00,76.82,38.604,53.06,29.28 +2020-06-15 22:45:00,77.6,35.296,53.06,29.28 +2020-06-15 23:00:00,71.23,31.458000000000002,46.148,29.28 +2020-06-15 23:15:00,71.79,28.58,46.148,29.28 +2020-06-15 23:30:00,73.2,27.590999999999998,46.148,29.28 +2020-06-15 23:45:00,71.26,26.781,46.148,29.28 +2020-06-16 00:00:00,68.96,24.359,44.625,29.28 +2020-06-16 00:15:00,69.29,25.055999999999997,44.625,29.28 +2020-06-16 00:30:00,68.44,23.929000000000002,44.625,29.28 +2020-06-16 00:45:00,68.79,23.45,44.625,29.28 +2020-06-16 01:00:00,66.84,23.256,41.733000000000004,29.28 +2020-06-16 01:15:00,68.54,22.443,41.733000000000004,29.28 +2020-06-16 01:30:00,67.64,20.79,41.733000000000004,29.28 +2020-06-16 01:45:00,68.65,20.428,41.733000000000004,29.28 +2020-06-16 02:00:00,68.14,19.965999999999998,39.872,29.28 +2020-06-16 02:15:00,67.92,18.408,39.872,29.28 +2020-06-16 02:30:00,67.69,20.671999999999997,39.872,29.28 +2020-06-16 02:45:00,68.5,21.430999999999997,39.872,29.28 +2020-06-16 03:00:00,68.84,23.045,38.711,29.28 +2020-06-16 03:15:00,70.43,21.794,38.711,29.28 +2020-06-16 03:30:00,71.46,21.061999999999998,38.711,29.28 +2020-06-16 03:45:00,71.24,20.377,38.711,29.28 +2020-06-16 04:00:00,78.78,26.963,39.823,29.28 +2020-06-16 04:15:00,83.37,34.927,39.823,29.28 +2020-06-16 04:30:00,86.33,32.236999999999995,39.823,29.28 +2020-06-16 04:45:00,84.83,32.514,39.823,29.28 +2020-06-16 05:00:00,89.13,48.638999999999996,43.228,29.28 +2020-06-16 05:15:00,95.5,59.591,43.228,29.28 +2020-06-16 05:30:00,102.96,51.088,43.228,29.28 +2020-06-16 05:45:00,106.21,47.806999999999995,43.228,29.28 +2020-06-16 06:00:00,110.04,49.923,54.316,29.28 +2020-06-16 06:15:00,106.31,50.313,54.316,29.28 +2020-06-16 06:30:00,112.22,48.407,54.316,29.28 +2020-06-16 06:45:00,116.7,49.299,54.316,29.28 +2020-06-16 07:00:00,118.4,50.067,65.758,29.28 +2020-06-16 07:15:00,114.45,49.853,65.758,29.28 +2020-06-16 07:30:00,120.01,47.098,65.758,29.28 +2020-06-16 07:45:00,121.47,46.503,65.758,29.28 +2020-06-16 08:00:00,122.52,43.562,57.983000000000004,29.28 +2020-06-16 08:15:00,120.87,46.042,57.983000000000004,29.28 +2020-06-16 08:30:00,119.62,46.19,57.983000000000004,29.28 +2020-06-16 08:45:00,120.59,48.538000000000004,57.983000000000004,29.28 +2020-06-16 09:00:00,119.48,43.669,52.653,29.28 +2020-06-16 09:15:00,115.43,42.684,52.653,29.28 +2020-06-16 09:30:00,115.25,46.261,52.653,29.28 +2020-06-16 09:45:00,119.15,48.981,52.653,29.28 +2020-06-16 10:00:00,124.46,43.623000000000005,51.408,29.28 +2020-06-16 10:15:00,124.81,45.39,51.408,29.28 +2020-06-16 10:30:00,116.98,45.55,51.408,29.28 +2020-06-16 10:45:00,119.25,47.229,51.408,29.28 +2020-06-16 11:00:00,119.52,43.428000000000004,51.913000000000004,29.28 +2020-06-16 11:15:00,106.77,44.788000000000004,51.913000000000004,29.28 +2020-06-16 11:30:00,105.75,46.257,51.913000000000004,29.28 +2020-06-16 11:45:00,111.19,48.073,51.913000000000004,29.28 +2020-06-16 12:00:00,110.92,44.413000000000004,49.508,29.28 +2020-06-16 12:15:00,110.35,44.346000000000004,49.508,29.28 +2020-06-16 12:30:00,105.59,43.29,49.508,29.28 +2020-06-16 12:45:00,115.07,44.753,49.508,29.28 +2020-06-16 13:00:00,112.54,45.398,50.007,29.28 +2020-06-16 13:15:00,111.53,46.568999999999996,50.007,29.28 +2020-06-16 13:30:00,107.86,44.854,50.007,29.28 +2020-06-16 13:45:00,113.1,43.668,50.007,29.28 +2020-06-16 14:00:00,111.48,45.714,49.778999999999996,29.28 +2020-06-16 14:15:00,108.96,44.407,49.778999999999996,29.28 +2020-06-16 14:30:00,105.74,43.292,49.778999999999996,29.28 +2020-06-16 14:45:00,106.26,43.869,49.778999999999996,29.28 +2020-06-16 15:00:00,102.33,45.617,51.559,29.28 +2020-06-16 15:15:00,103.42,43.174,51.559,29.28 +2020-06-16 15:30:00,111.85,41.39,51.559,29.28 +2020-06-16 15:45:00,116.04,39.3,51.559,29.28 +2020-06-16 16:00:00,111.53,42.037,53.531000000000006,29.28 +2020-06-16 16:15:00,109.87,41.598,53.531000000000006,29.28 +2020-06-16 16:30:00,107.38,40.802,53.531000000000006,29.28 +2020-06-16 16:45:00,111.98,37.24,53.531000000000006,29.28 +2020-06-16 17:00:00,113.93,40.24,59.497,29.28 +2020-06-16 17:15:00,112.65,40.22,59.497,29.28 +2020-06-16 17:30:00,108.82,39.359,59.497,29.28 +2020-06-16 17:45:00,114.35,37.766,59.497,29.28 +2020-06-16 18:00:00,115.88,40.343,59.861999999999995,29.28 +2020-06-16 18:15:00,116.48,40.538000000000004,59.861999999999995,29.28 +2020-06-16 18:30:00,107.47,38.215,59.861999999999995,29.28 +2020-06-16 18:45:00,107.61,42.318000000000005,59.861999999999995,29.28 +2020-06-16 19:00:00,111.62,42.979,60.989,29.28 +2020-06-16 19:15:00,106.07,42.001000000000005,60.989,29.28 +2020-06-16 19:30:00,102.41,41.065,60.989,29.28 +2020-06-16 19:45:00,95.32,41.147,60.989,29.28 +2020-06-16 20:00:00,92.13,38.826,68.35600000000001,29.28 +2020-06-16 20:15:00,94.97,38.798,68.35600000000001,29.28 +2020-06-16 20:30:00,91.06,39.229,68.35600000000001,29.28 +2020-06-16 20:45:00,94.21,39.921,68.35600000000001,29.28 +2020-06-16 21:00:00,89.7,38.23,59.251000000000005,29.28 +2020-06-16 21:15:00,89.12,40.126999999999995,59.251000000000005,29.28 +2020-06-16 21:30:00,86.59,41.136,59.251000000000005,29.28 +2020-06-16 21:45:00,85.8,42.263999999999996,59.251000000000005,29.28 +2020-06-16 22:00:00,81.71,39.696,54.736999999999995,29.28 +2020-06-16 22:15:00,82.26,41.648,54.736999999999995,29.28 +2020-06-16 22:30:00,79.77,36.277,54.736999999999995,29.28 +2020-06-16 22:45:00,81.67,32.983000000000004,54.736999999999995,29.28 +2020-06-16 23:00:00,74.51,28.76,46.806999999999995,29.28 +2020-06-16 23:15:00,74.79,27.148000000000003,46.806999999999995,29.28 +2020-06-16 23:30:00,82.15,25.996,46.806999999999995,29.28 +2020-06-16 23:45:00,74.96,25.283,46.806999999999995,29.28 +2020-06-17 00:00:00,70.38,24.259,43.824,29.28 +2020-06-17 00:15:00,71.92,24.956,43.824,29.28 +2020-06-17 00:30:00,70.72,23.831999999999997,43.824,29.28 +2020-06-17 00:45:00,71.52,23.358,43.824,29.28 +2020-06-17 01:00:00,69.75,23.180999999999997,39.86,29.28 +2020-06-17 01:15:00,70.66,22.351999999999997,39.86,29.28 +2020-06-17 01:30:00,70.25,20.694000000000003,39.86,29.28 +2020-06-17 01:45:00,70.36,20.323,39.86,29.28 +2020-06-17 02:00:00,69.85,19.865,37.931999999999995,29.28 +2020-06-17 02:15:00,69.97,18.305999999999997,37.931999999999995,29.28 +2020-06-17 02:30:00,77.19,20.566,37.931999999999995,29.28 +2020-06-17 02:45:00,79.52,21.33,37.931999999999995,29.28 +2020-06-17 03:00:00,75.47,22.943,37.579,29.28 +2020-06-17 03:15:00,71.44,21.693,37.579,29.28 +2020-06-17 03:30:00,73.65,20.968000000000004,37.579,29.28 +2020-06-17 03:45:00,74.61,20.305,37.579,29.28 +2020-06-17 04:00:00,75.19,26.839000000000002,37.931999999999995,29.28 +2020-06-17 04:15:00,77.28,34.758,37.931999999999995,29.28 +2020-06-17 04:30:00,80.23,32.056,37.931999999999995,29.28 +2020-06-17 04:45:00,86.39,32.33,37.931999999999995,29.28 +2020-06-17 05:00:00,92.75,48.354,40.942,29.28 +2020-06-17 05:15:00,94.78,59.178000000000004,40.942,29.28 +2020-06-17 05:30:00,102.45,50.743,40.942,29.28 +2020-06-17 05:45:00,103.25,47.508,40.942,29.28 +2020-06-17 06:00:00,109.39,49.636,56.516999999999996,29.28 +2020-06-17 06:15:00,108.76,50.011,56.516999999999996,29.28 +2020-06-17 06:30:00,106.58,48.129,56.516999999999996,29.28 +2020-06-17 06:45:00,115.87,49.049,56.516999999999996,29.28 +2020-06-17 07:00:00,120.05,49.805,71.707,29.28 +2020-06-17 07:15:00,118.84,49.608999999999995,71.707,29.28 +2020-06-17 07:30:00,112.36,46.847,71.707,29.28 +2020-06-17 07:45:00,116.82,46.285,71.707,29.28 +2020-06-17 08:00:00,118.62,43.354,61.17,29.28 +2020-06-17 08:15:00,120.54,45.867,61.17,29.28 +2020-06-17 08:30:00,112.25,46.007,61.17,29.28 +2020-06-17 08:45:00,110.41,48.36,61.17,29.28 +2020-06-17 09:00:00,112.65,43.483999999999995,57.282,29.28 +2020-06-17 09:15:00,116.01,42.503,57.282,29.28 +2020-06-17 09:30:00,115.28,46.07899999999999,57.282,29.28 +2020-06-17 09:45:00,117.39,48.817,57.282,29.28 +2020-06-17 10:00:00,119.51,43.468,54.026,29.28 +2020-06-17 10:15:00,118.12,45.247,54.026,29.28 +2020-06-17 10:30:00,115.99,45.409,54.026,29.28 +2020-06-17 10:45:00,112.31,47.093999999999994,54.026,29.28 +2020-06-17 11:00:00,117.86,43.288999999999994,54.277,29.28 +2020-06-17 11:15:00,118.81,44.655,54.277,29.28 +2020-06-17 11:30:00,112.48,46.113,54.277,29.28 +2020-06-17 11:45:00,106.68,47.93,54.277,29.28 +2020-06-17 12:00:00,107.8,44.3,52.552,29.28 +2020-06-17 12:15:00,109.75,44.235,52.552,29.28 +2020-06-17 12:30:00,109.97,43.158,52.552,29.28 +2020-06-17 12:45:00,106.0,44.623000000000005,52.552,29.28 +2020-06-17 13:00:00,99.76,45.25899999999999,52.111999999999995,29.28 +2020-06-17 13:15:00,101.66,46.431999999999995,52.111999999999995,29.28 +2020-06-17 13:30:00,106.4,44.727,52.111999999999995,29.28 +2020-06-17 13:45:00,109.32,43.545,52.111999999999995,29.28 +2020-06-17 14:00:00,108.0,45.607,52.066,29.28 +2020-06-17 14:15:00,102.64,44.297,52.066,29.28 +2020-06-17 14:30:00,105.38,43.159,52.066,29.28 +2020-06-17 14:45:00,107.65,43.742,52.066,29.28 +2020-06-17 15:00:00,106.16,45.522,52.523999999999994,29.28 +2020-06-17 15:15:00,98.78,43.071000000000005,52.523999999999994,29.28 +2020-06-17 15:30:00,97.44,41.277,52.523999999999994,29.28 +2020-06-17 15:45:00,102.09,39.176,52.523999999999994,29.28 +2020-06-17 16:00:00,102.74,41.942,54.101000000000006,29.28 +2020-06-17 16:15:00,111.61,41.501000000000005,54.101000000000006,29.28 +2020-06-17 16:30:00,112.07,40.723,54.101000000000006,29.28 +2020-06-17 16:45:00,111.13,37.138000000000005,54.101000000000006,29.28 +2020-06-17 17:00:00,107.3,40.161,58.155,29.28 +2020-06-17 17:15:00,106.07,40.135,58.155,29.28 +2020-06-17 17:30:00,109.67,39.269,58.155,29.28 +2020-06-17 17:45:00,109.22,37.658,58.155,29.28 +2020-06-17 18:00:00,115.64,40.247,60.205,29.28 +2020-06-17 18:15:00,115.65,40.415,60.205,29.28 +2020-06-17 18:30:00,112.11,38.086999999999996,60.205,29.28 +2020-06-17 18:45:00,108.54,42.19,60.205,29.28 +2020-06-17 19:00:00,111.91,42.853,61.568999999999996,29.28 +2020-06-17 19:15:00,107.65,41.867,61.568999999999996,29.28 +2020-06-17 19:30:00,104.81,40.923,61.568999999999996,29.28 +2020-06-17 19:45:00,100.79,41.0,61.568999999999996,29.28 +2020-06-17 20:00:00,95.94,38.662,68.145,29.28 +2020-06-17 20:15:00,95.76,38.63,68.145,29.28 +2020-06-17 20:30:00,95.13,39.07,68.145,29.28 +2020-06-17 20:45:00,95.74,39.789,68.145,29.28 +2020-06-17 21:00:00,93.35,38.104,59.696000000000005,29.28 +2020-06-17 21:15:00,92.86,40.008,59.696000000000005,29.28 +2020-06-17 21:30:00,90.82,40.994,59.696000000000005,29.28 +2020-06-17 21:45:00,88.68,42.13,59.696000000000005,29.28 +2020-06-17 22:00:00,84.2,39.58,54.861999999999995,29.28 +2020-06-17 22:15:00,84.82,41.541000000000004,54.861999999999995,29.28 +2020-06-17 22:30:00,81.78,36.164,54.861999999999995,29.28 +2020-06-17 22:45:00,80.67,32.86,54.861999999999995,29.28 +2020-06-17 23:00:00,74.94,28.62,45.568000000000005,29.28 +2020-06-17 23:15:00,76.51,27.043000000000003,45.568000000000005,29.28 +2020-06-17 23:30:00,75.84,25.903000000000002,45.568000000000005,29.28 +2020-06-17 23:45:00,75.6,25.178,45.568000000000005,29.28 +2020-06-18 00:00:00,73.4,24.163,40.181,29.28 +2020-06-18 00:15:00,72.87,24.86,40.181,29.28 +2020-06-18 00:30:00,72.34,23.739,40.181,29.28 +2020-06-18 00:45:00,73.84,23.271,40.181,29.28 +2020-06-18 01:00:00,71.31,23.109,38.296,29.28 +2020-06-18 01:15:00,72.98,22.264,38.296,29.28 +2020-06-18 01:30:00,73.12,20.601999999999997,38.296,29.28 +2020-06-18 01:45:00,72.86,20.224,38.296,29.28 +2020-06-18 02:00:00,72.69,19.769000000000002,36.575,29.28 +2020-06-18 02:15:00,73.69,18.209,36.575,29.28 +2020-06-18 02:30:00,80.7,20.464000000000002,36.575,29.28 +2020-06-18 02:45:00,81.54,21.235,36.575,29.28 +2020-06-18 03:00:00,76.54,22.845,36.394,29.28 +2020-06-18 03:15:00,74.22,21.598000000000003,36.394,29.28 +2020-06-18 03:30:00,74.27,20.877,36.394,29.28 +2020-06-18 03:45:00,75.37,20.236,36.394,29.28 +2020-06-18 04:00:00,78.58,26.719,37.207,29.28 +2020-06-18 04:15:00,77.79,34.595,37.207,29.28 +2020-06-18 04:30:00,81.29,31.881,37.207,29.28 +2020-06-18 04:45:00,87.9,32.150999999999996,37.207,29.28 +2020-06-18 05:00:00,91.79,48.077,40.713,29.28 +2020-06-18 05:15:00,94.18,58.776,40.713,29.28 +2020-06-18 05:30:00,102.83,50.407,40.713,29.28 +2020-06-18 05:45:00,105.92,47.218999999999994,40.713,29.28 +2020-06-18 06:00:00,112.75,49.357,50.952,29.28 +2020-06-18 06:15:00,110.24,49.718999999999994,50.952,29.28 +2020-06-18 06:30:00,106.85,47.858000000000004,50.952,29.28 +2020-06-18 06:45:00,108.84,48.808,50.952,29.28 +2020-06-18 07:00:00,116.96,49.552,64.88,29.28 +2020-06-18 07:15:00,117.84,49.375,64.88,29.28 +2020-06-18 07:30:00,118.78,46.605,64.88,29.28 +2020-06-18 07:45:00,110.21,46.077,64.88,29.28 +2020-06-18 08:00:00,112.37,43.156000000000006,55.133,29.28 +2020-06-18 08:15:00,120.59,45.7,55.133,29.28 +2020-06-18 08:30:00,116.62,45.832,55.133,29.28 +2020-06-18 08:45:00,113.88,48.188,55.133,29.28 +2020-06-18 09:00:00,111.01,43.306000000000004,48.912,29.28 +2020-06-18 09:15:00,116.08,42.32899999999999,48.912,29.28 +2020-06-18 09:30:00,116.1,45.903999999999996,48.912,29.28 +2020-06-18 09:45:00,110.88,48.659,48.912,29.28 +2020-06-18 10:00:00,111.72,43.321000000000005,45.968999999999994,29.28 +2020-06-18 10:15:00,116.87,45.11,45.968999999999994,29.28 +2020-06-18 10:30:00,119.29,45.273999999999994,45.968999999999994,29.28 +2020-06-18 10:45:00,117.36,46.965,45.968999999999994,29.28 +2020-06-18 11:00:00,115.58,43.156000000000006,44.067,29.28 +2020-06-18 11:15:00,108.97,44.528,44.067,29.28 +2020-06-18 11:30:00,104.55,45.975,44.067,29.28 +2020-06-18 11:45:00,104.96,47.792,44.067,29.28 +2020-06-18 12:00:00,117.07,44.193000000000005,41.501000000000005,29.28 +2020-06-18 12:15:00,118.98,44.129,41.501000000000005,29.28 +2020-06-18 12:30:00,111.45,43.032,41.501000000000005,29.28 +2020-06-18 12:45:00,105.65,44.498999999999995,41.501000000000005,29.28 +2020-06-18 13:00:00,107.61,45.126000000000005,41.117,29.28 +2020-06-18 13:15:00,111.84,46.298,41.117,29.28 +2020-06-18 13:30:00,112.94,44.604,41.117,29.28 +2020-06-18 13:45:00,114.13,43.426,41.117,29.28 +2020-06-18 14:00:00,110.3,45.501999999999995,41.492,29.28 +2020-06-18 14:15:00,112.76,44.191,41.492,29.28 +2020-06-18 14:30:00,115.17,43.031000000000006,41.492,29.28 +2020-06-18 14:45:00,117.92,43.619,41.492,29.28 +2020-06-18 15:00:00,115.32,45.431000000000004,43.711999999999996,29.28 +2020-06-18 15:15:00,123.12,42.968999999999994,43.711999999999996,29.28 +2020-06-18 15:30:00,122.03,41.169,43.711999999999996,29.28 +2020-06-18 15:45:00,118.04,39.056999999999995,43.711999999999996,29.28 +2020-06-18 16:00:00,118.78,41.851000000000006,45.446000000000005,29.28 +2020-06-18 16:15:00,112.38,41.406000000000006,45.446000000000005,29.28 +2020-06-18 16:30:00,118.25,40.647,45.446000000000005,29.28 +2020-06-18 16:45:00,122.87,37.039,45.446000000000005,29.28 +2020-06-18 17:00:00,115.45,40.086999999999996,48.803000000000004,29.28 +2020-06-18 17:15:00,111.22,40.056,48.803000000000004,29.28 +2020-06-18 17:30:00,107.59,39.183,48.803000000000004,29.28 +2020-06-18 17:45:00,109.13,37.556999999999995,48.803000000000004,29.28 +2020-06-18 18:00:00,114.28,40.154,51.167,29.28 +2020-06-18 18:15:00,108.94,40.298,51.167,29.28 +2020-06-18 18:30:00,104.66,37.966,51.167,29.28 +2020-06-18 18:45:00,108.51,42.067,51.167,29.28 +2020-06-18 19:00:00,107.19,42.733000000000004,52.486000000000004,29.28 +2020-06-18 19:15:00,97.5,41.736999999999995,52.486000000000004,29.28 +2020-06-18 19:30:00,95.56,40.787,52.486000000000004,29.28 +2020-06-18 19:45:00,94.72,40.857,52.486000000000004,29.28 +2020-06-18 20:00:00,92.52,38.503,59.635,29.28 +2020-06-18 20:15:00,92.24,38.47,59.635,29.28 +2020-06-18 20:30:00,92.21,38.916,59.635,29.28 +2020-06-18 20:45:00,92.12,39.663000000000004,59.635,29.28 +2020-06-18 21:00:00,90.57,37.983000000000004,54.353,29.28 +2020-06-18 21:15:00,89.89,39.894,54.353,29.28 +2020-06-18 21:30:00,85.01,40.857,54.353,29.28 +2020-06-18 21:45:00,85.71,42.0,54.353,29.28 +2020-06-18 22:00:00,80.59,39.469,49.431999999999995,29.28 +2020-06-18 22:15:00,80.6,41.437,49.431999999999995,29.28 +2020-06-18 22:30:00,78.43,36.054,49.431999999999995,29.28 +2020-06-18 22:45:00,78.92,32.741,49.431999999999995,29.28 +2020-06-18 23:00:00,74.17,28.486,42.872,29.28 +2020-06-18 23:15:00,75.08,26.943,42.872,29.28 +2020-06-18 23:30:00,71.55,25.811,42.872,29.28 +2020-06-18 23:45:00,74.47,25.078000000000003,42.872,29.28 +2020-06-19 00:00:00,71.17,22.217,39.819,29.28 +2020-06-19 00:15:00,70.01,23.143,39.819,29.28 +2020-06-19 00:30:00,67.45,22.285999999999998,39.819,29.28 +2020-06-19 00:45:00,69.33,22.254,39.819,29.28 +2020-06-19 01:00:00,66.99,21.705,37.797,29.28 +2020-06-19 01:15:00,71.13,20.305,37.797,29.28 +2020-06-19 01:30:00,76.08,19.335,37.797,29.28 +2020-06-19 01:45:00,76.62,18.707,37.797,29.28 +2020-06-19 02:00:00,72.72,19.19,36.905,29.28 +2020-06-19 02:15:00,76.55,17.572,36.905,29.28 +2020-06-19 02:30:00,75.24,20.678,36.905,29.28 +2020-06-19 02:45:00,72.74,20.775,36.905,29.28 +2020-06-19 03:00:00,70.27,23.093000000000004,37.1,29.28 +2020-06-19 03:15:00,72.1,20.705,37.1,29.28 +2020-06-19 03:30:00,71.55,19.752,37.1,29.28 +2020-06-19 03:45:00,73.4,20.027,37.1,29.28 +2020-06-19 04:00:00,75.24,26.611,37.882,29.28 +2020-06-19 04:15:00,77.24,32.875,37.882,29.28 +2020-06-19 04:30:00,79.93,31.11,37.882,29.28 +2020-06-19 04:45:00,81.23,30.662,37.882,29.28 +2020-06-19 05:00:00,90.71,45.931000000000004,40.777,29.28 +2020-06-19 05:15:00,98.92,57.556000000000004,40.777,29.28 +2020-06-19 05:30:00,104.38,49.518,40.777,29.28 +2020-06-19 05:45:00,106.32,45.928999999999995,40.777,29.28 +2020-06-19 06:00:00,107.95,48.331,55.528,29.28 +2020-06-19 06:15:00,110.49,48.743,55.528,29.28 +2020-06-19 06:30:00,109.88,46.794,55.528,29.28 +2020-06-19 06:45:00,113.13,47.756,55.528,29.28 +2020-06-19 07:00:00,124.83,49.074,67.749,29.28 +2020-06-19 07:15:00,124.36,49.885,67.749,29.28 +2020-06-19 07:30:00,123.58,45.167,67.749,29.28 +2020-06-19 07:45:00,117.62,44.401,67.749,29.28 +2020-06-19 08:00:00,115.83,42.183,57.55,29.28 +2020-06-19 08:15:00,118.6,45.393,57.55,29.28 +2020-06-19 08:30:00,123.82,45.49100000000001,57.55,29.28 +2020-06-19 08:45:00,128.81,47.595,57.55,29.28 +2020-06-19 09:00:00,126.71,40.461999999999996,52.588,29.28 +2020-06-19 09:15:00,114.96,41.42100000000001,52.588,29.28 +2020-06-19 09:30:00,122.02,44.286,52.588,29.28 +2020-06-19 09:45:00,127.81,47.428999999999995,52.588,29.28 +2020-06-19 10:00:00,122.67,41.851000000000006,49.772,29.28 +2020-06-19 10:15:00,122.54,43.483000000000004,49.772,29.28 +2020-06-19 10:30:00,122.79,44.187,49.772,29.28 +2020-06-19 10:45:00,121.18,45.756,49.772,29.28 +2020-06-19 11:00:00,122.52,42.196999999999996,49.226000000000006,29.28 +2020-06-19 11:15:00,124.31,42.438,49.226000000000006,29.28 +2020-06-19 11:30:00,125.71,43.64,49.226000000000006,29.28 +2020-06-19 11:45:00,123.32,44.515,49.226000000000006,29.28 +2020-06-19 12:00:00,114.63,41.446999999999996,45.705,29.28 +2020-06-19 12:15:00,122.04,40.604,45.705,29.28 +2020-06-19 12:30:00,117.45,39.614000000000004,45.705,29.28 +2020-06-19 12:45:00,117.38,40.341,45.705,29.28 +2020-06-19 13:00:00,106.5,41.613,43.133,29.28 +2020-06-19 13:15:00,107.87,43.023,43.133,29.28 +2020-06-19 13:30:00,118.64,42.123999999999995,43.133,29.28 +2020-06-19 13:45:00,120.24,41.25,43.133,29.28 +2020-06-19 14:00:00,114.0,42.461999999999996,41.989,29.28 +2020-06-19 14:15:00,101.38,41.568000000000005,41.989,29.28 +2020-06-19 14:30:00,104.98,41.913000000000004,41.989,29.28 +2020-06-19 14:45:00,100.54,41.845,41.989,29.28 +2020-06-19 15:00:00,102.98,43.575,43.728,29.28 +2020-06-19 15:15:00,95.22,40.839,43.728,29.28 +2020-06-19 15:30:00,100.15,38.366,43.728,29.28 +2020-06-19 15:45:00,103.04,37.007,43.728,29.28 +2020-06-19 16:00:00,102.24,38.91,45.93899999999999,29.28 +2020-06-19 16:15:00,97.18,38.972,45.93899999999999,29.28 +2020-06-19 16:30:00,99.11,38.058,45.93899999999999,29.28 +2020-06-19 16:45:00,103.2,33.641999999999996,45.93899999999999,29.28 +2020-06-19 17:00:00,108.34,38.445,50.488,29.28 +2020-06-19 17:15:00,102.87,38.21,50.488,29.28 +2020-06-19 17:30:00,103.68,37.475,50.488,29.28 +2020-06-19 17:45:00,106.28,35.653,50.488,29.28 +2020-06-19 18:00:00,106.78,38.35,52.408,29.28 +2020-06-19 18:15:00,100.21,37.504,52.408,29.28 +2020-06-19 18:30:00,105.97,35.091,52.408,29.28 +2020-06-19 18:45:00,105.56,39.607,52.408,29.28 +2020-06-19 19:00:00,102.94,41.207,52.736000000000004,29.28 +2020-06-19 19:15:00,96.06,40.876999999999995,52.736000000000004,29.28 +2020-06-19 19:30:00,98.92,39.952,52.736000000000004,29.28 +2020-06-19 19:45:00,97.55,38.978,52.736000000000004,29.28 +2020-06-19 20:00:00,95.78,36.455999999999996,59.68,29.28 +2020-06-19 20:15:00,97.37,37.21,59.68,29.28 +2020-06-19 20:30:00,92.83,37.177,59.68,29.28 +2020-06-19 20:45:00,91.07,37.179,59.68,29.28 +2020-06-19 21:00:00,94.6,36.84,54.343999999999994,29.28 +2020-06-19 21:15:00,92.39,40.45,54.343999999999994,29.28 +2020-06-19 21:30:00,85.76,41.243,54.343999999999994,29.28 +2020-06-19 21:45:00,83.91,42.629,54.343999999999994,29.28 +2020-06-19 22:00:00,82.56,40.035,49.672,29.28 +2020-06-19 22:15:00,84.43,41.754,49.672,29.28 +2020-06-19 22:30:00,80.48,41.489,49.672,29.28 +2020-06-19 22:45:00,74.58,39.236,49.672,29.28 +2020-06-19 23:00:00,68.57,36.621,42.065,29.28 +2020-06-19 23:15:00,75.75,33.433,42.065,29.28 +2020-06-19 23:30:00,75.04,30.408,42.065,29.28 +2020-06-19 23:45:00,71.81,29.502,42.065,29.28 +2020-06-20 00:00:00,66.09,23.177,38.829,29.17 +2020-06-20 00:15:00,61.95,23.165,38.829,29.17 +2020-06-20 00:30:00,69.46,21.93,38.829,29.17 +2020-06-20 00:45:00,70.54,21.221,38.829,29.17 +2020-06-20 01:00:00,69.8,21.002,34.63,29.17 +2020-06-20 01:15:00,64.85,20.109,34.63,29.17 +2020-06-20 01:30:00,62.76,18.302,34.63,29.17 +2020-06-20 01:45:00,68.84,18.891,34.63,29.17 +2020-06-20 02:00:00,66.79,18.442,32.465,29.17 +2020-06-20 02:15:00,62.57,16.019000000000002,32.465,29.17 +2020-06-20 02:30:00,62.89,18.262,32.465,29.17 +2020-06-20 02:45:00,62.21,19.153,32.465,29.17 +2020-06-20 03:00:00,63.7,20.147000000000002,31.925,29.17 +2020-06-20 03:15:00,68.4,16.994,31.925,29.17 +2020-06-20 03:30:00,69.28,16.274,31.925,29.17 +2020-06-20 03:45:00,66.18,18.046,31.925,29.17 +2020-06-20 04:00:00,64.28,22.455,31.309,29.17 +2020-06-20 04:15:00,67.18,27.634,31.309,29.17 +2020-06-20 04:30:00,67.69,24.068,31.309,29.17 +2020-06-20 04:45:00,65.96,23.858,31.309,29.17 +2020-06-20 05:00:00,64.27,29.932,30.323,29.17 +2020-06-20 05:15:00,63.38,29.461,30.323,29.17 +2020-06-20 05:30:00,66.59,23.021,30.323,29.17 +2020-06-20 05:45:00,73.18,24.226,30.323,29.17 +2020-06-20 06:00:00,77.68,38.798,31.438000000000002,29.17 +2020-06-20 06:15:00,75.5,47.883,31.438000000000002,29.17 +2020-06-20 06:30:00,73.46,42.826,31.438000000000002,29.17 +2020-06-20 06:45:00,70.8,40.108000000000004,31.438000000000002,29.17 +2020-06-20 07:00:00,78.74,39.754,34.891999999999996,29.17 +2020-06-20 07:15:00,82.01,39.227,34.891999999999996,29.17 +2020-06-20 07:30:00,82.18,36.159,34.891999999999996,29.17 +2020-06-20 07:45:00,82.18,36.577,34.891999999999996,29.17 +2020-06-20 08:00:00,80.59,35.275,39.608000000000004,29.17 +2020-06-20 08:15:00,79.69,38.524,39.608000000000004,29.17 +2020-06-20 08:30:00,85.25,38.698,39.608000000000004,29.17 +2020-06-20 08:45:00,92.26,41.931000000000004,39.608000000000004,29.17 +2020-06-20 09:00:00,94.83,37.946,40.894,29.17 +2020-06-20 09:15:00,91.28,39.433,40.894,29.17 +2020-06-20 09:30:00,90.28,42.839,40.894,29.17 +2020-06-20 09:45:00,90.39,45.566,40.894,29.17 +2020-06-20 10:00:00,96.46,40.611,39.525,29.17 +2020-06-20 10:15:00,102.27,42.614,39.525,29.17 +2020-06-20 10:30:00,103.51,42.993,39.525,29.17 +2020-06-20 10:45:00,99.42,44.306000000000004,39.525,29.17 +2020-06-20 11:00:00,94.12,40.628,36.718,29.17 +2020-06-20 11:15:00,91.99,41.68600000000001,36.718,29.17 +2020-06-20 11:30:00,90.43,43.083999999999996,36.718,29.17 +2020-06-20 11:45:00,91.18,44.558,36.718,29.17 +2020-06-20 12:00:00,88.9,41.86600000000001,35.688,29.17 +2020-06-20 12:15:00,87.4,41.9,35.688,29.17 +2020-06-20 12:30:00,86.31,40.782,35.688,29.17 +2020-06-20 12:45:00,85.13,42.178999999999995,35.688,29.17 +2020-06-20 13:00:00,84.77,42.562,32.858000000000004,29.17 +2020-06-20 13:15:00,85.22,43.309,32.858000000000004,29.17 +2020-06-20 13:30:00,84.09,42.575,32.858000000000004,29.17 +2020-06-20 13:45:00,82.67,40.41,32.858000000000004,29.17 +2020-06-20 14:00:00,81.43,41.786,31.738000000000003,29.17 +2020-06-20 14:15:00,81.85,39.626,31.738000000000003,29.17 +2020-06-20 14:30:00,81.06,39.414,31.738000000000003,29.17 +2020-06-20 14:45:00,82.67,39.839,31.738000000000003,29.17 +2020-06-20 15:00:00,82.96,42.092,34.35,29.17 +2020-06-20 15:15:00,80.52,40.094,34.35,29.17 +2020-06-20 15:30:00,81.22,37.909,34.35,29.17 +2020-06-20 15:45:00,82.66,35.633,34.35,29.17 +2020-06-20 16:00:00,85.43,39.605,37.522,29.17 +2020-06-20 16:15:00,84.06,38.836999999999996,37.522,29.17 +2020-06-20 16:30:00,83.74,38.161,37.522,29.17 +2020-06-20 16:45:00,84.53,33.751999999999995,37.522,29.17 +2020-06-20 17:00:00,86.97,37.381,42.498000000000005,29.17 +2020-06-20 17:15:00,87.28,35.118,42.498000000000005,29.17 +2020-06-20 17:30:00,88.27,34.24,42.498000000000005,29.17 +2020-06-20 17:45:00,87.85,32.866,42.498000000000005,29.17 +2020-06-20 18:00:00,88.24,36.915,44.701,29.17 +2020-06-20 18:15:00,86.6,37.805,44.701,29.17 +2020-06-20 18:30:00,88.94,36.808,44.701,29.17 +2020-06-20 18:45:00,84.9,37.689,44.701,29.17 +2020-06-20 19:00:00,81.63,37.759,45.727,29.17 +2020-06-20 19:15:00,76.92,36.394,45.727,29.17 +2020-06-20 19:30:00,78.37,36.266999999999996,45.727,29.17 +2020-06-20 19:45:00,75.8,37.018,45.727,29.17 +2020-06-20 20:00:00,74.14,35.438,43.391000000000005,29.17 +2020-06-20 20:15:00,71.76,35.691,43.391000000000005,29.17 +2020-06-20 20:30:00,72.4,34.777,43.391000000000005,29.17 +2020-06-20 20:45:00,71.24,36.609,43.391000000000005,29.17 +2020-06-20 21:00:00,70.11,34.961,41.231,29.17 +2020-06-20 21:15:00,68.91,38.260999999999996,41.231,29.17 +2020-06-20 21:30:00,66.55,39.293,41.231,29.17 +2020-06-20 21:45:00,64.82,40.121,41.231,29.17 +2020-06-20 22:00:00,64.11,37.602,40.798,29.17 +2020-06-20 22:15:00,62.69,39.724000000000004,40.798,29.17 +2020-06-20 22:30:00,62.16,39.457,40.798,29.17 +2020-06-20 22:45:00,58.77,37.674,40.798,29.17 +2020-06-20 23:00:00,54.05,34.529,34.402,29.17 +2020-06-20 23:15:00,54.66,31.689,34.402,29.17 +2020-06-20 23:30:00,53.59,30.991,34.402,29.17 +2020-06-20 23:45:00,53.51,30.281999999999996,34.402,29.17 +2020-06-21 00:00:00,51.37,24.479,30.171,29.17 +2020-06-21 00:15:00,51.6,23.31,30.171,29.17 +2020-06-21 00:30:00,51.28,21.910999999999998,30.171,29.17 +2020-06-21 00:45:00,50.78,21.142,30.171,29.17 +2020-06-21 01:00:00,47.84,21.191,27.15,29.17 +2020-06-21 01:15:00,50.1,20.225,27.15,29.17 +2020-06-21 01:30:00,49.76,18.312,27.15,29.17 +2020-06-21 01:45:00,49.33,18.498,27.15,29.17 +2020-06-21 02:00:00,48.37,18.084,25.403000000000002,29.17 +2020-06-21 02:15:00,48.73,16.308,25.403000000000002,29.17 +2020-06-21 02:30:00,48.89,18.782,25.403000000000002,29.17 +2020-06-21 02:45:00,48.92,19.419,25.403000000000002,29.17 +2020-06-21 03:00:00,48.71,21.072,23.386999999999997,29.17 +2020-06-21 03:15:00,49.4,18.149,23.386999999999997,29.17 +2020-06-21 03:30:00,50.71,16.795,23.386999999999997,29.17 +2020-06-21 03:45:00,50.44,17.798,23.386999999999997,29.17 +2020-06-21 04:00:00,49.72,22.112,23.941999999999997,29.17 +2020-06-21 04:15:00,50.86,26.76,23.941999999999997,29.17 +2020-06-21 04:30:00,51.2,24.508000000000003,23.941999999999997,29.17 +2020-06-21 04:45:00,52.17,23.851999999999997,23.941999999999997,29.17 +2020-06-21 05:00:00,52.67,29.799,23.026,29.17 +2020-06-21 05:15:00,53.27,28.311,23.026,29.17 +2020-06-21 05:30:00,54.64,21.53,23.026,29.17 +2020-06-21 05:45:00,56.86,22.523000000000003,23.026,29.17 +2020-06-21 06:00:00,58.47,34.766999999999996,23.223000000000003,29.17 +2020-06-21 06:15:00,59.86,44.56100000000001,23.223000000000003,29.17 +2020-06-21 06:30:00,61.63,38.775,23.223000000000003,29.17 +2020-06-21 06:45:00,63.3,35.046,23.223000000000003,29.17 +2020-06-21 07:00:00,66.04,35.114000000000004,24.968000000000004,29.17 +2020-06-21 07:15:00,66.82,32.942,24.968000000000004,29.17 +2020-06-21 07:30:00,67.37,31.054000000000002,24.968000000000004,29.17 +2020-06-21 07:45:00,67.58,31.428,24.968000000000004,29.17 +2020-06-21 08:00:00,66.37,30.988000000000003,29.131,29.17 +2020-06-21 08:15:00,68.59,35.423,29.131,29.17 +2020-06-21 08:30:00,65.93,36.542,29.131,29.17 +2020-06-21 08:45:00,64.07,39.815,29.131,29.17 +2020-06-21 09:00:00,66.26,35.691,29.904,29.17 +2020-06-21 09:15:00,65.59,36.727,29.904,29.17 +2020-06-21 09:30:00,63.46,40.547,29.904,29.17 +2020-06-21 09:45:00,64.99,44.266000000000005,29.904,29.17 +2020-06-21 10:00:00,69.22,39.995,28.943,29.17 +2020-06-21 10:15:00,70.76,42.17,28.943,29.17 +2020-06-21 10:30:00,72.73,42.803000000000004,28.943,29.17 +2020-06-21 10:45:00,73.48,45.013999999999996,28.943,29.17 +2020-06-21 11:00:00,70.43,41.044,31.682,29.17 +2020-06-21 11:15:00,67.81,41.674,31.682,29.17 +2020-06-21 11:30:00,68.94,43.528999999999996,31.682,29.17 +2020-06-21 11:45:00,70.2,45.287,31.682,29.17 +2020-06-21 12:00:00,72.15,43.645,27.315,29.17 +2020-06-21 12:15:00,71.91,43.158,27.315,29.17 +2020-06-21 12:30:00,70.77,42.159,27.315,29.17 +2020-06-21 12:45:00,71.96,42.878,27.315,29.17 +2020-06-21 13:00:00,67.38,42.913000000000004,23.894000000000002,29.17 +2020-06-21 13:15:00,68.77,43.246,23.894000000000002,29.17 +2020-06-21 13:30:00,67.21,41.412,23.894000000000002,29.17 +2020-06-21 13:45:00,64.71,40.32,23.894000000000002,29.17 +2020-06-21 14:00:00,59.95,42.907,21.148000000000003,29.17 +2020-06-21 14:15:00,61.83,41.231,21.148000000000003,29.17 +2020-06-21 14:30:00,64.05,39.84,21.148000000000003,29.17 +2020-06-21 14:45:00,66.51,39.191,21.148000000000003,29.17 +2020-06-21 15:00:00,65.17,41.54,21.229,29.17 +2020-06-21 15:15:00,65.5,38.769,21.229,29.17 +2020-06-21 15:30:00,67.43,36.427,21.229,29.17 +2020-06-21 15:45:00,68.84,34.457,21.229,29.17 +2020-06-21 16:00:00,69.3,36.935,25.037,29.17 +2020-06-21 16:15:00,70.82,36.345,25.037,29.17 +2020-06-21 16:30:00,76.2,36.694,25.037,29.17 +2020-06-21 16:45:00,75.99,32.335,25.037,29.17 +2020-06-21 17:00:00,79.94,36.341,37.11,29.17 +2020-06-21 17:15:00,79.54,35.556,37.11,29.17 +2020-06-21 17:30:00,79.33,35.486,37.11,29.17 +2020-06-21 17:45:00,79.29,34.625,37.11,29.17 +2020-06-21 18:00:00,82.67,39.243,42.215,29.17 +2020-06-21 18:15:00,79.05,39.774,42.215,29.17 +2020-06-21 18:30:00,78.46,38.423,42.215,29.17 +2020-06-21 18:45:00,78.31,39.51,42.215,29.17 +2020-06-21 19:00:00,79.89,41.713,44.383,29.17 +2020-06-21 19:15:00,78.41,39.275,44.383,29.17 +2020-06-21 19:30:00,76.51,38.885999999999996,44.383,29.17 +2020-06-21 19:45:00,76.7,39.255,44.383,29.17 +2020-06-21 20:00:00,77.36,37.827,43.426,29.17 +2020-06-21 20:15:00,76.94,37.992,43.426,29.17 +2020-06-21 20:30:00,78.36,37.949,43.426,29.17 +2020-06-21 20:45:00,78.33,38.084,43.426,29.17 +2020-06-21 21:00:00,77.45,36.18,42.265,29.17 +2020-06-21 21:15:00,76.37,39.157,42.265,29.17 +2020-06-21 21:30:00,74.61,39.539,42.265,29.17 +2020-06-21 21:45:00,74.02,40.735,42.265,29.17 +2020-06-21 22:00:00,72.94,40.181,42.26,29.17 +2020-06-21 22:15:00,71.13,40.609,42.26,29.17 +2020-06-21 22:30:00,69.5,39.675,42.26,29.17 +2020-06-21 22:45:00,73.05,36.58,42.26,29.17 +2020-06-21 23:00:00,65.27,32.983000000000004,36.609,29.17 +2020-06-21 23:15:00,66.55,31.525,36.609,29.17 +2020-06-21 23:30:00,65.95,30.346,36.609,29.17 +2020-06-21 23:45:00,64.51,29.828000000000003,36.609,29.17 +2020-06-22 00:00:00,62.06,26.113000000000003,34.611,29.28 +2020-06-22 00:15:00,63.43,25.878,34.611,29.28 +2020-06-22 00:30:00,63.24,24.111,34.611,29.28 +2020-06-22 00:45:00,62.85,22.93,34.611,29.28 +2020-06-22 01:00:00,61.06,23.374000000000002,33.552,29.28 +2020-06-22 01:15:00,62.06,22.379,33.552,29.28 +2020-06-22 01:30:00,62.07,20.820999999999998,33.552,29.28 +2020-06-22 01:45:00,62.34,20.910999999999998,33.552,29.28 +2020-06-22 02:00:00,61.38,20.945,32.351,29.28 +2020-06-22 02:15:00,62.36,18.246,32.351,29.28 +2020-06-22 02:30:00,62.99,20.894000000000002,32.351,29.28 +2020-06-22 02:45:00,69.91,21.36,32.351,29.28 +2020-06-22 03:00:00,71.83,23.574,30.793000000000003,29.28 +2020-06-22 03:15:00,74.38,21.448,30.793000000000003,29.28 +2020-06-22 03:30:00,68.44,20.76,30.793000000000003,29.28 +2020-06-22 03:45:00,72.17,21.3,30.793000000000003,29.28 +2020-06-22 04:00:00,73.67,28.855999999999998,31.274,29.28 +2020-06-22 04:15:00,81.48,36.568000000000005,31.274,29.28 +2020-06-22 04:30:00,85.31,33.944,31.274,29.28 +2020-06-22 04:45:00,87.89,33.66,31.274,29.28 +2020-06-22 05:00:00,90.15,47.393,37.75,29.28 +2020-06-22 05:15:00,92.28,57.013000000000005,37.75,29.28 +2020-06-22 05:30:00,98.06,48.794,37.75,29.28 +2020-06-22 05:45:00,104.24,46.501000000000005,37.75,29.28 +2020-06-22 06:00:00,114.07,47.684,55.36,29.28 +2020-06-22 06:15:00,113.59,47.718999999999994,55.36,29.28 +2020-06-22 06:30:00,120.64,46.295,55.36,29.28 +2020-06-22 06:45:00,117.35,48.265,55.36,29.28 +2020-06-22 07:00:00,125.32,48.82,65.87,29.28 +2020-06-22 07:15:00,124.38,48.982,65.87,29.28 +2020-06-22 07:30:00,123.8,46.201,65.87,29.28 +2020-06-22 07:45:00,120.42,46.763999999999996,65.87,29.28 +2020-06-22 08:00:00,127.99,43.946999999999996,55.695,29.28 +2020-06-22 08:15:00,124.81,47.11,55.695,29.28 +2020-06-22 08:30:00,122.97,47.053000000000004,55.695,29.28 +2020-06-22 08:45:00,126.89,50.353,55.695,29.28 +2020-06-22 09:00:00,125.76,45.153999999999996,50.881,29.28 +2020-06-22 09:15:00,128.65,44.243,50.881,29.28 +2020-06-22 09:30:00,129.68,47.093,50.881,29.28 +2020-06-22 09:45:00,120.54,48.583999999999996,50.881,29.28 +2020-06-22 10:00:00,114.39,44.678999999999995,49.138000000000005,29.28 +2020-06-22 10:15:00,122.56,46.699,49.138000000000005,29.28 +2020-06-22 10:30:00,122.96,46.832,49.138000000000005,29.28 +2020-06-22 10:45:00,125.29,47.528,49.138000000000005,29.28 +2020-06-22 11:00:00,120.65,43.623000000000005,49.178000000000004,29.28 +2020-06-22 11:15:00,114.96,44.631,49.178000000000004,29.28 +2020-06-22 11:30:00,112.14,47.236000000000004,49.178000000000004,29.28 +2020-06-22 11:45:00,104.36,49.468999999999994,49.178000000000004,29.28 +2020-06-22 12:00:00,104.38,46.236000000000004,47.698,29.28 +2020-06-22 12:15:00,103.64,45.858000000000004,47.698,29.28 +2020-06-22 12:30:00,103.64,43.793,47.698,29.28 +2020-06-22 12:45:00,102.45,44.586999999999996,47.698,29.28 +2020-06-22 13:00:00,96.63,45.56100000000001,48.104,29.28 +2020-06-22 13:15:00,99.66,44.949,48.104,29.28 +2020-06-22 13:30:00,103.52,43.265,48.104,29.28 +2020-06-22 13:45:00,107.28,43.06399999999999,48.104,29.28 +2020-06-22 14:00:00,103.1,44.723,48.53,29.28 +2020-06-22 14:15:00,98.24,43.601000000000006,48.53,29.28 +2020-06-22 14:30:00,97.96,41.998999999999995,48.53,29.28 +2020-06-22 14:45:00,98.66,43.403,48.53,29.28 +2020-06-22 15:00:00,96.96,45.501000000000005,49.351000000000006,29.28 +2020-06-22 15:15:00,95.96,42.066,49.351000000000006,29.28 +2020-06-22 15:30:00,103.76,40.439,49.351000000000006,29.28 +2020-06-22 15:45:00,101.27,37.955999999999996,49.351000000000006,29.28 +2020-06-22 16:00:00,100.46,41.551,51.44,29.28 +2020-06-22 16:15:00,102.13,40.983999999999995,51.44,29.28 +2020-06-22 16:30:00,101.46,40.643,51.44,29.28 +2020-06-22 16:45:00,101.72,36.227,51.44,29.28 +2020-06-22 17:00:00,104.77,39.12,56.868,29.28 +2020-06-22 17:15:00,101.5,38.658,56.868,29.28 +2020-06-22 17:30:00,104.47,38.171,56.868,29.28 +2020-06-22 17:45:00,106.24,36.828,56.868,29.28 +2020-06-22 18:00:00,107.49,40.416,57.229,29.28 +2020-06-22 18:15:00,104.01,38.998000000000005,57.229,29.28 +2020-06-22 18:30:00,102.52,36.928000000000004,57.229,29.28 +2020-06-22 18:45:00,102.38,41.181000000000004,57.229,29.28 +2020-06-22 19:00:00,100.87,43.022,57.744,29.28 +2020-06-22 19:15:00,100.34,41.828,57.744,29.28 +2020-06-22 19:30:00,96.04,41.091,57.744,29.28 +2020-06-22 19:45:00,97.08,40.805,57.744,29.28 +2020-06-22 20:00:00,93.07,37.99,66.05199999999999,29.28 +2020-06-22 20:15:00,93.95,39.416,66.05199999999999,29.28 +2020-06-22 20:30:00,95.77,39.804,66.05199999999999,29.28 +2020-06-22 20:45:00,99.45,40.259,66.05199999999999,29.28 +2020-06-22 21:00:00,94.07,37.760999999999996,59.396,29.28 +2020-06-22 21:15:00,93.7,41.123999999999995,59.396,29.28 +2020-06-22 21:30:00,90.26,41.853,59.396,29.28 +2020-06-22 21:45:00,88.36,42.81,59.396,29.28 +2020-06-22 22:00:00,84.6,40.099000000000004,53.06,29.28 +2020-06-22 22:15:00,85.31,42.441,53.06,29.28 +2020-06-22 22:30:00,82.83,36.732,53.06,29.28 +2020-06-22 22:45:00,81.6,33.37,53.06,29.28 +2020-06-22 23:00:00,76.8,29.838,46.148,29.28 +2020-06-22 23:15:00,77.46,26.904,46.148,29.28 +2020-06-22 23:30:00,78.9,25.831999999999997,46.148,29.28 +2020-06-22 23:45:00,78.36,24.899,46.148,29.28 +2020-06-23 00:00:00,73.53,23.746,44.625,29.28 +2020-06-23 00:15:00,75.33,24.444000000000003,44.625,29.28 +2020-06-23 00:30:00,74.92,23.34,44.625,29.28 +2020-06-23 00:45:00,74.6,22.901,44.625,29.28 +2020-06-23 01:00:00,73.27,22.811999999999998,41.733000000000004,29.28 +2020-06-23 01:15:00,75.07,21.89,41.733000000000004,29.28 +2020-06-23 01:30:00,73.73,20.209,41.733000000000004,29.28 +2020-06-23 01:45:00,74.21,19.797,41.733000000000004,29.28 +2020-06-23 02:00:00,73.89,19.355999999999998,39.872,29.28 +2020-06-23 02:15:00,73.43,17.804000000000002,39.872,29.28 +2020-06-23 02:30:00,80.85,20.026,39.872,29.28 +2020-06-23 02:45:00,82.26,20.824,39.872,29.28 +2020-06-23 03:00:00,78.93,22.423000000000002,38.711,29.28 +2020-06-23 03:15:00,75.46,21.189,38.711,29.28 +2020-06-23 03:30:00,78.81,20.5,38.711,29.28 +2020-06-23 03:45:00,76.4,19.961,38.711,29.28 +2020-06-23 04:00:00,84.82,26.201,39.823,29.28 +2020-06-23 04:15:00,90.66,33.869,39.823,29.28 +2020-06-23 04:30:00,92.85,31.098000000000003,39.823,29.28 +2020-06-23 04:45:00,92.53,31.357,39.823,29.28 +2020-06-23 05:00:00,96.07,46.823,43.228,29.28 +2020-06-23 05:15:00,98.92,56.934,43.228,29.28 +2020-06-23 05:30:00,106.98,48.886,43.228,29.28 +2020-06-23 05:45:00,113.0,45.909,43.228,29.28 +2020-06-23 06:00:00,118.57,48.091,54.316,29.28 +2020-06-23 06:15:00,120.37,48.391999999999996,54.316,29.28 +2020-06-23 06:30:00,119.5,46.638999999999996,54.316,29.28 +2020-06-23 06:45:00,123.82,47.731,54.316,29.28 +2020-06-23 07:00:00,124.88,48.418,65.758,29.28 +2020-06-23 07:15:00,122.94,48.336000000000006,65.758,29.28 +2020-06-23 07:30:00,120.91,45.54,65.758,29.28 +2020-06-23 07:45:00,125.1,45.174,65.758,29.28 +2020-06-23 08:00:00,122.4,42.302,57.983000000000004,29.28 +2020-06-23 08:15:00,118.74,44.994,57.983000000000004,29.28 +2020-06-23 08:30:00,118.28,45.086000000000006,57.983000000000004,29.28 +2020-06-23 08:45:00,122.7,47.455,57.983000000000004,29.28 +2020-06-23 09:00:00,122.76,42.547,52.653,29.28 +2020-06-23 09:15:00,122.01,41.583999999999996,52.653,29.28 +2020-06-23 09:30:00,120.53,45.15,52.653,29.28 +2020-06-23 09:45:00,118.95,47.977,52.653,29.28 +2020-06-23 10:00:00,130.1,42.687,51.408,29.28 +2020-06-23 10:15:00,123.77,44.523,51.408,29.28 +2020-06-23 10:30:00,123.38,44.691,51.408,29.28 +2020-06-23 10:45:00,116.88,46.407,51.408,29.28 +2020-06-23 11:00:00,116.32,42.581,51.913000000000004,29.28 +2020-06-23 11:15:00,111.31,43.983000000000004,51.913000000000004,29.28 +2020-06-23 11:30:00,115.49,45.371,51.913000000000004,29.28 +2020-06-23 11:45:00,107.36,47.188,51.913000000000004,29.28 +2020-06-23 12:00:00,107.78,43.729,49.508,29.28 +2020-06-23 12:15:00,112.97,43.67,49.508,29.28 +2020-06-23 12:30:00,115.46,42.479,49.508,29.28 +2020-06-23 12:45:00,111.06,43.953,49.508,29.28 +2020-06-23 13:00:00,110.37,44.533,50.007,29.28 +2020-06-23 13:15:00,106.59,45.705,50.007,29.28 +2020-06-23 13:30:00,108.56,44.053999999999995,50.007,29.28 +2020-06-23 13:45:00,117.29,42.903,50.007,29.28 +2020-06-23 14:00:00,107.17,45.04,49.778999999999996,29.28 +2020-06-23 14:15:00,109.31,43.724,49.778999999999996,29.28 +2020-06-23 14:30:00,107.16,42.458999999999996,49.778999999999996,29.28 +2020-06-23 14:45:00,97.6,43.075,49.778999999999996,29.28 +2020-06-23 15:00:00,108.88,45.023,51.559,29.28 +2020-06-23 15:15:00,109.4,42.522,51.559,29.28 +2020-06-23 15:30:00,106.06,40.688,51.559,29.28 +2020-06-23 15:45:00,108.71,38.524,51.559,29.28 +2020-06-23 16:00:00,110.94,41.452,53.531000000000006,29.28 +2020-06-23 16:15:00,110.74,40.99100000000001,53.531000000000006,29.28 +2020-06-23 16:30:00,113.66,40.326,53.531000000000006,29.28 +2020-06-23 16:45:00,109.97,36.621,53.531000000000006,29.28 +2020-06-23 17:00:00,116.03,39.773,59.497,29.28 +2020-06-23 17:15:00,115.14,39.727,59.497,29.28 +2020-06-23 17:30:00,117.22,38.826,59.497,29.28 +2020-06-23 17:45:00,112.66,37.13,59.497,29.28 +2020-06-23 18:00:00,114.14,39.775,59.861999999999995,29.28 +2020-06-23 18:15:00,114.54,39.8,59.861999999999995,29.28 +2020-06-23 18:30:00,116.69,37.45,59.861999999999995,29.28 +2020-06-23 18:45:00,112.08,41.543,59.861999999999995,29.28 +2020-06-23 19:00:00,104.05,42.224,60.989,29.28 +2020-06-23 19:15:00,100.72,41.187,60.989,29.28 +2020-06-23 19:30:00,98.01,40.196999999999996,60.989,29.28 +2020-06-23 19:45:00,97.06,40.241,60.989,29.28 +2020-06-23 20:00:00,96.0,37.812,68.35600000000001,29.28 +2020-06-23 20:15:00,96.1,37.766,68.35600000000001,29.28 +2020-06-23 20:30:00,95.0,38.244,68.35600000000001,29.28 +2020-06-23 20:45:00,98.48,39.116,68.35600000000001,29.28 +2020-06-23 21:00:00,95.1,37.458,59.251000000000005,29.28 +2020-06-23 21:15:00,93.78,39.403,59.251000000000005,29.28 +2020-06-23 21:30:00,89.52,40.253,59.251000000000005,29.28 +2020-06-23 21:45:00,88.7,41.42,59.251000000000005,29.28 +2020-06-23 22:00:00,83.01,38.971,54.736999999999995,29.28 +2020-06-23 22:15:00,84.33,40.971000000000004,54.736999999999995,29.28 +2020-06-23 22:30:00,82.74,35.55,54.736999999999995,29.28 +2020-06-23 22:45:00,81.15,32.186,54.736999999999995,29.28 +2020-06-23 23:00:00,78.29,27.874000000000002,46.806999999999995,29.28 +2020-06-23 23:15:00,77.68,26.494,46.806999999999995,29.28 +2020-06-23 23:30:00,76.77,25.414,46.806999999999995,29.28 +2020-06-23 23:45:00,75.36,24.633000000000003,46.806999999999995,29.28 +2020-06-24 00:00:00,72.75,23.675,43.824,29.28 +2020-06-24 00:15:00,73.59,24.374000000000002,43.824,29.28 +2020-06-24 00:30:00,73.6,23.273000000000003,43.824,29.28 +2020-06-24 00:45:00,73.92,22.840999999999998,43.824,29.28 +2020-06-24 01:00:00,72.73,22.764,39.86,29.28 +2020-06-24 01:15:00,73.28,21.828000000000003,39.86,29.28 +2020-06-24 01:30:00,72.51,20.145,39.86,29.28 +2020-06-24 01:45:00,73.33,19.726,39.86,29.28 +2020-06-24 02:00:00,77.46,19.287,37.931999999999995,29.28 +2020-06-24 02:15:00,82.15,17.739,37.931999999999995,29.28 +2020-06-24 02:30:00,81.13,19.952,37.931999999999995,29.28 +2020-06-24 02:45:00,74.48,20.756999999999998,37.931999999999995,29.28 +2020-06-24 03:00:00,77.13,22.351999999999997,37.579,29.28 +2020-06-24 03:15:00,76.09,21.122,37.579,29.28 +2020-06-24 03:30:00,76.54,20.439,37.579,29.28 +2020-06-24 03:45:00,76.21,19.92,37.579,29.28 +2020-06-24 04:00:00,84.58,26.114,37.931999999999995,29.28 +2020-06-24 04:15:00,88.31,33.742,37.931999999999995,29.28 +2020-06-24 04:30:00,92.93,30.961,37.931999999999995,29.28 +2020-06-24 04:45:00,91.92,31.218000000000004,37.931999999999995,29.28 +2020-06-24 05:00:00,95.34,46.599,40.942,29.28 +2020-06-24 05:15:00,98.24,56.599,40.942,29.28 +2020-06-24 05:30:00,101.16,48.613,40.942,29.28 +2020-06-24 05:45:00,103.36,45.676,40.942,29.28 +2020-06-24 06:00:00,111.68,47.864,56.516999999999996,29.28 +2020-06-24 06:15:00,116.87,48.153999999999996,56.516999999999996,29.28 +2020-06-24 06:30:00,116.87,46.42100000000001,56.516999999999996,29.28 +2020-06-24 06:45:00,117.03,47.542,56.516999999999996,29.28 +2020-06-24 07:00:00,116.69,48.217,71.707,29.28 +2020-06-24 07:15:00,116.48,48.155,71.707,29.28 +2020-06-24 07:30:00,116.84,45.356,71.707,29.28 +2020-06-24 07:45:00,112.07,45.023,71.707,29.28 +2020-06-24 08:00:00,109.37,42.161,61.17,29.28 +2020-06-24 08:15:00,108.4,44.878,61.17,29.28 +2020-06-24 08:30:00,114.26,44.963,61.17,29.28 +2020-06-24 08:45:00,114.72,47.333,61.17,29.28 +2020-06-24 09:00:00,112.86,42.42,57.282,29.28 +2020-06-24 09:15:00,108.55,41.458999999999996,57.282,29.28 +2020-06-24 09:30:00,118.09,45.023,57.282,29.28 +2020-06-24 09:45:00,123.97,47.863,57.282,29.28 +2020-06-24 10:00:00,122.12,42.582,54.026,29.28 +2020-06-24 10:15:00,117.01,44.424,54.026,29.28 +2020-06-24 10:30:00,123.16,44.593,54.026,29.28 +2020-06-24 10:45:00,121.18,46.31399999999999,54.026,29.28 +2020-06-24 11:00:00,123.52,42.483999999999995,54.277,29.28 +2020-06-24 11:15:00,117.69,43.891000000000005,54.277,29.28 +2020-06-24 11:30:00,115.43,45.269,54.277,29.28 +2020-06-24 11:45:00,119.14,47.083999999999996,54.277,29.28 +2020-06-24 12:00:00,124.99,43.653,52.552,29.28 +2020-06-24 12:15:00,127.32,43.593,52.552,29.28 +2020-06-24 12:30:00,126.8,42.385,52.552,29.28 +2020-06-24 12:45:00,117.73,43.86,52.552,29.28 +2020-06-24 13:00:00,101.49,44.428999999999995,52.111999999999995,29.28 +2020-06-24 13:15:00,105.29,45.601000000000006,52.111999999999995,29.28 +2020-06-24 13:30:00,116.19,43.958,52.111999999999995,29.28 +2020-06-24 13:45:00,110.54,42.81100000000001,52.111999999999995,29.28 +2020-06-24 14:00:00,99.9,44.958999999999996,52.066,29.28 +2020-06-24 14:15:00,102.05,43.643,52.066,29.28 +2020-06-24 14:30:00,110.45,42.358000000000004,52.066,29.28 +2020-06-24 14:45:00,109.15,42.979,52.066,29.28 +2020-06-24 15:00:00,108.94,44.952,52.523999999999994,29.28 +2020-06-24 15:15:00,101.7,42.445,52.523999999999994,29.28 +2020-06-24 15:30:00,107.34,40.605,52.523999999999994,29.28 +2020-06-24 15:45:00,106.1,38.431999999999995,52.523999999999994,29.28 +2020-06-24 16:00:00,104.67,41.382,54.101000000000006,29.28 +2020-06-24 16:15:00,105.58,40.921,54.101000000000006,29.28 +2020-06-24 16:30:00,112.42,40.273,54.101000000000006,29.28 +2020-06-24 16:45:00,110.04,36.552,54.101000000000006,29.28 +2020-06-24 17:00:00,111.19,39.722,58.155,29.28 +2020-06-24 17:15:00,108.5,39.675,58.155,29.28 +2020-06-24 17:30:00,112.41,38.77,58.155,29.28 +2020-06-24 17:45:00,117.25,37.063,58.155,29.28 +2020-06-24 18:00:00,116.0,39.716,60.205,29.28 +2020-06-24 18:15:00,111.3,39.717,60.205,29.28 +2020-06-24 18:30:00,108.34,37.365,60.205,29.28 +2020-06-24 18:45:00,107.8,41.457,60.205,29.28 +2020-06-24 19:00:00,103.84,42.141000000000005,61.568999999999996,29.28 +2020-06-24 19:15:00,101.09,41.096000000000004,61.568999999999996,29.28 +2020-06-24 19:30:00,102.04,40.098,61.568999999999996,29.28 +2020-06-24 19:45:00,97.68,40.138000000000005,61.568999999999996,29.28 +2020-06-24 20:00:00,95.61,37.694,68.145,29.28 +2020-06-24 20:15:00,94.76,37.645,68.145,29.28 +2020-06-24 20:30:00,94.42,38.129,68.145,29.28 +2020-06-24 20:45:00,94.83,39.023,68.145,29.28 +2020-06-24 21:00:00,96.89,37.37,59.696000000000005,29.28 +2020-06-24 21:15:00,94.06,39.32,59.696000000000005,29.28 +2020-06-24 21:30:00,90.13,40.148,59.696000000000005,29.28 +2020-06-24 21:45:00,88.79,41.318000000000005,59.696000000000005,29.28 +2020-06-24 22:00:00,84.68,38.884,54.861999999999995,29.28 +2020-06-24 22:15:00,87.11,40.889,54.861999999999995,29.28 +2020-06-24 22:30:00,84.09,35.459,54.861999999999995,29.28 +2020-06-24 22:45:00,84.16,32.086,54.861999999999995,29.28 +2020-06-24 23:00:00,79.54,27.765,45.568000000000005,29.28 +2020-06-24 23:15:00,76.73,26.414,45.568000000000005,29.28 +2020-06-24 23:30:00,75.86,25.346,45.568000000000005,29.28 +2020-06-24 23:45:00,78.52,24.555999999999997,45.568000000000005,29.28 +2020-06-25 00:00:00,75.55,23.608,40.181,29.28 +2020-06-25 00:15:00,75.77,24.307,40.181,29.28 +2020-06-25 00:30:00,72.44,23.21,40.181,29.28 +2020-06-25 00:45:00,75.39,22.784000000000002,40.181,29.28 +2020-06-25 01:00:00,72.29,22.72,38.296,29.28 +2020-06-25 01:15:00,74.01,21.771,38.296,29.28 +2020-06-25 01:30:00,72.67,20.085,38.296,29.28 +2020-06-25 01:45:00,72.74,19.66,38.296,29.28 +2020-06-25 02:00:00,73.25,19.224,36.575,29.28 +2020-06-25 02:15:00,74.48,17.678,36.575,29.28 +2020-06-25 02:30:00,74.61,19.884,36.575,29.28 +2020-06-25 02:45:00,74.51,20.693,36.575,29.28 +2020-06-25 03:00:00,75.05,22.285,36.394,29.28 +2020-06-25 03:15:00,76.2,21.059,36.394,29.28 +2020-06-25 03:30:00,83.01,20.384,36.394,29.28 +2020-06-25 03:45:00,85.84,19.883,36.394,29.28 +2020-06-25 04:00:00,80.84,26.031999999999996,37.207,29.28 +2020-06-25 04:15:00,81.65,33.622,37.207,29.28 +2020-06-25 04:30:00,84.58,30.829,37.207,29.28 +2020-06-25 04:45:00,88.67,31.086,37.207,29.28 +2020-06-25 05:00:00,97.63,46.383,40.713,29.28 +2020-06-25 05:15:00,98.52,56.276,40.713,29.28 +2020-06-25 05:30:00,107.02,48.352,40.713,29.28 +2020-06-25 05:45:00,107.47,45.452,40.713,29.28 +2020-06-25 06:00:00,115.01,47.646,50.952,29.28 +2020-06-25 06:15:00,111.77,47.926,50.952,29.28 +2020-06-25 06:30:00,117.15,46.214,50.952,29.28 +2020-06-25 06:45:00,118.68,47.363,50.952,29.28 +2020-06-25 07:00:00,115.38,48.026,64.88,29.28 +2020-06-25 07:15:00,114.08,47.983000000000004,64.88,29.28 +2020-06-25 07:30:00,113.71,45.181999999999995,64.88,29.28 +2020-06-25 07:45:00,114.99,44.88,64.88,29.28 +2020-06-25 08:00:00,113.05,42.027,55.133,29.28 +2020-06-25 08:15:00,112.54,44.771,55.133,29.28 +2020-06-25 08:30:00,110.54,44.848,55.133,29.28 +2020-06-25 08:45:00,116.65,47.22,55.133,29.28 +2020-06-25 09:00:00,118.67,42.302,48.912,29.28 +2020-06-25 09:15:00,119.01,41.343,48.912,29.28 +2020-06-25 09:30:00,116.77,44.903999999999996,48.912,29.28 +2020-06-25 09:45:00,122.25,47.756,48.912,29.28 +2020-06-25 10:00:00,123.53,42.483000000000004,45.968999999999994,29.28 +2020-06-25 10:15:00,129.33,44.333,45.968999999999994,29.28 +2020-06-25 10:30:00,122.87,44.501000000000005,45.968999999999994,29.28 +2020-06-25 10:45:00,119.98,46.227,45.968999999999994,29.28 +2020-06-25 11:00:00,120.33,42.395,44.067,29.28 +2020-06-25 11:15:00,118.71,43.806000000000004,44.067,29.28 +2020-06-25 11:30:00,112.17,45.172,44.067,29.28 +2020-06-25 11:45:00,113.06,46.986999999999995,44.067,29.28 +2020-06-25 12:00:00,123.41,43.581,41.501000000000005,29.28 +2020-06-25 12:15:00,127.98,43.521,41.501000000000005,29.28 +2020-06-25 12:30:00,129.51,42.29600000000001,41.501000000000005,29.28 +2020-06-25 12:45:00,125.72,43.772,41.501000000000005,29.28 +2020-06-25 13:00:00,119.23,44.33,41.117,29.28 +2020-06-25 13:15:00,120.12,45.5,41.117,29.28 +2020-06-25 13:30:00,125.64,43.867,41.117,29.28 +2020-06-25 13:45:00,128.35,42.726000000000006,41.117,29.28 +2020-06-25 14:00:00,128.81,44.882,41.492,29.28 +2020-06-25 14:15:00,125.95,43.567,41.492,29.28 +2020-06-25 14:30:00,120.48,42.263999999999996,41.492,29.28 +2020-06-25 14:45:00,120.88,42.89,41.492,29.28 +2020-06-25 15:00:00,123.63,44.88399999999999,43.711999999999996,29.28 +2020-06-25 15:15:00,121.05,42.37,43.711999999999996,29.28 +2020-06-25 15:30:00,122.08,40.525999999999996,43.711999999999996,29.28 +2020-06-25 15:45:00,120.83,38.344,43.711999999999996,29.28 +2020-06-25 16:00:00,127.83,41.317,45.446000000000005,29.28 +2020-06-25 16:15:00,122.67,40.853,45.446000000000005,29.28 +2020-06-25 16:30:00,122.29,40.224000000000004,45.446000000000005,29.28 +2020-06-25 16:45:00,126.06,36.488,45.446000000000005,29.28 +2020-06-25 17:00:00,123.94,39.675,48.803000000000004,29.28 +2020-06-25 17:15:00,123.31,39.628,48.803000000000004,29.28 +2020-06-25 17:30:00,120.34,38.719,48.803000000000004,29.28 +2020-06-25 17:45:00,114.21,37.001,48.803000000000004,29.28 +2020-06-25 18:00:00,118.99,39.661,51.167,29.28 +2020-06-25 18:15:00,118.56,39.641,51.167,29.28 +2020-06-25 18:30:00,119.87,37.286,51.167,29.28 +2020-06-25 18:45:00,112.47,41.376000000000005,51.167,29.28 +2020-06-25 19:00:00,107.94,42.06399999999999,52.486000000000004,29.28 +2020-06-25 19:15:00,104.55,41.011,52.486000000000004,29.28 +2020-06-25 19:30:00,102.42,40.006,52.486000000000004,29.28 +2020-06-25 19:45:00,103.35,40.04,52.486000000000004,29.28 +2020-06-25 20:00:00,97.55,37.582,59.635,29.28 +2020-06-25 20:15:00,97.58,37.531,59.635,29.28 +2020-06-25 20:30:00,98.76,38.021,59.635,29.28 +2020-06-25 20:45:00,98.12,38.935,59.635,29.28 +2020-06-25 21:00:00,96.92,37.287,54.353,29.28 +2020-06-25 21:15:00,92.89,39.243,54.353,29.28 +2020-06-25 21:30:00,90.17,40.049,54.353,29.28 +2020-06-25 21:45:00,88.89,41.221000000000004,54.353,29.28 +2020-06-25 22:00:00,85.17,38.8,49.431999999999995,29.28 +2020-06-25 22:15:00,84.56,40.81,49.431999999999995,29.28 +2020-06-25 22:30:00,81.54,35.371,49.431999999999995,29.28 +2020-06-25 22:45:00,80.23,31.987,49.431999999999995,29.28 +2020-06-25 23:00:00,78.24,27.659000000000002,42.872,29.28 +2020-06-25 23:15:00,77.2,26.339000000000002,42.872,29.28 +2020-06-25 23:30:00,76.09,25.281999999999996,42.872,29.28 +2020-06-25 23:45:00,74.95,24.483,42.872,29.28 +2020-06-26 00:00:00,73.91,21.691,39.819,29.28 +2020-06-26 00:15:00,73.66,22.618000000000002,39.819,29.28 +2020-06-26 00:30:00,73.5,21.787,39.819,29.28 +2020-06-26 00:45:00,73.41,21.796999999999997,39.819,29.28 +2020-06-26 01:00:00,72.61,21.344,37.797,29.28 +2020-06-26 01:15:00,73.69,19.842,37.797,29.28 +2020-06-26 01:30:00,73.2,18.852,37.797,29.28 +2020-06-26 01:45:00,73.02,18.176,37.797,29.28 +2020-06-26 02:00:00,77.1,18.678,36.905,29.28 +2020-06-26 02:15:00,81.05,17.077,36.905,29.28 +2020-06-26 02:30:00,79.47,20.13,36.905,29.28 +2020-06-26 02:45:00,73.65,20.267,36.905,29.28 +2020-06-26 03:00:00,76.43,22.564,37.1,29.28 +2020-06-26 03:15:00,75.3,20.2,37.1,29.28 +2020-06-26 03:30:00,76.44,19.292,37.1,29.28 +2020-06-26 03:45:00,77.02,19.705,37.1,29.28 +2020-06-26 04:00:00,77.12,25.962,37.882,29.28 +2020-06-26 04:15:00,84.19,31.944000000000003,37.882,29.28 +2020-06-26 04:30:00,91.07,30.104,37.882,29.28 +2020-06-26 04:45:00,88.15,29.641,37.882,29.28 +2020-06-26 05:00:00,93.96,44.298,40.777,29.28 +2020-06-26 05:15:00,96.44,55.133,40.777,29.28 +2020-06-26 05:30:00,99.55,47.538000000000004,40.777,29.28 +2020-06-26 05:45:00,102.21,44.23,40.777,29.28 +2020-06-26 06:00:00,105.49,46.681999999999995,55.528,29.28 +2020-06-26 06:15:00,106.56,47.016000000000005,55.528,29.28 +2020-06-26 06:30:00,111.38,45.214,55.528,29.28 +2020-06-26 06:45:00,113.94,46.373000000000005,55.528,29.28 +2020-06-26 07:00:00,119.24,47.61,67.749,29.28 +2020-06-26 07:15:00,120.18,48.556999999999995,67.749,29.28 +2020-06-26 07:30:00,118.22,43.81100000000001,67.749,29.28 +2020-06-26 07:45:00,111.2,43.27,67.749,29.28 +2020-06-26 08:00:00,111.58,41.121,57.55,29.28 +2020-06-26 08:15:00,114.37,44.523999999999994,57.55,29.28 +2020-06-26 08:30:00,114.99,44.568000000000005,57.55,29.28 +2020-06-26 08:45:00,113.74,46.685,57.55,29.28 +2020-06-26 09:00:00,109.16,39.516999999999996,52.588,29.28 +2020-06-26 09:15:00,113.26,40.493,52.588,29.28 +2020-06-26 09:30:00,112.71,43.34,52.588,29.28 +2020-06-26 09:45:00,110.41,46.576,52.588,29.28 +2020-06-26 10:00:00,105.72,41.063,49.772,29.28 +2020-06-26 10:15:00,112.85,42.751000000000005,49.772,29.28 +2020-06-26 10:30:00,112.6,43.458,49.772,29.28 +2020-06-26 10:45:00,111.95,45.062,49.772,29.28 +2020-06-26 11:00:00,106.15,41.48,49.226000000000006,29.28 +2020-06-26 11:15:00,102.95,41.75899999999999,49.226000000000006,29.28 +2020-06-26 11:30:00,108.99,42.881,49.226000000000006,29.28 +2020-06-26 11:45:00,107.29,43.751000000000005,49.226000000000006,29.28 +2020-06-26 12:00:00,102.93,40.873000000000005,45.705,29.28 +2020-06-26 12:15:00,97.81,40.031,45.705,29.28 +2020-06-26 12:30:00,99.61,38.917,45.705,29.28 +2020-06-26 12:45:00,101.18,39.65,45.705,29.28 +2020-06-26 13:00:00,99.27,40.851,43.133,29.28 +2020-06-26 13:15:00,96.97,42.258,43.133,29.28 +2020-06-26 13:30:00,91.8,41.42,43.133,29.28 +2020-06-26 13:45:00,90.58,40.583,43.133,29.28 +2020-06-26 14:00:00,89.92,41.871,41.989,29.28 +2020-06-26 14:15:00,91.04,40.973,41.989,29.28 +2020-06-26 14:30:00,96.86,41.178999999999995,41.989,29.28 +2020-06-26 14:45:00,96.54,41.148999999999994,41.989,29.28 +2020-06-26 15:00:00,96.12,43.053999999999995,43.728,29.28 +2020-06-26 15:15:00,92.58,40.266,43.728,29.28 +2020-06-26 15:30:00,92.76,37.751999999999995,43.728,29.28 +2020-06-26 15:45:00,99.14,36.326,43.728,29.28 +2020-06-26 16:00:00,102.55,38.402,45.93899999999999,29.28 +2020-06-26 16:15:00,102.02,38.446,45.93899999999999,29.28 +2020-06-26 16:30:00,97.83,37.661,45.93899999999999,29.28 +2020-06-26 16:45:00,104.14,33.126,45.93899999999999,29.28 +2020-06-26 17:00:00,107.5,38.062,50.488,29.28 +2020-06-26 17:15:00,108.79,37.815,50.488,29.28 +2020-06-26 17:30:00,102.7,37.045,50.488,29.28 +2020-06-26 17:45:00,108.93,35.137,50.488,29.28 +2020-06-26 18:00:00,110.72,37.895,52.408,29.28 +2020-06-26 18:15:00,106.92,36.888000000000005,52.408,29.28 +2020-06-26 18:30:00,100.88,34.454,52.408,29.28 +2020-06-26 18:45:00,99.23,38.957,52.408,29.28 +2020-06-26 19:00:00,92.97,40.580999999999996,52.736000000000004,29.28 +2020-06-26 19:15:00,93.69,40.194,52.736000000000004,29.28 +2020-06-26 19:30:00,98.71,39.216,52.736000000000004,29.28 +2020-06-26 19:45:00,97.1,38.205,52.736000000000004,29.28 +2020-06-26 20:00:00,93.03,35.582,59.68,29.28 +2020-06-26 20:15:00,87.14,36.32,59.68,29.28 +2020-06-26 20:30:00,89.87,36.326,59.68,29.28 +2020-06-26 20:45:00,87.72,36.488,59.68,29.28 +2020-06-26 21:00:00,86.85,36.183,54.343999999999994,29.28 +2020-06-26 21:15:00,86.7,39.836,54.343999999999994,29.28 +2020-06-26 21:30:00,87.8,40.472,54.343999999999994,29.28 +2020-06-26 21:45:00,87.75,41.882,54.343999999999994,29.28 +2020-06-26 22:00:00,84.36,39.395,49.672,29.28 +2020-06-26 22:15:00,81.7,41.153,49.672,29.28 +2020-06-26 22:30:00,76.68,40.829,49.672,29.28 +2020-06-26 22:45:00,74.71,38.507,49.672,29.28 +2020-06-26 23:00:00,71.08,35.825,42.065,29.28 +2020-06-26 23:15:00,71.04,32.855,42.065,29.28 +2020-06-26 23:30:00,66.37,29.904,42.065,29.28 +2020-06-26 23:45:00,69.55,28.935,42.065,29.28 +2020-06-27 00:00:00,65.06,22.680999999999997,38.829,29.17 +2020-06-27 00:15:00,66.26,22.67,38.829,29.17 +2020-06-27 00:30:00,67.1,21.462,38.829,29.17 +2020-06-27 00:45:00,66.18,20.795,38.829,29.17 +2020-06-27 01:00:00,63.32,20.668000000000003,34.63,29.17 +2020-06-27 01:15:00,71.54,19.678,34.63,29.17 +2020-06-27 01:30:00,69.12,17.852,34.63,29.17 +2020-06-27 01:45:00,70.08,18.394000000000002,34.63,29.17 +2020-06-27 02:00:00,62.8,17.964000000000002,32.465,29.17 +2020-06-27 02:15:00,62.11,15.561,32.465,29.17 +2020-06-27 02:30:00,65.37,17.749000000000002,32.465,29.17 +2020-06-27 02:45:00,68.43,18.676,32.465,29.17 +2020-06-27 03:00:00,68.92,19.649,31.925,29.17 +2020-06-27 03:15:00,68.83,16.522000000000002,31.925,29.17 +2020-06-27 03:30:00,65.01,15.847999999999999,31.925,29.17 +2020-06-27 03:45:00,60.82,17.756,31.925,29.17 +2020-06-27 04:00:00,61.0,21.845,31.309,29.17 +2020-06-27 04:15:00,68.68,26.747,31.309,29.17 +2020-06-27 04:30:00,68.89,23.107,31.309,29.17 +2020-06-27 04:45:00,67.89,22.884,31.309,29.17 +2020-06-27 05:00:00,63.25,28.361,30.323,29.17 +2020-06-27 05:15:00,62.79,27.12,30.323,29.17 +2020-06-27 05:30:00,67.71,21.116,30.323,29.17 +2020-06-27 05:45:00,71.77,22.594,30.323,29.17 +2020-06-27 06:00:00,75.97,37.208,31.438000000000002,29.17 +2020-06-27 06:15:00,71.83,46.221000000000004,31.438000000000002,29.17 +2020-06-27 06:30:00,70.47,41.309,31.438000000000002,29.17 +2020-06-27 06:45:00,72.75,38.786,31.438000000000002,29.17 +2020-06-27 07:00:00,76.92,38.353,34.891999999999996,29.17 +2020-06-27 07:15:00,77.53,37.963,34.891999999999996,29.17 +2020-06-27 07:30:00,75.96,34.871,34.891999999999996,29.17 +2020-06-27 07:45:00,74.03,35.513000000000005,34.891999999999996,29.17 +2020-06-27 08:00:00,75.68,34.279,39.608000000000004,29.17 +2020-06-27 08:15:00,76.83,37.715,39.608000000000004,29.17 +2020-06-27 08:30:00,81.14,37.835,39.608000000000004,29.17 +2020-06-27 08:45:00,76.55,41.08,39.608000000000004,29.17 +2020-06-27 09:00:00,72.19,37.061,40.894,29.17 +2020-06-27 09:15:00,77.15,38.563,40.894,29.17 +2020-06-27 09:30:00,77.14,41.949,40.894,29.17 +2020-06-27 09:45:00,75.67,44.763999999999996,40.894,29.17 +2020-06-27 10:00:00,75.84,39.874,39.525,29.17 +2020-06-27 10:15:00,73.35,41.927,39.525,29.17 +2020-06-27 10:30:00,70.92,42.309,39.525,29.17 +2020-06-27 10:45:00,72.03,43.653999999999996,39.525,29.17 +2020-06-27 11:00:00,67.93,39.957,36.718,29.17 +2020-06-27 11:15:00,67.39,41.049,36.718,29.17 +2020-06-27 11:30:00,68.26,42.368,36.718,29.17 +2020-06-27 11:45:00,65.04,43.833,36.718,29.17 +2020-06-27 12:00:00,61.59,41.327,35.688,29.17 +2020-06-27 12:15:00,61.46,41.361999999999995,35.688,29.17 +2020-06-27 12:30:00,59.79,40.125,35.688,29.17 +2020-06-27 12:45:00,61.07,41.525,35.688,29.17 +2020-06-27 13:00:00,56.5,41.835,32.858000000000004,29.17 +2020-06-27 13:15:00,57.59,42.577,32.858000000000004,29.17 +2020-06-27 13:30:00,57.29,41.902,32.858000000000004,29.17 +2020-06-27 13:45:00,57.27,39.775999999999996,32.858000000000004,29.17 +2020-06-27 14:00:00,56.82,41.222,31.738000000000003,29.17 +2020-06-27 14:15:00,57.12,39.06,31.738000000000003,29.17 +2020-06-27 14:30:00,57.83,38.715,31.738000000000003,29.17 +2020-06-27 14:45:00,60.56,39.176,31.738000000000003,29.17 +2020-06-27 15:00:00,59.67,41.595,34.35,29.17 +2020-06-27 15:15:00,59.31,39.548,34.35,29.17 +2020-06-27 15:30:00,60.2,37.325,34.35,29.17 +2020-06-27 15:45:00,61.46,34.984,34.35,29.17 +2020-06-27 16:00:00,62.44,39.122,37.522,29.17 +2020-06-27 16:15:00,63.64,38.339,37.522,29.17 +2020-06-27 16:30:00,66.31,37.792,37.522,29.17 +2020-06-27 16:45:00,67.86,33.27,37.522,29.17 +2020-06-27 17:00:00,70.65,37.027,42.498000000000005,29.17 +2020-06-27 17:15:00,71.24,34.755,42.498000000000005,29.17 +2020-06-27 17:30:00,73.98,33.844,42.498000000000005,29.17 +2020-06-27 17:45:00,75.04,32.39,42.498000000000005,29.17 +2020-06-27 18:00:00,77.44,36.498000000000005,44.701,29.17 +2020-06-27 18:15:00,76.21,37.229,44.701,29.17 +2020-06-27 18:30:00,76.3,36.213,44.701,29.17 +2020-06-27 18:45:00,76.3,37.082,44.701,29.17 +2020-06-27 19:00:00,76.28,37.175,45.727,29.17 +2020-06-27 19:15:00,73.26,35.755,45.727,29.17 +2020-06-27 19:30:00,72.4,35.575,45.727,29.17 +2020-06-27 19:45:00,70.94,36.29,45.727,29.17 +2020-06-27 20:00:00,71.82,34.611,43.391000000000005,29.17 +2020-06-27 20:15:00,71.23,34.848,43.391000000000005,29.17 +2020-06-27 20:30:00,71.26,33.97,43.391000000000005,29.17 +2020-06-27 20:45:00,72.04,35.955999999999996,43.391000000000005,29.17 +2020-06-27 21:00:00,74.06,34.341,41.231,29.17 +2020-06-27 21:15:00,74.57,37.683,41.231,29.17 +2020-06-27 21:30:00,70.48,38.56,41.231,29.17 +2020-06-27 21:45:00,70.41,39.407,41.231,29.17 +2020-06-27 22:00:00,62.13,36.991,40.798,29.17 +2020-06-27 22:15:00,64.93,39.149,40.798,29.17 +2020-06-27 22:30:00,60.41,38.819,40.798,29.17 +2020-06-27 22:45:00,62.8,36.968,40.798,29.17 +2020-06-27 23:00:00,59.35,33.760999999999996,34.402,29.17 +2020-06-27 23:15:00,59.05,31.136999999999997,34.402,29.17 +2020-06-27 23:30:00,58.0,30.514,34.402,29.17 +2020-06-27 23:45:00,57.81,29.741999999999997,34.402,29.17 +2020-06-28 00:00:00,52.34,24.013,30.171,29.17 +2020-06-28 00:15:00,55.36,22.846,30.171,29.17 +2020-06-28 00:30:00,54.45,21.474,30.171,29.17 +2020-06-28 00:45:00,54.19,20.747,30.171,29.17 +2020-06-28 01:00:00,53.18,20.885,27.15,29.17 +2020-06-28 01:15:00,50.44,19.824,27.15,29.17 +2020-06-28 01:30:00,52.82,17.895,27.15,29.17 +2020-06-28 01:45:00,53.02,18.034000000000002,27.15,29.17 +2020-06-28 02:00:00,52.68,17.64,25.403000000000002,29.17 +2020-06-28 02:15:00,52.39,15.887,25.403000000000002,29.17 +2020-06-28 02:30:00,52.08,18.303,25.403000000000002,29.17 +2020-06-28 02:45:00,52.53,18.977,25.403000000000002,29.17 +2020-06-28 03:00:00,52.73,20.605999999999998,23.386999999999997,29.17 +2020-06-28 03:15:00,53.01,17.711,23.386999999999997,29.17 +2020-06-28 03:30:00,52.27,16.403,23.386999999999997,29.17 +2020-06-28 03:45:00,52.48,17.54,23.386999999999997,29.17 +2020-06-28 04:00:00,51.85,21.539,23.941999999999997,29.17 +2020-06-28 04:15:00,51.64,25.916999999999998,23.941999999999997,29.17 +2020-06-28 04:30:00,52.18,23.593000000000004,23.941999999999997,29.17 +2020-06-28 04:45:00,52.88,22.923000000000002,23.941999999999997,29.17 +2020-06-28 05:00:00,52.47,28.29,23.026,29.17 +2020-06-28 05:15:00,52.43,26.049,23.026,29.17 +2020-06-28 05:30:00,49.33,19.701,23.026,29.17 +2020-06-28 05:45:00,52.47,20.958000000000002,23.026,29.17 +2020-06-28 06:00:00,53.34,33.241,23.223000000000003,29.17 +2020-06-28 06:15:00,53.67,42.965,23.223000000000003,29.17 +2020-06-28 06:30:00,54.36,37.324,23.223000000000003,29.17 +2020-06-28 06:45:00,54.97,33.788000000000004,23.223000000000003,29.17 +2020-06-28 07:00:00,54.97,33.775,24.968000000000004,29.17 +2020-06-28 07:15:00,54.23,31.741999999999997,24.968000000000004,29.17 +2020-06-28 07:30:00,53.95,29.835,24.968000000000004,29.17 +2020-06-28 07:45:00,53.99,30.430999999999997,24.968000000000004,29.17 +2020-06-28 08:00:00,54.31,30.06,29.131,29.17 +2020-06-28 08:15:00,53.99,34.674,29.131,29.17 +2020-06-28 08:30:00,54.29,35.741,29.131,29.17 +2020-06-28 08:45:00,55.38,39.021,29.131,29.17 +2020-06-28 09:00:00,53.19,34.866,29.904,29.17 +2020-06-28 09:15:00,54.67,35.915,29.904,29.17 +2020-06-28 09:30:00,54.92,39.713,29.904,29.17 +2020-06-28 09:45:00,55.03,43.516000000000005,29.904,29.17 +2020-06-28 10:00:00,56.5,39.309,28.943,29.17 +2020-06-28 10:15:00,57.14,41.528999999999996,28.943,29.17 +2020-06-28 10:30:00,58.29,42.163000000000004,28.943,29.17 +2020-06-28 10:45:00,58.46,44.405,28.943,29.17 +2020-06-28 11:00:00,57.77,40.417,31.682,29.17 +2020-06-28 11:15:00,55.17,41.08,31.682,29.17 +2020-06-28 11:30:00,54.11,42.855,31.682,29.17 +2020-06-28 11:45:00,54.05,44.603,31.682,29.17 +2020-06-28 12:00:00,52.24,43.143,27.315,29.17 +2020-06-28 12:15:00,54.17,42.653999999999996,27.315,29.17 +2020-06-28 12:30:00,51.79,41.54,27.315,29.17 +2020-06-28 12:45:00,52.87,42.261,27.315,29.17 +2020-06-28 13:00:00,48.04,42.218999999999994,23.894000000000002,29.17 +2020-06-28 13:15:00,48.71,42.547,23.894000000000002,29.17 +2020-06-28 13:30:00,47.9,40.772,23.894000000000002,29.17 +2020-06-28 13:45:00,51.51,39.719,23.894000000000002,29.17 +2020-06-28 14:00:00,51.52,42.372,21.148000000000003,29.17 +2020-06-28 14:15:00,49.54,40.695,21.148000000000003,29.17 +2020-06-28 14:30:00,49.02,39.174,21.148000000000003,29.17 +2020-06-28 14:45:00,52.42,38.562,21.148000000000003,29.17 +2020-06-28 15:00:00,52.14,41.068000000000005,21.229,29.17 +2020-06-28 15:15:00,52.45,38.25,21.229,29.17 +2020-06-28 15:30:00,53.38,35.873000000000005,21.229,29.17 +2020-06-28 15:45:00,50.9,33.839,21.229,29.17 +2020-06-28 16:00:00,52.91,36.479,25.037,29.17 +2020-06-28 16:15:00,55.27,35.875,25.037,29.17 +2020-06-28 16:30:00,56.98,36.352,25.037,29.17 +2020-06-28 16:45:00,61.07,31.888,25.037,29.17 +2020-06-28 17:00:00,63.55,36.014,37.11,29.17 +2020-06-28 17:15:00,65.49,35.226,37.11,29.17 +2020-06-28 17:30:00,66.42,35.125,37.11,29.17 +2020-06-28 17:45:00,67.75,34.19,37.11,29.17 +2020-06-28 18:00:00,72.02,38.864000000000004,42.215,29.17 +2020-06-28 18:15:00,71.79,39.239000000000004,42.215,29.17 +2020-06-28 18:30:00,71.81,37.871,42.215,29.17 +2020-06-28 18:45:00,73.23,38.946,42.215,29.17 +2020-06-28 19:00:00,75.51,41.174,44.383,29.17 +2020-06-28 19:15:00,72.91,38.681,44.383,29.17 +2020-06-28 19:30:00,72.05,38.239000000000004,44.383,29.17 +2020-06-28 19:45:00,71.62,38.571999999999996,44.383,29.17 +2020-06-28 20:00:00,72.23,37.048,43.426,29.17 +2020-06-28 20:15:00,72.93,37.198,43.426,29.17 +2020-06-28 20:30:00,75.71,37.187,43.426,29.17 +2020-06-28 20:45:00,76.24,37.47,43.426,29.17 +2020-06-28 21:00:00,79.17,35.599000000000004,42.265,29.17 +2020-06-28 21:15:00,79.66,38.616,42.265,29.17 +2020-06-28 21:30:00,77.53,38.846,42.265,29.17 +2020-06-28 21:45:00,76.34,40.054,42.265,29.17 +2020-06-28 22:00:00,71.7,39.599000000000004,42.26,29.17 +2020-06-28 22:15:00,73.09,40.06,42.26,29.17 +2020-06-28 22:30:00,70.56,39.058,42.26,29.17 +2020-06-28 22:45:00,71.0,35.898,42.26,29.17 +2020-06-28 23:00:00,64.44,32.248000000000005,36.609,29.17 +2020-06-28 23:15:00,64.62,30.998,36.609,29.17 +2020-06-28 23:30:00,61.55,29.895,36.609,29.17 +2020-06-28 23:45:00,63.88,29.316,36.609,29.17 +2020-06-29 00:00:00,66.83,25.676,34.611,29.28 +2020-06-29 00:15:00,67.56,25.444000000000003,34.611,29.28 +2020-06-29 00:30:00,66.99,23.704,34.611,29.28 +2020-06-29 00:45:00,65.2,22.566,34.611,29.28 +2020-06-29 01:00:00,61.33,23.096,33.552,29.28 +2020-06-29 01:15:00,61.25,22.009,33.552,29.28 +2020-06-29 01:30:00,68.73,20.438,33.552,29.28 +2020-06-29 01:45:00,69.26,20.480999999999998,33.552,29.28 +2020-06-29 02:00:00,67.73,20.535999999999998,32.351,29.28 +2020-06-29 02:15:00,64.1,17.863,32.351,29.28 +2020-06-29 02:30:00,65.25,20.449,32.351,29.28 +2020-06-29 02:45:00,62.46,20.95,32.351,29.28 +2020-06-29 03:00:00,63.93,23.139,30.793000000000003,29.28 +2020-06-29 03:15:00,64.66,21.044,30.793000000000003,29.28 +2020-06-29 03:30:00,64.42,20.402,30.793000000000003,29.28 +2020-06-29 03:45:00,65.17,21.073,30.793000000000003,29.28 +2020-06-29 04:00:00,69.29,28.322,31.274,29.28 +2020-06-29 04:15:00,69.39,35.769,31.274,29.28 +2020-06-29 04:30:00,73.21,33.074,31.274,29.28 +2020-06-29 04:45:00,76.37,32.777,31.274,29.28 +2020-06-29 05:00:00,84.05,45.946999999999996,37.75,29.28 +2020-06-29 05:15:00,89.26,54.832,37.75,29.28 +2020-06-29 05:30:00,91.77,47.041000000000004,37.75,29.28 +2020-06-29 05:45:00,100.7,45.004,37.75,29.28 +2020-06-29 06:00:00,106.18,46.221000000000004,55.36,29.28 +2020-06-29 06:15:00,104.78,46.18899999999999,55.36,29.28 +2020-06-29 06:30:00,101.12,44.909,55.36,29.28 +2020-06-29 06:45:00,106.27,47.068999999999996,55.36,29.28 +2020-06-29 07:00:00,110.35,47.54600000000001,65.87,29.28 +2020-06-29 07:15:00,106.69,47.847,65.87,29.28 +2020-06-29 07:30:00,102.74,45.05,65.87,29.28 +2020-06-29 07:45:00,98.31,45.833999999999996,65.87,29.28 +2020-06-29 08:00:00,101.86,43.085,55.695,29.28 +2020-06-29 08:15:00,104.63,46.422,55.695,29.28 +2020-06-29 08:30:00,103.4,46.312,55.695,29.28 +2020-06-29 08:45:00,98.22,49.619,55.695,29.28 +2020-06-29 09:00:00,97.83,44.388000000000005,50.881,29.28 +2020-06-29 09:15:00,98.5,43.49100000000001,50.881,29.28 +2020-06-29 09:30:00,97.26,46.31399999999999,50.881,29.28 +2020-06-29 09:45:00,99.53,47.886,50.881,29.28 +2020-06-29 10:00:00,105.48,44.043,49.138000000000005,29.28 +2020-06-29 10:15:00,107.62,46.105,49.138000000000005,29.28 +2020-06-29 10:30:00,106.69,46.236999999999995,49.138000000000005,29.28 +2020-06-29 10:45:00,101.86,46.96,49.138000000000005,29.28 +2020-06-29 11:00:00,108.46,43.041000000000004,49.178000000000004,29.28 +2020-06-29 11:15:00,104.0,44.08,49.178000000000004,29.28 +2020-06-29 11:30:00,102.52,46.606,49.178000000000004,29.28 +2020-06-29 11:45:00,97.39,48.825,49.178000000000004,29.28 +2020-06-29 12:00:00,95.56,45.77,47.698,29.28 +2020-06-29 12:15:00,98.26,45.39,47.698,29.28 +2020-06-29 12:30:00,92.89,43.213,47.698,29.28 +2020-06-29 12:45:00,91.17,44.008,47.698,29.28 +2020-06-29 13:00:00,90.56,44.903,48.104,29.28 +2020-06-29 13:15:00,91.19,44.283,48.104,29.28 +2020-06-29 13:30:00,93.09,42.658,48.104,29.28 +2020-06-29 13:45:00,90.72,42.498000000000005,48.104,29.28 +2020-06-29 14:00:00,92.67,44.217,48.53,29.28 +2020-06-29 14:15:00,90.61,43.095,48.53,29.28 +2020-06-29 14:30:00,89.74,41.367,48.53,29.28 +2020-06-29 14:45:00,89.22,42.806999999999995,48.53,29.28 +2020-06-29 15:00:00,92.79,45.053999999999995,49.351000000000006,29.28 +2020-06-29 15:15:00,89.48,41.575,49.351000000000006,29.28 +2020-06-29 15:30:00,89.59,39.916,49.351000000000006,29.28 +2020-06-29 15:45:00,91.17,37.37,49.351000000000006,29.28 +2020-06-29 16:00:00,92.45,41.121,51.44,29.28 +2020-06-29 16:15:00,93.63,40.543,51.44,29.28 +2020-06-29 16:30:00,98.96,40.327,51.44,29.28 +2020-06-29 16:45:00,97.02,35.815,51.44,29.28 +2020-06-29 17:00:00,99.29,38.823,56.868,29.28 +2020-06-29 17:15:00,99.23,38.361,56.868,29.28 +2020-06-29 17:30:00,99.19,37.845,56.868,29.28 +2020-06-29 17:45:00,102.14,36.434,56.868,29.28 +2020-06-29 18:00:00,102.78,40.075,57.229,29.28 +2020-06-29 18:15:00,101.5,38.505,57.229,29.28 +2020-06-29 18:30:00,103.73,36.42,57.229,29.28 +2020-06-29 18:45:00,102.37,40.659,57.229,29.28 +2020-06-29 19:00:00,99.56,42.528,57.744,29.28 +2020-06-29 19:15:00,95.89,41.278,57.744,29.28 +2020-06-29 19:30:00,96.63,40.488,57.744,29.28 +2020-06-29 19:45:00,92.86,40.168,57.744,29.28 +2020-06-29 20:00:00,91.91,37.258,66.05199999999999,29.28 +2020-06-29 20:15:00,90.4,38.671,66.05199999999999,29.28 +2020-06-29 20:30:00,93.06,39.088,66.05199999999999,29.28 +2020-06-29 20:45:00,90.78,39.683,66.05199999999999,29.28 +2020-06-29 21:00:00,92.65,37.217,59.396,29.28 +2020-06-29 21:15:00,88.69,40.618,59.396,29.28 +2020-06-29 21:30:00,86.02,41.196000000000005,59.396,29.28 +2020-06-29 21:45:00,84.23,42.163000000000004,59.396,29.28 +2020-06-29 22:00:00,80.64,39.546,53.06,29.28 +2020-06-29 22:15:00,79.82,41.918,53.06,29.28 +2020-06-29 22:30:00,78.02,36.138000000000005,53.06,29.28 +2020-06-29 22:45:00,80.24,32.711,53.06,29.28 +2020-06-29 23:00:00,73.65,29.131,46.148,29.28 +2020-06-29 23:15:00,74.02,26.403000000000002,46.148,29.28 +2020-06-29 23:30:00,72.94,25.406999999999996,46.148,29.28 +2020-06-29 23:45:00,71.27,24.415,46.148,29.28 +2020-06-30 00:00:00,72.03,23.34,44.625,29.28 +2020-06-30 00:15:00,69.91,24.04,44.625,29.28 +2020-06-30 00:30:00,68.65,22.963,44.625,29.28 +2020-06-30 00:45:00,71.12,22.569000000000003,44.625,29.28 +2020-06-30 01:00:00,68.89,22.561,41.733000000000004,29.28 +2020-06-30 01:15:00,68.91,21.552,41.733000000000004,29.28 +2020-06-30 01:30:00,69.21,19.86,41.733000000000004,29.28 +2020-06-30 01:45:00,72.69,19.401,41.733000000000004,29.28 +2020-06-30 02:00:00,69.55,18.98,39.872,29.28 +2020-06-30 02:15:00,70.67,17.457,39.872,29.28 +2020-06-30 02:30:00,77.04,19.615,39.872,29.28 +2020-06-30 02:45:00,77.77,20.448,39.872,29.28 +2020-06-30 03:00:00,73.56,22.02,38.711,29.28 +2020-06-30 03:15:00,71.83,20.819000000000003,38.711,29.28 +2020-06-30 03:30:00,74.59,20.177,38.711,29.28 +2020-06-30 03:45:00,72.48,19.767,38.711,29.28 +2020-06-30 04:00:00,73.67,25.706,39.823,29.28 +2020-06-30 04:15:00,76.58,33.113,39.823,29.28 +2020-06-30 04:30:00,78.87,30.273000000000003,39.823,29.28 +2020-06-30 04:45:00,84.64,30.521,39.823,29.28 +2020-06-30 05:00:00,90.01,45.441,43.228,29.28 +2020-06-30 05:15:00,94.67,54.836000000000006,43.228,29.28 +2020-06-30 05:30:00,100.35,47.21,43.228,29.28 +2020-06-30 05:45:00,104.12,44.481,43.228,29.28 +2020-06-30 06:00:00,104.34,46.69,54.316,29.28 +2020-06-30 06:15:00,106.09,46.928999999999995,54.316,29.28 +2020-06-30 06:30:00,104.25,45.317,54.316,29.28 +2020-06-30 06:45:00,111.24,46.6,54.316,29.28 +2020-06-30 07:00:00,112.68,47.207,65.758,29.28 +2020-06-30 07:15:00,112.93,47.266000000000005,65.758,29.28 +2020-06-30 07:30:00,106.91,44.458999999999996,65.758,29.28 +2020-06-30 07:45:00,109.59,44.313,65.758,29.28 +2020-06-30 08:00:00,108.14,41.508,57.983000000000004,29.28 +2020-06-30 08:15:00,105.14,44.367,57.983000000000004,29.28 +2020-06-30 08:30:00,102.56,44.407,57.983000000000004,29.28 +2020-06-30 08:45:00,109.58,46.778999999999996,57.983000000000004,29.28 +2020-06-30 09:00:00,108.83,41.842,52.653,29.28 +2020-06-30 09:15:00,107.0,40.89,52.653,29.28 +2020-06-30 09:30:00,102.96,44.428000000000004,52.653,29.28 +2020-06-30 09:45:00,100.07,47.33,52.653,29.28 +2020-06-30 10:00:00,106.46,42.102,51.408,29.28 +2020-06-30 10:15:00,108.26,43.974,51.408,29.28 +2020-06-30 10:30:00,109.08,44.14,51.408,29.28 +2020-06-30 10:45:00,106.82,45.883,51.408,29.28 +2020-06-30 11:00:00,100.17,42.043,51.913000000000004,29.28 +2020-06-30 11:15:00,103.91,43.474,51.913000000000004,29.28 +2020-06-30 11:30:00,106.58,44.784,51.913000000000004,29.28 +2020-06-30 11:45:00,101.37,46.586000000000006,51.913000000000004,29.28 +2020-06-30 12:00:00,98.37,43.301,49.508,29.28 +2020-06-30 12:15:00,96.43,43.236999999999995,49.508,29.28 +2020-06-30 12:30:00,100.54,41.938,49.508,29.28 +2020-06-30 12:45:00,100.76,43.412,49.508,29.28 +2020-06-30 13:00:00,99.12,43.91,50.007,29.28 +2020-06-30 13:15:00,93.41,45.073,50.007,29.28 +2020-06-30 13:30:00,100.0,43.479,50.007,29.28 +2020-06-30 13:45:00,100.4,42.37,50.007,29.28 +2020-06-30 14:00:00,101.59,44.562,49.778999999999996,29.28 +2020-06-30 14:15:00,93.92,43.248000000000005,49.778999999999996,29.28 +2020-06-30 14:30:00,98.79,41.861000000000004,49.778999999999996,29.28 +2020-06-30 14:45:00,99.07,42.513000000000005,49.778999999999996,29.28 +2020-06-30 15:00:00,98.76,44.603,51.559,29.28 +2020-06-30 15:15:00,96.36,42.058,51.559,29.28 +2020-06-30 15:30:00,97.72,40.195,51.559,29.28 +2020-06-30 15:45:00,100.74,37.971,51.559,29.28 +2020-06-30 16:00:00,101.72,41.047,53.531000000000006,29.28 +2020-06-30 16:15:00,97.36,40.577,53.531000000000006,29.28 +2020-06-30 16:30:00,100.31,40.037,53.531000000000006,29.28 +2020-06-30 16:45:00,100.27,36.243,53.531000000000006,29.28 +2020-06-30 17:00:00,108.34,39.504,59.497,29.28 +2020-06-30 17:15:00,109.28,39.463,59.497,29.28 +2020-06-30 17:30:00,107.79,38.535,59.497,29.28 +2020-06-30 17:45:00,105.96,36.778,59.497,29.28 +2020-06-30 18:00:00,107.94,39.473,59.861999999999995,29.28 +2020-06-30 18:15:00,109.77,39.347,59.861999999999995,29.28 +2020-06-30 18:30:00,112.0,36.985,59.861999999999995,29.28 +2020-06-30 18:45:00,107.95,41.065,59.861999999999995,29.28 +2020-06-30 19:00:00,102.45,41.773,60.989,29.28 +2020-06-30 19:15:00,102.48,40.681999999999995,60.989,29.28 +2020-06-30 19:30:00,105.3,39.64,60.989,29.28 +2020-06-30 19:45:00,101.39,39.65,60.989,29.28 +2020-06-30 20:00:00,93.96,37.129,68.35600000000001,29.28 +2020-06-30 20:15:00,92.47,37.068000000000005,68.35600000000001,29.28 +2020-06-30 20:30:00,92.77,37.573,68.35600000000001,29.28 +2020-06-30 20:45:00,95.45,38.578,68.35600000000001,29.28 +2020-06-30 21:00:00,91.73,36.955,59.251000000000005,29.28 +2020-06-30 21:15:00,91.24,38.936,59.251000000000005,29.28 +2020-06-30 21:30:00,89.56,39.635,59.251000000000005,29.28 +2020-06-30 21:45:00,87.06,40.806,59.251000000000005,29.28 +2020-06-30 22:00:00,82.92,38.446999999999996,54.736999999999995,29.28 +2020-06-30 22:15:00,83.13,40.474000000000004,54.736999999999995,29.28 +2020-06-30 22:30:00,79.39,34.979,54.736999999999995,29.28 +2020-06-30 22:45:00,78.5,31.551,54.736999999999995,29.28 +2020-06-30 23:00:00,75.71,27.199,46.806999999999995,29.28 +2020-06-30 23:15:00,75.56,26.018,46.806999999999995,29.28 +2020-06-30 23:30:00,74.43,25.016,46.806999999999995,29.28 +2020-06-30 23:45:00,73.31,24.177,46.806999999999995,29.28 +2020-07-01 00:00:00,70.43,18.788,42.195,29.509 +2020-07-01 00:15:00,71.7,19.662,42.195,29.509 +2020-07-01 00:30:00,70.95,18.385,42.195,29.509 +2020-07-01 00:45:00,71.27,18.276,42.195,29.509 +2020-07-01 01:00:00,69.68,18.217,38.82,29.509 +2020-07-01 01:15:00,70.95,17.452,38.82,29.509 +2020-07-01 01:30:00,71.25,15.89,38.82,29.509 +2020-07-01 01:45:00,71.45,15.93,38.82,29.509 +2020-07-01 02:00:00,70.66,15.644,37.023,29.509 +2020-07-01 02:15:00,70.4,14.513,37.023,29.509 +2020-07-01 02:30:00,71.19,16.352999999999998,37.023,29.509 +2020-07-01 02:45:00,71.81,17.062,37.023,29.509 +2020-07-01 03:00:00,72.87,18.293,36.818000000000005,29.509 +2020-07-01 03:15:00,71.24,17.483,36.818000000000005,29.509 +2020-07-01 03:30:00,72.47,16.722,36.818000000000005,29.509 +2020-07-01 03:45:00,73.77,15.866,36.818000000000005,29.509 +2020-07-01 04:00:00,79.37,20.849,37.495,29.509 +2020-07-01 04:15:00,83.66,27.185,37.495,29.509 +2020-07-01 04:30:00,85.87,24.576,37.495,29.509 +2020-07-01 04:45:00,82.08,24.599,37.495,29.509 +2020-07-01 05:00:00,87.68,37.652,39.858000000000004,29.509 +2020-07-01 05:15:00,91.0,45.316,39.858000000000004,29.509 +2020-07-01 05:30:00,94.48,39.075,39.858000000000004,29.509 +2020-07-01 05:45:00,96.89,36.391999999999996,39.858000000000004,29.509 +2020-07-01 06:00:00,102.46,37.272,52.867,29.509 +2020-07-01 06:15:00,102.72,36.881,52.867,29.509 +2020-07-01 06:30:00,102.82,36.024,52.867,29.509 +2020-07-01 06:45:00,103.56,37.891999999999996,52.867,29.509 +2020-07-01 07:00:00,106.17,38.054,66.061,29.509 +2020-07-01 07:15:00,107.32,38.36,66.061,29.509 +2020-07-01 07:30:00,103.25,36.042,66.061,29.509 +2020-07-01 07:45:00,105.44,36.064,66.061,29.509 +2020-07-01 08:00:00,102.77,31.921,58.532,29.509 +2020-07-01 08:15:00,106.3,34.949,58.532,29.509 +2020-07-01 08:30:00,109.35,36.171,58.532,29.509 +2020-07-01 08:45:00,110.17,38.823,58.532,29.509 +2020-07-01 09:00:00,107.3,32.952,56.047,29.509 +2020-07-01 09:15:00,102.83,32.253,56.047,29.509 +2020-07-01 09:30:00,105.13,36.139,56.047,29.509 +2020-07-01 09:45:00,108.99,39.617,56.047,29.509 +2020-07-01 10:00:00,108.91,36.763000000000005,53.823,29.509 +2020-07-01 10:15:00,111.42,38.422,53.823,29.509 +2020-07-01 10:30:00,107.94,38.482,53.823,29.509 +2020-07-01 10:45:00,106.69,39.787,53.823,29.509 +2020-07-01 11:00:00,108.14,37.555,54.184,29.509 +2020-07-01 11:15:00,107.34,38.873000000000005,54.184,29.509 +2020-07-01 11:30:00,103.63,40.246,54.184,29.509 +2020-07-01 11:45:00,99.79,41.605,54.184,29.509 +2020-07-01 12:00:00,101.32,36.647,52.628,29.509 +2020-07-01 12:15:00,105.42,36.146,52.628,29.509 +2020-07-01 12:30:00,103.81,35.018,52.628,29.509 +2020-07-01 12:45:00,100.08,36.165,52.628,29.509 +2020-07-01 13:00:00,97.35,36.506,52.31,29.509 +2020-07-01 13:15:00,99.57,37.897,52.31,29.509 +2020-07-01 13:30:00,102.34,36.091,52.31,29.509 +2020-07-01 13:45:00,101.61,35.67,52.31,29.509 +2020-07-01 14:00:00,97.36,37.439,52.278999999999996,29.509 +2020-07-01 14:15:00,95.86,36.361,52.278999999999996,29.509 +2020-07-01 14:30:00,94.71,35.399,52.278999999999996,29.509 +2020-07-01 14:45:00,102.25,35.926,52.278999999999996,29.509 +2020-07-01 15:00:00,101.58,37.861999999999995,53.306999999999995,29.509 +2020-07-01 15:15:00,106.6,35.504,53.306999999999995,29.509 +2020-07-01 15:30:00,109.06,33.898,53.306999999999995,29.509 +2020-07-01 15:45:00,110.7,32.225,53.306999999999995,29.509 +2020-07-01 16:00:00,104.2,34.999,55.358999999999995,29.509 +2020-07-01 16:15:00,100.24,34.702,55.358999999999995,29.509 +2020-07-01 16:30:00,107.63,33.689,55.358999999999995,29.509 +2020-07-01 16:45:00,110.17,30.451999999999998,55.358999999999995,29.509 +2020-07-01 17:00:00,110.35,34.147,59.211999999999996,29.509 +2020-07-01 17:15:00,104.03,34.079,59.211999999999996,29.509 +2020-07-01 17:30:00,107.88,32.936,59.211999999999996,29.509 +2020-07-01 17:45:00,108.5,31.721,59.211999999999996,29.509 +2020-07-01 18:00:00,113.12,34.732,60.403999999999996,29.509 +2020-07-01 18:15:00,112.56,34.763000000000005,60.403999999999996,29.509 +2020-07-01 18:30:00,109.0,32.714,60.403999999999996,29.509 +2020-07-01 18:45:00,106.69,35.778,60.403999999999996,29.509 +2020-07-01 19:00:00,103.85,36.946999999999996,60.993,29.509 +2020-07-01 19:15:00,104.5,36.014,60.993,29.509 +2020-07-01 19:30:00,99.49,34.96,60.993,29.509 +2020-07-01 19:45:00,102.64,34.247,60.993,29.509 +2020-07-01 20:00:00,93.35,31.489,66.6,29.509 +2020-07-01 20:15:00,92.62,31.057,66.6,29.509 +2020-07-01 20:30:00,94.73,31.609,66.6,29.509 +2020-07-01 20:45:00,92.77,32.135999999999996,66.6,29.509 +2020-07-01 21:00:00,93.61,30.487,59.855,29.509 +2020-07-01 21:15:00,91.43,32.09,59.855,29.509 +2020-07-01 21:30:00,88.22,33.063,59.855,29.509 +2020-07-01 21:45:00,86.19,34.155,59.855,29.509 +2020-07-01 22:00:00,82.68,31.27,54.942,29.509 +2020-07-01 22:15:00,82.47,33.906,54.942,29.509 +2020-07-01 22:30:00,79.33,29.747,54.942,29.509 +2020-07-01 22:45:00,79.58,26.566999999999997,54.942,29.509 +2020-07-01 23:00:00,76.55,23.329,46.056000000000004,29.509 +2020-07-01 23:15:00,75.74,21.73,46.056000000000004,29.509 +2020-07-01 23:30:00,76.51,20.483,46.056000000000004,29.509 +2020-07-01 23:45:00,75.37,19.579,46.056000000000004,29.509 +2020-07-02 00:00:00,71.27,18.766,40.859,29.509 +2020-07-02 00:15:00,75.23,19.639,40.859,29.509 +2020-07-02 00:30:00,72.87,18.366,40.859,29.509 +2020-07-02 00:45:00,72.99,18.262,40.859,29.509 +2020-07-02 01:00:00,73.16,18.214000000000002,39.06,29.509 +2020-07-02 01:15:00,71.58,17.437,39.06,29.509 +2020-07-02 01:30:00,70.03,15.875,39.06,29.509 +2020-07-02 01:45:00,71.6,15.907,39.06,29.509 +2020-07-02 02:00:00,71.98,15.627,37.592,29.509 +2020-07-02 02:15:00,72.45,14.513,37.592,29.509 +2020-07-02 02:30:00,71.95,16.332,37.592,29.509 +2020-07-02 02:45:00,73.87,17.044,37.592,29.509 +2020-07-02 03:00:00,73.17,18.271,37.416,29.509 +2020-07-02 03:15:00,73.55,17.469,37.416,29.509 +2020-07-02 03:30:00,74.45,16.714000000000002,37.416,29.509 +2020-07-02 03:45:00,76.07,15.876,37.416,29.509 +2020-07-02 04:00:00,85.78,20.818,38.176,29.509 +2020-07-02 04:15:00,84.85,27.118000000000002,38.176,29.509 +2020-07-02 04:30:00,81.2,24.5,38.176,29.509 +2020-07-02 04:45:00,82.99,24.521,38.176,29.509 +2020-07-02 05:00:00,91.83,37.501999999999995,41.203,29.509 +2020-07-02 05:15:00,92.7,45.07,41.203,29.509 +2020-07-02 05:30:00,93.71,38.889,41.203,29.509 +2020-07-02 05:45:00,100.65,36.238,41.203,29.509 +2020-07-02 06:00:00,111.15,37.12,51.09,29.509 +2020-07-02 06:15:00,112.6,36.724000000000004,51.09,29.509 +2020-07-02 06:30:00,107.72,35.889,51.09,29.509 +2020-07-02 06:45:00,109.61,37.786,51.09,29.509 +2020-07-02 07:00:00,106.92,37.939,63.541000000000004,29.509 +2020-07-02 07:15:00,111.87,38.266,63.541000000000004,29.509 +2020-07-02 07:30:00,113.84,35.949,63.541000000000004,29.509 +2020-07-02 07:45:00,113.44,36.0,63.541000000000004,29.509 +2020-07-02 08:00:00,107.91,31.868000000000002,55.65,29.509 +2020-07-02 08:15:00,107.55,34.914,55.65,29.509 +2020-07-02 08:30:00,114.18,36.126,55.65,29.509 +2020-07-02 08:45:00,115.94,38.775,55.65,29.509 +2020-07-02 09:00:00,110.22,32.898,51.833999999999996,29.509 +2020-07-02 09:15:00,109.29,32.201,51.833999999999996,29.509 +2020-07-02 09:30:00,107.38,36.082,51.833999999999996,29.509 +2020-07-02 09:45:00,111.58,39.568000000000005,51.833999999999996,29.509 +2020-07-02 10:00:00,112.97,36.721,49.70399999999999,29.509 +2020-07-02 10:15:00,113.92,38.382,49.70399999999999,29.509 +2020-07-02 10:30:00,108.83,38.439,49.70399999999999,29.509 +2020-07-02 10:45:00,113.08,39.747,49.70399999999999,29.509 +2020-07-02 11:00:00,112.65,37.512,48.593999999999994,29.509 +2020-07-02 11:15:00,108.39,38.832,48.593999999999994,29.509 +2020-07-02 11:30:00,105.46,40.194,48.593999999999994,29.509 +2020-07-02 11:45:00,103.74,41.549,48.593999999999994,29.509 +2020-07-02 12:00:00,102.28,36.616,46.275,29.509 +2020-07-02 12:15:00,112.02,36.114000000000004,46.275,29.509 +2020-07-02 12:30:00,110.12,34.974000000000004,46.275,29.509 +2020-07-02 12:45:00,111.95,36.118,46.275,29.509 +2020-07-02 13:00:00,108.93,36.444,45.803000000000004,29.509 +2020-07-02 13:15:00,104.36,37.830999999999996,45.803000000000004,29.509 +2020-07-02 13:30:00,111.63,36.033,45.803000000000004,29.509 +2020-07-02 13:45:00,108.98,35.619,45.803000000000004,29.509 +2020-07-02 14:00:00,114.1,37.393,46.251999999999995,29.509 +2020-07-02 14:15:00,102.95,36.316,46.251999999999995,29.509 +2020-07-02 14:30:00,101.66,35.339,46.251999999999995,29.509 +2020-07-02 14:45:00,108.11,35.873000000000005,46.251999999999995,29.509 +2020-07-02 15:00:00,117.54,37.826,48.309,29.509 +2020-07-02 15:15:00,115.68,35.461,48.309,29.509 +2020-07-02 15:30:00,115.1,33.853,48.309,29.509 +2020-07-02 15:45:00,108.35,32.172,48.309,29.509 +2020-07-02 16:00:00,106.86,34.964,49.681999999999995,29.509 +2020-07-02 16:15:00,116.47,34.668,49.681999999999995,29.509 +2020-07-02 16:30:00,115.12,33.673,49.681999999999995,29.509 +2020-07-02 16:45:00,115.58,30.432,49.681999999999995,29.509 +2020-07-02 17:00:00,113.83,34.135999999999996,53.086000000000006,29.509 +2020-07-02 17:15:00,108.49,34.075,53.086000000000006,29.509 +2020-07-02 17:30:00,108.27,32.933,53.086000000000006,29.509 +2020-07-02 17:45:00,110.25,31.714000000000002,53.086000000000006,29.509 +2020-07-02 18:00:00,117.44,34.733000000000004,54.038999999999994,29.509 +2020-07-02 18:15:00,113.72,34.743,54.038999999999994,29.509 +2020-07-02 18:30:00,110.69,32.695,54.038999999999994,29.509 +2020-07-02 18:45:00,103.51,35.757,54.038999999999994,29.509 +2020-07-02 19:00:00,99.92,36.928000000000004,53.408,29.509 +2020-07-02 19:15:00,95.01,35.986999999999995,53.408,29.509 +2020-07-02 19:30:00,95.4,34.926,53.408,29.509 +2020-07-02 19:45:00,91.38,34.207,53.408,29.509 +2020-07-02 20:00:00,91.86,31.435,55.309,29.509 +2020-07-02 20:15:00,89.76,31.002,55.309,29.509 +2020-07-02 20:30:00,90.27,31.554000000000002,55.309,29.509 +2020-07-02 20:45:00,93.88,32.098,55.309,29.509 +2020-07-02 21:00:00,88.63,30.451999999999998,51.585,29.509 +2020-07-02 21:15:00,87.77,32.058,51.585,29.509 +2020-07-02 21:30:00,87.98,33.01,51.585,29.509 +2020-07-02 21:45:00,85.55,34.099000000000004,51.585,29.509 +2020-07-02 22:00:00,81.55,31.225,48.006,29.509 +2020-07-02 22:15:00,79.87,33.864000000000004,48.006,29.509 +2020-07-02 22:30:00,78.85,29.693,48.006,29.509 +2020-07-02 22:45:00,80.14,26.506,48.006,29.509 +2020-07-02 23:00:00,74.23,23.264,42.309,29.509 +2020-07-02 23:15:00,74.85,21.691999999999997,42.309,29.509 +2020-07-02 23:30:00,74.97,20.459,42.309,29.509 +2020-07-02 23:45:00,73.89,19.547,42.309,29.509 +2020-07-03 00:00:00,70.93,16.918,39.649,29.509 +2020-07-03 00:15:00,74.24,18.007,39.649,29.509 +2020-07-03 00:30:00,70.69,17.027,39.649,29.509 +2020-07-03 00:45:00,71.66,17.366,39.649,29.509 +2020-07-03 01:00:00,70.83,16.942,37.744,29.509 +2020-07-03 01:15:00,71.09,15.489,37.744,29.509 +2020-07-03 01:30:00,73.13,14.683,37.744,29.509 +2020-07-03 01:45:00,70.01,14.513,37.744,29.509 +2020-07-03 02:00:00,69.99,15.138,36.965,29.509 +2020-07-03 02:15:00,72.95,14.513,36.965,29.509 +2020-07-03 02:30:00,70.26,16.631,36.965,29.509 +2020-07-03 02:45:00,72.71,16.631,36.965,29.509 +2020-07-03 03:00:00,71.77,18.701,37.678000000000004,29.509 +2020-07-03 03:15:00,71.51,16.611,37.678000000000004,29.509 +2020-07-03 03:30:00,76.38,15.615,37.678000000000004,29.509 +2020-07-03 03:45:00,80.75,15.698,37.678000000000004,29.509 +2020-07-03 04:00:00,85.8,20.732,38.591,29.509 +2020-07-03 04:15:00,79.97,25.381,38.591,29.509 +2020-07-03 04:30:00,79.88,23.738000000000003,38.591,29.509 +2020-07-03 04:45:00,87.51,23.125999999999998,38.591,29.509 +2020-07-03 05:00:00,88.68,35.584,40.666,29.509 +2020-07-03 05:15:00,99.92,44.004,40.666,29.509 +2020-07-03 05:30:00,102.65,38.053000000000004,40.666,29.509 +2020-07-03 05:45:00,105.28,34.976,40.666,29.509 +2020-07-03 06:00:00,112.07,36.077,51.784,29.509 +2020-07-03 06:15:00,109.3,35.889,51.784,29.509 +2020-07-03 06:30:00,114.85,35.047,51.784,29.509 +2020-07-03 06:45:00,117.65,36.794000000000004,51.784,29.509 +2020-07-03 07:00:00,118.78,37.632,61.383,29.509 +2020-07-03 07:15:00,114.49,38.888000000000005,61.383,29.509 +2020-07-03 07:30:00,122.05,34.546,61.383,29.509 +2020-07-03 07:45:00,121.0,34.44,61.383,29.509 +2020-07-03 08:00:00,120.65,31.176,55.272,29.509 +2020-07-03 08:15:00,115.76,34.957,55.272,29.509 +2020-07-03 08:30:00,118.63,36.018,55.272,29.509 +2020-07-03 08:45:00,124.55,38.578,55.272,29.509 +2020-07-03 09:00:00,124.35,30.26,53.506,29.509 +2020-07-03 09:15:00,123.23,31.552,53.506,29.509 +2020-07-03 09:30:00,117.79,34.724000000000004,53.506,29.509 +2020-07-03 09:45:00,124.21,38.628,53.506,29.509 +2020-07-03 10:00:00,124.18,35.675,51.363,29.509 +2020-07-03 10:15:00,125.33,37.067,51.363,29.509 +2020-07-03 10:30:00,113.6,37.708,51.363,29.509 +2020-07-03 10:45:00,112.47,38.94,51.363,29.509 +2020-07-03 11:00:00,117.84,36.973,51.043,29.509 +2020-07-03 11:15:00,119.54,37.192,51.043,29.509 +2020-07-03 11:30:00,114.56,38.071,51.043,29.509 +2020-07-03 11:45:00,111.63,38.407,51.043,29.509 +2020-07-03 12:00:00,106.04,33.891999999999996,47.52,29.509 +2020-07-03 12:15:00,110.61,32.827,47.52,29.509 +2020-07-03 12:30:00,107.32,31.789,47.52,29.509 +2020-07-03 12:45:00,103.64,32.069,47.52,29.509 +2020-07-03 13:00:00,101.08,32.958,45.494,29.509 +2020-07-03 13:15:00,99.8,34.49,45.494,29.509 +2020-07-03 13:30:00,95.66,33.538000000000004,45.494,29.509 +2020-07-03 13:45:00,101.57,33.458,45.494,29.509 +2020-07-03 14:00:00,98.0,34.446999999999996,43.883,29.509 +2020-07-03 14:15:00,99.34,33.841,43.883,29.509 +2020-07-03 14:30:00,102.34,34.419000000000004,43.883,29.509 +2020-07-03 14:45:00,107.73,34.214,43.883,29.509 +2020-07-03 15:00:00,109.4,36.125,45.714,29.509 +2020-07-03 15:15:00,106.77,33.525,45.714,29.509 +2020-07-03 15:30:00,104.22,31.396,45.714,29.509 +2020-07-03 15:45:00,105.17,30.504,45.714,29.509 +2020-07-03 16:00:00,101.31,32.468,48.222,29.509 +2020-07-03 16:15:00,98.01,32.68,48.222,29.509 +2020-07-03 16:30:00,102.98,31.509,48.222,29.509 +2020-07-03 16:45:00,107.56,27.44,48.222,29.509 +2020-07-03 17:00:00,110.19,32.964,52.619,29.509 +2020-07-03 17:15:00,103.65,32.747,52.619,29.509 +2020-07-03 17:30:00,108.81,31.796,52.619,29.509 +2020-07-03 17:45:00,110.01,30.412,52.619,29.509 +2020-07-03 18:00:00,107.36,33.431,52.99,29.509 +2020-07-03 18:15:00,103.91,32.449,52.99,29.509 +2020-07-03 18:30:00,105.74,30.276,52.99,29.509 +2020-07-03 18:45:00,106.53,33.777,52.99,29.509 +2020-07-03 19:00:00,100.94,35.813,51.923,29.509 +2020-07-03 19:15:00,91.66,35.426,51.923,29.509 +2020-07-03 19:30:00,95.59,34.438,51.923,29.509 +2020-07-03 19:45:00,96.63,32.675,51.923,29.509 +2020-07-03 20:00:00,95.47,29.728,56.238,29.509 +2020-07-03 20:15:00,91.08,30.12,56.238,29.509 +2020-07-03 20:30:00,87.72,30.166,56.238,29.509 +2020-07-03 20:45:00,93.25,29.855,56.238,29.509 +2020-07-03 21:00:00,93.94,29.558000000000003,52.426,29.509 +2020-07-03 21:15:00,91.58,32.889,52.426,29.509 +2020-07-03 21:30:00,84.87,33.661,52.426,29.509 +2020-07-03 21:45:00,81.0,34.939,52.426,29.509 +2020-07-03 22:00:00,81.22,31.889,48.196000000000005,29.509 +2020-07-03 22:15:00,83.92,34.275999999999996,48.196000000000005,29.509 +2020-07-03 22:30:00,81.87,34.796,48.196000000000005,29.509 +2020-07-03 22:45:00,78.52,32.36,48.196000000000005,29.509 +2020-07-03 23:00:00,69.31,30.891,41.71,29.509 +2020-07-03 23:15:00,70.51,27.789,41.71,29.509 +2020-07-03 23:30:00,75.91,24.73,41.71,29.509 +2020-07-03 23:45:00,74.86,23.709,41.71,29.509 +2020-07-04 00:00:00,71.28,18.308,41.105,29.398000000000003 +2020-07-04 00:15:00,66.58,18.862000000000002,41.105,29.398000000000003 +2020-07-04 00:30:00,68.4,17.344,41.105,29.398000000000003 +2020-07-04 00:45:00,72.06,16.899,41.105,29.398000000000003 +2020-07-04 01:00:00,70.88,16.746,36.934,29.398000000000003 +2020-07-04 01:15:00,68.8,15.936,36.934,29.398000000000003 +2020-07-04 01:30:00,62.1,14.513,36.934,29.398000000000003 +2020-07-04 01:45:00,61.89,15.368,36.934,29.398000000000003 +2020-07-04 02:00:00,61.38,15.015999999999998,34.782,29.398000000000003 +2020-07-04 02:15:00,61.18,14.513,34.782,29.398000000000003 +2020-07-04 02:30:00,67.97,14.918,34.782,29.398000000000003 +2020-07-04 02:45:00,68.39,15.735999999999999,34.782,29.398000000000003 +2020-07-04 03:00:00,67.7,16.349,34.489000000000004,29.398000000000003 +2020-07-04 03:15:00,65.75,14.513,34.489000000000004,29.398000000000003 +2020-07-04 03:30:00,69.36,14.513,34.489000000000004,29.398000000000003 +2020-07-04 03:45:00,67.53,14.645,34.489000000000004,29.398000000000003 +2020-07-04 04:00:00,64.42,17.846,34.111,29.398000000000003 +2020-07-04 04:15:00,60.03,21.635,34.111,29.398000000000003 +2020-07-04 04:30:00,62.56,18.337,34.111,29.398000000000003 +2020-07-04 04:45:00,68.6,18.028,34.111,29.398000000000003 +2020-07-04 05:00:00,69.39,22.414,33.283,29.398000000000003 +2020-07-04 05:15:00,67.18,20.141,33.283,29.398000000000003 +2020-07-04 05:30:00,62.58,15.806,33.283,29.398000000000003 +2020-07-04 05:45:00,66.37,17.168,33.283,29.398000000000003 +2020-07-04 06:00:00,67.09,29.13,33.653,29.398000000000003 +2020-07-04 06:15:00,70.36,36.363,33.653,29.398000000000003 +2020-07-04 06:30:00,75.13,32.815,33.653,29.398000000000003 +2020-07-04 06:45:00,76.55,31.62,33.653,29.398000000000003 +2020-07-04 07:00:00,74.19,31.340999999999998,36.732,29.398000000000003 +2020-07-04 07:15:00,69.0,31.328000000000003,36.732,29.398000000000003 +2020-07-04 07:30:00,75.39,28.43,36.732,29.398000000000003 +2020-07-04 07:45:00,78.46,29.125999999999998,36.732,29.398000000000003 +2020-07-04 08:00:00,78.82,26.471,41.318999999999996,29.398000000000003 +2020-07-04 08:15:00,75.62,29.854,41.318999999999996,29.398000000000003 +2020-07-04 08:30:00,72.05,30.805,41.318999999999996,29.398000000000003 +2020-07-04 08:45:00,72.92,34.181999999999995,41.318999999999996,29.398000000000003 +2020-07-04 09:00:00,75.51,29.045,43.195,29.398000000000003 +2020-07-04 09:15:00,75.93,30.805,43.195,29.398000000000003 +2020-07-04 09:30:00,76.19,34.44,43.195,29.398000000000003 +2020-07-04 09:45:00,69.16,37.868,43.195,29.398000000000003 +2020-07-04 10:00:00,68.23,35.586999999999996,41.843999999999994,29.398000000000003 +2020-07-04 10:15:00,68.67,37.345,41.843999999999994,29.398000000000003 +2020-07-04 10:30:00,72.15,37.611999999999995,41.843999999999994,29.398000000000003 +2020-07-04 10:45:00,69.35,38.399,41.843999999999994,29.398000000000003 +2020-07-04 11:00:00,67.52,36.268,39.035,29.398000000000003 +2020-07-04 11:15:00,67.46,37.444,39.035,29.398000000000003 +2020-07-04 11:30:00,65.74,38.674,39.035,29.398000000000003 +2020-07-04 11:45:00,64.54,39.766999999999996,39.035,29.398000000000003 +2020-07-04 12:00:00,62.25,35.786,38.001,29.398000000000003 +2020-07-04 12:15:00,60.77,35.577,38.001,29.398000000000003 +2020-07-04 12:30:00,60.3,34.369,38.001,29.398000000000003 +2020-07-04 12:45:00,59.23,35.464,38.001,29.398000000000003 +2020-07-04 13:00:00,57.7,35.444,34.747,29.398000000000003 +2020-07-04 13:15:00,57.19,36.525999999999996,34.747,29.398000000000003 +2020-07-04 13:30:00,57.53,35.806,34.747,29.398000000000003 +2020-07-04 13:45:00,57.64,34.283,34.747,29.398000000000003 +2020-07-04 14:00:00,57.47,35.258,33.434,29.398000000000003 +2020-07-04 14:15:00,57.5,33.368,33.434,29.398000000000003 +2020-07-04 14:30:00,57.46,33.594,33.434,29.398000000000003 +2020-07-04 14:45:00,58.13,33.887,33.434,29.398000000000003 +2020-07-04 15:00:00,59.46,36.232,35.921,29.398000000000003 +2020-07-04 15:15:00,59.91,34.321,35.921,29.398000000000003 +2020-07-04 15:30:00,60.67,32.314,35.921,29.398000000000003 +2020-07-04 15:45:00,61.72,30.444000000000003,35.921,29.398000000000003 +2020-07-04 16:00:00,63.63,34.691,39.427,29.398000000000003 +2020-07-04 16:15:00,64.21,33.919000000000004,39.427,29.398000000000003 +2020-07-04 16:30:00,69.13,33.007,39.427,29.398000000000003 +2020-07-04 16:45:00,69.2,28.857,39.427,29.398000000000003 +2020-07-04 17:00:00,69.91,33.164,44.096000000000004,29.398000000000003 +2020-07-04 17:15:00,72.05,30.614,44.096000000000004,29.398000000000003 +2020-07-04 17:30:00,73.58,29.529,44.096000000000004,29.398000000000003 +2020-07-04 17:45:00,75.0,28.683000000000003,44.096000000000004,29.398000000000003 +2020-07-04 18:00:00,76.84,33.131,43.931000000000004,29.398000000000003 +2020-07-04 18:15:00,76.16,33.784,43.931000000000004,29.398000000000003 +2020-07-04 18:30:00,76.26,32.959,43.931000000000004,29.398000000000003 +2020-07-04 18:45:00,77.12,33.004,43.931000000000004,29.398000000000003 +2020-07-04 19:00:00,75.57,33.28,42.187,29.398000000000003 +2020-07-04 19:15:00,72.31,31.864,42.187,29.398000000000003 +2020-07-04 19:30:00,71.21,31.631999999999998,42.187,29.398000000000003 +2020-07-04 19:45:00,70.88,31.693,42.187,29.398000000000003 +2020-07-04 20:00:00,70.25,29.52,38.315,29.398000000000003 +2020-07-04 20:15:00,69.83,29.163,38.315,29.398000000000003 +2020-07-04 20:30:00,69.99,28.314,38.315,29.398000000000003 +2020-07-04 20:45:00,70.95,29.953000000000003,38.315,29.398000000000003 +2020-07-04 21:00:00,71.53,28.039,36.843,29.398000000000003 +2020-07-04 21:15:00,71.29,30.995,36.843,29.398000000000003 +2020-07-04 21:30:00,70.14,31.895,36.843,29.398000000000003 +2020-07-04 21:45:00,68.61,32.623000000000005,36.843,29.398000000000003 +2020-07-04 22:00:00,66.07,29.506999999999998,37.260999999999996,29.398000000000003 +2020-07-04 22:15:00,66.0,32.06,37.260999999999996,29.398000000000003 +2020-07-04 22:30:00,62.91,31.948,37.260999999999996,29.398000000000003 +2020-07-04 22:45:00,62.98,29.82,37.260999999999996,29.398000000000003 +2020-07-04 23:00:00,59.32,27.59,32.148,29.398000000000003 +2020-07-04 23:15:00,58.95,25.004,32.148,29.398000000000003 +2020-07-04 23:30:00,58.24,24.543000000000003,32.148,29.398000000000003 +2020-07-04 23:45:00,57.81,23.965999999999998,32.148,29.398000000000003 +2020-07-05 00:00:00,55.67,19.707,28.905,29.398000000000003 +2020-07-05 00:15:00,55.37,19.081,28.905,29.398000000000003 +2020-07-05 00:30:00,54.85,17.43,28.905,29.398000000000003 +2020-07-05 00:45:00,54.72,16.848,28.905,29.398000000000003 +2020-07-05 01:00:00,53.6,16.983,26.906999999999996,29.398000000000003 +2020-07-05 01:15:00,53.84,15.994000000000002,26.906999999999996,29.398000000000003 +2020-07-05 01:30:00,54.02,14.513,26.906999999999996,29.398000000000003 +2020-07-05 01:45:00,54.04,14.877,26.906999999999996,29.398000000000003 +2020-07-05 02:00:00,53.25,14.642000000000001,25.938000000000002,29.398000000000003 +2020-07-05 02:15:00,53.53,14.513,25.938000000000002,29.398000000000003 +2020-07-05 02:30:00,53.44,15.464,25.938000000000002,29.398000000000003 +2020-07-05 02:45:00,52.89,15.967,25.938000000000002,29.398000000000003 +2020-07-05 03:00:00,52.29,17.232,24.693,29.398000000000003 +2020-07-05 03:15:00,53.06,14.776,24.693,29.398000000000003 +2020-07-05 03:30:00,52.75,14.513,24.693,29.398000000000003 +2020-07-05 03:45:00,51.46,14.513,24.693,29.398000000000003 +2020-07-05 04:00:00,51.22,17.359,25.683000000000003,29.398000000000003 +2020-07-05 04:15:00,51.09,20.709,25.683000000000003,29.398000000000003 +2020-07-05 04:30:00,50.47,18.782,25.683000000000003,29.398000000000003 +2020-07-05 04:45:00,51.05,17.98,25.683000000000003,29.398000000000003 +2020-07-05 05:00:00,50.86,22.613000000000003,26.023000000000003,29.398000000000003 +2020-07-05 05:15:00,50.47,19.552,26.023000000000003,29.398000000000003 +2020-07-05 05:30:00,48.1,14.87,26.023000000000003,29.398000000000003 +2020-07-05 05:45:00,51.33,15.972000000000001,26.023000000000003,29.398000000000003 +2020-07-05 06:00:00,51.68,25.519000000000002,25.834,29.398000000000003 +2020-07-05 06:15:00,51.55,33.667,25.834,29.398000000000003 +2020-07-05 06:30:00,52.79,29.468000000000004,25.834,29.398000000000003 +2020-07-05 06:45:00,53.9,27.323,25.834,29.398000000000003 +2020-07-05 07:00:00,53.77,27.261999999999997,27.765,29.398000000000003 +2020-07-05 07:15:00,53.67,25.614,27.765,29.398000000000003 +2020-07-05 07:30:00,52.11,24.101,27.765,29.398000000000003 +2020-07-05 07:45:00,54.99,24.828000000000003,27.765,29.398000000000003 +2020-07-05 08:00:00,55.61,22.898000000000003,31.357,29.398000000000003 +2020-07-05 08:15:00,55.39,27.529,31.357,29.398000000000003 +2020-07-05 08:30:00,56.12,29.285999999999998,31.357,29.398000000000003 +2020-07-05 08:45:00,56.36,32.464,31.357,29.398000000000003 +2020-07-05 09:00:00,57.17,27.238000000000003,33.238,29.398000000000003 +2020-07-05 09:15:00,57.85,28.448,33.238,29.398000000000003 +2020-07-05 09:30:00,60.75,32.537,33.238,29.398000000000003 +2020-07-05 09:45:00,58.0,37.027,33.238,29.398000000000003 +2020-07-05 10:00:00,61.54,35.167,34.22,29.398000000000003 +2020-07-05 10:15:00,62.53,37.032,34.22,29.398000000000003 +2020-07-05 10:30:00,60.64,37.488,34.22,29.398000000000003 +2020-07-05 10:45:00,64.41,39.474000000000004,34.22,29.398000000000003 +2020-07-05 11:00:00,63.97,36.913000000000004,36.298,29.398000000000003 +2020-07-05 11:15:00,62.55,37.61,36.298,29.398000000000003 +2020-07-05 11:30:00,61.59,39.439,36.298,29.398000000000003 +2020-07-05 11:45:00,61.53,40.755,36.298,29.398000000000003 +2020-07-05 12:00:00,58.57,37.967,33.52,29.398000000000003 +2020-07-05 12:15:00,59.34,36.952,33.52,29.398000000000003 +2020-07-05 12:30:00,56.89,36.068000000000005,33.52,29.398000000000003 +2020-07-05 12:45:00,56.14,36.559,33.52,29.398000000000003 +2020-07-05 13:00:00,56.09,36.25,30.12,29.398000000000003 +2020-07-05 13:15:00,58.54,36.499,30.12,29.398000000000003 +2020-07-05 13:30:00,53.91,34.625,30.12,29.398000000000003 +2020-07-05 13:45:00,54.48,34.336999999999996,30.12,29.398000000000003 +2020-07-05 14:00:00,53.1,36.564,27.233,29.398000000000003 +2020-07-05 14:15:00,52.56,35.042,27.233,29.398000000000003 +2020-07-05 14:30:00,53.24,33.861999999999995,27.233,29.398000000000003 +2020-07-05 14:45:00,53.16,33.063,27.233,29.398000000000003 +2020-07-05 15:00:00,52.17,35.689,27.468000000000004,29.398000000000003 +2020-07-05 15:15:00,53.08,32.861999999999995,27.468000000000004,29.398000000000003 +2020-07-05 15:30:00,52.72,30.621,27.468000000000004,29.398000000000003 +2020-07-05 15:45:00,52.87,28.991999999999997,27.468000000000004,29.398000000000003 +2020-07-05 16:00:00,58.7,31.435,30.8,29.398000000000003 +2020-07-05 16:15:00,59.41,30.967,30.8,29.398000000000003 +2020-07-05 16:30:00,58.69,31.108,30.8,29.398000000000003 +2020-07-05 16:45:00,63.11,27.015,30.8,29.398000000000003 +2020-07-05 17:00:00,67.05,31.717,37.806,29.398000000000003 +2020-07-05 17:15:00,64.23,30.783,37.806,29.398000000000003 +2020-07-05 17:30:00,65.25,30.528000000000002,37.806,29.398000000000003 +2020-07-05 17:45:00,68.4,29.974,37.806,29.398000000000003 +2020-07-05 18:00:00,72.02,35.104,40.766,29.398000000000003 +2020-07-05 18:15:00,71.05,35.239000000000004,40.766,29.398000000000003 +2020-07-05 18:30:00,71.03,34.275999999999996,40.766,29.398000000000003 +2020-07-05 18:45:00,71.01,34.335,40.766,29.398000000000003 +2020-07-05 19:00:00,71.74,36.906,41.163000000000004,29.398000000000003 +2020-07-05 19:15:00,70.17,34.306999999999995,41.163000000000004,29.398000000000003 +2020-07-05 19:30:00,70.19,33.819,41.163000000000004,29.398000000000003 +2020-07-05 19:45:00,70.61,33.332,41.163000000000004,29.398000000000003 +2020-07-05 20:00:00,70.45,31.316,39.885999999999996,29.398000000000003 +2020-07-05 20:15:00,71.14,30.765,39.885999999999996,29.398000000000003 +2020-07-05 20:30:00,75.63,30.69,39.885999999999996,29.398000000000003 +2020-07-05 20:45:00,75.49,30.666,39.885999999999996,29.398000000000003 +2020-07-05 21:00:00,75.9,28.771,38.900999999999996,29.398000000000003 +2020-07-05 21:15:00,76.48,31.448,38.900999999999996,29.398000000000003 +2020-07-05 21:30:00,74.17,31.642,38.900999999999996,29.398000000000003 +2020-07-05 21:45:00,73.12,32.73,38.900999999999996,29.398000000000003 +2020-07-05 22:00:00,70.17,31.783,39.806999999999995,29.398000000000003 +2020-07-05 22:15:00,70.35,32.641999999999996,39.806999999999995,29.398000000000003 +2020-07-05 22:30:00,68.44,32.145,39.806999999999995,29.398000000000003 +2020-07-05 22:45:00,68.89,28.736,39.806999999999995,29.398000000000003 +2020-07-05 23:00:00,66.69,26.328000000000003,35.564,29.398000000000003 +2020-07-05 23:15:00,65.66,24.987,35.564,29.398000000000003 +2020-07-05 23:30:00,64.82,23.948,35.564,29.398000000000003 +2020-07-05 23:45:00,63.72,23.485,35.564,29.398000000000003 +2020-07-06 00:00:00,62.93,21.053,36.578,29.509 +2020-07-06 00:15:00,62.05,21.09,36.578,29.509 +2020-07-06 00:30:00,61.97,19.039,36.578,29.509 +2020-07-06 00:45:00,61.79,18.081,36.578,29.509 +2020-07-06 01:00:00,61.64,18.625,35.292,29.509 +2020-07-06 01:15:00,60.91,17.667,35.292,29.509 +2020-07-06 01:30:00,60.67,16.254,35.292,29.509 +2020-07-06 01:45:00,59.95,16.808,35.292,29.509 +2020-07-06 02:00:00,61.3,17.049,34.319,29.509 +2020-07-06 02:15:00,61.83,14.513,34.319,29.509 +2020-07-06 02:30:00,62.07,16.898,34.319,29.509 +2020-07-06 02:45:00,62.17,17.285,34.319,29.509 +2020-07-06 03:00:00,65.43,19.002,33.13,29.509 +2020-07-06 03:15:00,64.03,17.215,33.13,29.509 +2020-07-06 03:30:00,65.09,16.549,33.13,29.509 +2020-07-06 03:45:00,65.84,16.95,33.13,29.509 +2020-07-06 04:00:00,67.57,23.045,33.851,29.509 +2020-07-06 04:15:00,68.87,29.180999999999997,33.851,29.509 +2020-07-06 04:30:00,71.48,26.636999999999997,33.851,29.509 +2020-07-06 04:45:00,75.03,26.21,33.851,29.509 +2020-07-06 05:00:00,80.19,37.529,38.718,29.509 +2020-07-06 05:15:00,84.78,44.1,38.718,29.509 +2020-07-06 05:30:00,88.76,37.885,38.718,29.509 +2020-07-06 05:45:00,91.27,36.103,38.718,29.509 +2020-07-06 06:00:00,94.97,35.835,51.648999999999994,29.509 +2020-07-06 06:15:00,100.81,35.295,51.648999999999994,29.509 +2020-07-06 06:30:00,107.42,34.845,51.648999999999994,29.509 +2020-07-06 06:45:00,109.2,37.764,51.648999999999994,29.509 +2020-07-06 07:00:00,100.07,37.709,60.159,29.509 +2020-07-06 07:15:00,99.42,38.38,60.159,29.509 +2020-07-06 07:30:00,109.47,36.021,60.159,29.509 +2020-07-06 07:45:00,108.74,37.223,60.159,29.509 +2020-07-06 08:00:00,103.48,33.216,53.8,29.509 +2020-07-06 08:15:00,101.9,36.735,53.8,29.509 +2020-07-06 08:30:00,102.67,37.72,53.8,29.509 +2020-07-06 08:45:00,102.51,41.309,53.8,29.509 +2020-07-06 09:00:00,108.05,34.997,50.583,29.509 +2020-07-06 09:15:00,108.16,34.559,50.583,29.509 +2020-07-06 09:30:00,103.79,37.734,50.583,29.509 +2020-07-06 09:45:00,100.1,39.804,50.583,29.509 +2020-07-06 10:00:00,100.26,38.406,49.11600000000001,29.509 +2020-07-06 10:15:00,98.97,40.135999999999996,49.11600000000001,29.509 +2020-07-06 10:30:00,101.45,40.166,49.11600000000001,29.509 +2020-07-06 10:45:00,105.24,40.429,49.11600000000001,29.509 +2020-07-06 11:00:00,115.14,38.292,49.056000000000004,29.509 +2020-07-06 11:15:00,114.12,39.161,49.056000000000004,29.509 +2020-07-06 11:30:00,109.51,41.618,49.056000000000004,29.509 +2020-07-06 11:45:00,107.16,43.486999999999995,49.056000000000004,29.509 +2020-07-06 12:00:00,98.95,38.736,47.227,29.509 +2020-07-06 12:15:00,97.34,37.839,47.227,29.509 +2020-07-06 12:30:00,94.65,35.788000000000004,47.227,29.509 +2020-07-06 12:45:00,94.35,36.159,47.227,29.509 +2020-07-06 13:00:00,95.56,36.779,47.006,29.509 +2020-07-06 13:15:00,94.55,36.189,47.006,29.509 +2020-07-06 13:30:00,93.02,34.541,47.006,29.509 +2020-07-06 13:45:00,98.7,35.205999999999996,47.006,29.509 +2020-07-06 14:00:00,95.14,36.535,47.19,29.509 +2020-07-06 14:15:00,91.16,35.689,47.19,29.509 +2020-07-06 14:30:00,89.41,34.361999999999995,47.19,29.509 +2020-07-06 14:45:00,88.76,35.762,47.19,29.509 +2020-07-06 15:00:00,87.58,37.905,47.846000000000004,29.509 +2020-07-06 15:15:00,88.49,34.553000000000004,47.846000000000004,29.509 +2020-07-06 15:30:00,88.04,33.184,47.846000000000004,29.509 +2020-07-06 15:45:00,88.3,31.066999999999997,47.846000000000004,29.509 +2020-07-06 16:00:00,88.8,34.711999999999996,49.641000000000005,29.509 +2020-07-06 16:15:00,90.56,34.364000000000004,49.641000000000005,29.509 +2020-07-06 16:30:00,93.03,33.887,49.641000000000005,29.509 +2020-07-06 16:45:00,95.66,29.892,49.641000000000005,29.509 +2020-07-06 17:00:00,98.75,33.438,54.133,29.509 +2020-07-06 17:15:00,99.58,32.971,54.133,29.509 +2020-07-06 17:30:00,99.52,32.346,54.133,29.509 +2020-07-06 17:45:00,99.38,31.47,54.133,29.509 +2020-07-06 18:00:00,99.13,35.501999999999995,53.761,29.509 +2020-07-06 18:15:00,101.41,33.836999999999996,53.761,29.509 +2020-07-06 18:30:00,101.33,32.049,53.761,29.509 +2020-07-06 18:45:00,99.47,35.366,53.761,29.509 +2020-07-06 19:00:00,96.1,37.764,53.923,29.509 +2020-07-06 19:15:00,92.71,36.589,53.923,29.509 +2020-07-06 19:30:00,92.02,35.689,53.923,29.509 +2020-07-06 19:45:00,92.82,34.603,53.923,29.509 +2020-07-06 20:00:00,91.58,31.381,58.786,29.509 +2020-07-06 20:15:00,89.83,32.415,58.786,29.509 +2020-07-06 20:30:00,90.8,32.982,58.786,29.509 +2020-07-06 20:45:00,90.9,33.107,58.786,29.509 +2020-07-06 21:00:00,88.74,30.525,54.591,29.509 +2020-07-06 21:15:00,87.51,33.721,54.591,29.509 +2020-07-06 21:30:00,85.2,34.366,54.591,29.509 +2020-07-06 21:45:00,83.29,35.247,54.591,29.509 +2020-07-06 22:00:00,80.54,32.321999999999996,51.551,29.509 +2020-07-06 22:15:00,79.21,35.305,51.551,29.509 +2020-07-06 22:30:00,78.44,30.779,51.551,29.509 +2020-07-06 22:45:00,76.79,27.581999999999997,51.551,29.509 +2020-07-06 23:00:00,75.39,25.141,44.716,29.509 +2020-07-06 23:15:00,74.53,21.991,44.716,29.509 +2020-07-06 23:30:00,74.57,20.794,44.716,29.509 +2020-07-06 23:45:00,72.99,19.643,44.716,29.509 +2020-07-07 00:00:00,71.53,18.721,43.01,29.509 +2020-07-07 00:15:00,70.84,19.582,43.01,29.509 +2020-07-07 00:30:00,71.39,18.327,43.01,29.509 +2020-07-07 00:45:00,70.56,18.253,43.01,29.509 +2020-07-07 01:00:00,71.2,18.25,40.687,29.509 +2020-07-07 01:15:00,70.9,17.422,40.687,29.509 +2020-07-07 01:30:00,70.33,15.867,40.687,29.509 +2020-07-07 01:45:00,70.39,15.862,40.687,29.509 +2020-07-07 02:00:00,70.97,15.605,39.554,29.509 +2020-07-07 02:15:00,70.84,14.513,39.554,29.509 +2020-07-07 02:30:00,77.92,16.289,39.554,29.509 +2020-07-07 02:45:00,78.65,17.02,39.554,29.509 +2020-07-07 03:00:00,75.67,18.214000000000002,38.958,29.509 +2020-07-07 03:15:00,70.77,17.46,38.958,29.509 +2020-07-07 03:30:00,74.81,16.742,38.958,29.509 +2020-07-07 03:45:00,74.03,15.989,38.958,29.509 +2020-07-07 04:00:00,77.7,20.735,39.783,29.509 +2020-07-07 04:15:00,82.93,26.872,39.783,29.509 +2020-07-07 04:30:00,86.8,24.212,39.783,29.509 +2020-07-07 04:45:00,87.2,24.226999999999997,39.783,29.509 +2020-07-07 05:00:00,87.99,36.885999999999996,42.281000000000006,29.509 +2020-07-07 05:15:00,95.27,44.023999999999994,42.281000000000006,29.509 +2020-07-07 05:30:00,96.03,38.126999999999995,42.281000000000006,29.509 +2020-07-07 05:45:00,100.96,35.614000000000004,42.281000000000006,29.509 +2020-07-07 06:00:00,111.3,36.497,50.801,29.509 +2020-07-07 06:15:00,114.63,36.082,50.801,29.509 +2020-07-07 06:30:00,116.56,35.349000000000004,50.801,29.509 +2020-07-07 06:45:00,116.13,37.391,50.801,29.509 +2020-07-07 07:00:00,116.59,37.498000000000005,60.202,29.509 +2020-07-07 07:15:00,121.92,37.93,60.202,29.509 +2020-07-07 07:30:00,124.36,35.623000000000005,60.202,29.509 +2020-07-07 07:45:00,123.71,35.817,60.202,29.509 +2020-07-07 08:00:00,119.38,31.735,54.461000000000006,29.509 +2020-07-07 08:15:00,124.37,34.859,54.461000000000006,29.509 +2020-07-07 08:30:00,128.67,36.022,54.461000000000006,29.509 +2020-07-07 08:45:00,130.1,38.647,54.461000000000006,29.509 +2020-07-07 09:00:00,129.85,32.748000000000005,50.753,29.509 +2020-07-07 09:15:00,128.0,32.054,50.753,29.509 +2020-07-07 09:30:00,130.61,35.906,50.753,29.509 +2020-07-07 09:45:00,129.99,39.417,50.753,29.509 +2020-07-07 10:00:00,122.45,36.611999999999995,49.703,29.509 +2020-07-07 10:15:00,120.63,38.272,49.703,29.509 +2020-07-07 10:30:00,122.78,38.313,49.703,29.509 +2020-07-07 10:45:00,123.45,39.629,49.703,29.509 +2020-07-07 11:00:00,128.68,37.385,49.42100000000001,29.509 +2020-07-07 11:15:00,125.65,38.714,49.42100000000001,29.509 +2020-07-07 11:30:00,122.76,40.018,49.42100000000001,29.509 +2020-07-07 11:45:00,121.95,41.35,49.42100000000001,29.509 +2020-07-07 12:00:00,118.98,36.525999999999996,47.155,29.509 +2020-07-07 12:15:00,125.09,36.022,47.155,29.509 +2020-07-07 12:30:00,122.87,34.824,47.155,29.509 +2020-07-07 12:45:00,119.92,35.959,47.155,29.509 +2020-07-07 13:00:00,121.51,36.201,47.515,29.509 +2020-07-07 13:15:00,124.02,37.568000000000005,47.515,29.509 +2020-07-07 13:30:00,121.3,35.8,47.515,29.509 +2020-07-07 13:45:00,120.45,35.42,47.515,29.509 +2020-07-07 14:00:00,111.95,37.211,47.575,29.509 +2020-07-07 14:15:00,112.15,36.14,47.575,29.509 +2020-07-07 14:30:00,118.73,35.104,47.575,29.509 +2020-07-07 14:45:00,117.88,35.671,47.575,29.509 +2020-07-07 15:00:00,112.52,37.69,48.903,29.509 +2020-07-07 15:15:00,106.84,35.296,48.903,29.509 +2020-07-07 15:30:00,107.4,33.681999999999995,48.903,29.509 +2020-07-07 15:45:00,99.77,31.961,48.903,29.509 +2020-07-07 16:00:00,96.93,34.83,50.218999999999994,29.509 +2020-07-07 16:15:00,106.09,34.543,50.218999999999994,29.509 +2020-07-07 16:30:00,106.41,33.641,50.218999999999994,29.509 +2020-07-07 16:45:00,104.59,30.386999999999997,50.218999999999994,29.509 +2020-07-07 17:00:00,99.96,34.125,55.396,29.509 +2020-07-07 17:15:00,103.8,34.11,55.396,29.509 +2020-07-07 17:30:00,100.99,32.974000000000004,55.396,29.509 +2020-07-07 17:45:00,102.8,31.754,55.396,29.509 +2020-07-07 18:00:00,108.74,34.804,55.583999999999996,29.509 +2020-07-07 18:15:00,108.95,34.722,55.583999999999996,29.509 +2020-07-07 18:30:00,107.59,32.679,55.583999999999996,29.509 +2020-07-07 18:45:00,103.26,35.739000000000004,55.583999999999996,29.509 +2020-07-07 19:00:00,99.07,36.917,56.071000000000005,29.509 +2020-07-07 19:15:00,94.57,35.938,56.071000000000005,29.509 +2020-07-07 19:30:00,92.99,34.843,56.071000000000005,29.509 +2020-07-07 19:45:00,92.6,34.101,56.071000000000005,29.509 +2020-07-07 20:00:00,91.59,31.27,61.55,29.509 +2020-07-07 20:15:00,90.41,30.826999999999998,61.55,29.509 +2020-07-07 20:30:00,90.06,31.373,61.55,29.509 +2020-07-07 20:45:00,90.65,31.985,61.55,29.509 +2020-07-07 21:00:00,90.29,30.355999999999998,55.94,29.509 +2020-07-07 21:15:00,88.11,31.968000000000004,55.94,29.509 +2020-07-07 21:30:00,85.72,32.819,55.94,29.509 +2020-07-07 21:45:00,84.05,33.887,55.94,29.509 +2020-07-07 22:00:00,82.01,31.055,52.857,29.509 +2020-07-07 22:15:00,79.35,33.702,52.857,29.509 +2020-07-07 22:30:00,77.27,29.464000000000002,52.857,29.509 +2020-07-07 22:45:00,75.9,26.239,52.857,29.509 +2020-07-07 23:00:00,73.88,23.0,46.04,29.509 +2020-07-07 23:15:00,72.06,21.55,46.04,29.509 +2020-07-07 23:30:00,71.73,20.384,46.04,29.509 +2020-07-07 23:45:00,70.42,19.437,46.04,29.509 +2020-07-08 00:00:00,68.87,18.723,42.195,29.509 +2020-07-08 00:15:00,68.28,19.582,42.195,29.509 +2020-07-08 00:30:00,67.17,18.331,42.195,29.509 +2020-07-08 00:45:00,67.5,18.262,42.195,29.509 +2020-07-08 01:00:00,68.65,18.267,38.82,29.509 +2020-07-08 01:15:00,68.23,17.430999999999997,38.82,29.509 +2020-07-08 01:30:00,67.33,15.877,38.82,29.509 +2020-07-08 01:45:00,67.32,15.865,38.82,29.509 +2020-07-08 02:00:00,67.95,15.613,37.023,29.509 +2020-07-08 02:15:00,67.93,14.513,37.023,29.509 +2020-07-08 02:30:00,75.12,16.292,37.023,29.509 +2020-07-08 02:45:00,73.61,17.028,37.023,29.509 +2020-07-08 03:00:00,77.07,18.215,36.818000000000005,29.509 +2020-07-08 03:15:00,72.12,17.471,36.818000000000005,29.509 +2020-07-08 03:30:00,73.91,16.761,36.818000000000005,29.509 +2020-07-08 03:45:00,71.79,16.022000000000002,36.818000000000005,29.509 +2020-07-08 04:00:00,77.39,20.733,37.495,29.509 +2020-07-08 04:15:00,82.0,26.840999999999998,37.495,29.509 +2020-07-08 04:30:00,84.87,24.173000000000002,37.495,29.509 +2020-07-08 04:45:00,83.56,24.188000000000002,37.495,29.509 +2020-07-08 05:00:00,86.92,36.789,39.858000000000004,29.509 +2020-07-08 05:15:00,93.46,43.852,39.858000000000004,29.509 +2020-07-08 05:30:00,93.59,38.008,39.858000000000004,29.509 +2020-07-08 05:45:00,96.52,35.519,39.858000000000004,29.509 +2020-07-08 06:00:00,99.68,36.399,52.867,29.509 +2020-07-08 06:15:00,102.4,35.982,52.867,29.509 +2020-07-08 06:30:00,108.54,35.269,52.867,29.509 +2020-07-08 06:45:00,113.47,37.338,52.867,29.509 +2020-07-08 07:00:00,108.0,37.436,66.061,29.509 +2020-07-08 07:15:00,102.54,37.89,66.061,29.509 +2020-07-08 07:30:00,100.01,35.586,66.061,29.509 +2020-07-08 07:45:00,101.07,35.809,66.061,29.509 +2020-07-08 08:00:00,108.39,31.736,58.532,29.509 +2020-07-08 08:15:00,116.1,34.872,58.532,29.509 +2020-07-08 08:30:00,117.34,36.025,58.532,29.509 +2020-07-08 08:45:00,115.72,38.645,58.532,29.509 +2020-07-08 09:00:00,111.76,32.743,56.047,29.509 +2020-07-08 09:15:00,108.9,32.047,56.047,29.509 +2020-07-08 09:30:00,108.08,35.893,56.047,29.509 +2020-07-08 09:45:00,116.56,39.406,56.047,29.509 +2020-07-08 10:00:00,120.29,36.609,53.823,29.509 +2020-07-08 10:15:00,118.95,38.266999999999996,53.823,29.509 +2020-07-08 10:30:00,112.11,38.305,53.823,29.509 +2020-07-08 10:45:00,109.74,39.623000000000005,53.823,29.509 +2020-07-08 11:00:00,109.06,37.376999999999995,54.184,29.509 +2020-07-08 11:15:00,108.4,38.705999999999996,54.184,29.509 +2020-07-08 11:30:00,105.6,40.0,54.184,29.509 +2020-07-08 11:45:00,107.2,41.326,54.184,29.509 +2020-07-08 12:00:00,114.53,36.522,52.628,29.509 +2020-07-08 12:15:00,112.75,36.016,52.628,29.509 +2020-07-08 12:30:00,101.99,34.808,52.628,29.509 +2020-07-08 12:45:00,99.23,35.94,52.628,29.509 +2020-07-08 13:00:00,100.2,36.167,52.31,29.509 +2020-07-08 13:15:00,102.73,37.527,52.31,29.509 +2020-07-08 13:30:00,102.61,35.765,52.31,29.509 +2020-07-08 13:45:00,102.47,35.391999999999996,52.31,29.509 +2020-07-08 14:00:00,116.5,37.185,52.278999999999996,29.509 +2020-07-08 14:15:00,112.73,36.115,52.278999999999996,29.509 +2020-07-08 14:30:00,108.85,35.069,52.278999999999996,29.509 +2020-07-08 14:45:00,109.45,35.644,52.278999999999996,29.509 +2020-07-08 15:00:00,101.37,37.671,53.306999999999995,29.509 +2020-07-08 15:15:00,100.94,35.272,53.306999999999995,29.509 +2020-07-08 15:30:00,99.62,33.659,53.306999999999995,29.509 +2020-07-08 15:45:00,97.35,31.93,53.306999999999995,29.509 +2020-07-08 16:00:00,93.45,34.812,55.358999999999995,29.509 +2020-07-08 16:15:00,94.89,34.528,55.358999999999995,29.509 +2020-07-08 16:30:00,98.99,33.643,55.358999999999995,29.509 +2020-07-08 16:45:00,99.62,30.389,55.358999999999995,29.509 +2020-07-08 17:00:00,98.93,34.132,59.211999999999996,29.509 +2020-07-08 17:15:00,98.18,34.126999999999995,59.211999999999996,29.509 +2020-07-08 17:30:00,98.72,32.994,59.211999999999996,29.509 +2020-07-08 17:45:00,100.38,31.776999999999997,59.211999999999996,29.509 +2020-07-08 18:00:00,99.58,34.832,60.403999999999996,29.509 +2020-07-08 18:15:00,99.0,34.734,60.403999999999996,29.509 +2020-07-08 18:30:00,100.18,32.691,60.403999999999996,29.509 +2020-07-08 18:45:00,98.99,35.751999999999995,60.403999999999996,29.509 +2020-07-08 19:00:00,96.37,36.931999999999995,60.993,29.509 +2020-07-08 19:15:00,92.43,35.945,60.993,29.509 +2020-07-08 19:30:00,91.2,34.844,60.993,29.509 +2020-07-08 19:45:00,90.88,34.098,60.993,29.509 +2020-07-08 20:00:00,90.29,31.255,66.6,29.509 +2020-07-08 20:15:00,88.95,30.811,66.6,29.509 +2020-07-08 20:30:00,89.13,31.355999999999998,66.6,29.509 +2020-07-08 20:45:00,91.03,31.976999999999997,66.6,29.509 +2020-07-08 21:00:00,89.33,30.351999999999997,59.855,29.509 +2020-07-08 21:15:00,87.63,31.965999999999998,59.855,29.509 +2020-07-08 21:30:00,85.13,32.796,59.855,29.509 +2020-07-08 21:45:00,83.76,33.858000000000004,59.855,29.509 +2020-07-08 22:00:00,81.36,31.031999999999996,54.942,29.509 +2020-07-08 22:15:00,79.43,33.68,54.942,29.509 +2020-07-08 22:30:00,76.01,29.427,54.942,29.509 +2020-07-08 22:45:00,74.2,26.193,54.942,29.509 +2020-07-08 23:00:00,74.17,22.959,46.056000000000004,29.509 +2020-07-08 23:15:00,72.84,21.531999999999996,46.056000000000004,29.509 +2020-07-08 23:30:00,72.63,20.379,46.056000000000004,29.509 +2020-07-08 23:45:00,72.27,19.425,46.056000000000004,29.509 +2020-07-09 00:00:00,70.77,18.729,40.859,29.509 +2020-07-09 00:15:00,69.68,19.587,40.859,29.509 +2020-07-09 00:30:00,68.62,18.339000000000002,40.859,29.509 +2020-07-09 00:45:00,68.26,18.276,40.859,29.509 +2020-07-09 01:00:00,68.62,18.287,39.06,29.509 +2020-07-09 01:15:00,67.79,17.444000000000003,39.06,29.509 +2020-07-09 01:30:00,66.52,15.892000000000001,39.06,29.509 +2020-07-09 01:45:00,67.76,15.872,39.06,29.509 +2020-07-09 02:00:00,68.72,15.626,37.592,29.509 +2020-07-09 02:15:00,67.64,14.513,37.592,29.509 +2020-07-09 02:30:00,67.33,16.301,37.592,29.509 +2020-07-09 02:45:00,67.89,17.039,37.592,29.509 +2020-07-09 03:00:00,69.13,18.219,37.416,29.509 +2020-07-09 03:15:00,70.24,17.485,37.416,29.509 +2020-07-09 03:30:00,71.26,16.783,37.416,29.509 +2020-07-09 03:45:00,72.08,16.06,37.416,29.509 +2020-07-09 04:00:00,76.24,20.737,38.176,29.509 +2020-07-09 04:15:00,75.02,26.815,38.176,29.509 +2020-07-09 04:30:00,77.62,24.14,38.176,29.509 +2020-07-09 04:45:00,80.21,24.154,38.176,29.509 +2020-07-09 05:00:00,85.53,36.703,41.203,29.509 +2020-07-09 05:15:00,89.78,43.691,41.203,29.509 +2020-07-09 05:30:00,93.38,37.9,41.203,29.509 +2020-07-09 05:45:00,101.99,35.434,41.203,29.509 +2020-07-09 06:00:00,106.23,36.31,51.09,29.509 +2020-07-09 06:15:00,109.0,35.891999999999996,51.09,29.509 +2020-07-09 06:30:00,106.78,35.196999999999996,51.09,29.509 +2020-07-09 06:45:00,104.81,37.294000000000004,51.09,29.509 +2020-07-09 07:00:00,106.36,37.384,63.541000000000004,29.509 +2020-07-09 07:15:00,110.69,37.859,63.541000000000004,29.509 +2020-07-09 07:30:00,112.06,35.56,63.541000000000004,29.509 +2020-07-09 07:45:00,118.5,35.809,63.541000000000004,29.509 +2020-07-09 08:00:00,121.66,31.746,55.65,29.509 +2020-07-09 08:15:00,122.22,34.893,55.65,29.509 +2020-07-09 08:30:00,118.81,36.037,55.65,29.509 +2020-07-09 08:45:00,116.9,38.649,55.65,29.509 +2020-07-09 09:00:00,117.8,32.743,51.833999999999996,29.509 +2020-07-09 09:15:00,120.31,32.048,51.833999999999996,29.509 +2020-07-09 09:30:00,120.69,35.887,51.833999999999996,29.509 +2020-07-09 09:45:00,124.35,39.402,51.833999999999996,29.509 +2020-07-09 10:00:00,130.54,36.613,49.70399999999999,29.509 +2020-07-09 10:15:00,131.59,38.268,49.70399999999999,29.509 +2020-07-09 10:30:00,127.98,38.302,49.70399999999999,29.509 +2020-07-09 10:45:00,122.86,39.621,49.70399999999999,29.509 +2020-07-09 11:00:00,122.53,37.374,48.593999999999994,29.509 +2020-07-09 11:15:00,121.63,38.705,48.593999999999994,29.509 +2020-07-09 11:30:00,121.85,39.986,48.593999999999994,29.509 +2020-07-09 11:45:00,120.39,41.306999999999995,48.593999999999994,29.509 +2020-07-09 12:00:00,120.74,36.523,46.275,29.509 +2020-07-09 12:15:00,120.27,36.015,46.275,29.509 +2020-07-09 12:30:00,117.63,34.798,46.275,29.509 +2020-07-09 12:45:00,114.26,35.927,46.275,29.509 +2020-07-09 13:00:00,109.73,36.135999999999996,45.803000000000004,29.509 +2020-07-09 13:15:00,108.7,37.491,45.803000000000004,29.509 +2020-07-09 13:30:00,113.78,35.734,45.803000000000004,29.509 +2020-07-09 13:45:00,117.0,35.368,45.803000000000004,29.509 +2020-07-09 14:00:00,110.72,37.163000000000004,46.251999999999995,29.509 +2020-07-09 14:15:00,111.27,36.095,46.251999999999995,29.509 +2020-07-09 14:30:00,112.08,35.039,46.251999999999995,29.509 +2020-07-09 14:45:00,109.67,35.62,46.251999999999995,29.509 +2020-07-09 15:00:00,109.08,37.655,48.309,29.509 +2020-07-09 15:15:00,104.89,35.251,48.309,29.509 +2020-07-09 15:30:00,100.7,33.638000000000005,48.309,29.509 +2020-07-09 15:45:00,104.1,31.903000000000002,48.309,29.509 +2020-07-09 16:00:00,103.6,34.797,49.681999999999995,29.509 +2020-07-09 16:15:00,105.47,34.515,49.681999999999995,29.509 +2020-07-09 16:30:00,100.69,33.648,49.681999999999995,29.509 +2020-07-09 16:45:00,101.97,30.396,49.681999999999995,29.509 +2020-07-09 17:00:00,103.65,34.143,53.086000000000006,29.509 +2020-07-09 17:15:00,103.74,34.149,53.086000000000006,29.509 +2020-07-09 17:30:00,105.78,33.018,53.086000000000006,29.509 +2020-07-09 17:45:00,108.82,31.804000000000002,53.086000000000006,29.509 +2020-07-09 18:00:00,108.39,34.865,54.038999999999994,29.509 +2020-07-09 18:15:00,108.03,34.75,54.038999999999994,29.509 +2020-07-09 18:30:00,106.59,32.711,54.038999999999994,29.509 +2020-07-09 18:45:00,103.0,35.77,54.038999999999994,29.509 +2020-07-09 19:00:00,100.88,36.952,53.408,29.509 +2020-07-09 19:15:00,97.96,35.959,53.408,29.509 +2020-07-09 19:30:00,94.18,34.851,53.408,29.509 +2020-07-09 19:45:00,92.67,34.101,53.408,29.509 +2020-07-09 20:00:00,90.99,31.249000000000002,55.309,29.509 +2020-07-09 20:15:00,90.23,30.802,55.309,29.509 +2020-07-09 20:30:00,90.8,31.345,55.309,29.509 +2020-07-09 20:45:00,92.1,31.975,55.309,29.509 +2020-07-09 21:00:00,89.34,30.354,51.585,29.509 +2020-07-09 21:15:00,87.34,31.968000000000004,51.585,29.509 +2020-07-09 21:30:00,84.56,32.778,51.585,29.509 +2020-07-09 21:45:00,83.79,33.833,51.585,29.509 +2020-07-09 22:00:00,80.79,31.013,48.006,29.509 +2020-07-09 22:15:00,78.88,33.661,48.006,29.509 +2020-07-09 22:30:00,76.76,29.392,48.006,29.509 +2020-07-09 22:45:00,75.3,26.151999999999997,48.006,29.509 +2020-07-09 23:00:00,73.05,22.921,42.309,29.509 +2020-07-09 23:15:00,72.92,21.516,42.309,29.509 +2020-07-09 23:30:00,71.81,20.377,42.309,29.509 +2020-07-09 23:45:00,70.78,19.418,42.309,29.509 +2020-07-10 00:00:00,75.87,16.908,39.649,29.509 +2020-07-10 00:15:00,78.17,17.980999999999998,39.649,29.509 +2020-07-10 00:30:00,75.85,17.027,39.649,29.509 +2020-07-10 00:45:00,71.62,17.408,39.649,29.509 +2020-07-10 01:00:00,69.81,17.038,37.744,29.509 +2020-07-10 01:15:00,68.97,15.522,37.744,29.509 +2020-07-10 01:30:00,67.92,14.729000000000001,37.744,29.509 +2020-07-10 01:45:00,69.22,14.513,37.744,29.509 +2020-07-10 02:00:00,68.97,15.165999999999999,36.965,29.509 +2020-07-10 02:15:00,71.25,14.513,36.965,29.509 +2020-07-10 02:30:00,75.07,16.63,36.965,29.509 +2020-07-10 02:45:00,74.85,16.655,36.965,29.509 +2020-07-10 03:00:00,75.76,18.677,37.678000000000004,29.509 +2020-07-10 03:15:00,71.23,16.657,37.678000000000004,29.509 +2020-07-10 03:30:00,77.96,15.714,37.678000000000004,29.509 +2020-07-10 03:45:00,73.99,15.908,37.678000000000004,29.509 +2020-07-10 04:00:00,73.54,20.688000000000002,38.591,29.509 +2020-07-10 04:15:00,74.61,25.119,38.591,29.509 +2020-07-10 04:30:00,77.28,23.421,38.591,29.509 +2020-07-10 04:45:00,79.7,22.803,38.591,29.509 +2020-07-10 05:00:00,86.23,34.849000000000004,40.666,29.509 +2020-07-10 05:15:00,91.4,42.71,40.666,29.509 +2020-07-10 05:30:00,95.84,37.144,40.666,29.509 +2020-07-10 05:45:00,103.23,34.24,40.666,29.509 +2020-07-10 06:00:00,106.94,35.33,51.784,29.509 +2020-07-10 06:15:00,106.98,35.125,51.784,29.509 +2020-07-10 06:30:00,107.3,34.42,51.784,29.509 +2020-07-10 06:45:00,110.82,36.363,51.784,29.509 +2020-07-10 07:00:00,103.93,37.14,61.383,29.509 +2020-07-10 07:15:00,103.93,38.543,61.383,29.509 +2020-07-10 07:30:00,100.62,34.223,61.383,29.509 +2020-07-10 07:45:00,107.12,34.312,61.383,29.509 +2020-07-10 08:00:00,114.53,31.116999999999997,55.272,29.509 +2020-07-10 08:15:00,110.12,34.992,55.272,29.509 +2020-07-10 08:30:00,106.93,35.983000000000004,55.272,29.509 +2020-07-10 08:45:00,109.04,38.505,55.272,29.509 +2020-07-10 09:00:00,114.76,30.16,53.506,29.509 +2020-07-10 09:15:00,113.51,31.453000000000003,53.506,29.509 +2020-07-10 09:30:00,110.89,34.580999999999996,53.506,29.509 +2020-07-10 09:45:00,107.02,38.509,53.506,29.509 +2020-07-10 10:00:00,113.21,35.611,51.363,29.509 +2020-07-10 10:15:00,111.84,36.994,51.363,29.509 +2020-07-10 10:30:00,112.42,37.61,51.363,29.509 +2020-07-10 10:45:00,110.71,38.852,51.363,29.509 +2020-07-10 11:00:00,112.67,36.874,51.043,29.509 +2020-07-10 11:15:00,113.11,37.103,51.043,29.509 +2020-07-10 11:30:00,107.88,37.903,51.043,29.509 +2020-07-10 11:45:00,109.91,38.202,51.043,29.509 +2020-07-10 12:00:00,111.54,33.830999999999996,47.52,29.509 +2020-07-10 12:15:00,112.77,32.756,47.52,29.509 +2020-07-10 12:30:00,109.24,31.646,47.52,29.509 +2020-07-10 12:45:00,105.69,31.91,47.52,29.509 +2020-07-10 13:00:00,106.61,32.681,45.494,29.509 +2020-07-10 13:15:00,112.86,34.179,45.494,29.509 +2020-07-10 13:30:00,115.59,33.268,45.494,29.509 +2020-07-10 13:45:00,109.93,33.236999999999995,45.494,29.509 +2020-07-10 14:00:00,105.89,34.242,43.883,29.509 +2020-07-10 14:15:00,101.49,33.646,43.883,29.509 +2020-07-10 14:30:00,95.86,34.148,43.883,29.509 +2020-07-10 14:45:00,97.16,33.991,43.883,29.509 +2020-07-10 15:00:00,94.55,35.974000000000004,45.714,29.509 +2020-07-10 15:15:00,89.18,33.336999999999996,45.714,29.509 +2020-07-10 15:30:00,91.05,31.205,45.714,29.509 +2020-07-10 15:45:00,100.25,30.261999999999997,45.714,29.509 +2020-07-10 16:00:00,97.71,32.321999999999996,48.222,29.509 +2020-07-10 16:15:00,101.07,32.548,48.222,29.509 +2020-07-10 16:30:00,98.92,31.503,48.222,29.509 +2020-07-10 16:45:00,98.38,27.433000000000003,48.222,29.509 +2020-07-10 17:00:00,103.96,32.991,52.619,29.509 +2020-07-10 17:15:00,105.3,32.848,52.619,29.509 +2020-07-10 17:30:00,104.89,31.909000000000002,52.619,29.509 +2020-07-10 17:45:00,101.36,30.537,52.619,29.509 +2020-07-10 18:00:00,102.68,33.596,52.99,29.509 +2020-07-10 18:15:00,105.55,32.494,52.99,29.509 +2020-07-10 18:30:00,106.3,30.329,52.99,29.509 +2020-07-10 18:45:00,103.31,33.828,52.99,29.509 +2020-07-10 19:00:00,97.76,35.876,51.923,29.509 +2020-07-10 19:15:00,92.42,35.438,51.923,29.509 +2020-07-10 19:30:00,92.95,34.405,51.923,29.509 +2020-07-10 19:45:00,97.4,32.611,51.923,29.509 +2020-07-10 20:00:00,95.69,29.585,56.238,29.509 +2020-07-10 20:15:00,92.52,29.965999999999998,56.238,29.509 +2020-07-10 20:30:00,86.88,30.0,56.238,29.509 +2020-07-10 20:45:00,87.29,29.768,56.238,29.509 +2020-07-10 21:00:00,83.47,29.494,52.426,29.509 +2020-07-10 21:15:00,83.93,32.832,52.426,29.509 +2020-07-10 21:30:00,80.36,33.465,52.426,29.509 +2020-07-10 21:45:00,84.98,34.704,52.426,29.509 +2020-07-10 22:00:00,82.87,31.701999999999998,48.196000000000005,29.509 +2020-07-10 22:15:00,79.75,34.096,48.196000000000005,29.509 +2020-07-10 22:30:00,73.88,34.513000000000005,48.196000000000005,29.509 +2020-07-10 22:45:00,70.67,32.027,48.196000000000005,29.509 +2020-07-10 23:00:00,71.98,30.574,41.71,29.509 +2020-07-10 23:15:00,75.31,27.635,41.71,29.509 +2020-07-10 23:30:00,73.88,24.671999999999997,41.71,29.509 +2020-07-10 23:45:00,69.5,23.603,41.71,29.509 +2020-07-11 00:00:00,66.78,18.323,41.105,29.398000000000003 +2020-07-11 00:15:00,65.61,18.863,41.105,29.398000000000003 +2020-07-11 00:30:00,68.91,17.372,41.105,29.398000000000003 +2020-07-11 00:45:00,71.3,16.97,41.105,29.398000000000003 +2020-07-11 01:00:00,71.31,16.866,36.934,29.398000000000003 +2020-07-11 01:15:00,66.13,15.995999999999999,36.934,29.398000000000003 +2020-07-11 01:30:00,62.39,14.513,36.934,29.398000000000003 +2020-07-11 01:45:00,65.34,15.394,36.934,29.398000000000003 +2020-07-11 02:00:00,69.2,15.074000000000002,34.782,29.398000000000003 +2020-07-11 02:15:00,69.08,14.513,34.782,29.398000000000003 +2020-07-11 02:30:00,66.46,14.948,34.782,29.398000000000003 +2020-07-11 02:45:00,61.07,15.79,34.782,29.398000000000003 +2020-07-11 03:00:00,61.65,16.352,34.489000000000004,29.398000000000003 +2020-07-11 03:15:00,67.74,14.513,34.489000000000004,29.398000000000003 +2020-07-11 03:30:00,68.92,14.513,34.489000000000004,29.398000000000003 +2020-07-11 03:45:00,67.2,14.882,34.489000000000004,29.398000000000003 +2020-07-11 04:00:00,61.39,17.837,34.111,29.398000000000003 +2020-07-11 04:15:00,62.51,21.416,34.111,29.398000000000003 +2020-07-11 04:30:00,67.0,18.064,34.111,29.398000000000003 +2020-07-11 04:45:00,68.68,17.75,34.111,29.398000000000003 +2020-07-11 05:00:00,67.76,21.743000000000002,33.283,29.398000000000003 +2020-07-11 05:15:00,65.79,18.933,33.283,29.398000000000003 +2020-07-11 05:30:00,71.55,14.975999999999999,33.283,29.398000000000003 +2020-07-11 05:45:00,73.19,16.502,33.283,29.398000000000003 +2020-07-11 06:00:00,72.98,28.447,33.653,29.398000000000003 +2020-07-11 06:15:00,70.87,35.666,33.653,29.398000000000003 +2020-07-11 06:30:00,69.27,32.253,33.653,29.398000000000003 +2020-07-11 06:45:00,72.03,31.250999999999998,33.653,29.398000000000003 +2020-07-11 07:00:00,78.34,30.91,36.732,29.398000000000003 +2020-07-11 07:15:00,79.62,31.045,36.732,29.398000000000003 +2020-07-11 07:30:00,79.34,28.175,36.732,29.398000000000003 +2020-07-11 07:45:00,76.51,29.061,36.732,29.398000000000003 +2020-07-11 08:00:00,81.35,26.475,41.318999999999996,29.398000000000003 +2020-07-11 08:15:00,81.1,29.944000000000003,41.318999999999996,29.398000000000003 +2020-07-11 08:30:00,79.87,30.825,41.318999999999996,29.398000000000003 +2020-07-11 08:45:00,74.11,34.164,41.318999999999996,29.398000000000003 +2020-07-11 09:00:00,76.56,29.0,43.195,29.398000000000003 +2020-07-11 09:15:00,73.1,30.758000000000003,43.195,29.398000000000003 +2020-07-11 09:30:00,79.0,34.347,43.195,29.398000000000003 +2020-07-11 09:45:00,80.51,37.795,43.195,29.398000000000003 +2020-07-11 10:00:00,81.2,35.568000000000005,41.843999999999994,29.398000000000003 +2020-07-11 10:15:00,76.6,37.312,41.843999999999994,29.398000000000003 +2020-07-11 10:30:00,80.08,37.554,41.843999999999994,29.398000000000003 +2020-07-11 10:45:00,79.57,38.348,41.843999999999994,29.398000000000003 +2020-07-11 11:00:00,72.28,36.21,39.035,29.398000000000003 +2020-07-11 11:15:00,72.4,37.393,39.035,29.398000000000003 +2020-07-11 11:30:00,66.56,38.545,39.035,29.398000000000003 +2020-07-11 11:45:00,64.38,39.599000000000004,39.035,29.398000000000003 +2020-07-11 12:00:00,63.06,35.757,38.001,29.398000000000003 +2020-07-11 12:15:00,61.1,35.538000000000004,38.001,29.398000000000003 +2020-07-11 12:30:00,60.22,34.263000000000005,38.001,29.398000000000003 +2020-07-11 12:45:00,59.31,35.336999999999996,38.001,29.398000000000003 +2020-07-11 13:00:00,58.37,35.196999999999996,34.747,29.398000000000003 +2020-07-11 13:15:00,58.07,36.245,34.747,29.398000000000003 +2020-07-11 13:30:00,57.74,35.563,34.747,29.398000000000003 +2020-07-11 13:45:00,57.83,34.092,34.747,29.398000000000003 +2020-07-11 14:00:00,58.61,35.078,33.434,29.398000000000003 +2020-07-11 14:15:00,58.61,33.199,33.434,29.398000000000003 +2020-07-11 14:30:00,59.59,33.353,33.434,29.398000000000003 +2020-07-11 14:45:00,60.3,33.694,33.434,29.398000000000003 +2020-07-11 15:00:00,61.37,36.101,35.921,29.398000000000003 +2020-07-11 15:15:00,61.08,34.154,35.921,29.398000000000003 +2020-07-11 15:30:00,61.78,32.147,35.921,29.398000000000003 +2020-07-11 15:45:00,62.97,30.228,35.921,29.398000000000003 +2020-07-11 16:00:00,64.97,34.564,39.427,29.398000000000003 +2020-07-11 16:15:00,65.55,33.809,39.427,29.398000000000003 +2020-07-11 16:30:00,67.43,33.021,39.427,29.398000000000003 +2020-07-11 16:45:00,69.59,28.877,39.427,29.398000000000003 +2020-07-11 17:00:00,71.58,33.213,44.096000000000004,29.398000000000003 +2020-07-11 17:15:00,72.95,30.74,44.096000000000004,29.398000000000003 +2020-07-11 17:30:00,74.4,29.671,44.096000000000004,29.398000000000003 +2020-07-11 17:45:00,76.23,28.840999999999998,44.096000000000004,29.398000000000003 +2020-07-11 18:00:00,76.65,33.328,43.931000000000004,29.398000000000003 +2020-07-11 18:15:00,76.76,33.864000000000004,43.931000000000004,29.398000000000003 +2020-07-11 18:30:00,77.06,33.051,43.931000000000004,29.398000000000003 +2020-07-11 18:45:00,77.28,33.094,43.931000000000004,29.398000000000003 +2020-07-11 19:00:00,75.61,33.381,42.187,29.398000000000003 +2020-07-11 19:15:00,73.06,31.915,42.187,29.398000000000003 +2020-07-11 19:30:00,72.09,31.64,42.187,29.398000000000003 +2020-07-11 19:45:00,72.06,31.671,42.187,29.398000000000003 +2020-07-11 20:00:00,71.02,29.423000000000002,38.315,29.398000000000003 +2020-07-11 20:15:00,70.88,29.055,38.315,29.398000000000003 +2020-07-11 20:30:00,71.67,28.191999999999997,38.315,29.398000000000003 +2020-07-11 20:45:00,73.05,29.901999999999997,38.315,29.398000000000003 +2020-07-11 21:00:00,72.16,28.011999999999997,36.843,29.398000000000003 +2020-07-11 21:15:00,70.77,30.971999999999998,36.843,29.398000000000003 +2020-07-11 21:30:00,69.19,31.735,36.843,29.398000000000003 +2020-07-11 21:45:00,68.12,32.418,36.843,29.398000000000003 +2020-07-11 22:00:00,65.88,29.346,37.260999999999996,29.398000000000003 +2020-07-11 22:15:00,64.83,31.903000000000002,37.260999999999996,29.398000000000003 +2020-07-11 22:30:00,63.5,31.685,37.260999999999996,29.398000000000003 +2020-07-11 22:45:00,61.86,29.505,37.260999999999996,29.398000000000003 +2020-07-11 23:00:00,60.37,27.302,32.148,29.398000000000003 +2020-07-11 23:15:00,59.02,24.873,32.148,29.398000000000003 +2020-07-11 23:30:00,58.04,24.506999999999998,32.148,29.398000000000003 +2020-07-11 23:45:00,57.38,23.886,32.148,29.398000000000003 +2020-07-12 00:00:00,56.58,19.749000000000002,28.905,29.398000000000003 +2020-07-12 00:15:00,55.15,19.109,28.905,29.398000000000003 +2020-07-12 00:30:00,55.08,17.485,28.905,29.398000000000003 +2020-07-12 00:45:00,55.02,16.945,28.905,29.398000000000003 +2020-07-12 01:00:00,54.63,17.125999999999998,26.906999999999996,29.398000000000003 +2020-07-12 01:15:00,54.08,16.081,26.906999999999996,29.398000000000003 +2020-07-12 01:30:00,54.07,14.513,26.906999999999996,29.398000000000003 +2020-07-12 01:45:00,53.22,14.932,26.906999999999996,29.398000000000003 +2020-07-12 02:00:00,53.3,14.73,25.938000000000002,29.398000000000003 +2020-07-12 02:15:00,52.94,14.513,25.938000000000002,29.398000000000003 +2020-07-12 02:30:00,53.05,15.523,25.938000000000002,29.398000000000003 +2020-07-12 02:45:00,52.79,16.05,25.938000000000002,29.398000000000003 +2020-07-12 03:00:00,53.08,17.264,24.693,29.398000000000003 +2020-07-12 03:15:00,52.25,14.882,24.693,29.398000000000003 +2020-07-12 03:30:00,52.6,14.513,24.693,29.398000000000003 +2020-07-12 03:45:00,52.24,14.513,24.693,29.398000000000003 +2020-07-12 04:00:00,51.42,17.385,25.683000000000003,29.398000000000003 +2020-07-12 04:15:00,51.0,20.531,25.683000000000003,29.398000000000003 +2020-07-12 04:30:00,50.5,18.554000000000002,25.683000000000003,29.398000000000003 +2020-07-12 04:45:00,50.97,17.746,25.683000000000003,29.398000000000003 +2020-07-12 05:00:00,51.24,22.005,26.023000000000003,29.398000000000003 +2020-07-12 05:15:00,51.43,18.430999999999997,26.023000000000003,29.398000000000003 +2020-07-12 05:30:00,51.69,14.513,26.023000000000003,29.398000000000003 +2020-07-12 05:45:00,52.25,15.375,26.023000000000003,29.398000000000003 +2020-07-12 06:00:00,52.24,24.9,25.834,29.398000000000003 +2020-07-12 06:15:00,52.9,33.04,25.834,29.398000000000003 +2020-07-12 06:30:00,53.7,28.971,25.834,29.398000000000003 +2020-07-12 06:45:00,55.15,27.016,25.834,29.398000000000003 +2020-07-12 07:00:00,54.82,26.895,27.765,29.398000000000003 +2020-07-12 07:15:00,55.23,25.395,27.765,29.398000000000003 +2020-07-12 07:30:00,56.51,23.913,27.765,29.398000000000003 +2020-07-12 07:45:00,57.17,24.826999999999998,27.765,29.398000000000003 +2020-07-12 08:00:00,55.26,22.965,31.357,29.398000000000003 +2020-07-12 08:15:00,55.02,27.676,31.357,29.398000000000003 +2020-07-12 08:30:00,54.22,29.362,31.357,29.398000000000003 +2020-07-12 08:45:00,55.5,32.498000000000005,31.357,29.398000000000003 +2020-07-12 09:00:00,56.55,27.249000000000002,33.238,29.398000000000003 +2020-07-12 09:15:00,54.44,28.454,33.238,29.398000000000003 +2020-07-12 09:30:00,53.99,32.496,33.238,29.398000000000003 +2020-07-12 09:45:00,54.89,37.0,33.238,29.398000000000003 +2020-07-12 10:00:00,55.54,35.193000000000005,34.22,29.398000000000003 +2020-07-12 10:15:00,57.68,37.039,34.22,29.398000000000003 +2020-07-12 10:30:00,59.4,37.47,34.22,29.398000000000003 +2020-07-12 10:45:00,58.87,39.461,34.22,29.398000000000003 +2020-07-12 11:00:00,56.65,36.895,36.298,29.398000000000003 +2020-07-12 11:15:00,55.61,37.598,36.298,29.398000000000003 +2020-07-12 11:30:00,53.69,39.349000000000004,36.298,29.398000000000003 +2020-07-12 11:45:00,51.82,40.623000000000005,36.298,29.398000000000003 +2020-07-12 12:00:00,49.79,37.971,33.52,29.398000000000003 +2020-07-12 12:15:00,48.34,36.944,33.52,29.398000000000003 +2020-07-12 12:30:00,47.06,35.995,33.52,29.398000000000003 +2020-07-12 12:45:00,46.84,36.466,33.52,29.398000000000003 +2020-07-12 13:00:00,45.47,36.035,30.12,29.398000000000003 +2020-07-12 13:15:00,44.9,36.248000000000005,30.12,29.398000000000003 +2020-07-12 13:30:00,46.01,34.412,30.12,29.398000000000003 +2020-07-12 13:45:00,48.63,34.175,30.12,29.398000000000003 +2020-07-12 14:00:00,47.77,36.409,27.233,29.398000000000003 +2020-07-12 14:15:00,50.67,34.899,27.233,29.398000000000003 +2020-07-12 14:30:00,49.51,33.652,27.233,29.398000000000003 +2020-07-12 14:45:00,50.09,32.899,27.233,29.398000000000003 +2020-07-12 15:00:00,49.11,35.577,27.468000000000004,29.398000000000003 +2020-07-12 15:15:00,51.61,32.717,27.468000000000004,29.398000000000003 +2020-07-12 15:30:00,52.28,30.478,27.468000000000004,29.398000000000003 +2020-07-12 15:45:00,54.69,28.804000000000002,27.468000000000004,29.398000000000003 +2020-07-12 16:00:00,56.69,31.328000000000003,30.8,29.398000000000003 +2020-07-12 16:15:00,57.79,30.877,30.8,29.398000000000003 +2020-07-12 16:30:00,60.36,31.142,30.8,29.398000000000003 +2020-07-12 16:45:00,62.51,27.061999999999998,30.8,29.398000000000003 +2020-07-12 17:00:00,64.81,31.787,37.806,29.398000000000003 +2020-07-12 17:15:00,65.45,30.935,37.806,29.398000000000003 +2020-07-12 17:30:00,67.71,30.695999999999998,37.806,29.398000000000003 +2020-07-12 17:45:00,68.61,30.166999999999998,37.806,29.398000000000003 +2020-07-12 18:00:00,71.24,35.333,40.766,29.398000000000003 +2020-07-12 18:15:00,71.8,35.356,40.766,29.398000000000003 +2020-07-12 18:30:00,72.57,34.407,40.766,29.398000000000003 +2020-07-12 18:45:00,72.89,34.463,40.766,29.398000000000003 +2020-07-12 19:00:00,72.83,37.047,41.163000000000004,29.398000000000003 +2020-07-12 19:15:00,71.76,34.399,41.163000000000004,29.398000000000003 +2020-07-12 19:30:00,70.93,33.869,41.163000000000004,29.398000000000003 +2020-07-12 19:45:00,71.3,33.354,41.163000000000004,29.398000000000003 +2020-07-12 20:00:00,68.86,31.265,39.885999999999996,29.398000000000003 +2020-07-12 20:15:00,69.89,30.703000000000003,39.885999999999996,29.398000000000003 +2020-07-12 20:30:00,71.35,30.611,39.885999999999996,29.398000000000003 +2020-07-12 20:45:00,74.04,30.651,39.885999999999996,29.398000000000003 +2020-07-12 21:00:00,76.76,28.779,38.900999999999996,29.398000000000003 +2020-07-12 21:15:00,76.45,31.459,38.900999999999996,29.398000000000003 +2020-07-12 21:30:00,75.21,31.52,38.900999999999996,29.398000000000003 +2020-07-12 21:45:00,73.92,32.556,38.900999999999996,29.398000000000003 +2020-07-12 22:00:00,72.32,31.649,39.806999999999995,29.398000000000003 +2020-07-12 22:15:00,71.26,32.507,39.806999999999995,29.398000000000003 +2020-07-12 22:30:00,70.12,31.899,39.806999999999995,29.398000000000003 +2020-07-12 22:45:00,69.17,28.441999999999997,39.806999999999995,29.398000000000003 +2020-07-12 23:00:00,66.25,26.066999999999997,35.564,29.398000000000003 +2020-07-12 23:15:00,66.98,24.875999999999998,35.564,29.398000000000003 +2020-07-12 23:30:00,67.33,23.935,35.564,29.398000000000003 +2020-07-12 23:45:00,65.65,23.429000000000002,35.564,29.398000000000003 +2020-07-13 00:00:00,63.25,21.121,36.578,29.509 +2020-07-13 00:15:00,63.73,21.145,36.578,29.509 +2020-07-13 00:30:00,63.24,19.121,36.578,29.509 +2020-07-13 00:45:00,64.24,18.207,36.578,29.509 +2020-07-13 01:00:00,62.98,18.792,35.292,29.509 +2020-07-13 01:15:00,64.06,17.78,35.292,29.509 +2020-07-13 01:30:00,62.35,16.386,35.292,29.509 +2020-07-13 01:45:00,62.1,16.892,35.292,29.509 +2020-07-13 02:00:00,62.13,17.167,34.319,29.509 +2020-07-13 02:15:00,63.93,14.513,34.319,29.509 +2020-07-13 02:30:00,62.7,16.987000000000002,34.319,29.509 +2020-07-13 02:45:00,68.66,17.396,34.319,29.509 +2020-07-13 03:00:00,71.71,19.061,33.13,29.509 +2020-07-13 03:15:00,70.72,17.35,33.13,29.509 +2020-07-13 03:30:00,68.96,16.737000000000002,33.13,29.509 +2020-07-13 03:45:00,68.2,17.241,33.13,29.509 +2020-07-13 04:00:00,71.65,23.105999999999998,33.851,29.509 +2020-07-13 04:15:00,75.48,29.046,33.851,29.509 +2020-07-13 04:30:00,81.43,26.454,33.851,29.509 +2020-07-13 04:45:00,88.04,26.021,33.851,29.509 +2020-07-13 05:00:00,87.82,36.985,38.718,29.509 +2020-07-13 05:15:00,92.38,43.06399999999999,38.718,29.509 +2020-07-13 05:30:00,92.03,37.214,38.718,29.509 +2020-07-13 05:45:00,94.27,35.575,38.718,29.509 +2020-07-13 06:00:00,105.23,35.279,51.648999999999994,29.509 +2020-07-13 06:15:00,111.06,34.735,51.648999999999994,29.509 +2020-07-13 06:30:00,115.27,34.412,51.648999999999994,29.509 +2020-07-13 06:45:00,111.43,37.519,51.648999999999994,29.509 +2020-07-13 07:00:00,115.92,37.404,60.159,29.509 +2020-07-13 07:15:00,114.27,38.225,60.159,29.509 +2020-07-13 07:30:00,121.23,35.899,60.159,29.509 +2020-07-13 07:45:00,121.9,37.286,60.159,29.509 +2020-07-13 08:00:00,121.83,33.344,53.8,29.509 +2020-07-13 08:15:00,117.34,36.936,53.8,29.509 +2020-07-13 08:30:00,119.03,37.851,53.8,29.509 +2020-07-13 08:45:00,125.58,41.396,53.8,29.509 +2020-07-13 09:00:00,127.01,35.063,50.583,29.509 +2020-07-13 09:15:00,124.63,34.619,50.583,29.509 +2020-07-13 09:30:00,120.25,37.743,50.583,29.509 +2020-07-13 09:45:00,127.67,39.821999999999996,50.583,29.509 +2020-07-13 10:00:00,129.63,38.476,49.11600000000001,29.509 +2020-07-13 10:15:00,127.89,40.185,49.11600000000001,29.509 +2020-07-13 10:30:00,123.11,40.189,49.11600000000001,29.509 +2020-07-13 10:45:00,122.39,40.455,49.11600000000001,29.509 +2020-07-13 11:00:00,118.84,38.315,49.056000000000004,29.509 +2020-07-13 11:15:00,121.61,39.187,49.056000000000004,29.509 +2020-07-13 11:30:00,114.5,41.566,49.056000000000004,29.509 +2020-07-13 11:45:00,110.96,43.393,49.056000000000004,29.509 +2020-07-13 12:00:00,107.31,38.772,47.227,29.509 +2020-07-13 12:15:00,105.66,37.86,47.227,29.509 +2020-07-13 12:30:00,102.31,35.75,47.227,29.509 +2020-07-13 12:45:00,93.18,36.098,47.227,29.509 +2020-07-13 13:00:00,92.65,36.595,47.006,29.509 +2020-07-13 13:15:00,92.56,35.967,47.006,29.509 +2020-07-13 13:30:00,91.16,34.355,47.006,29.509 +2020-07-13 13:45:00,91.85,35.071999999999996,47.006,29.509 +2020-07-13 14:00:00,90.82,36.405,47.19,29.509 +2020-07-13 14:15:00,91.72,35.571999999999996,47.19,29.509 +2020-07-13 14:30:00,90.69,34.181999999999995,47.19,29.509 +2020-07-13 14:45:00,90.03,35.629,47.19,29.509 +2020-07-13 15:00:00,93.94,37.814,47.846000000000004,29.509 +2020-07-13 15:15:00,99.23,34.43,47.846000000000004,29.509 +2020-07-13 15:30:00,95.18,33.066,47.846000000000004,29.509 +2020-07-13 15:45:00,96.44,30.903000000000002,47.846000000000004,29.509 +2020-07-13 16:00:00,96.49,34.625,49.641000000000005,29.509 +2020-07-13 16:15:00,95.72,34.295,49.641000000000005,29.509 +2020-07-13 16:30:00,96.85,33.941,49.641000000000005,29.509 +2020-07-13 16:45:00,94.4,29.968000000000004,49.641000000000005,29.509 +2020-07-13 17:00:00,98.43,33.529,54.133,29.509 +2020-07-13 17:15:00,98.75,33.147,54.133,29.509 +2020-07-13 17:30:00,99.74,32.542,54.133,29.509 +2020-07-13 17:45:00,98.7,31.698,54.133,29.509 +2020-07-13 18:00:00,102.1,35.762,53.761,29.509 +2020-07-13 18:15:00,99.07,33.991,53.761,29.509 +2020-07-13 18:30:00,100.45,32.218,53.761,29.509 +2020-07-13 18:45:00,100.43,35.531,53.761,29.509 +2020-07-13 19:00:00,97.31,37.945,53.923,29.509 +2020-07-13 19:15:00,93.81,36.722,53.923,29.509 +2020-07-13 19:30:00,93.3,35.78,53.923,29.509 +2020-07-13 19:45:00,91.72,34.668,53.923,29.509 +2020-07-13 20:00:00,90.68,31.375,58.786,29.509 +2020-07-13 20:15:00,90.77,32.399,58.786,29.509 +2020-07-13 20:30:00,90.87,32.946,58.786,29.509 +2020-07-13 20:45:00,91.51,33.126999999999995,58.786,29.509 +2020-07-13 21:00:00,89.62,30.568,54.591,29.509 +2020-07-13 21:15:00,88.41,33.764,54.591,29.509 +2020-07-13 21:30:00,85.66,34.279,54.591,29.509 +2020-07-13 21:45:00,85.22,35.104,54.591,29.509 +2020-07-13 22:00:00,79.21,32.213,51.551,29.509 +2020-07-13 22:15:00,80.28,35.194,51.551,29.509 +2020-07-13 22:30:00,79.16,30.554000000000002,51.551,29.509 +2020-07-13 22:45:00,78.47,27.307,51.551,29.509 +2020-07-13 23:00:00,72.96,24.906999999999996,44.716,29.509 +2020-07-13 23:15:00,73.07,21.904,44.716,29.509 +2020-07-13 23:30:00,69.81,20.805,44.716,29.509 +2020-07-13 23:45:00,72.76,19.611,44.716,29.509 +2020-07-14 00:00:00,67.54,18.816,43.01,29.509 +2020-07-14 00:15:00,69.07,19.663,43.01,29.509 +2020-07-14 00:30:00,66.45,18.436,43.01,29.509 +2020-07-14 00:45:00,70.02,18.406,43.01,29.509 +2020-07-14 01:00:00,69.42,18.439,40.687,29.509 +2020-07-14 01:15:00,69.69,17.563,40.687,29.509 +2020-07-14 01:30:00,67.81,16.028,40.687,29.509 +2020-07-14 01:45:00,69.25,15.975999999999999,40.687,29.509 +2020-07-14 02:00:00,68.49,15.753,39.554,29.509 +2020-07-14 02:15:00,69.32,14.513,39.554,29.509 +2020-07-14 02:30:00,69.78,16.408,39.554,29.509 +2020-07-14 02:45:00,69.55,17.16,39.554,29.509 +2020-07-14 03:00:00,70.53,18.301,38.958,29.509 +2020-07-14 03:15:00,71.95,17.624000000000002,38.958,29.509 +2020-07-14 03:30:00,71.88,16.959,38.958,29.509 +2020-07-14 03:45:00,75.76,16.305,38.958,29.509 +2020-07-14 04:00:00,74.52,20.831999999999997,39.783,29.509 +2020-07-14 04:15:00,75.95,26.779,39.783,29.509 +2020-07-14 04:30:00,79.03,24.072,39.783,29.509 +2020-07-14 04:45:00,81.77,24.083000000000002,39.783,29.509 +2020-07-14 05:00:00,88.06,36.406,42.281000000000006,29.509 +2020-07-14 05:15:00,90.34,43.077,42.281000000000006,29.509 +2020-07-14 05:30:00,92.69,37.535,42.281000000000006,29.509 +2020-07-14 05:45:00,98.51,35.157,42.281000000000006,29.509 +2020-07-14 06:00:00,109.3,36.005,50.801,29.509 +2020-07-14 06:15:00,108.62,35.589,50.801,29.509 +2020-07-14 06:30:00,105.84,34.981,50.801,29.509 +2020-07-14 06:45:00,105.08,37.208,50.801,29.509 +2020-07-14 07:00:00,116.07,37.256,60.202,29.509 +2020-07-14 07:15:00,115.56,37.838,60.202,29.509 +2020-07-14 07:30:00,111.32,35.568000000000005,60.202,29.509 +2020-07-14 07:45:00,103.14,35.945,60.202,29.509 +2020-07-14 08:00:00,106.06,31.928,54.461000000000006,29.509 +2020-07-14 08:15:00,103.57,35.115,54.461000000000006,29.509 +2020-07-14 08:30:00,103.42,36.209,54.461000000000006,29.509 +2020-07-14 08:45:00,106.65,38.788000000000004,54.461000000000006,29.509 +2020-07-14 09:00:00,110.63,32.869,50.753,29.509 +2020-07-14 09:15:00,110.97,32.167,50.753,29.509 +2020-07-14 09:30:00,104.6,35.965,50.753,29.509 +2020-07-14 09:45:00,103.21,39.481,50.753,29.509 +2020-07-14 10:00:00,108.94,36.727,49.703,29.509 +2020-07-14 10:15:00,110.62,38.361,49.703,29.509 +2020-07-14 10:30:00,109.15,38.375,49.703,29.509 +2020-07-14 10:45:00,102.77,39.693000000000005,49.703,29.509 +2020-07-14 11:00:00,100.68,37.448,49.42100000000001,29.509 +2020-07-14 11:15:00,104.47,38.778,49.42100000000001,29.509 +2020-07-14 11:30:00,103.85,40.007,49.42100000000001,29.509 +2020-07-14 11:45:00,104.68,41.293,49.42100000000001,29.509 +2020-07-14 12:00:00,101.13,36.594,47.155,29.509 +2020-07-14 12:15:00,100.12,36.073,47.155,29.509 +2020-07-14 12:30:00,97.38,34.82,47.155,29.509 +2020-07-14 12:45:00,103.21,35.93,47.155,29.509 +2020-07-14 13:00:00,104.0,36.05,47.515,29.509 +2020-07-14 13:15:00,102.08,37.375,47.515,29.509 +2020-07-14 13:30:00,96.92,35.641,47.515,29.509 +2020-07-14 13:45:00,97.9,35.316,47.515,29.509 +2020-07-14 14:00:00,96.33,37.105,47.575,29.509 +2020-07-14 14:15:00,101.9,36.049,47.575,29.509 +2020-07-14 14:30:00,99.58,34.953,47.575,29.509 +2020-07-14 14:45:00,98.18,35.567,47.575,29.509 +2020-07-14 15:00:00,93.03,37.619,48.903,29.509 +2020-07-14 15:15:00,90.27,35.195,48.903,29.509 +2020-07-14 15:30:00,93.61,33.589,48.903,29.509 +2020-07-14 15:45:00,99.29,31.824,48.903,29.509 +2020-07-14 16:00:00,102.48,34.764,50.218999999999994,29.509 +2020-07-14 16:15:00,101.61,34.497,50.218999999999994,29.509 +2020-07-14 16:30:00,97.93,33.715,50.218999999999994,29.509 +2020-07-14 16:45:00,99.0,30.489,50.218999999999994,29.509 +2020-07-14 17:00:00,100.95,34.236999999999995,55.396,29.509 +2020-07-14 17:15:00,101.47,34.311,55.396,29.509 +2020-07-14 17:30:00,101.51,33.196999999999996,55.396,29.509 +2020-07-14 17:45:00,104.76,32.015,55.396,29.509 +2020-07-14 18:00:00,107.85,35.096,55.583999999999996,29.509 +2020-07-14 18:15:00,108.01,34.913000000000004,55.583999999999996,29.509 +2020-07-14 18:30:00,105.94,32.885999999999996,55.583999999999996,29.509 +2020-07-14 18:45:00,101.24,35.943000000000005,55.583999999999996,29.509 +2020-07-14 19:00:00,102.79,37.137,56.071000000000005,29.509 +2020-07-14 19:15:00,102.8,36.111,56.071000000000005,29.509 +2020-07-14 19:30:00,101.74,34.975,56.071000000000005,29.509 +2020-07-14 19:45:00,95.47,34.209,56.071000000000005,29.509 +2020-07-14 20:00:00,91.86,31.309,61.55,29.509 +2020-07-14 20:15:00,91.07,30.857,61.55,29.509 +2020-07-14 20:30:00,91.99,31.381,61.55,29.509 +2020-07-14 20:45:00,93.25,32.04,61.55,29.509 +2020-07-14 21:00:00,90.18,30.436,55.94,29.509 +2020-07-14 21:15:00,89.36,32.047,55.94,29.509 +2020-07-14 21:30:00,86.61,32.768,55.94,29.509 +2020-07-14 21:45:00,85.12,33.775,55.94,29.509 +2020-07-14 22:00:00,80.32,30.973000000000003,52.857,29.509 +2020-07-14 22:15:00,79.79,33.615,52.857,29.509 +2020-07-14 22:30:00,78.37,29.256999999999998,52.857,29.509 +2020-07-14 22:45:00,77.97,25.984,52.857,29.509 +2020-07-14 23:00:00,72.49,22.794,46.04,29.509 +2020-07-14 23:15:00,73.85,21.485,46.04,29.509 +2020-07-14 23:30:00,70.65,20.417,46.04,29.509 +2020-07-14 23:45:00,72.46,19.43,46.04,29.509 +2020-07-15 00:00:00,70.34,18.844,42.195,29.509 +2020-07-15 00:15:00,71.25,19.69,42.195,29.509 +2020-07-15 00:30:00,70.44,18.468,42.195,29.509 +2020-07-15 00:45:00,70.41,18.444000000000003,42.195,29.509 +2020-07-15 01:00:00,67.46,18.48,38.82,29.509 +2020-07-15 01:15:00,68.86,17.599,38.82,29.509 +2020-07-15 01:30:00,67.2,16.067999999999998,38.82,29.509 +2020-07-15 01:45:00,67.83,16.009,38.82,29.509 +2020-07-15 02:00:00,66.75,15.790999999999999,37.023,29.509 +2020-07-15 02:15:00,67.72,14.513,37.023,29.509 +2020-07-15 02:30:00,67.76,16.441,37.023,29.509 +2020-07-15 02:45:00,67.84,17.197,37.023,29.509 +2020-07-15 03:00:00,66.87,18.329,36.818000000000005,29.509 +2020-07-15 03:15:00,71.03,17.664,36.818000000000005,29.509 +2020-07-15 03:30:00,71.12,17.007,36.818000000000005,29.509 +2020-07-15 03:45:00,72.97,16.365,36.818000000000005,29.509 +2020-07-15 04:00:00,73.68,20.866,37.495,29.509 +2020-07-15 04:15:00,76.29,26.789,37.495,29.509 +2020-07-15 04:30:00,80.11,24.078000000000003,37.495,29.509 +2020-07-15 04:45:00,84.96,24.088,37.495,29.509 +2020-07-15 05:00:00,87.78,36.374,39.858000000000004,29.509 +2020-07-15 05:15:00,87.33,42.99100000000001,39.858000000000004,29.509 +2020-07-15 05:30:00,94.23,37.495,39.858000000000004,29.509 +2020-07-15 05:45:00,94.63,35.131,39.858000000000004,29.509 +2020-07-15 06:00:00,100.78,35.971,52.867,29.509 +2020-07-15 06:15:00,108.9,35.558,52.867,29.509 +2020-07-15 06:30:00,110.63,34.966,52.867,29.509 +2020-07-15 06:45:00,111.18,37.216,52.867,29.509 +2020-07-15 07:00:00,105.52,37.258,66.061,29.509 +2020-07-15 07:15:00,103.46,37.86,66.061,29.509 +2020-07-15 07:30:00,103.47,35.599000000000004,66.061,29.509 +2020-07-15 07:45:00,102.77,35.999,66.061,29.509 +2020-07-15 08:00:00,103.23,31.991,58.532,29.509 +2020-07-15 08:15:00,108.72,35.184,58.532,29.509 +2020-07-15 08:30:00,110.2,36.266999999999996,58.532,29.509 +2020-07-15 08:45:00,110.41,38.838,58.532,29.509 +2020-07-15 09:00:00,110.93,32.918,56.047,29.509 +2020-07-15 09:15:00,114.49,32.213,56.047,29.509 +2020-07-15 09:30:00,113.05,36.003,56.047,29.509 +2020-07-15 09:45:00,111.8,39.516999999999996,56.047,29.509 +2020-07-15 10:00:00,110.53,36.769,53.823,29.509 +2020-07-15 10:15:00,113.38,38.397,53.823,29.509 +2020-07-15 10:30:00,118.08,38.407,53.823,29.509 +2020-07-15 10:45:00,119.14,39.724000000000004,53.823,29.509 +2020-07-15 11:00:00,114.85,37.48,54.184,29.509 +2020-07-15 11:15:00,105.35,38.809,54.184,29.509 +2020-07-15 11:30:00,107.75,40.027,54.184,29.509 +2020-07-15 11:45:00,104.65,41.305,54.184,29.509 +2020-07-15 12:00:00,101.57,36.622,52.628,29.509 +2020-07-15 12:15:00,102.98,36.098,52.628,29.509 +2020-07-15 12:30:00,101.37,34.839,52.628,29.509 +2020-07-15 12:45:00,100.1,35.945,52.628,29.509 +2020-07-15 13:00:00,95.13,36.046,52.31,29.509 +2020-07-15 13:15:00,97.79,37.365,52.31,29.509 +2020-07-15 13:30:00,92.09,35.635,52.31,29.509 +2020-07-15 13:45:00,93.99,35.318000000000005,52.31,29.509 +2020-07-15 14:00:00,91.96,37.105,52.278999999999996,29.509 +2020-07-15 14:15:00,93.66,36.051,52.278999999999996,29.509 +2020-07-15 14:30:00,93.43,34.949,52.278999999999996,29.509 +2020-07-15 14:45:00,94.43,35.57,52.278999999999996,29.509 +2020-07-15 15:00:00,90.17,37.62,53.306999999999995,29.509 +2020-07-15 15:15:00,89.69,35.193000000000005,53.306999999999995,29.509 +2020-07-15 15:30:00,94.34,33.59,53.306999999999995,29.509 +2020-07-15 15:45:00,93.63,31.82,53.306999999999995,29.509 +2020-07-15 16:00:00,94.82,34.765,55.358999999999995,29.509 +2020-07-15 16:15:00,96.19,34.501999999999995,55.358999999999995,29.509 +2020-07-15 16:30:00,95.74,33.736999999999995,55.358999999999995,29.509 +2020-07-15 16:45:00,96.06,30.52,55.358999999999995,29.509 +2020-07-15 17:00:00,98.6,34.265,59.211999999999996,29.509 +2020-07-15 17:15:00,98.46,34.354,59.211999999999996,29.509 +2020-07-15 17:30:00,98.19,33.245,59.211999999999996,29.509 +2020-07-15 17:45:00,101.1,32.071999999999996,59.211999999999996,29.509 +2020-07-15 18:00:00,101.97,35.156,60.403999999999996,29.509 +2020-07-15 18:15:00,100.51,34.96,60.403999999999996,29.509 +2020-07-15 18:30:00,99.62,32.937,60.403999999999996,29.509 +2020-07-15 18:45:00,99.61,35.994,60.403999999999996,29.509 +2020-07-15 19:00:00,96.67,37.191,60.993,29.509 +2020-07-15 19:15:00,92.57,36.158,60.993,29.509 +2020-07-15 19:30:00,91.19,35.016999999999996,60.993,29.509 +2020-07-15 19:45:00,90.05,34.249,60.993,29.509 +2020-07-15 20:00:00,89.39,31.340999999999998,66.6,29.509 +2020-07-15 20:15:00,89.79,30.886999999999997,66.6,29.509 +2020-07-15 20:30:00,90.48,31.406999999999996,66.6,29.509 +2020-07-15 20:45:00,94.33,32.068000000000005,66.6,29.509 +2020-07-15 21:00:00,89.0,30.467,59.855,29.509 +2020-07-15 21:15:00,90.24,32.078,59.855,29.509 +2020-07-15 21:30:00,86.46,32.781,59.855,29.509 +2020-07-15 21:45:00,84.69,33.777,59.855,29.509 +2020-07-15 22:00:00,80.58,30.976999999999997,54.942,29.509 +2020-07-15 22:15:00,79.78,33.615,54.942,29.509 +2020-07-15 22:30:00,77.4,29.239,54.942,29.509 +2020-07-15 22:45:00,76.55,25.959,54.942,29.509 +2020-07-15 23:00:00,70.55,22.781,46.056000000000004,29.509 +2020-07-15 23:15:00,73.75,21.489,46.056000000000004,29.509 +2020-07-15 23:30:00,72.68,20.434,46.056000000000004,29.509 +2020-07-15 23:45:00,71.83,19.444000000000003,46.056000000000004,29.509 +2020-07-16 00:00:00,68.4,17.695999999999998,40.859,29.509 +2020-07-16 00:15:00,69.33,18.609,40.859,29.509 +2020-07-16 00:30:00,69.21,17.279,40.859,29.509 +2020-07-16 00:45:00,69.57,17.358,40.859,29.509 +2020-07-16 01:00:00,66.81,17.385,39.06,29.509 +2020-07-16 01:15:00,69.01,16.565,39.06,29.509 +2020-07-16 01:30:00,67.69,15.054,39.06,29.509 +2020-07-16 01:45:00,68.28,15.171,39.06,29.509 +2020-07-16 02:00:00,68.0,15.002,37.592,29.509 +2020-07-16 02:15:00,67.65,14.513,37.592,29.509 +2020-07-16 02:30:00,67.91,15.694,37.592,29.509 +2020-07-16 02:45:00,69.21,16.421,37.592,29.509 +2020-07-16 03:00:00,68.8,17.433,37.416,29.509 +2020-07-16 03:15:00,69.64,16.917,37.416,29.509 +2020-07-16 03:30:00,71.41,16.209,37.416,29.509 +2020-07-16 03:45:00,74.92,15.402999999999999,37.416,29.509 +2020-07-16 04:00:00,75.15,19.62,38.176,29.509 +2020-07-16 04:15:00,76.88,25.261999999999997,38.176,29.509 +2020-07-16 04:30:00,76.66,22.57,38.176,29.509 +2020-07-16 04:45:00,80.18,22.496,38.176,29.509 +2020-07-16 05:00:00,87.87,34.303000000000004,41.203,29.509 +2020-07-16 05:15:00,89.57,40.372,41.203,29.509 +2020-07-16 05:30:00,96.26,35.325,41.203,29.509 +2020-07-16 05:45:00,101.5,32.945,41.203,29.509 +2020-07-16 06:00:00,107.07,33.306,51.09,29.509 +2020-07-16 06:15:00,102.95,32.66,51.09,29.509 +2020-07-16 06:30:00,101.95,32.357,51.09,29.509 +2020-07-16 06:45:00,107.07,34.909,51.09,29.509 +2020-07-16 07:00:00,109.85,34.71,63.541000000000004,29.509 +2020-07-16 07:15:00,107.55,35.439,63.541000000000004,29.509 +2020-07-16 07:30:00,105.46,33.343,63.541000000000004,29.509 +2020-07-16 07:45:00,99.9,33.835,63.541000000000004,29.509 +2020-07-16 08:00:00,103.71,29.159000000000002,55.65,29.509 +2020-07-16 08:15:00,106.39,32.507,55.65,29.509 +2020-07-16 08:30:00,107.35,34.055,55.65,29.509 +2020-07-16 08:45:00,103.5,36.774,55.65,29.509 +2020-07-16 09:00:00,99.32,32.482,51.833999999999996,29.509 +2020-07-16 09:15:00,101.09,32.001999999999995,51.833999999999996,29.509 +2020-07-16 09:30:00,106.4,35.815,51.833999999999996,29.509 +2020-07-16 09:45:00,112.1,39.141,51.833999999999996,29.509 +2020-07-16 10:00:00,113.34,36.639,49.70399999999999,29.509 +2020-07-16 10:15:00,103.53,38.18,49.70399999999999,29.509 +2020-07-16 10:30:00,108.61,38.09,49.70399999999999,29.509 +2020-07-16 10:45:00,110.26,39.092,49.70399999999999,29.509 +2020-07-16 11:00:00,107.49,36.751999999999995,48.593999999999994,29.509 +2020-07-16 11:15:00,101.65,38.056999999999995,48.593999999999994,29.509 +2020-07-16 11:30:00,99.95,39.297,48.593999999999994,29.509 +2020-07-16 11:45:00,105.9,40.42,48.593999999999994,29.509 +2020-07-16 12:00:00,104.35,34.999,46.275,29.509 +2020-07-16 12:15:00,106.27,34.306999999999995,46.275,29.509 +2020-07-16 12:30:00,97.52,33.074,46.275,29.509 +2020-07-16 12:45:00,98.65,34.076,46.275,29.509 +2020-07-16 13:00:00,102.47,34.053000000000004,45.803000000000004,29.509 +2020-07-16 13:15:00,104.02,35.477,45.803000000000004,29.509 +2020-07-16 13:30:00,100.98,33.608000000000004,45.803000000000004,29.509 +2020-07-16 13:45:00,94.53,33.578,45.803000000000004,29.509 +2020-07-16 14:00:00,96.85,35.39,46.251999999999995,29.509 +2020-07-16 14:15:00,96.71,34.388000000000005,46.251999999999995,29.509 +2020-07-16 14:30:00,99.91,33.429,46.251999999999995,29.509 +2020-07-16 14:45:00,99.62,34.041,46.251999999999995,29.509 +2020-07-16 15:00:00,96.59,36.379,48.309,29.509 +2020-07-16 15:15:00,93.27,34.016999999999996,48.309,29.509 +2020-07-16 15:30:00,95.88,32.552,48.309,29.509 +2020-07-16 15:45:00,100.36,30.926,48.309,29.509 +2020-07-16 16:00:00,100.07,33.31,49.681999999999995,29.509 +2020-07-16 16:15:00,95.77,33.111,49.681999999999995,29.509 +2020-07-16 16:30:00,98.62,32.172,49.681999999999995,29.509 +2020-07-16 16:45:00,100.86,29.104,49.681999999999995,29.509 +2020-07-16 17:00:00,105.52,33.069,53.086000000000006,29.509 +2020-07-16 17:15:00,107.45,33.188,53.086000000000006,29.509 +2020-07-16 17:30:00,104.16,32.003,53.086000000000006,29.509 +2020-07-16 17:45:00,103.07,31.022,53.086000000000006,29.509 +2020-07-16 18:00:00,101.84,34.315,54.038999999999994,29.509 +2020-07-16 18:15:00,100.31,34.167,54.038999999999994,29.509 +2020-07-16 18:30:00,100.5,32.226,54.038999999999994,29.509 +2020-07-16 18:45:00,106.08,34.979,54.038999999999994,29.509 +2020-07-16 19:00:00,105.9,36.38,53.408,29.509 +2020-07-16 19:15:00,102.77,35.374,53.408,29.509 +2020-07-16 19:30:00,96.1,34.2,53.408,29.509 +2020-07-16 19:45:00,95.11,33.135,53.408,29.509 +2020-07-16 20:00:00,91.57,30.674,55.309,29.509 +2020-07-16 20:15:00,94.42,29.885,55.309,29.509 +2020-07-16 20:30:00,93.02,30.358,55.309,29.509 +2020-07-16 20:45:00,93.42,30.788,55.309,29.509 +2020-07-16 21:00:00,91.2,29.548000000000002,51.585,29.509 +2020-07-16 21:15:00,90.29,30.741999999999997,51.585,29.509 +2020-07-16 21:30:00,87.58,31.38,51.585,29.509 +2020-07-16 21:45:00,86.37,32.32,51.585,29.509 +2020-07-16 22:00:00,81.95,29.609,48.006,29.509 +2020-07-16 22:15:00,81.66,32.455,48.006,29.509 +2020-07-16 22:30:00,79.46,28.267,48.006,29.509 +2020-07-16 22:45:00,80.13,25.146,48.006,29.509 +2020-07-16 23:00:00,75.25,21.895,42.309,29.509 +2020-07-16 23:15:00,75.85,20.484,42.309,29.509 +2020-07-16 23:30:00,74.85,19.387,42.309,29.509 +2020-07-16 23:45:00,73.27,18.362000000000002,42.309,29.509 +2020-07-17 00:00:00,69.67,15.872,39.649,29.509 +2020-07-17 00:15:00,72.11,17.002,39.649,29.509 +2020-07-17 00:30:00,72.27,15.982999999999999,39.649,29.509 +2020-07-17 00:45:00,72.1,16.518,39.649,29.509 +2020-07-17 01:00:00,69.69,16.156,37.744,29.509 +2020-07-17 01:15:00,70.44,14.607000000000001,37.744,29.509 +2020-07-17 01:30:00,70.83,14.513,37.744,29.509 +2020-07-17 01:45:00,73.34,14.513,37.744,29.509 +2020-07-17 02:00:00,70.32,14.565999999999999,36.965,29.509 +2020-07-17 02:15:00,70.93,14.513,36.965,29.509 +2020-07-17 02:30:00,70.09,16.064,36.965,29.509 +2020-07-17 02:45:00,69.59,16.048,36.965,29.509 +2020-07-17 03:00:00,70.93,17.969,37.678000000000004,29.509 +2020-07-17 03:15:00,72.45,16.085,37.678000000000004,29.509 +2020-07-17 03:30:00,74.49,15.127,37.678000000000004,29.509 +2020-07-17 03:45:00,74.49,15.26,37.678000000000004,29.509 +2020-07-17 04:00:00,76.78,19.583,38.591,29.509 +2020-07-17 04:15:00,83.05,23.531999999999996,38.591,29.509 +2020-07-17 04:30:00,83.79,21.848000000000003,38.591,29.509 +2020-07-17 04:45:00,82.32,21.16,38.591,29.509 +2020-07-17 05:00:00,92.53,32.51,40.666,29.509 +2020-07-17 05:15:00,93.78,39.453,40.666,29.509 +2020-07-17 05:30:00,99.45,34.591,40.666,29.509 +2020-07-17 05:45:00,102.94,31.750999999999998,40.666,29.509 +2020-07-17 06:00:00,108.29,32.311,51.784,29.509 +2020-07-17 06:15:00,101.27,31.945999999999998,51.784,29.509 +2020-07-17 06:30:00,101.61,31.660999999999998,51.784,29.509 +2020-07-17 06:45:00,108.21,33.99,51.784,29.509 +2020-07-17 07:00:00,112.7,34.54,61.383,29.509 +2020-07-17 07:15:00,110.87,36.194,61.383,29.509 +2020-07-17 07:30:00,107.13,31.997,61.383,29.509 +2020-07-17 07:45:00,105.72,32.355,61.383,29.509 +2020-07-17 08:00:00,109.76,28.636,55.272,29.509 +2020-07-17 08:15:00,109.09,32.758,55.272,29.509 +2020-07-17 08:30:00,106.95,34.098,55.272,29.509 +2020-07-17 08:45:00,103.14,36.793,55.272,29.509 +2020-07-17 09:00:00,105.78,29.919,53.506,29.509 +2020-07-17 09:15:00,106.52,31.503,53.506,29.509 +2020-07-17 09:30:00,107.91,34.588,53.506,29.509 +2020-07-17 09:45:00,105.7,38.348,53.506,29.509 +2020-07-17 10:00:00,103.69,35.79,51.363,29.509 +2020-07-17 10:15:00,108.86,37.004,51.363,29.509 +2020-07-17 10:30:00,108.56,37.53,51.363,29.509 +2020-07-17 10:45:00,105.22,38.471,51.363,29.509 +2020-07-17 11:00:00,99.4,36.409,51.043,29.509 +2020-07-17 11:15:00,104.69,36.599000000000004,51.043,29.509 +2020-07-17 11:30:00,105.83,37.249,51.043,29.509 +2020-07-17 11:45:00,105.9,37.297,51.043,29.509 +2020-07-17 12:00:00,97.56,32.258,47.52,29.509 +2020-07-17 12:15:00,96.68,31.066,47.52,29.509 +2020-07-17 12:30:00,91.93,29.941999999999997,47.52,29.509 +2020-07-17 12:45:00,92.95,30.011999999999997,47.52,29.509 +2020-07-17 13:00:00,96.42,30.528000000000002,45.494,29.509 +2020-07-17 13:15:00,97.53,32.061,45.494,29.509 +2020-07-17 13:30:00,94.43,31.075,45.494,29.509 +2020-07-17 13:45:00,91.76,31.401999999999997,45.494,29.509 +2020-07-17 14:00:00,92.13,32.44,43.883,29.509 +2020-07-17 14:15:00,90.45,31.94,43.883,29.509 +2020-07-17 14:30:00,91.4,32.594,43.883,29.509 +2020-07-17 14:45:00,93.5,32.418,43.883,29.509 +2020-07-17 15:00:00,94.71,34.715,45.714,29.509 +2020-07-17 15:15:00,95.27,32.129,45.714,29.509 +2020-07-17 15:30:00,88.29,30.189,45.714,29.509 +2020-07-17 15:45:00,92.07,29.386,45.714,29.509 +2020-07-17 16:00:00,91.83,30.943,48.222,29.509 +2020-07-17 16:15:00,100.32,31.264,48.222,29.509 +2020-07-17 16:30:00,98.96,30.133000000000003,48.222,29.509 +2020-07-17 16:45:00,102.8,26.223000000000003,48.222,29.509 +2020-07-17 17:00:00,96.41,32.058,52.619,29.509 +2020-07-17 17:15:00,97.39,32.042,52.619,29.509 +2020-07-17 17:30:00,96.03,31.074,52.619,29.509 +2020-07-17 17:45:00,101.63,29.944000000000003,52.619,29.509 +2020-07-17 18:00:00,104.59,33.204,52.99,29.509 +2020-07-17 18:15:00,104.12,32.05,52.99,29.509 +2020-07-17 18:30:00,99.36,29.965999999999998,52.99,29.509 +2020-07-17 18:45:00,95.42,33.177,52.99,29.509 +2020-07-17 19:00:00,99.76,35.439,51.923,29.509 +2020-07-17 19:15:00,99.09,34.96,51.923,29.509 +2020-07-17 19:30:00,93.72,33.879,51.923,29.509 +2020-07-17 19:45:00,89.16,31.75,51.923,29.509 +2020-07-17 20:00:00,86.65,29.105999999999998,56.238,29.509 +2020-07-17 20:15:00,91.65,29.175,56.238,29.509 +2020-07-17 20:30:00,91.6,29.116999999999997,56.238,29.509 +2020-07-17 20:45:00,89.51,28.627,56.238,29.509 +2020-07-17 21:00:00,81.75,28.764,52.426,29.509 +2020-07-17 21:15:00,84.05,31.724,52.426,29.509 +2020-07-17 21:30:00,85.59,32.179,52.426,29.509 +2020-07-17 21:45:00,85.67,33.286,52.426,29.509 +2020-07-17 22:00:00,79.08,30.348000000000003,48.196000000000005,29.509 +2020-07-17 22:15:00,73.88,32.935,48.196000000000005,29.509 +2020-07-17 22:30:00,71.96,33.379,48.196000000000005,29.509 +2020-07-17 22:45:00,70.94,30.925,48.196000000000005,29.509 +2020-07-17 23:00:00,69.13,29.535999999999998,41.71,29.509 +2020-07-17 23:15:00,72.96,26.595,41.71,29.509 +2020-07-17 23:30:00,74.34,23.662,41.71,29.509 +2020-07-17 23:45:00,73.55,22.548000000000002,41.71,29.509 +2020-07-18 00:00:00,64.97,17.482,41.105,29.398000000000003 +2020-07-18 00:15:00,64.83,18.22,41.105,29.398000000000003 +2020-07-18 00:30:00,69.52,16.590999999999998,41.105,29.398000000000003 +2020-07-18 00:45:00,69.35,16.285,41.105,29.398000000000003 +2020-07-18 01:00:00,67.98,16.169,36.934,29.398000000000003 +2020-07-18 01:15:00,63.54,15.332,36.934,29.398000000000003 +2020-07-18 01:30:00,65.47,14.513,36.934,29.398000000000003 +2020-07-18 01:45:00,67.32,14.98,36.934,29.398000000000003 +2020-07-18 02:00:00,66.36,14.703,34.782,29.398000000000003 +2020-07-18 02:15:00,62.18,14.513,34.782,29.398000000000003 +2020-07-18 02:30:00,60.75,14.605,34.782,29.398000000000003 +2020-07-18 02:45:00,66.16,15.431,34.782,29.398000000000003 +2020-07-18 03:00:00,65.84,15.811,34.489000000000004,29.398000000000003 +2020-07-18 03:15:00,67.15,14.513,34.489000000000004,29.398000000000003 +2020-07-18 03:30:00,60.47,14.513,34.489000000000004,29.398000000000003 +2020-07-18 03:45:00,66.51,14.547,34.489000000000004,29.398000000000003 +2020-07-18 04:00:00,65.6,17.13,34.111,29.398000000000003 +2020-07-18 04:15:00,60.82,20.293,34.111,29.398000000000003 +2020-07-18 04:30:00,65.33,16.974,34.111,29.398000000000003 +2020-07-18 04:45:00,61.82,16.62,34.111,29.398000000000003 +2020-07-18 05:00:00,61.13,20.213,33.283,29.398000000000003 +2020-07-18 05:15:00,60.73,16.794,33.283,29.398000000000003 +2020-07-18 05:30:00,62.16,14.513,33.283,29.398000000000003 +2020-07-18 05:45:00,66.61,15.129000000000001,33.283,29.398000000000003 +2020-07-18 06:00:00,72.23,26.281,33.653,29.398000000000003 +2020-07-18 06:15:00,71.83,33.03,33.653,29.398000000000003 +2020-07-18 06:30:00,69.3,30.125,33.653,29.398000000000003 +2020-07-18 06:45:00,68.38,29.725,33.653,29.398000000000003 +2020-07-18 07:00:00,73.72,29.413,36.732,29.398000000000003 +2020-07-18 07:15:00,77.89,29.795,36.732,29.398000000000003 +2020-07-18 07:30:00,79.76,26.998,36.732,29.398000000000003 +2020-07-18 07:45:00,78.08,28.014,36.732,29.398000000000003 +2020-07-18 08:00:00,79.55,24.814,41.318999999999996,29.398000000000003 +2020-07-18 08:15:00,85.3,28.331999999999997,41.318999999999996,29.398000000000003 +2020-07-18 08:30:00,84.34,29.479,41.318999999999996,29.398000000000003 +2020-07-18 08:45:00,82.38,32.879,41.318999999999996,29.398000000000003 +2020-07-18 09:00:00,79.5,29.301,43.195,29.398000000000003 +2020-07-18 09:15:00,79.99,31.334,43.195,29.398000000000003 +2020-07-18 09:30:00,88.35,34.856,43.195,29.398000000000003 +2020-07-18 09:45:00,91.53,38.098,43.195,29.398000000000003 +2020-07-18 10:00:00,87.81,36.235,41.843999999999994,29.398000000000003 +2020-07-18 10:15:00,83.2,37.816,41.843999999999994,29.398000000000003 +2020-07-18 10:30:00,84.65,37.939,41.843999999999994,29.398000000000003 +2020-07-18 10:45:00,92.01,38.341,41.843999999999994,29.398000000000003 +2020-07-18 11:00:00,88.51,36.077,39.035,29.398000000000003 +2020-07-18 11:15:00,83.11,37.3,39.035,29.398000000000003 +2020-07-18 11:30:00,78.1,38.375,39.035,29.398000000000003 +2020-07-18 11:45:00,76.27,39.258,39.035,29.398000000000003 +2020-07-18 12:00:00,74.23,34.78,38.001,29.398000000000003 +2020-07-18 12:15:00,71.26,34.455,38.001,29.398000000000003 +2020-07-18 12:30:00,65.03,33.147,38.001,29.398000000000003 +2020-07-18 12:45:00,61.3,34.099000000000004,38.001,29.398000000000003 +2020-07-18 13:00:00,60.24,33.71,34.747,29.398000000000003 +2020-07-18 13:15:00,62.03,34.865,34.747,29.398000000000003 +2020-07-18 13:30:00,65.18,34.141,34.747,29.398000000000003 +2020-07-18 13:45:00,70.0,32.931999999999995,34.747,29.398000000000003 +2020-07-18 14:00:00,72.75,33.854,33.434,29.398000000000003 +2020-07-18 14:15:00,74.53,32.037,33.434,29.398000000000003 +2020-07-18 14:30:00,77.56,32.413000000000004,33.434,29.398000000000003 +2020-07-18 14:45:00,79.82,32.749,33.434,29.398000000000003 +2020-07-18 15:00:00,79.73,35.416,35.921,29.398000000000003 +2020-07-18 15:15:00,78.71,33.518,35.921,29.398000000000003 +2020-07-18 15:30:00,78.58,31.645,35.921,29.398000000000003 +2020-07-18 15:45:00,78.43,29.824,35.921,29.398000000000003 +2020-07-18 16:00:00,77.97,33.81,39.427,29.398000000000003 +2020-07-18 16:15:00,75.31,33.069,39.427,29.398000000000003 +2020-07-18 16:30:00,75.91,32.207,39.427,29.398000000000003 +2020-07-18 16:45:00,77.36,28.189,39.427,29.398000000000003 +2020-07-18 17:00:00,78.24,32.734,44.096000000000004,29.398000000000003 +2020-07-18 17:15:00,78.03,30.230999999999998,44.096000000000004,29.398000000000003 +2020-07-18 17:30:00,78.44,29.135,44.096000000000004,29.398000000000003 +2020-07-18 17:45:00,80.13,28.595,44.096000000000004,29.398000000000003 +2020-07-18 18:00:00,79.23,33.352,43.931000000000004,29.398000000000003 +2020-07-18 18:15:00,78.26,33.839,43.931000000000004,29.398000000000003 +2020-07-18 18:30:00,77.24,33.111,43.931000000000004,29.398000000000003 +2020-07-18 18:45:00,76.69,32.857,43.931000000000004,29.398000000000003 +2020-07-18 19:00:00,73.66,33.239000000000004,42.187,29.398000000000003 +2020-07-18 19:15:00,71.27,31.711,42.187,29.398000000000003 +2020-07-18 19:30:00,70.94,31.392,42.187,29.398000000000003 +2020-07-18 19:45:00,71.36,31.168000000000003,42.187,29.398000000000003 +2020-07-18 20:00:00,71.01,29.235,38.315,29.398000000000003 +2020-07-18 20:15:00,72.39,28.453000000000003,38.315,29.398000000000003 +2020-07-18 20:30:00,72.9,27.475,38.315,29.398000000000003 +2020-07-18 20:45:00,72.58,29.005,38.315,29.398000000000003 +2020-07-18 21:00:00,71.06,27.381,36.843,29.398000000000003 +2020-07-18 21:15:00,70.83,29.934,36.843,29.398000000000003 +2020-07-18 21:30:00,68.87,30.485,36.843,29.398000000000003 +2020-07-18 21:45:00,68.49,31.026,36.843,29.398000000000003 +2020-07-18 22:00:00,65.43,27.968000000000004,37.260999999999996,29.398000000000003 +2020-07-18 22:15:00,65.56,30.64,37.260999999999996,29.398000000000003 +2020-07-18 22:30:00,63.24,30.226,37.260999999999996,29.398000000000003 +2020-07-18 22:45:00,62.82,28.031999999999996,37.260999999999996,29.398000000000003 +2020-07-18 23:00:00,57.26,25.816999999999997,32.148,29.398000000000003 +2020-07-18 23:15:00,59.35,23.447,32.148,29.398000000000003 +2020-07-18 23:30:00,58.36,23.248,32.148,29.398000000000003 +2020-07-18 23:45:00,57.61,22.671,32.148,29.398000000000003 +2020-07-19 00:00:00,55.61,18.977,28.905,29.398000000000003 +2020-07-19 00:15:00,55.79,18.499000000000002,28.905,29.398000000000003 +2020-07-19 00:30:00,54.68,16.748,28.905,29.398000000000003 +2020-07-19 00:45:00,54.38,16.272000000000002,28.905,29.398000000000003 +2020-07-19 01:00:00,51.87,16.452,26.906999999999996,29.398000000000003 +2020-07-19 01:15:00,53.39,15.395999999999999,26.906999999999996,29.398000000000003 +2020-07-19 01:30:00,53.69,14.513,26.906999999999996,29.398000000000003 +2020-07-19 01:45:00,53.75,14.513,26.906999999999996,29.398000000000003 +2020-07-19 02:00:00,52.09,14.513,25.938000000000002,29.398000000000003 +2020-07-19 02:15:00,52.77,14.513,25.938000000000002,29.398000000000003 +2020-07-19 02:30:00,52.15,15.205,25.938000000000002,29.398000000000003 +2020-07-19 02:45:00,52.3,15.685,25.938000000000002,29.398000000000003 +2020-07-19 03:00:00,51.95,16.727999999999998,24.693,29.398000000000003 +2020-07-19 03:15:00,52.28,14.535,24.693,29.398000000000003 +2020-07-19 03:30:00,53.32,14.513,24.693,29.398000000000003 +2020-07-19 03:45:00,53.05,14.513,24.693,29.398000000000003 +2020-07-19 04:00:00,52.24,16.62,25.683000000000003,29.398000000000003 +2020-07-19 04:15:00,51.65,19.378,25.683000000000003,29.398000000000003 +2020-07-19 04:30:00,51.6,17.485,25.683000000000003,29.398000000000003 +2020-07-19 04:45:00,52.23,16.61,25.683000000000003,29.398000000000003 +2020-07-19 05:00:00,51.43,20.625,26.023000000000003,29.398000000000003 +2020-07-19 05:15:00,51.17,16.522000000000002,26.023000000000003,29.398000000000003 +2020-07-19 05:30:00,51.75,14.513,26.023000000000003,29.398000000000003 +2020-07-19 05:45:00,52.6,14.513,26.023000000000003,29.398000000000003 +2020-07-19 06:00:00,54.27,22.829,25.834,29.398000000000003 +2020-07-19 06:15:00,54.5,30.6,25.834,29.398000000000003 +2020-07-19 06:30:00,55.07,27.053,25.834,29.398000000000003 +2020-07-19 06:45:00,57.47,25.699,25.834,29.398000000000003 +2020-07-19 07:00:00,59.13,25.54,27.765,29.398000000000003 +2020-07-19 07:15:00,59.65,24.248,27.765,29.398000000000003 +2020-07-19 07:30:00,60.2,22.959,27.765,29.398000000000003 +2020-07-19 07:45:00,60.84,24.031999999999996,27.765,29.398000000000003 +2020-07-19 08:00:00,61.14,21.519000000000002,31.357,29.398000000000003 +2020-07-19 08:15:00,58.21,26.334,31.357,29.398000000000003 +2020-07-19 08:30:00,57.28,28.247,31.357,29.398000000000003 +2020-07-19 08:45:00,58.82,31.338,31.357,29.398000000000003 +2020-07-19 09:00:00,59.11,27.697,33.238,29.398000000000003 +2020-07-19 09:15:00,60.1,29.121,33.238,29.398000000000003 +2020-07-19 09:30:00,59.77,33.126,33.238,29.398000000000003 +2020-07-19 09:45:00,62.12,37.48,33.238,29.398000000000003 +2020-07-19 10:00:00,62.62,35.931,34.22,29.398000000000003 +2020-07-19 10:15:00,63.28,37.588,34.22,29.398000000000003 +2020-07-19 10:30:00,63.94,37.878,34.22,29.398000000000003 +2020-07-19 10:45:00,63.69,39.635,34.22,29.398000000000003 +2020-07-19 11:00:00,63.64,36.87,36.298,29.398000000000003 +2020-07-19 11:15:00,65.93,37.582,36.298,29.398000000000003 +2020-07-19 11:30:00,62.62,39.328,36.298,29.398000000000003 +2020-07-19 11:45:00,61.61,40.412,36.298,29.398000000000003 +2020-07-19 12:00:00,60.44,37.195,33.52,29.398000000000003 +2020-07-19 12:15:00,58.9,35.936,33.52,29.398000000000003 +2020-07-19 12:30:00,58.57,35.041,33.52,29.398000000000003 +2020-07-19 12:45:00,56.89,35.4,33.52,29.398000000000003 +2020-07-19 13:00:00,54.98,34.747,30.12,29.398000000000003 +2020-07-19 13:15:00,53.24,34.882,30.12,29.398000000000003 +2020-07-19 13:30:00,51.7,32.954,30.12,29.398000000000003 +2020-07-19 13:45:00,54.52,33.073,30.12,29.398000000000003 +2020-07-19 14:00:00,54.96,35.285,27.233,29.398000000000003 +2020-07-19 14:15:00,52.39,33.801,27.233,29.398000000000003 +2020-07-19 14:30:00,50.08,32.659,27.233,29.398000000000003 +2020-07-19 14:45:00,49.54,31.871,27.233,29.398000000000003 +2020-07-19 15:00:00,51.32,34.882,27.468000000000004,29.398000000000003 +2020-07-19 15:15:00,51.02,32.001,27.468000000000004,29.398000000000003 +2020-07-19 15:30:00,52.26,29.862,27.468000000000004,29.398000000000003 +2020-07-19 15:45:00,52.6,28.27,27.468000000000004,29.398000000000003 +2020-07-19 16:00:00,56.74,30.274,30.8,29.398000000000003 +2020-07-19 16:15:00,57.44,29.891,30.8,29.398000000000003 +2020-07-19 16:30:00,60.95,30.116999999999997,30.8,29.398000000000003 +2020-07-19 16:45:00,61.94,26.168000000000003,30.8,29.398000000000003 +2020-07-19 17:00:00,67.22,31.125999999999998,37.806,29.398000000000003 +2020-07-19 17:15:00,71.64,30.329,37.806,29.398000000000003 +2020-07-19 17:30:00,73.51,30.09,37.806,29.398000000000003 +2020-07-19 17:45:00,74.82,29.78,37.806,29.398000000000003 +2020-07-19 18:00:00,75.5,35.266,40.766,29.398000000000003 +2020-07-19 18:15:00,73.22,35.173,40.766,29.398000000000003 +2020-07-19 18:30:00,72.29,34.387,40.766,29.398000000000003 +2020-07-19 18:45:00,72.76,34.076,40.766,29.398000000000003 +2020-07-19 19:00:00,74.69,36.863,41.163000000000004,29.398000000000003 +2020-07-19 19:15:00,73.14,34.086,41.163000000000004,29.398000000000003 +2020-07-19 19:30:00,71.41,33.510999999999996,41.163000000000004,29.398000000000003 +2020-07-19 19:45:00,72.67,32.666,41.163000000000004,29.398000000000003 +2020-07-19 20:00:00,73.53,30.916,39.885999999999996,29.398000000000003 +2020-07-19 20:15:00,75.2,29.899,39.885999999999996,29.398000000000003 +2020-07-19 20:30:00,76.95,29.673000000000002,39.885999999999996,29.398000000000003 +2020-07-19 20:45:00,76.76,29.506,39.885999999999996,29.398000000000003 +2020-07-19 21:00:00,74.32,28.004,38.900999999999996,29.398000000000003 +2020-07-19 21:15:00,74.99,30.284000000000002,38.900999999999996,29.398000000000003 +2020-07-19 21:30:00,73.19,30.101999999999997,38.900999999999996,29.398000000000003 +2020-07-19 21:45:00,71.17,30.999000000000002,38.900999999999996,29.398000000000003 +2020-07-19 22:00:00,66.49,30.218000000000004,39.806999999999995,29.398000000000003 +2020-07-19 22:15:00,68.96,31.16,39.806999999999995,29.398000000000003 +2020-07-19 22:30:00,67.63,30.438000000000002,39.806999999999995,29.398000000000003 +2020-07-19 22:45:00,67.17,26.953000000000003,39.806999999999995,29.398000000000003 +2020-07-19 23:00:00,62.05,24.655,35.564,29.398000000000003 +2020-07-19 23:15:00,63.9,23.502,35.564,29.398000000000003 +2020-07-19 23:30:00,62.49,22.683000000000003,35.564,29.398000000000003 +2020-07-19 23:45:00,62.2,22.201,35.564,29.398000000000003 +2020-07-20 00:00:00,60.09,20.273,36.578,29.509 +2020-07-20 00:15:00,60.71,20.372,36.578,29.509 +2020-07-20 00:30:00,59.91,18.199,36.578,29.509 +2020-07-20 00:45:00,60.28,17.354,36.578,29.509 +2020-07-20 01:00:00,58.64,17.95,35.292,29.509 +2020-07-20 01:15:00,58.78,16.954,35.292,29.509 +2020-07-20 01:30:00,59.4,15.62,35.292,29.509 +2020-07-20 01:45:00,58.95,16.297,35.292,29.509 +2020-07-20 02:00:00,58.17,16.67,34.319,29.509 +2020-07-20 02:15:00,59.43,14.513,34.319,29.509 +2020-07-20 02:30:00,65.62,16.453,34.319,29.509 +2020-07-20 02:45:00,68.61,16.833,34.319,29.509 +2020-07-20 03:00:00,68.25,18.299,33.13,29.509 +2020-07-20 03:15:00,63.19,16.746,33.13,29.509 +2020-07-20 03:30:00,64.3,16.148,33.13,29.509 +2020-07-20 03:45:00,71.14,16.613,33.13,29.509 +2020-07-20 04:00:00,74.7,22.09,33.851,29.509 +2020-07-20 04:15:00,76.07,27.603,33.851,29.509 +2020-07-20 04:30:00,72.95,24.997,33.851,29.509 +2020-07-20 04:45:00,78.22,24.505,33.851,29.509 +2020-07-20 05:00:00,83.67,34.926,38.718,29.509 +2020-07-20 05:15:00,86.56,40.138000000000005,38.718,29.509 +2020-07-20 05:30:00,86.91,34.939,38.718,29.509 +2020-07-20 05:45:00,96.34,33.38,38.718,29.509 +2020-07-20 06:00:00,101.08,32.46,51.648999999999994,29.509 +2020-07-20 06:15:00,103.96,31.761999999999997,51.648999999999994,29.509 +2020-07-20 06:30:00,100.97,31.808000000000003,51.648999999999994,29.509 +2020-07-20 06:45:00,101.61,35.384,51.648999999999994,29.509 +2020-07-20 07:00:00,101.25,34.973,60.159,29.509 +2020-07-20 07:15:00,101.47,36.051,60.159,29.509 +2020-07-20 07:30:00,105.36,33.915,60.159,29.509 +2020-07-20 07:45:00,106.42,35.589,60.159,29.509 +2020-07-20 08:00:00,107.88,31.041,53.8,29.509 +2020-07-20 08:15:00,105.6,34.771,53.8,29.509 +2020-07-20 08:30:00,101.05,36.065,53.8,29.509 +2020-07-20 08:45:00,107.78,39.739000000000004,53.8,29.509 +2020-07-20 09:00:00,106.48,34.959,50.583,29.509 +2020-07-20 09:15:00,103.76,34.821,50.583,29.509 +2020-07-20 09:30:00,100.88,37.909,50.583,29.509 +2020-07-20 09:45:00,97.4,39.689,50.583,29.509 +2020-07-20 10:00:00,103.26,38.675,49.11600000000001,29.509 +2020-07-20 10:15:00,107.19,40.2,49.11600000000001,29.509 +2020-07-20 10:30:00,105.63,40.084,49.11600000000001,29.509 +2020-07-20 10:45:00,97.62,39.985,49.11600000000001,29.509 +2020-07-20 11:00:00,97.57,37.84,49.056000000000004,29.509 +2020-07-20 11:15:00,103.01,38.643,49.056000000000004,29.509 +2020-07-20 11:30:00,102.55,40.983000000000004,49.056000000000004,29.509 +2020-07-20 11:45:00,105.27,42.663999999999994,49.056000000000004,29.509 +2020-07-20 12:00:00,98.29,37.333,47.227,29.509 +2020-07-20 12:15:00,99.49,36.194,47.227,29.509 +2020-07-20 12:30:00,97.27,34.074,47.227,29.509 +2020-07-20 12:45:00,96.77,34.235,47.227,29.509 +2020-07-20 13:00:00,95.98,34.484,47.006,29.509 +2020-07-20 13:15:00,91.51,33.8,47.006,29.509 +2020-07-20 13:30:00,91.54,32.129,47.006,29.509 +2020-07-20 13:45:00,91.45,33.25,47.006,29.509 +2020-07-20 14:00:00,91.7,34.586,47.19,29.509 +2020-07-20 14:15:00,91.78,33.838,47.19,29.509 +2020-07-20 14:30:00,88.92,32.577,47.19,29.509 +2020-07-20 14:45:00,90.77,34.091,47.19,29.509 +2020-07-20 15:00:00,87.71,36.536,47.846000000000004,29.509 +2020-07-20 15:15:00,88.74,33.169000000000004,47.846000000000004,29.509 +2020-07-20 15:30:00,89.17,31.98,47.846000000000004,29.509 +2020-07-20 15:45:00,88.84,29.899,47.846000000000004,29.509 +2020-07-20 16:00:00,89.26,33.132,49.641000000000005,29.509 +2020-07-20 16:15:00,89.55,32.911,49.641000000000005,29.509 +2020-07-20 16:30:00,91.94,32.528,49.641000000000005,29.509 +2020-07-20 16:45:00,94.53,28.75,49.641000000000005,29.509 +2020-07-20 17:00:00,95.39,32.519,54.133,29.509 +2020-07-20 17:15:00,94.5,32.255,54.133,29.509 +2020-07-20 17:30:00,96.79,31.66,54.133,29.509 +2020-07-20 17:45:00,98.25,31.088,54.133,29.509 +2020-07-20 18:00:00,98.64,35.414,53.761,29.509 +2020-07-20 18:15:00,97.32,33.545,53.761,29.509 +2020-07-20 18:30:00,97.45,31.878,53.761,29.509 +2020-07-20 18:45:00,96.82,34.93,53.761,29.509 +2020-07-20 19:00:00,93.5,37.611,53.923,29.509 +2020-07-20 19:15:00,90.15,36.361,53.923,29.509 +2020-07-20 19:30:00,89.51,35.339,53.923,29.509 +2020-07-20 19:45:00,89.22,33.908,53.923,29.509 +2020-07-20 20:00:00,86.74,31.003,58.786,29.509 +2020-07-20 20:15:00,87.84,31.72,58.786,29.509 +2020-07-20 20:30:00,88.62,32.219,58.786,29.509 +2020-07-20 20:45:00,88.28,32.134,58.786,29.509 +2020-07-20 21:00:00,86.92,29.896,54.591,29.509 +2020-07-20 21:15:00,85.22,32.747,54.591,29.509 +2020-07-20 21:30:00,80.97,33.064,54.591,29.509 +2020-07-20 21:45:00,80.99,33.756,54.591,29.509 +2020-07-20 22:00:00,74.55,31.004,51.551,29.509 +2020-07-20 22:15:00,75.32,34.176,51.551,29.509 +2020-07-20 22:30:00,74.29,29.589000000000002,51.551,29.509 +2020-07-20 22:45:00,72.88,26.478,51.551,29.509 +2020-07-20 23:00:00,68.65,24.109,44.716,29.509 +2020-07-20 23:15:00,69.81,20.999000000000002,44.716,29.509 +2020-07-20 23:30:00,68.67,19.935,44.716,29.509 +2020-07-20 23:45:00,67.93,18.665,44.716,29.509 +2020-07-21 00:00:00,63.82,17.929000000000002,43.01,29.509 +2020-07-21 00:15:00,65.39,18.83,43.01,29.509 +2020-07-21 00:30:00,64.02,17.522000000000002,43.01,29.509 +2020-07-21 00:45:00,64.2,17.633,43.01,29.509 +2020-07-21 01:00:00,64.16,17.659000000000002,40.687,29.509 +2020-07-21 01:15:00,65.18,16.822,40.687,29.509 +2020-07-21 01:30:00,64.43,15.337,40.687,29.509 +2020-07-21 01:45:00,65.07,15.425,40.687,29.509 +2020-07-21 02:00:00,63.66,15.28,39.554,29.509 +2020-07-21 02:15:00,65.32,14.513,39.554,29.509 +2020-07-21 02:30:00,72.4,15.956,39.554,29.509 +2020-07-21 02:45:00,73.53,16.692,39.554,29.509 +2020-07-21 03:00:00,68.84,17.657,38.958,29.509 +2020-07-21 03:15:00,66.73,17.21,38.958,29.509 +2020-07-21 03:30:00,70.8,16.541,38.958,29.509 +2020-07-21 03:45:00,75.24,15.792,38.958,29.509 +2020-07-21 04:00:00,77.83,19.896,39.783,29.509 +2020-07-21 04:15:00,79.77,25.436,39.783,29.509 +2020-07-21 04:30:00,76.2,22.725,39.783,29.509 +2020-07-21 04:45:00,78.11,22.65,39.783,29.509 +2020-07-21 05:00:00,86.55,34.32,42.281000000000006,29.509 +2020-07-21 05:15:00,88.52,40.176,42.281000000000006,29.509 +2020-07-21 05:30:00,90.48,35.345,42.281000000000006,29.509 +2020-07-21 05:45:00,99.68,33.008,42.281000000000006,29.509 +2020-07-21 06:00:00,105.81,33.314,50.801,29.509 +2020-07-21 06:15:00,105.55,32.696,50.801,29.509 +2020-07-21 06:30:00,99.73,32.465,50.801,29.509 +2020-07-21 06:45:00,103.62,35.137,50.801,29.509 +2020-07-21 07:00:00,102.74,34.907,60.202,29.509 +2020-07-21 07:15:00,100.9,35.742,60.202,29.509 +2020-07-21 07:30:00,99.85,33.693000000000005,60.202,29.509 +2020-07-21 07:45:00,99.38,34.294000000000004,60.202,29.509 +2020-07-21 08:00:00,99.42,29.659000000000002,54.461000000000006,29.509 +2020-07-21 08:15:00,99.3,33.006,54.461000000000006,29.509 +2020-07-21 08:30:00,105.13,34.493,54.461000000000006,29.509 +2020-07-21 08:45:00,106.32,37.162,54.461000000000006,29.509 +2020-07-21 09:00:00,105.96,32.863,50.753,29.509 +2020-07-21 09:15:00,101.04,32.369,50.753,29.509 +2020-07-21 09:30:00,99.71,36.133,50.753,29.509 +2020-07-21 09:45:00,98.06,39.431999999999995,50.753,29.509 +2020-07-21 10:00:00,97.74,36.961999999999996,49.703,29.509 +2020-07-21 10:15:00,100.93,38.46,49.703,29.509 +2020-07-21 10:30:00,104.39,38.344,49.703,29.509 +2020-07-21 10:45:00,104.51,39.336999999999996,49.703,29.509 +2020-07-21 11:00:00,102.28,37.006,49.42100000000001,29.509 +2020-07-21 11:15:00,99.28,38.304,49.42100000000001,29.509 +2020-07-21 11:30:00,95.02,39.493,49.42100000000001,29.509 +2020-07-21 11:45:00,101.89,40.571,49.42100000000001,29.509 +2020-07-21 12:00:00,99.86,35.217,47.155,29.509 +2020-07-21 12:15:00,95.66,34.506,47.155,29.509 +2020-07-21 12:30:00,93.76,33.255,47.155,29.509 +2020-07-21 12:45:00,93.59,34.227,47.155,29.509 +2020-07-21 13:00:00,92.33,34.104,47.515,29.509 +2020-07-21 13:15:00,91.18,35.485,47.515,29.509 +2020-07-21 13:30:00,89.86,33.629,47.515,29.509 +2020-07-21 13:45:00,90.0,33.645,47.515,29.509 +2020-07-21 14:00:00,90.09,35.437,47.575,29.509 +2020-07-21 14:15:00,92.68,34.449,47.575,29.509 +2020-07-21 14:30:00,90.15,33.472,47.575,29.509 +2020-07-21 14:45:00,88.28,34.118,47.575,29.509 +2020-07-21 15:00:00,88.62,36.428000000000004,48.903,29.509 +2020-07-21 15:15:00,88.99,34.052,48.903,29.509 +2020-07-21 15:30:00,89.61,32.603,48.903,29.509 +2020-07-21 15:45:00,91.36,30.955,48.903,29.509 +2020-07-21 16:00:00,93.5,33.351,50.218999999999994,29.509 +2020-07-21 16:15:00,91.9,33.176,50.218999999999994,29.509 +2020-07-21 16:30:00,94.2,32.317,50.218999999999994,29.509 +2020-07-21 16:45:00,95.32,29.316,50.218999999999994,29.509 +2020-07-21 17:00:00,95.98,33.25,55.396,29.509 +2020-07-21 17:15:00,97.01,33.464,55.396,29.509 +2020-07-21 17:30:00,98.47,32.316,55.396,29.509 +2020-07-21 17:45:00,99.18,31.405,55.396,29.509 +2020-07-21 18:00:00,98.99,34.709,55.583999999999996,29.509 +2020-07-21 18:15:00,99.15,34.52,55.583999999999996,29.509 +2020-07-21 18:30:00,101.85,32.603,55.583999999999996,29.509 +2020-07-21 18:45:00,98.64,35.358000000000004,55.583999999999996,29.509 +2020-07-21 19:00:00,97.79,36.768,56.071000000000005,29.509 +2020-07-21 19:15:00,91.91,35.734,56.071000000000005,29.509 +2020-07-21 19:30:00,90.5,34.538000000000004,56.071000000000005,29.509 +2020-07-21 19:45:00,89.8,33.467,56.071000000000005,29.509 +2020-07-21 20:00:00,87.79,30.967,61.55,29.509 +2020-07-21 20:15:00,91.21,30.176,61.55,29.509 +2020-07-21 20:30:00,90.1,30.614,61.55,29.509 +2020-07-21 20:45:00,90.93,31.037,61.55,29.509 +2020-07-21 21:00:00,86.27,29.811999999999998,55.94,29.509 +2020-07-21 21:15:00,86.42,30.99,55.94,29.509 +2020-07-21 21:30:00,83.72,31.548000000000002,55.94,29.509 +2020-07-21 21:45:00,82.77,32.415,55.94,29.509 +2020-07-21 22:00:00,76.63,29.698,52.857,29.509 +2020-07-21 22:15:00,77.71,32.525999999999996,52.857,29.509 +2020-07-21 22:30:00,75.72,28.224,52.857,29.509 +2020-07-21 22:45:00,76.32,25.078000000000003,52.857,29.509 +2020-07-21 23:00:00,71.02,21.905,46.04,29.509 +2020-07-21 23:15:00,71.7,20.565,46.04,29.509 +2020-07-21 23:30:00,70.36,19.544,46.04,29.509 +2020-07-21 23:45:00,70.53,18.503,46.04,29.509 +2020-07-22 00:00:00,67.59,17.987000000000002,42.195,29.509 +2020-07-22 00:15:00,68.74,18.885,42.195,29.509 +2020-07-22 00:30:00,66.81,17.581,42.195,29.509 +2020-07-22 00:45:00,67.64,17.7,42.195,29.509 +2020-07-22 01:00:00,66.47,17.722,38.82,29.509 +2020-07-22 01:15:00,67.91,16.884,38.82,29.509 +2020-07-22 01:30:00,65.56,15.405999999999999,38.82,29.509 +2020-07-22 01:45:00,66.27,15.485999999999999,38.82,29.509 +2020-07-22 02:00:00,64.97,15.347999999999999,37.023,29.509 +2020-07-22 02:15:00,72.4,14.513,37.023,29.509 +2020-07-22 02:30:00,74.12,16.021,37.023,29.509 +2020-07-22 02:45:00,73.22,16.758,37.023,29.509 +2020-07-22 03:00:00,66.46,17.713,36.818000000000005,29.509 +2020-07-22 03:15:00,75.15,17.28,36.818000000000005,29.509 +2020-07-22 03:30:00,76.71,16.618,36.818000000000005,29.509 +2020-07-22 03:45:00,78.64,15.88,36.818000000000005,29.509 +2020-07-22 04:00:00,77.87,19.965,37.495,29.509 +2020-07-22 04:15:00,72.77,25.488000000000003,37.495,29.509 +2020-07-22 04:30:00,76.27,22.774,37.495,29.509 +2020-07-22 04:45:00,81.51,22.7,37.495,29.509 +2020-07-22 05:00:00,85.93,34.351,39.858000000000004,29.509 +2020-07-22 05:15:00,89.79,40.176,39.858000000000004,29.509 +2020-07-22 05:30:00,98.41,35.385,39.858000000000004,29.509 +2020-07-22 05:45:00,102.38,33.051,39.858000000000004,29.509 +2020-07-22 06:00:00,107.43,33.343,52.867,29.509 +2020-07-22 06:15:00,101.45,32.733000000000004,52.867,29.509 +2020-07-22 06:30:00,105.73,32.514,52.867,29.509 +2020-07-22 06:45:00,102.48,35.209,52.867,29.509 +2020-07-22 07:00:00,105.43,34.973,66.061,29.509 +2020-07-22 07:15:00,109.7,35.829,66.061,29.509 +2020-07-22 07:30:00,109.95,33.79,66.061,29.509 +2020-07-22 07:45:00,108.67,34.412,66.061,29.509 +2020-07-22 08:00:00,103.21,29.785,58.532,29.509 +2020-07-22 08:15:00,103.06,33.128,58.532,29.509 +2020-07-22 08:30:00,103.46,34.603,58.532,29.509 +2020-07-22 08:45:00,102.16,37.262,58.532,29.509 +2020-07-22 09:00:00,103.55,32.961999999999996,56.047,29.509 +2020-07-22 09:15:00,112.12,32.464,56.047,29.509 +2020-07-22 09:30:00,113.73,36.217,56.047,29.509 +2020-07-22 09:45:00,114.83,39.509,56.047,29.509 +2020-07-22 10:00:00,106.75,37.044000000000004,53.823,29.509 +2020-07-22 10:15:00,110.2,38.532,53.823,29.509 +2020-07-22 10:30:00,111.64,38.41,53.823,29.509 +2020-07-22 10:45:00,111.39,39.402,53.823,29.509 +2020-07-22 11:00:00,107.68,37.074,54.184,29.509 +2020-07-22 11:15:00,106.1,38.369,54.184,29.509 +2020-07-22 11:30:00,109.13,39.548,54.184,29.509 +2020-07-22 11:45:00,107.03,40.616,54.184,29.509 +2020-07-22 12:00:00,105.01,35.274,52.628,29.509 +2020-07-22 12:15:00,102.07,34.558,52.628,29.509 +2020-07-22 12:30:00,96.47,33.306,52.628,29.509 +2020-07-22 12:45:00,97.11,34.27,52.628,29.509 +2020-07-22 13:00:00,97.9,34.126999999999995,52.31,29.509 +2020-07-22 13:15:00,101.75,35.499,52.31,29.509 +2020-07-22 13:30:00,97.86,33.644,52.31,29.509 +2020-07-22 13:45:00,95.02,33.67,52.31,29.509 +2020-07-22 14:00:00,94.83,35.455999999999996,52.278999999999996,29.509 +2020-07-22 14:15:00,98.49,34.472,52.278999999999996,29.509 +2020-07-22 14:30:00,95.78,33.493,52.278999999999996,29.509 +2020-07-22 14:45:00,94.64,34.146,52.278999999999996,29.509 +2020-07-22 15:00:00,92.12,36.445,53.306999999999995,29.509 +2020-07-22 15:15:00,93.44,34.067,53.306999999999995,29.509 +2020-07-22 15:30:00,92.66,32.624,53.306999999999995,29.509 +2020-07-22 15:45:00,95.61,30.971999999999998,53.306999999999995,29.509 +2020-07-22 16:00:00,95.06,33.367,55.358999999999995,29.509 +2020-07-22 16:15:00,96.05,33.196,55.358999999999995,29.509 +2020-07-22 16:30:00,96.79,32.354,55.358999999999995,29.509 +2020-07-22 16:45:00,97.88,29.37,55.358999999999995,29.509 +2020-07-22 17:00:00,99.85,33.293,59.211999999999996,29.509 +2020-07-22 17:15:00,99.72,33.529,59.211999999999996,29.509 +2020-07-22 17:30:00,100.86,32.388000000000005,59.211999999999996,29.509 +2020-07-22 17:45:00,101.07,31.495,59.211999999999996,29.509 +2020-07-22 18:00:00,101.72,34.8,60.403999999999996,29.509 +2020-07-22 18:15:00,102.59,34.605,60.403999999999996,29.509 +2020-07-22 18:30:00,100.5,32.693000000000005,60.403999999999996,29.509 +2020-07-22 18:45:00,100.9,35.45,60.403999999999996,29.509 +2020-07-22 19:00:00,97.19,36.861,60.993,29.509 +2020-07-22 19:15:00,97.17,35.821999999999996,60.993,29.509 +2020-07-22 19:30:00,92.14,34.623000000000005,60.993,29.509 +2020-07-22 19:45:00,91.77,33.551,60.993,29.509 +2020-07-22 20:00:00,90.35,31.044,66.6,29.509 +2020-07-22 20:15:00,91.78,30.253,66.6,29.509 +2020-07-22 20:30:00,95.13,30.684,66.6,29.509 +2020-07-22 20:45:00,92.78,31.101999999999997,66.6,29.509 +2020-07-22 21:00:00,90.14,29.88,59.855,29.509 +2020-07-22 21:15:00,88.39,31.054000000000002,59.855,29.509 +2020-07-22 21:30:00,85.36,31.596,59.855,29.509 +2020-07-22 21:45:00,84.32,32.446,59.855,29.509 +2020-07-22 22:00:00,79.02,29.726,54.942,29.509 +2020-07-22 22:15:00,78.99,32.55,54.942,29.509 +2020-07-22 22:30:00,76.96,28.224,54.942,29.509 +2020-07-22 22:45:00,79.38,25.072,54.942,29.509 +2020-07-22 23:00:00,71.81,21.918000000000003,46.056000000000004,29.509 +2020-07-22 23:15:00,73.46,20.590999999999998,46.056000000000004,29.509 +2020-07-22 23:30:00,72.52,19.585,46.056000000000004,29.509 +2020-07-22 23:45:00,71.69,18.542,46.056000000000004,29.509 +2020-07-23 00:00:00,68.3,18.047,40.859,29.509 +2020-07-23 00:15:00,70.02,18.944000000000003,40.859,29.509 +2020-07-23 00:30:00,68.78,17.644000000000002,40.859,29.509 +2020-07-23 00:45:00,69.1,17.771,40.859,29.509 +2020-07-23 01:00:00,68.25,17.789,39.06,29.509 +2020-07-23 01:15:00,68.94,16.949,39.06,29.509 +2020-07-23 01:30:00,67.5,15.479000000000001,39.06,29.509 +2020-07-23 01:45:00,67.58,15.552999999999999,39.06,29.509 +2020-07-23 02:00:00,66.85,15.42,37.592,29.509 +2020-07-23 02:15:00,71.03,14.513,37.592,29.509 +2020-07-23 02:30:00,76.38,16.089000000000002,37.592,29.509 +2020-07-23 02:45:00,76.3,16.827,37.592,29.509 +2020-07-23 03:00:00,71.84,17.772000000000002,37.416,29.509 +2020-07-23 03:15:00,72.28,17.354,37.416,29.509 +2020-07-23 03:30:00,79.96,16.701,37.416,29.509 +2020-07-23 03:45:00,81.25,15.972000000000001,37.416,29.509 +2020-07-23 04:00:00,80.97,20.04,38.176,29.509 +2020-07-23 04:15:00,74.82,25.546,38.176,29.509 +2020-07-23 04:30:00,77.22,22.83,38.176,29.509 +2020-07-23 04:45:00,84.81,22.756999999999998,38.176,29.509 +2020-07-23 05:00:00,87.82,34.391,41.203,29.509 +2020-07-23 05:15:00,95.47,40.189,41.203,29.509 +2020-07-23 05:30:00,100.21,35.435,41.203,29.509 +2020-07-23 05:45:00,104.43,33.103,41.203,29.509 +2020-07-23 06:00:00,105.17,33.383,51.09,29.509 +2020-07-23 06:15:00,102.39,32.779,51.09,29.509 +2020-07-23 06:30:00,107.01,32.573,51.09,29.509 +2020-07-23 06:45:00,109.32,35.289,51.09,29.509 +2020-07-23 07:00:00,113.51,35.048,63.541000000000004,29.509 +2020-07-23 07:15:00,107.64,35.925,63.541000000000004,29.509 +2020-07-23 07:30:00,106.21,33.898,63.541000000000004,29.509 +2020-07-23 07:45:00,107.98,34.539,63.541000000000004,29.509 +2020-07-23 08:00:00,109.58,29.919,55.65,29.509 +2020-07-23 08:15:00,109.46,33.257,55.65,29.509 +2020-07-23 08:30:00,109.2,34.72,55.65,29.509 +2020-07-23 08:45:00,111.06,37.368,55.65,29.509 +2020-07-23 09:00:00,111.53,33.069,51.833999999999996,29.509 +2020-07-23 09:15:00,109.8,32.566,51.833999999999996,29.509 +2020-07-23 09:30:00,107.6,36.308,51.833999999999996,29.509 +2020-07-23 09:45:00,106.21,39.592,51.833999999999996,29.509 +2020-07-23 10:00:00,111.27,37.133,49.70399999999999,29.509 +2020-07-23 10:15:00,114.48,38.61,49.70399999999999,29.509 +2020-07-23 10:30:00,116.26,38.482,49.70399999999999,29.509 +2020-07-23 10:45:00,112.02,39.472,49.70399999999999,29.509 +2020-07-23 11:00:00,112.26,37.147,48.593999999999994,29.509 +2020-07-23 11:15:00,110.11,38.439,48.593999999999994,29.509 +2020-07-23 11:30:00,107.88,39.608000000000004,48.593999999999994,29.509 +2020-07-23 11:45:00,103.41,40.667,48.593999999999994,29.509 +2020-07-23 12:00:00,100.19,35.335,46.275,29.509 +2020-07-23 12:15:00,101.53,34.614000000000004,46.275,29.509 +2020-07-23 12:30:00,98.55,33.36,46.275,29.509 +2020-07-23 12:45:00,97.18,34.318000000000005,46.275,29.509 +2020-07-23 13:00:00,95.62,34.154,45.803000000000004,29.509 +2020-07-23 13:15:00,94.73,35.516,45.803000000000004,29.509 +2020-07-23 13:30:00,96.45,33.664,45.803000000000004,29.509 +2020-07-23 13:45:00,95.72,33.7,45.803000000000004,29.509 +2020-07-23 14:00:00,95.01,35.48,46.251999999999995,29.509 +2020-07-23 14:15:00,93.53,34.498000000000005,46.251999999999995,29.509 +2020-07-23 14:30:00,92.34,33.518,46.251999999999995,29.509 +2020-07-23 14:45:00,91.59,34.177,46.251999999999995,29.509 +2020-07-23 15:00:00,90.99,36.466,48.309,29.509 +2020-07-23 15:15:00,91.91,34.086,48.309,29.509 +2020-07-23 15:30:00,92.29,32.647,48.309,29.509 +2020-07-23 15:45:00,98.7,30.991999999999997,48.309,29.509 +2020-07-23 16:00:00,94.54,33.385,49.681999999999995,29.509 +2020-07-23 16:15:00,95.86,33.22,49.681999999999995,29.509 +2020-07-23 16:30:00,97.35,32.391999999999996,49.681999999999995,29.509 +2020-07-23 16:45:00,99.16,29.426,49.681999999999995,29.509 +2020-07-23 17:00:00,99.71,33.339,53.086000000000006,29.509 +2020-07-23 17:15:00,100.72,33.598,53.086000000000006,29.509 +2020-07-23 17:30:00,104.23,32.466,53.086000000000006,29.509 +2020-07-23 17:45:00,102.39,31.589000000000002,53.086000000000006,29.509 +2020-07-23 18:00:00,103.82,34.895,54.038999999999994,29.509 +2020-07-23 18:15:00,102.29,34.695,54.038999999999994,29.509 +2020-07-23 18:30:00,102.15,32.79,54.038999999999994,29.509 +2020-07-23 18:45:00,101.8,35.545,54.038999999999994,29.509 +2020-07-23 19:00:00,98.33,36.96,53.408,29.509 +2020-07-23 19:15:00,94.82,35.916,53.408,29.509 +2020-07-23 19:30:00,93.96,34.714,53.408,29.509 +2020-07-23 19:45:00,92.17,33.641,53.408,29.509 +2020-07-23 20:00:00,91.92,31.128,55.309,29.509 +2020-07-23 20:15:00,92.98,30.337,55.309,29.509 +2020-07-23 20:30:00,94.08,30.759,55.309,29.509 +2020-07-23 20:45:00,96.13,31.17,55.309,29.509 +2020-07-23 21:00:00,89.56,29.951999999999998,51.585,29.509 +2020-07-23 21:15:00,88.93,31.123,51.585,29.509 +2020-07-23 21:30:00,84.47,31.65,51.585,29.509 +2020-07-23 21:45:00,83.82,32.483000000000004,51.585,29.509 +2020-07-23 22:00:00,79.54,29.758000000000003,48.006,29.509 +2020-07-23 22:15:00,79.02,32.576,48.006,29.509 +2020-07-23 22:30:00,77.0,28.225,48.006,29.509 +2020-07-23 22:45:00,76.14,25.069000000000003,48.006,29.509 +2020-07-23 23:00:00,71.58,21.935,42.309,29.509 +2020-07-23 23:15:00,73.24,20.619,42.309,29.509 +2020-07-23 23:30:00,70.94,19.628,42.309,29.509 +2020-07-23 23:45:00,70.55,18.584,42.309,29.509 +2020-07-24 00:00:00,68.03,16.248,39.649,29.509 +2020-07-24 00:15:00,69.75,17.363,39.649,29.509 +2020-07-24 00:30:00,68.38,16.374000000000002,39.649,29.509 +2020-07-24 00:45:00,68.41,16.957,39.649,29.509 +2020-07-24 01:00:00,66.25,16.582,37.744,29.509 +2020-07-24 01:15:00,67.66,15.015,37.744,29.509 +2020-07-24 01:30:00,66.78,14.513,37.744,29.509 +2020-07-24 01:45:00,67.15,14.513,37.744,29.509 +2020-07-24 02:00:00,66.84,15.012,36.965,29.509 +2020-07-24 02:15:00,67.65,14.513,36.965,29.509 +2020-07-24 02:30:00,73.02,16.486,36.965,29.509 +2020-07-24 02:45:00,75.8,16.48,36.965,29.509 +2020-07-24 03:00:00,71.52,18.334,37.678000000000004,29.509 +2020-07-24 03:15:00,70.7,16.549,37.678000000000004,29.509 +2020-07-24 03:30:00,77.5,15.645999999999999,37.678000000000004,29.509 +2020-07-24 03:45:00,79.57,15.852,37.678000000000004,29.509 +2020-07-24 04:00:00,80.0,20.035999999999998,38.591,29.509 +2020-07-24 04:15:00,75.24,23.857,38.591,29.509 +2020-07-24 04:30:00,76.43,22.151999999999997,38.591,29.509 +2020-07-24 04:45:00,79.48,21.465,38.591,29.509 +2020-07-24 05:00:00,87.6,32.664,40.666,29.509 +2020-07-24 05:15:00,90.22,39.36,40.666,29.509 +2020-07-24 05:30:00,93.14,34.783,40.666,29.509 +2020-07-24 05:45:00,91.92,31.98,40.666,29.509 +2020-07-24 06:00:00,106.2,32.453,51.784,29.509 +2020-07-24 06:15:00,107.22,32.135,51.784,29.509 +2020-07-24 06:30:00,110.0,31.943,51.784,29.509 +2020-07-24 06:45:00,107.88,34.431999999999995,51.784,29.509 +2020-07-24 07:00:00,114.31,34.941,61.383,29.509 +2020-07-24 07:15:00,115.72,36.741,61.383,29.509 +2020-07-24 07:30:00,112.69,32.617,61.383,29.509 +2020-07-24 07:45:00,107.73,33.121,61.383,29.509 +2020-07-24 08:00:00,109.58,29.456,55.272,29.509 +2020-07-24 08:15:00,115.15,33.56,55.272,29.509 +2020-07-24 08:30:00,122.28,34.815,55.272,29.509 +2020-07-24 08:45:00,123.43,37.437,55.272,29.509 +2020-07-24 09:00:00,124.99,30.558000000000003,53.506,29.509 +2020-07-24 09:15:00,124.06,32.117,53.506,29.509 +2020-07-24 09:30:00,113.17,35.128,53.506,29.509 +2020-07-24 09:45:00,102.68,38.842,53.506,29.509 +2020-07-24 10:00:00,108.12,36.326,51.363,29.509 +2020-07-24 10:15:00,109.83,37.472,51.363,29.509 +2020-07-24 10:30:00,109.0,37.959,51.363,29.509 +2020-07-24 10:45:00,104.36,38.887,51.363,29.509 +2020-07-24 11:00:00,106.38,36.842,51.043,29.509 +2020-07-24 11:15:00,106.17,37.016999999999996,51.043,29.509 +2020-07-24 11:30:00,105.41,37.599000000000004,51.043,29.509 +2020-07-24 11:45:00,102.94,37.579,51.043,29.509 +2020-07-24 12:00:00,101.2,32.623000000000005,47.52,29.509 +2020-07-24 12:15:00,101.62,31.401999999999997,47.52,29.509 +2020-07-24 12:30:00,104.8,30.261,47.52,29.509 +2020-07-24 12:45:00,107.68,30.285,47.52,29.509 +2020-07-24 13:00:00,109.01,30.66,45.494,29.509 +2020-07-24 13:15:00,111.33,32.129,45.494,29.509 +2020-07-24 13:30:00,109.44,31.159000000000002,45.494,29.509 +2020-07-24 13:45:00,103.01,31.551,45.494,29.509 +2020-07-24 14:00:00,93.71,32.553000000000004,43.883,29.509 +2020-07-24 14:15:00,95.41,32.075,43.883,29.509 +2020-07-24 14:30:00,104.39,32.713,43.883,29.509 +2020-07-24 14:45:00,101.16,32.585,43.883,29.509 +2020-07-24 15:00:00,96.2,34.821,45.714,29.509 +2020-07-24 15:15:00,94.94,32.219,45.714,29.509 +2020-07-24 15:30:00,91.29,30.305999999999997,45.714,29.509 +2020-07-24 15:45:00,89.73,29.476,45.714,29.509 +2020-07-24 16:00:00,90.1,31.035999999999998,48.222,29.509 +2020-07-24 16:15:00,94.51,31.392,48.222,29.509 +2020-07-24 16:30:00,101.04,30.37,48.222,29.509 +2020-07-24 16:45:00,100.07,26.570999999999998,48.222,29.509 +2020-07-24 17:00:00,103.96,32.347,52.619,29.509 +2020-07-24 17:15:00,101.89,32.475,52.619,29.509 +2020-07-24 17:30:00,100.32,31.561,52.619,29.509 +2020-07-24 17:45:00,99.63,30.541999999999998,52.619,29.509 +2020-07-24 18:00:00,100.58,33.814,52.99,29.509 +2020-07-24 18:15:00,97.61,32.611999999999995,52.99,29.509 +2020-07-24 18:30:00,96.16,30.566,52.99,29.509 +2020-07-24 18:45:00,95.07,33.78,52.99,29.509 +2020-07-24 19:00:00,91.05,36.055,51.923,29.509 +2020-07-24 19:15:00,89.09,35.54,51.923,29.509 +2020-07-24 19:30:00,87.63,34.434,51.923,29.509 +2020-07-24 19:45:00,86.2,32.297,51.923,29.509 +2020-07-24 20:00:00,85.56,29.603,56.238,29.509 +2020-07-24 20:15:00,85.67,29.671,56.238,29.509 +2020-07-24 20:30:00,86.67,29.561,56.238,29.509 +2020-07-24 20:45:00,85.85,29.044,56.238,29.509 +2020-07-24 21:00:00,80.66,29.201999999999998,52.426,29.509 +2020-07-24 21:15:00,79.89,32.137,52.426,29.509 +2020-07-24 21:30:00,76.38,32.484,52.426,29.509 +2020-07-24 21:45:00,77.28,33.479,52.426,29.509 +2020-07-24 22:00:00,72.52,30.523000000000003,48.196000000000005,29.509 +2020-07-24 22:15:00,73.32,33.078,48.196000000000005,29.509 +2020-07-24 22:30:00,71.32,33.355,48.196000000000005,29.509 +2020-07-24 22:45:00,70.83,30.868000000000002,48.196000000000005,29.509 +2020-07-24 23:00:00,64.51,29.603,41.71,29.509 +2020-07-24 23:15:00,66.35,26.752,41.71,29.509 +2020-07-24 23:30:00,63.12,23.924,41.71,29.509 +2020-07-24 23:45:00,64.62,22.791,41.71,29.509 +2020-07-25 00:00:00,60.52,17.883,41.105,29.398000000000003 +2020-07-25 00:15:00,63.43,18.605999999999998,41.105,29.398000000000003 +2020-07-25 00:30:00,61.73,17.007,41.105,29.398000000000003 +2020-07-25 00:45:00,60.92,16.749000000000002,41.105,29.398000000000003 +2020-07-25 01:00:00,58.7,16.616,36.934,29.398000000000003 +2020-07-25 01:15:00,60.36,15.764000000000001,36.934,29.398000000000003 +2020-07-25 01:30:00,59.34,14.513,36.934,29.398000000000003 +2020-07-25 01:45:00,59.81,15.419,36.934,29.398000000000003 +2020-07-25 02:00:00,63.49,15.175999999999998,34.782,29.398000000000003 +2020-07-25 02:15:00,67.28,14.513,34.782,29.398000000000003 +2020-07-25 02:30:00,64.51,15.055,34.782,29.398000000000003 +2020-07-25 02:45:00,58.8,15.890999999999998,34.782,29.398000000000003 +2020-07-25 03:00:00,58.48,16.201,34.489000000000004,29.398000000000003 +2020-07-25 03:15:00,60.49,14.513,34.489000000000004,29.398000000000003 +2020-07-25 03:30:00,60.97,14.513,34.489000000000004,29.398000000000003 +2020-07-25 03:45:00,67.24,15.163,34.489000000000004,29.398000000000003 +2020-07-25 04:00:00,67.49,17.617,34.111,29.398000000000003 +2020-07-25 04:15:00,64.17,20.659000000000002,34.111,29.398000000000003 +2020-07-25 04:30:00,56.16,17.320999999999998,34.111,29.398000000000003 +2020-07-25 04:45:00,60.64,16.969,34.111,29.398000000000003 +2020-07-25 05:00:00,64.22,20.432000000000002,33.283,29.398000000000003 +2020-07-25 05:15:00,62.07,16.791,33.283,29.398000000000003 +2020-07-25 05:30:00,66.1,14.513,33.283,29.398000000000003 +2020-07-25 05:45:00,71.54,15.429,33.283,29.398000000000003 +2020-07-25 06:00:00,72.53,26.487,33.653,29.398000000000003 +2020-07-25 06:15:00,71.91,33.288000000000004,33.653,29.398000000000003 +2020-07-25 06:30:00,69.01,30.473000000000003,33.653,29.398000000000003 +2020-07-25 06:45:00,69.56,30.228,33.653,29.398000000000003 +2020-07-25 07:00:00,73.11,29.875999999999998,36.732,29.398000000000003 +2020-07-25 07:15:00,76.36,30.405,36.732,29.398000000000003 +2020-07-25 07:30:00,78.06,27.684,36.732,29.398000000000003 +2020-07-25 07:45:00,80.04,28.840999999999998,36.732,29.398000000000003 +2020-07-25 08:00:00,77.09,25.694000000000003,41.318999999999996,29.398000000000003 +2020-07-25 08:15:00,79.78,29.186,41.318999999999996,29.398000000000003 +2020-07-25 08:30:00,80.24,30.247,41.318999999999996,29.398000000000003 +2020-07-25 08:45:00,80.25,33.574,41.318999999999996,29.398000000000003 +2020-07-25 09:00:00,76.95,29.991999999999997,43.195,29.398000000000003 +2020-07-25 09:15:00,80.1,31.998,43.195,29.398000000000003 +2020-07-25 09:30:00,78.95,35.445,43.195,29.398000000000003 +2020-07-25 09:45:00,77.75,38.635999999999996,43.195,29.398000000000003 +2020-07-25 10:00:00,76.41,36.813,41.843999999999994,29.398000000000003 +2020-07-25 10:15:00,81.4,38.321999999999996,41.843999999999994,29.398000000000003 +2020-07-25 10:30:00,76.14,38.407,41.843999999999994,29.398000000000003 +2020-07-25 10:45:00,74.84,38.793,41.843999999999994,29.398000000000003 +2020-07-25 11:00:00,74.35,36.548,39.035,29.398000000000003 +2020-07-25 11:15:00,74.37,37.754,39.035,29.398000000000003 +2020-07-25 11:30:00,82.18,38.760999999999996,39.035,29.398000000000003 +2020-07-25 11:45:00,79.36,39.576,39.035,29.398000000000003 +2020-07-25 12:00:00,73.47,35.176,38.001,29.398000000000003 +2020-07-25 12:15:00,71.39,34.818000000000005,38.001,29.398000000000003 +2020-07-25 12:30:00,73.92,33.497,38.001,29.398000000000003 +2020-07-25 12:45:00,73.51,34.403,38.001,29.398000000000003 +2020-07-25 13:00:00,62.75,33.873000000000005,34.747,29.398000000000003 +2020-07-25 13:15:00,60.52,34.961,34.747,29.398000000000003 +2020-07-25 13:30:00,64.66,34.251,34.747,29.398000000000003 +2020-07-25 13:45:00,71.77,33.11,34.747,29.398000000000003 +2020-07-25 14:00:00,74.39,33.992,33.434,29.398000000000003 +2020-07-25 14:15:00,80.98,32.196,33.434,29.398000000000003 +2020-07-25 14:30:00,77.88,32.56,33.434,29.398000000000003 +2020-07-25 14:45:00,74.01,32.943000000000005,33.434,29.398000000000003 +2020-07-25 15:00:00,67.72,35.54,35.921,29.398000000000003 +2020-07-25 15:15:00,63.96,33.629,35.921,29.398000000000003 +2020-07-25 15:30:00,60.29,31.785,35.921,29.398000000000003 +2020-07-25 15:45:00,70.07,29.939,35.921,29.398000000000003 +2020-07-25 16:00:00,80.46,33.92,39.427,29.398000000000003 +2020-07-25 16:15:00,81.09,33.215,39.427,29.398000000000003 +2020-07-25 16:30:00,81.48,32.46,39.427,29.398000000000003 +2020-07-25 16:45:00,81.44,28.561,39.427,29.398000000000003 +2020-07-25 17:00:00,83.25,33.041,44.096000000000004,29.398000000000003 +2020-07-25 17:15:00,81.38,30.686,44.096000000000004,29.398000000000003 +2020-07-25 17:30:00,81.62,29.645,44.096000000000004,29.398000000000003 +2020-07-25 17:45:00,80.18,29.224,44.096000000000004,29.398000000000003 +2020-07-25 18:00:00,82.87,33.99,43.931000000000004,29.398000000000003 +2020-07-25 18:15:00,82.76,34.434,43.931000000000004,29.398000000000003 +2020-07-25 18:30:00,83.27,33.746,43.931000000000004,29.398000000000003 +2020-07-25 18:45:00,81.38,33.495,43.931000000000004,29.398000000000003 +2020-07-25 19:00:00,79.1,33.891999999999996,42.187,29.398000000000003 +2020-07-25 19:15:00,73.98,32.328,42.187,29.398000000000003 +2020-07-25 19:30:00,74.69,31.985,42.187,29.398000000000003 +2020-07-25 19:45:00,73.47,31.756999999999998,42.187,29.398000000000003 +2020-07-25 20:00:00,74.38,29.776,38.315,29.398000000000003 +2020-07-25 20:15:00,74.87,28.994,38.315,29.398000000000003 +2020-07-25 20:30:00,74.53,27.961,38.315,29.398000000000003 +2020-07-25 20:45:00,73.21,29.456,38.315,29.398000000000003 +2020-07-25 21:00:00,69.25,27.854,36.843,29.398000000000003 +2020-07-25 21:15:00,69.72,30.38,36.843,29.398000000000003 +2020-07-25 21:30:00,66.85,30.825,36.843,29.398000000000003 +2020-07-25 21:45:00,66.91,31.249000000000002,36.843,29.398000000000003 +2020-07-25 22:00:00,63.87,28.166999999999998,37.260999999999996,29.398000000000003 +2020-07-25 22:15:00,65.21,30.805999999999997,37.260999999999996,29.398000000000003 +2020-07-25 22:30:00,60.75,30.22,37.260999999999996,29.398000000000003 +2020-07-25 22:45:00,62.66,27.993000000000002,37.260999999999996,29.398000000000003 +2020-07-25 23:00:00,57.37,25.910999999999998,32.148,29.398000000000003 +2020-07-25 23:15:00,58.97,23.625,32.148,29.398000000000003 +2020-07-25 23:30:00,57.29,23.531,32.148,29.398000000000003 +2020-07-25 23:45:00,57.1,22.938000000000002,32.148,29.398000000000003 +2020-07-26 00:00:00,52.63,19.403,28.905,29.398000000000003 +2020-07-26 00:15:00,55.31,18.910999999999998,28.905,29.398000000000003 +2020-07-26 00:30:00,54.02,17.189,28.905,29.398000000000003 +2020-07-26 00:45:00,54.11,16.762999999999998,28.905,29.398000000000003 +2020-07-26 01:00:00,51.5,16.92,26.906999999999996,29.398000000000003 +2020-07-26 01:15:00,52.88,15.854000000000001,26.906999999999996,29.398000000000003 +2020-07-26 01:30:00,53.34,14.513,26.906999999999996,29.398000000000003 +2020-07-26 01:45:00,53.47,14.937999999999999,26.906999999999996,29.398000000000003 +2020-07-26 02:00:00,55.77,14.847000000000001,25.938000000000002,29.398000000000003 +2020-07-26 02:15:00,52.74,14.513,25.938000000000002,29.398000000000003 +2020-07-26 02:30:00,51.94,15.683,25.938000000000002,29.398000000000003 +2020-07-26 02:45:00,52.62,16.17,25.938000000000002,29.398000000000003 +2020-07-26 03:00:00,52.69,17.144000000000002,24.693,29.398000000000003 +2020-07-26 03:15:00,52.83,15.054,24.693,29.398000000000003 +2020-07-26 03:30:00,53.82,14.513,24.693,29.398000000000003 +2020-07-26 03:45:00,53.8,14.713,24.693,29.398000000000003 +2020-07-26 04:00:00,53.27,17.141,25.683000000000003,29.398000000000003 +2020-07-26 04:15:00,52.43,19.784000000000002,25.683000000000003,29.398000000000003 +2020-07-26 04:30:00,51.66,17.875999999999998,25.683000000000003,29.398000000000003 +2020-07-26 04:45:00,52.2,17.003,25.683000000000003,29.398000000000003 +2020-07-26 05:00:00,51.2,20.91,26.023000000000003,29.398000000000003 +2020-07-26 05:15:00,51.8,16.61,26.023000000000003,29.398000000000003 +2020-07-26 05:30:00,52.03,14.513,26.023000000000003,29.398000000000003 +2020-07-26 05:45:00,52.83,14.56,26.023000000000003,29.398000000000003 +2020-07-26 06:00:00,54.33,23.101,25.834,29.398000000000003 +2020-07-26 06:15:00,54.38,30.927,25.834,29.398000000000003 +2020-07-26 06:30:00,54.62,27.465999999999998,25.834,29.398000000000003 +2020-07-26 06:45:00,55.22,26.263,25.834,29.398000000000003 +2020-07-26 07:00:00,57.6,26.066,27.765,29.398000000000003 +2020-07-26 07:15:00,56.19,24.92,27.765,29.398000000000003 +2020-07-26 07:30:00,57.26,23.711,27.765,29.398000000000003 +2020-07-26 07:45:00,57.26,24.921,27.765,29.398000000000003 +2020-07-26 08:00:00,55.54,22.46,31.357,29.398000000000003 +2020-07-26 08:15:00,55.35,27.239,31.357,29.398000000000003 +2020-07-26 08:30:00,54.77,29.068,31.357,29.398000000000003 +2020-07-26 08:45:00,57.54,32.083,31.357,29.398000000000003 +2020-07-26 09:00:00,55.42,28.44,33.238,29.398000000000003 +2020-07-26 09:15:00,54.51,29.836,33.238,29.398000000000003 +2020-07-26 09:30:00,54.13,33.762,33.238,29.398000000000003 +2020-07-26 09:45:00,56.02,38.06,33.238,29.398000000000003 +2020-07-26 10:00:00,57.58,36.551,34.22,29.398000000000003 +2020-07-26 10:15:00,58.34,38.132,34.22,29.398000000000003 +2020-07-26 10:30:00,58.17,38.382,34.22,29.398000000000003 +2020-07-26 10:45:00,57.3,40.123000000000005,34.22,29.398000000000003 +2020-07-26 11:00:00,52.42,37.379,36.298,29.398000000000003 +2020-07-26 11:15:00,55.26,38.073,36.298,29.398000000000003 +2020-07-26 11:30:00,54.51,39.75,36.298,29.398000000000003 +2020-07-26 11:45:00,58.89,40.765,36.298,29.398000000000003 +2020-07-26 12:00:00,51.91,37.62,33.52,29.398000000000003 +2020-07-26 12:15:00,53.07,36.328,33.52,29.398000000000003 +2020-07-26 12:30:00,55.05,35.425,33.52,29.398000000000003 +2020-07-26 12:45:00,57.13,35.735,33.52,29.398000000000003 +2020-07-26 13:00:00,54.79,34.94,30.12,29.398000000000003 +2020-07-26 13:15:00,57.11,35.007,30.12,29.398000000000003 +2020-07-26 13:30:00,57.12,33.091,30.12,29.398000000000003 +2020-07-26 13:45:00,53.35,33.279,30.12,29.398000000000003 +2020-07-26 14:00:00,53.46,35.445,27.233,29.398000000000003 +2020-07-26 14:15:00,50.5,33.985,27.233,29.398000000000003 +2020-07-26 14:30:00,48.85,32.835,27.233,29.398000000000003 +2020-07-26 14:45:00,48.31,32.092,27.233,29.398000000000003 +2020-07-26 15:00:00,50.34,35.024,27.468000000000004,29.398000000000003 +2020-07-26 15:15:00,51.44,32.132,27.468000000000004,29.398000000000003 +2020-07-26 15:30:00,51.5,30.025,27.468000000000004,29.398000000000003 +2020-07-26 15:45:00,56.42,28.409000000000002,27.468000000000004,29.398000000000003 +2020-07-26 16:00:00,57.3,30.403000000000002,30.8,29.398000000000003 +2020-07-26 16:15:00,58.79,30.057,30.8,29.398000000000003 +2020-07-26 16:30:00,61.59,30.386999999999997,30.8,29.398000000000003 +2020-07-26 16:45:00,60.88,26.564,30.8,29.398000000000003 +2020-07-26 17:00:00,65.37,31.451,37.806,29.398000000000003 +2020-07-26 17:15:00,66.63,30.805999999999997,37.806,29.398000000000003 +2020-07-26 17:30:00,68.97,30.625999999999998,37.806,29.398000000000003 +2020-07-26 17:45:00,71.33,30.439,37.806,29.398000000000003 +2020-07-26 18:00:00,72.0,35.931999999999995,40.766,29.398000000000003 +2020-07-26 18:15:00,74.61,35.803000000000004,40.766,29.398000000000003 +2020-07-26 18:30:00,73.46,35.058,40.766,29.398000000000003 +2020-07-26 18:45:00,72.82,34.749,40.766,29.398000000000003 +2020-07-26 19:00:00,75.82,37.553000000000004,41.163000000000004,29.398000000000003 +2020-07-26 19:15:00,72.02,34.741,41.163000000000004,29.398000000000003 +2020-07-26 19:30:00,72.59,34.144,41.163000000000004,29.398000000000003 +2020-07-26 19:45:00,73.1,33.296,41.163000000000004,29.398000000000003 +2020-07-26 20:00:00,77.43,31.500999999999998,39.885999999999996,29.398000000000003 +2020-07-26 20:15:00,75.87,30.485,39.885999999999996,29.398000000000003 +2020-07-26 20:30:00,76.9,30.201999999999998,39.885999999999996,29.398000000000003 +2020-07-26 20:45:00,76.0,29.991,39.885999999999996,29.398000000000003 +2020-07-26 21:00:00,74.18,28.511,38.900999999999996,29.398000000000003 +2020-07-26 21:15:00,74.35,30.761999999999997,38.900999999999996,29.398000000000003 +2020-07-26 21:30:00,73.05,30.476999999999997,38.900999999999996,29.398000000000003 +2020-07-26 21:45:00,74.93,31.252,38.900999999999996,29.398000000000003 +2020-07-26 22:00:00,69.15,30.444000000000003,39.806999999999995,29.398000000000003 +2020-07-26 22:15:00,68.91,31.346999999999998,39.806999999999995,29.398000000000003 +2020-07-26 22:30:00,67.21,30.45,39.806999999999995,29.398000000000003 +2020-07-26 22:45:00,66.92,26.933000000000003,39.806999999999995,29.398000000000003 +2020-07-26 23:00:00,62.44,24.776,35.564,29.398000000000003 +2020-07-26 23:15:00,64.2,23.701,35.564,29.398000000000003 +2020-07-26 23:30:00,63.75,22.988000000000003,35.564,29.398000000000003 +2020-07-26 23:45:00,63.65,22.491,35.564,29.398000000000003 +2020-07-27 00:00:00,58.08,20.721999999999998,36.578,29.509 +2020-07-27 00:15:00,59.82,20.807,36.578,29.509 +2020-07-27 00:30:00,59.44,18.666,36.578,29.509 +2020-07-27 00:45:00,59.65,17.87,36.578,29.509 +2020-07-27 01:00:00,58.51,18.438,35.292,29.509 +2020-07-27 01:15:00,59.94,17.435,35.292,29.509 +2020-07-27 01:30:00,58.16,16.151,35.292,29.509 +2020-07-27 01:45:00,59.68,16.791,35.292,29.509 +2020-07-27 02:00:00,59.36,17.198,34.319,29.509 +2020-07-27 02:15:00,60.33,14.605,34.319,29.509 +2020-07-27 02:30:00,60.15,16.959,34.319,29.509 +2020-07-27 02:45:00,68.55,17.345,34.319,29.509 +2020-07-27 03:00:00,69.23,18.741,33.13,29.509 +2020-07-27 03:15:00,68.09,17.291,33.13,29.509 +2020-07-27 03:30:00,64.78,16.747,33.13,29.509 +2020-07-27 03:45:00,67.17,17.275,33.13,29.509 +2020-07-27 04:00:00,69.51,22.643,33.851,29.509 +2020-07-27 04:15:00,71.44,28.05,33.851,29.509 +2020-07-27 04:30:00,75.51,25.430999999999997,33.851,29.509 +2020-07-27 04:45:00,75.8,24.941,33.851,29.509 +2020-07-27 05:00:00,83.51,35.275999999999996,38.718,29.509 +2020-07-27 05:15:00,93.98,40.316,38.718,29.509 +2020-07-27 05:30:00,96.18,35.374,38.718,29.509 +2020-07-27 05:45:00,97.7,33.821,38.718,29.509 +2020-07-27 06:00:00,98.3,32.796,51.648999999999994,29.509 +2020-07-27 06:15:00,101.88,32.159,51.648999999999994,29.509 +2020-07-27 06:30:00,101.45,32.286,51.648999999999994,29.509 +2020-07-27 06:45:00,102.71,36.009,51.648999999999994,29.509 +2020-07-27 07:00:00,109.88,35.56,60.159,29.509 +2020-07-27 07:15:00,110.12,36.783,60.159,29.509 +2020-07-27 07:30:00,108.64,34.733000000000004,60.159,29.509 +2020-07-27 07:45:00,103.69,36.539,60.159,29.509 +2020-07-27 08:00:00,102.39,32.04,53.8,29.509 +2020-07-27 08:15:00,101.23,35.729,53.8,29.509 +2020-07-27 08:30:00,102.32,36.937,53.8,29.509 +2020-07-27 08:45:00,101.63,40.533,53.8,29.509 +2020-07-27 09:00:00,100.95,35.754,50.583,29.509 +2020-07-27 09:15:00,100.48,35.586,50.583,29.509 +2020-07-27 09:30:00,99.92,38.594,50.583,29.509 +2020-07-27 09:45:00,102.25,40.314,50.583,29.509 +2020-07-27 10:00:00,100.85,39.336,49.11600000000001,29.509 +2020-07-27 10:15:00,105.36,40.782,49.11600000000001,29.509 +2020-07-27 10:30:00,110.58,40.625,49.11600000000001,29.509 +2020-07-27 10:45:00,104.99,40.507,49.11600000000001,29.509 +2020-07-27 11:00:00,104.14,38.387,49.056000000000004,29.509 +2020-07-27 11:15:00,99.79,39.169000000000004,49.056000000000004,29.509 +2020-07-27 11:30:00,97.15,41.443999999999996,49.056000000000004,29.509 +2020-07-27 11:45:00,97.01,43.053000000000004,49.056000000000004,29.509 +2020-07-27 12:00:00,94.59,37.788000000000004,47.227,29.509 +2020-07-27 12:15:00,96.64,36.614000000000004,47.227,29.509 +2020-07-27 12:30:00,94.46,34.49,47.227,29.509 +2020-07-27 12:45:00,96.43,34.6,47.227,29.509 +2020-07-27 13:00:00,94.41,34.707,47.006,29.509 +2020-07-27 13:15:00,98.63,33.953,47.006,29.509 +2020-07-27 13:30:00,92.15,32.293,47.006,29.509 +2020-07-27 13:45:00,92.79,33.483000000000004,47.006,29.509 +2020-07-27 14:00:00,96.24,34.769,47.19,29.509 +2020-07-27 14:15:00,94.96,34.046,47.19,29.509 +2020-07-27 14:30:00,97.53,32.781,47.19,29.509 +2020-07-27 14:45:00,94.66,34.342,47.19,29.509 +2020-07-27 15:00:00,96.06,36.696,47.846000000000004,29.509 +2020-07-27 15:15:00,96.69,33.32,47.846000000000004,29.509 +2020-07-27 15:30:00,94.08,32.165,47.846000000000004,29.509 +2020-07-27 15:45:00,95.96,30.063000000000002,47.846000000000004,29.509 +2020-07-27 16:00:00,97.15,33.278,49.641000000000005,29.509 +2020-07-27 16:15:00,97.99,33.095,49.641000000000005,29.509 +2020-07-27 16:30:00,102.46,32.815,49.641000000000005,29.509 +2020-07-27 16:45:00,101.93,29.17,49.641000000000005,29.509 +2020-07-27 17:00:00,100.45,32.861999999999995,54.133,29.509 +2020-07-27 17:15:00,98.37,32.753,54.133,29.509 +2020-07-27 17:30:00,98.03,32.219,54.133,29.509 +2020-07-27 17:45:00,97.96,31.778000000000002,54.133,29.509 +2020-07-27 18:00:00,100.96,36.108000000000004,53.761,29.509 +2020-07-27 18:15:00,102.23,34.207,53.761,29.509 +2020-07-27 18:30:00,99.94,32.584,53.761,29.509 +2020-07-27 18:45:00,102.7,35.639,53.761,29.509 +2020-07-27 19:00:00,97.52,38.338,53.923,29.509 +2020-07-27 19:15:00,93.44,37.054,53.923,29.509 +2020-07-27 19:30:00,92.1,36.010999999999996,53.923,29.509 +2020-07-27 19:45:00,92.41,34.579,53.923,29.509 +2020-07-27 20:00:00,90.77,31.631999999999998,58.786,29.509 +2020-07-27 20:15:00,91.86,32.35,58.786,29.509 +2020-07-27 20:30:00,92.73,32.789,58.786,29.509 +2020-07-27 20:45:00,92.17,32.653,58.786,29.509 +2020-07-27 21:00:00,88.4,30.436,54.591,29.509 +2020-07-27 21:15:00,86.32,33.257,54.591,29.509 +2020-07-27 21:30:00,81.87,33.473,54.591,29.509 +2020-07-27 21:45:00,81.44,34.038000000000004,54.591,29.509 +2020-07-27 22:00:00,75.29,31.253,51.551,29.509 +2020-07-27 22:15:00,76.35,34.385999999999996,51.551,29.509 +2020-07-27 22:30:00,74.74,29.62,51.551,29.509 +2020-07-27 22:45:00,78.59,26.478,51.551,29.509 +2020-07-27 23:00:00,69.51,24.256,44.716,29.509 +2020-07-27 23:15:00,70.81,21.219,44.716,29.509 +2020-07-27 23:30:00,70.12,20.26,44.716,29.509 +2020-07-27 23:45:00,69.6,18.979,44.716,29.509 +2020-07-28 00:00:00,66.06,18.403,43.01,29.509 +2020-07-28 00:15:00,67.08,19.291,43.01,29.509 +2020-07-28 00:30:00,66.41,18.014,43.01,29.509 +2020-07-28 00:45:00,67.0,18.176,43.01,29.509 +2020-07-28 01:00:00,64.96,18.167,40.687,29.509 +2020-07-28 01:15:00,66.69,17.328,40.687,29.509 +2020-07-28 01:30:00,66.57,15.895,40.687,29.509 +2020-07-28 01:45:00,67.11,15.945,40.687,29.509 +2020-07-28 02:00:00,65.29,15.835,39.554,29.509 +2020-07-28 02:15:00,66.22,14.524000000000001,39.554,29.509 +2020-07-28 02:30:00,67.12,16.489,39.554,29.509 +2020-07-28 02:45:00,74.51,17.230999999999998,39.554,29.509 +2020-07-28 03:00:00,75.51,18.124000000000002,38.958,29.509 +2020-07-28 03:15:00,71.6,17.781,38.958,29.509 +2020-07-28 03:30:00,69.58,17.167,38.958,29.509 +2020-07-28 03:45:00,71.85,16.477999999999998,38.958,29.509 +2020-07-28 04:00:00,75.9,20.482,39.783,29.509 +2020-07-28 04:15:00,74.67,25.921999999999997,39.783,29.509 +2020-07-28 04:30:00,75.33,23.201999999999998,39.783,29.509 +2020-07-28 04:45:00,79.19,23.13,39.783,29.509 +2020-07-28 05:00:00,91.86,34.734,42.281000000000006,29.509 +2020-07-28 05:15:00,95.84,40.444,42.281000000000006,29.509 +2020-07-28 05:30:00,96.24,35.861,42.281000000000006,29.509 +2020-07-28 05:45:00,94.7,33.519,42.281000000000006,29.509 +2020-07-28 06:00:00,104.47,33.715,50.801,29.509 +2020-07-28 06:15:00,108.31,33.161,50.801,29.509 +2020-07-28 06:30:00,109.99,33.008,50.801,29.509 +2020-07-28 06:45:00,108.51,35.823,50.801,29.509 +2020-07-28 07:00:00,107.45,35.556,60.202,29.509 +2020-07-28 07:15:00,105.46,36.536,60.202,29.509 +2020-07-28 07:30:00,107.66,34.574,60.202,29.509 +2020-07-28 07:45:00,111.91,35.306,60.202,29.509 +2020-07-28 08:00:00,111.65,30.717,54.461000000000006,29.509 +2020-07-28 08:15:00,111.03,34.015,54.461000000000006,29.509 +2020-07-28 08:30:00,104.82,35.417,54.461000000000006,29.509 +2020-07-28 08:45:00,105.65,38.006,54.461000000000006,29.509 +2020-07-28 09:00:00,105.34,33.71,50.753,29.509 +2020-07-28 09:15:00,104.97,33.184,50.753,29.509 +2020-07-28 09:30:00,111.63,36.865,50.753,29.509 +2020-07-28 09:45:00,111.1,40.098,50.753,29.509 +2020-07-28 10:00:00,103.92,37.664,49.703,29.509 +2020-07-28 10:15:00,108.58,39.079,49.703,29.509 +2020-07-28 10:30:00,104.47,38.921,49.703,29.509 +2020-07-28 10:45:00,102.44,39.895,49.703,29.509 +2020-07-28 11:00:00,99.91,37.591,49.42100000000001,29.509 +2020-07-28 11:15:00,101.99,38.866,49.42100000000001,29.509 +2020-07-28 11:30:00,102.94,39.99,49.42100000000001,29.509 +2020-07-28 11:45:00,104.11,40.995,49.42100000000001,29.509 +2020-07-28 12:00:00,103.58,35.702,47.155,29.509 +2020-07-28 12:15:00,108.58,34.954,47.155,29.509 +2020-07-28 12:30:00,112.24,33.703,47.155,29.509 +2020-07-28 12:45:00,113.51,34.622,47.155,29.509 +2020-07-28 13:00:00,110.53,34.356,47.515,29.509 +2020-07-28 13:15:00,113.4,35.666,47.515,29.509 +2020-07-28 13:30:00,111.67,33.818000000000005,47.515,29.509 +2020-07-28 13:45:00,108.55,33.906,47.515,29.509 +2020-07-28 14:00:00,98.05,35.644,47.575,29.509 +2020-07-28 14:15:00,94.38,34.681,47.575,29.509 +2020-07-28 14:30:00,93.99,33.705,47.575,29.509 +2020-07-28 14:45:00,93.91,34.396,47.575,29.509 +2020-07-28 15:00:00,99.21,36.606,48.903,29.509 +2020-07-28 15:15:00,101.07,34.223,48.903,29.509 +2020-07-28 15:30:00,100.87,32.81,48.903,29.509 +2020-07-28 15:45:00,100.92,31.144000000000002,48.903,29.509 +2020-07-28 16:00:00,97.59,33.515,50.218999999999994,29.509 +2020-07-28 16:15:00,98.84,33.376999999999995,50.218999999999994,29.509 +2020-07-28 16:30:00,99.43,32.621,50.218999999999994,29.509 +2020-07-28 16:45:00,99.6,29.761,50.218999999999994,29.509 +2020-07-28 17:00:00,102.62,33.61,55.396,29.509 +2020-07-28 17:15:00,101.28,33.985,55.396,29.509 +2020-07-28 17:30:00,100.85,32.898,55.396,29.509 +2020-07-28 17:45:00,102.53,32.125,55.396,29.509 +2020-07-28 18:00:00,102.54,35.43,55.583999999999996,29.509 +2020-07-28 18:15:00,100.74,35.217,55.583999999999996,29.509 +2020-07-28 18:30:00,102.02,33.344,55.583999999999996,29.509 +2020-07-28 18:45:00,102.2,36.103,55.583999999999996,29.509 +2020-07-28 19:00:00,97.76,37.53,56.071000000000005,29.509 +2020-07-28 19:15:00,95.49,36.464,56.071000000000005,29.509 +2020-07-28 19:30:00,94.9,35.249,56.071000000000005,29.509 +2020-07-28 19:45:00,93.33,34.178000000000004,56.071000000000005,29.509 +2020-07-28 20:00:00,91.91,31.64,61.55,29.509 +2020-07-28 20:15:00,93.19,30.85,61.55,29.509 +2020-07-28 20:30:00,94.82,31.225,61.55,29.509 +2020-07-28 20:45:00,93.22,31.589000000000002,61.55,29.509 +2020-07-28 21:00:00,89.1,30.386999999999997,55.94,29.509 +2020-07-28 21:15:00,87.92,31.531999999999996,55.94,29.509 +2020-07-28 21:30:00,84.69,31.991999999999997,55.94,29.509 +2020-07-28 21:45:00,84.68,32.727,55.94,29.509 +2020-07-28 22:00:00,78.94,29.971,52.857,29.509 +2020-07-28 22:15:00,85.09,32.757,52.857,29.509 +2020-07-28 22:30:00,81.12,28.272,52.857,29.509 +2020-07-28 22:45:00,81.25,25.096999999999998,52.857,29.509 +2020-07-28 23:00:00,77.11,22.079,46.04,29.509 +2020-07-28 23:15:00,78.13,20.805999999999997,46.04,29.509 +2020-07-28 23:30:00,77.41,19.891,46.04,29.509 +2020-07-28 23:45:00,77.45,18.84,46.04,29.509 +2020-07-29 00:00:00,72.28,18.484,42.195,29.509 +2020-07-29 00:15:00,73.33,19.371,42.195,29.509 +2020-07-29 00:30:00,73.27,18.098,42.195,29.509 +2020-07-29 00:45:00,72.85,18.268,42.195,29.509 +2020-07-29 01:00:00,71.33,18.252,38.82,29.509 +2020-07-29 01:15:00,72.86,17.414,38.82,29.509 +2020-07-29 01:30:00,73.45,15.99,38.82,29.509 +2020-07-29 01:45:00,73.58,16.035,38.82,29.509 +2020-07-29 02:00:00,71.67,15.93,37.023,29.509 +2020-07-29 02:15:00,72.3,14.637,37.023,29.509 +2020-07-29 02:30:00,69.76,16.581,37.023,29.509 +2020-07-29 02:45:00,79.06,17.323,37.023,29.509 +2020-07-29 03:00:00,79.64,18.204,36.818000000000005,29.509 +2020-07-29 03:15:00,77.13,17.878,36.818000000000005,29.509 +2020-07-29 03:30:00,73.32,17.271,36.818000000000005,29.509 +2020-07-29 03:45:00,77.35,16.589000000000002,36.818000000000005,29.509 +2020-07-29 04:00:00,79.17,20.584,37.495,29.509 +2020-07-29 04:15:00,78.45,26.015,37.495,29.509 +2020-07-29 04:30:00,78.67,23.294,37.495,29.509 +2020-07-29 04:45:00,81.9,23.224,37.495,29.509 +2020-07-29 05:00:00,97.1,34.83,39.858000000000004,29.509 +2020-07-29 05:15:00,99.54,40.534,39.858000000000004,29.509 +2020-07-29 05:30:00,103.49,35.981,39.858000000000004,29.509 +2020-07-29 05:45:00,98.13,33.632,39.858000000000004,29.509 +2020-07-29 06:00:00,105.66,33.81,52.867,29.509 +2020-07-29 06:15:00,110.73,33.266999999999996,52.867,29.509 +2020-07-29 06:30:00,111.3,33.122,52.867,29.509 +2020-07-29 06:45:00,112.66,35.955,52.867,29.509 +2020-07-29 07:00:00,108.85,35.685,66.061,29.509 +2020-07-29 07:15:00,105.98,36.684,66.061,29.509 +2020-07-29 07:30:00,105.83,34.736999999999995,66.061,29.509 +2020-07-29 07:45:00,104.0,35.484,66.061,29.509 +2020-07-29 08:00:00,106.11,30.903000000000002,58.532,29.509 +2020-07-29 08:15:00,104.13,34.188,58.532,29.509 +2020-07-29 08:30:00,106.01,35.577,58.532,29.509 +2020-07-29 08:45:00,106.01,38.155,58.532,29.509 +2020-07-29 09:00:00,106.42,33.86,56.047,29.509 +2020-07-29 09:15:00,105.79,33.329,56.047,29.509 +2020-07-29 09:30:00,105.45,36.997,56.047,29.509 +2020-07-29 09:45:00,103.21,40.218,56.047,29.509 +2020-07-29 10:00:00,102.4,37.788000000000004,53.823,29.509 +2020-07-29 10:15:00,106.01,39.188,53.823,29.509 +2020-07-29 10:30:00,104.96,39.025,53.823,29.509 +2020-07-29 10:45:00,104.1,39.994,53.823,29.509 +2020-07-29 11:00:00,108.14,37.696,54.184,29.509 +2020-07-29 11:15:00,108.27,38.968,54.184,29.509 +2020-07-29 11:30:00,108.1,40.082,54.184,29.509 +2020-07-29 11:45:00,102.46,41.075,54.184,29.509 +2020-07-29 12:00:00,102.99,35.788000000000004,52.628,29.509 +2020-07-29 12:15:00,100.87,35.034,52.628,29.509 +2020-07-29 12:30:00,100.15,33.786,52.628,29.509 +2020-07-29 12:45:00,98.46,34.696,52.628,29.509 +2020-07-29 13:00:00,98.77,34.41,52.31,29.509 +2020-07-29 13:15:00,97.85,35.708,52.31,29.509 +2020-07-29 13:30:00,97.02,33.861,52.31,29.509 +2020-07-29 13:45:00,97.78,33.959,52.31,29.509 +2020-07-29 14:00:00,96.14,35.687,52.278999999999996,29.509 +2020-07-29 14:15:00,95.9,34.727,52.278999999999996,29.509 +2020-07-29 14:30:00,96.19,33.756,52.278999999999996,29.509 +2020-07-29 14:45:00,96.56,34.452,52.278999999999996,29.509 +2020-07-29 15:00:00,95.81,36.641999999999996,53.306999999999995,29.509 +2020-07-29 15:15:00,92.1,34.259,53.306999999999995,29.509 +2020-07-29 15:30:00,94.45,32.853,53.306999999999995,29.509 +2020-07-29 15:45:00,96.56,31.185,53.306999999999995,29.509 +2020-07-29 16:00:00,97.51,33.548,55.358999999999995,29.509 +2020-07-29 16:15:00,98.55,33.417,55.358999999999995,29.509 +2020-07-29 16:30:00,98.44,32.673,55.358999999999995,29.509 +2020-07-29 16:45:00,98.51,29.837,55.358999999999995,29.509 +2020-07-29 17:00:00,100.98,33.671,59.211999999999996,29.509 +2020-07-29 17:15:00,101.93,34.071,59.211999999999996,29.509 +2020-07-29 17:30:00,102.63,32.995,59.211999999999996,29.509 +2020-07-29 17:45:00,103.93,32.245,59.211999999999996,29.509 +2020-07-29 18:00:00,104.7,35.549,60.403999999999996,29.509 +2020-07-29 18:15:00,103.25,35.335,60.403999999999996,29.509 +2020-07-29 18:30:00,103.93,33.47,60.403999999999996,29.509 +2020-07-29 18:45:00,103.57,36.229,60.403999999999996,29.509 +2020-07-29 19:00:00,99.94,37.659,60.993,29.509 +2020-07-29 19:15:00,95.58,36.588,60.993,29.509 +2020-07-29 19:30:00,95.02,35.373000000000005,60.993,29.509 +2020-07-29 19:45:00,95.21,34.303000000000004,60.993,29.509 +2020-07-29 20:00:00,93.06,31.76,66.6,29.509 +2020-07-29 20:15:00,95.46,30.971999999999998,66.6,29.509 +2020-07-29 20:30:00,95.47,31.337,66.6,29.509 +2020-07-29 20:45:00,94.87,31.686999999999998,66.6,29.509 +2020-07-29 21:00:00,91.12,30.487,59.855,29.509 +2020-07-29 21:15:00,90.81,31.628,59.855,29.509 +2020-07-29 21:30:00,87.11,32.074,59.855,29.509 +2020-07-29 21:45:00,85.59,32.789,59.855,29.509 +2020-07-29 22:00:00,80.94,30.025,54.942,29.509 +2020-07-29 22:15:00,79.82,32.803000000000004,54.942,29.509 +2020-07-29 22:30:00,78.83,28.29,54.942,29.509 +2020-07-29 22:45:00,78.28,25.111,54.942,29.509 +2020-07-29 23:00:00,73.71,22.119,46.056000000000004,29.509 +2020-07-29 23:15:00,75.13,20.851999999999997,46.056000000000004,29.509 +2020-07-29 23:30:00,74.23,19.952,46.056000000000004,29.509 +2020-07-29 23:45:00,72.75,18.901,46.056000000000004,29.509 +2020-07-30 00:00:00,66.91,18.569000000000003,40.859,29.509 +2020-07-30 00:15:00,70.85,19.454,40.859,29.509 +2020-07-30 00:30:00,68.48,18.186,40.859,29.509 +2020-07-30 00:45:00,71.01,18.364,40.859,29.509 +2020-07-30 01:00:00,68.53,18.339000000000002,39.06,29.509 +2020-07-30 01:15:00,69.68,17.503,39.06,29.509 +2020-07-30 01:30:00,69.53,16.087,39.06,29.509 +2020-07-30 01:45:00,69.27,16.129,39.06,29.509 +2020-07-30 02:00:00,69.25,16.028,37.592,29.509 +2020-07-30 02:15:00,69.73,14.753,37.592,29.509 +2020-07-30 02:30:00,69.73,16.677,37.592,29.509 +2020-07-30 02:45:00,70.05,17.419,37.592,29.509 +2020-07-30 03:00:00,70.14,18.289,37.416,29.509 +2020-07-30 03:15:00,67.95,17.977999999999998,37.416,29.509 +2020-07-30 03:30:00,71.64,17.379,37.416,29.509 +2020-07-30 03:45:00,74.13,16.704,37.416,29.509 +2020-07-30 04:00:00,78.12,20.691,38.176,29.509 +2020-07-30 04:15:00,77.94,26.114,38.176,29.509 +2020-07-30 04:30:00,82.51,23.393,38.176,29.509 +2020-07-30 04:45:00,88.96,23.324,38.176,29.509 +2020-07-30 05:00:00,95.56,34.935,41.203,29.509 +2020-07-30 05:15:00,96.07,40.637,41.203,29.509 +2020-07-30 05:30:00,94.6,36.111999999999995,41.203,29.509 +2020-07-30 05:45:00,97.84,33.755,41.203,29.509 +2020-07-30 06:00:00,103.74,33.912,51.09,29.509 +2020-07-30 06:15:00,106.27,33.382,51.09,29.509 +2020-07-30 06:30:00,107.84,33.246,51.09,29.509 +2020-07-30 06:45:00,108.65,36.096,51.09,29.509 +2020-07-30 07:00:00,115.44,35.821,63.541000000000004,29.509 +2020-07-30 07:15:00,115.82,36.841,63.541000000000004,29.509 +2020-07-30 07:30:00,119.1,34.909,63.541000000000004,29.509 +2020-07-30 07:45:00,116.84,35.671,63.541000000000004,29.509 +2020-07-30 08:00:00,114.55,31.096,55.65,29.509 +2020-07-30 08:15:00,110.16,34.368,55.65,29.509 +2020-07-30 08:30:00,112.03,35.745,55.65,29.509 +2020-07-30 08:45:00,108.34,38.311,55.65,29.509 +2020-07-30 09:00:00,110.22,34.018,51.833999999999996,29.509 +2020-07-30 09:15:00,117.14,33.48,51.833999999999996,29.509 +2020-07-30 09:30:00,114.83,37.135,51.833999999999996,29.509 +2020-07-30 09:45:00,112.31,40.343,51.833999999999996,29.509 +2020-07-30 10:00:00,105.58,37.918,49.70399999999999,29.509 +2020-07-30 10:15:00,106.54,39.303000000000004,49.70399999999999,29.509 +2020-07-30 10:30:00,105.24,39.134,49.70399999999999,29.509 +2020-07-30 10:45:00,108.78,40.099000000000004,49.70399999999999,29.509 +2020-07-30 11:00:00,108.28,37.806,48.593999999999994,29.509 +2020-07-30 11:15:00,108.61,39.073,48.593999999999994,29.509 +2020-07-30 11:30:00,108.06,40.179,48.593999999999994,29.509 +2020-07-30 11:45:00,105.5,41.161,48.593999999999994,29.509 +2020-07-30 12:00:00,101.8,35.878,46.275,29.509 +2020-07-30 12:15:00,100.72,35.118,46.275,29.509 +2020-07-30 12:30:00,98.24,33.873000000000005,46.275,29.509 +2020-07-30 12:45:00,98.08,34.773,46.275,29.509 +2020-07-30 13:00:00,96.54,34.467,45.803000000000004,29.509 +2020-07-30 13:15:00,96.92,35.754,45.803000000000004,29.509 +2020-07-30 13:30:00,96.23,33.906,45.803000000000004,29.509 +2020-07-30 13:45:00,97.99,34.016,45.803000000000004,29.509 +2020-07-30 14:00:00,96.53,35.733000000000004,46.251999999999995,29.509 +2020-07-30 14:15:00,96.4,34.777,46.251999999999995,29.509 +2020-07-30 14:30:00,96.01,33.809,46.251999999999995,29.509 +2020-07-30 14:45:00,97.0,34.510999999999996,46.251999999999995,29.509 +2020-07-30 15:00:00,95.45,36.68,48.309,29.509 +2020-07-30 15:15:00,95.82,34.297,48.309,29.509 +2020-07-30 15:30:00,95.51,32.898,48.309,29.509 +2020-07-30 15:45:00,96.03,31.229,48.309,29.509 +2020-07-30 16:00:00,97.23,33.583,49.681999999999995,29.509 +2020-07-30 16:15:00,98.16,33.459,49.681999999999995,29.509 +2020-07-30 16:30:00,98.84,32.728,49.681999999999995,29.509 +2020-07-30 16:45:00,101.61,29.916999999999998,49.681999999999995,29.509 +2020-07-30 17:00:00,101.98,33.736,53.086000000000006,29.509 +2020-07-30 17:15:00,101.92,34.16,53.086000000000006,29.509 +2020-07-30 17:30:00,102.4,33.095,53.086000000000006,29.509 +2020-07-30 17:45:00,104.67,32.369,53.086000000000006,29.509 +2020-07-30 18:00:00,104.01,35.671,54.038999999999994,29.509 +2020-07-30 18:15:00,102.55,35.457,54.038999999999994,29.509 +2020-07-30 18:30:00,101.69,33.601,54.038999999999994,29.509 +2020-07-30 18:45:00,101.62,36.359,54.038999999999994,29.509 +2020-07-30 19:00:00,97.79,37.793,53.408,29.509 +2020-07-30 19:15:00,95.25,36.719,53.408,29.509 +2020-07-30 19:30:00,94.88,35.501999999999995,53.408,29.509 +2020-07-30 19:45:00,94.26,34.434,53.408,29.509 +2020-07-30 20:00:00,94.38,31.886,55.309,29.509 +2020-07-30 20:15:00,95.53,31.099,55.309,29.509 +2020-07-30 20:30:00,95.23,31.453000000000003,55.309,29.509 +2020-07-30 20:45:00,94.49,31.789,55.309,29.509 +2020-07-30 21:00:00,90.29,30.593000000000004,51.585,29.509 +2020-07-30 21:15:00,90.19,31.728,51.585,29.509 +2020-07-30 21:30:00,85.83,32.162,51.585,29.509 +2020-07-30 21:45:00,84.2,32.855,51.585,29.509 +2020-07-30 22:00:00,79.37,30.081999999999997,48.006,29.509 +2020-07-30 22:15:00,79.95,32.852,48.006,29.509 +2020-07-30 22:30:00,77.7,28.309,48.006,29.509 +2020-07-30 22:45:00,78.53,25.127,48.006,29.509 +2020-07-30 23:00:00,73.03,22.162,42.309,29.509 +2020-07-30 23:15:00,73.29,20.901999999999997,42.309,29.509 +2020-07-30 23:30:00,72.79,20.016,42.309,29.509 +2020-07-30 23:45:00,72.36,18.965999999999998,42.309,29.509 +2020-07-31 00:00:00,67.66,16.794,39.649,29.509 +2020-07-31 00:15:00,67.46,17.897000000000002,39.649,29.509 +2020-07-31 00:30:00,69.3,16.939,39.649,29.509 +2020-07-31 00:45:00,69.18,17.575,39.649,29.509 +2020-07-31 01:00:00,68.54,17.151,37.744,29.509 +2020-07-31 01:15:00,69.64,15.592,37.744,29.509 +2020-07-31 01:30:00,68.26,14.98,37.744,29.509 +2020-07-31 01:45:00,68.52,14.735999999999999,37.744,29.509 +2020-07-31 02:00:00,66.7,15.647,36.965,29.509 +2020-07-31 02:15:00,69.12,14.513,36.965,29.509 +2020-07-31 02:30:00,68.98,17.101,36.965,29.509 +2020-07-31 02:45:00,68.73,17.097,36.965,29.509 +2020-07-31 03:00:00,69.73,18.875999999999998,37.678000000000004,29.509 +2020-07-31 03:15:00,70.06,17.2,37.678000000000004,29.509 +2020-07-31 03:30:00,71.02,16.35,37.678000000000004,29.509 +2020-07-31 03:45:00,73.59,16.607,37.678000000000004,29.509 +2020-07-31 04:00:00,77.51,20.721,38.591,29.509 +2020-07-31 04:15:00,84.16,24.464000000000002,38.591,29.509 +2020-07-31 04:30:00,85.18,22.758000000000003,38.591,29.509 +2020-07-31 04:45:00,83.87,22.075,38.591,29.509 +2020-07-31 05:00:00,88.03,33.272,40.666,29.509 +2020-07-31 05:15:00,93.14,39.897,40.666,29.509 +2020-07-31 05:30:00,96.11,35.541,40.666,29.509 +2020-07-31 05:45:00,103.78,32.701,40.666,29.509 +2020-07-31 06:00:00,109.56,33.047,51.784,29.509 +2020-07-31 06:15:00,108.31,32.806,51.784,29.509 +2020-07-31 06:30:00,104.13,32.679,51.784,29.509 +2020-07-31 06:45:00,103.29,35.299,51.784,29.509 +2020-07-31 07:00:00,105.64,35.775,61.383,29.509 +2020-07-31 07:15:00,107.2,37.719,61.383,29.509 +2020-07-31 07:30:00,106.76,33.693000000000005,61.383,29.509 +2020-07-31 07:45:00,108.81,34.313,61.383,29.509 +2020-07-31 08:00:00,110.94,30.691,55.272,29.509 +2020-07-31 08:15:00,111.98,34.72,55.272,29.509 +2020-07-31 08:30:00,111.09,35.89,55.272,29.509 +2020-07-31 08:45:00,105.5,38.428000000000004,55.272,29.509 +2020-07-31 09:00:00,112.21,31.557,53.506,29.509 +2020-07-31 09:15:00,112.71,33.08,53.506,29.509 +2020-07-31 09:30:00,117.26,36.003,53.506,29.509 +2020-07-31 09:45:00,117.46,39.635,53.506,29.509 +2020-07-31 10:00:00,118.86,37.153,51.363,29.509 +2020-07-31 10:15:00,109.46,38.202,51.363,29.509 +2020-07-31 10:30:00,106.07,38.647,51.363,29.509 +2020-07-31 10:45:00,104.7,39.549,51.363,29.509 +2020-07-31 11:00:00,102.28,37.538000000000004,51.043,29.509 +2020-07-31 11:15:00,99.07,37.687,51.043,29.509 +2020-07-31 11:30:00,98.78,38.205999999999996,51.043,29.509 +2020-07-31 11:45:00,102.16,38.109,51.043,29.509 +2020-07-31 12:00:00,98.03,33.196,47.52,29.509 +2020-07-31 12:15:00,98.7,31.933000000000003,47.52,29.509 +2020-07-31 12:30:00,97.7,30.805999999999997,47.52,29.509 +2020-07-31 12:45:00,101.31,30.771,47.52,29.509 +2020-07-31 13:00:00,97.21,31.002,45.494,29.509 +2020-07-31 13:15:00,94.94,32.394,45.494,29.509 +2020-07-31 13:30:00,95.09,31.428,45.494,29.509 +2020-07-31 13:45:00,99.62,31.894000000000002,45.494,29.509 +2020-07-31 14:00:00,99.4,32.83,43.883,29.509 +2020-07-31 14:15:00,102.69,32.379,43.883,29.509 +2020-07-31 14:30:00,104.82,33.031,43.883,29.509 +2020-07-31 14:45:00,108.76,32.946,43.883,29.509 +2020-07-31 15:00:00,107.19,35.053000000000004,45.714,29.509 +2020-07-31 15:15:00,107.16,32.45,45.714,29.509 +2020-07-31 15:30:00,102.02,30.58,45.714,29.509 +2020-07-31 15:45:00,98.73,29.737,45.714,29.509 +2020-07-31 16:00:00,97.24,31.250999999999998,48.222,29.509 +2020-07-31 16:15:00,96.65,31.649,48.222,29.509 +2020-07-31 16:30:00,95.0,30.721999999999998,48.222,29.509 +2020-07-31 16:45:00,98.3,27.085,48.222,29.509 +2020-07-31 17:00:00,100.32,32.76,52.619,29.509 +2020-07-31 17:15:00,100.27,33.058,52.619,29.509 +2020-07-31 17:30:00,99.43,32.214,52.619,29.509 +2020-07-31 17:45:00,99.98,31.351999999999997,52.619,29.509 +2020-07-31 18:00:00,100.2,34.617,52.99,29.509 +2020-07-31 18:15:00,101.05,33.407,52.99,29.509 +2020-07-31 18:30:00,100.24,31.410999999999998,52.99,29.509 +2020-07-31 18:45:00,97.28,34.628,52.99,29.509 +2020-07-31 19:00:00,94.41,36.923,51.923,29.509 +2020-07-31 19:15:00,93.63,36.38,51.923,29.509 +2020-07-31 19:30:00,90.61,35.259,51.923,29.509 +2020-07-31 19:45:00,91.18,33.129,51.923,29.509 +2020-07-31 20:00:00,91.95,30.405,56.238,29.509 +2020-07-31 20:15:00,91.38,30.476999999999997,56.238,29.509 +2020-07-31 20:30:00,90.78,30.296999999999997,56.238,29.509 +2020-07-31 20:45:00,90.26,29.695,56.238,29.509 +2020-07-31 21:00:00,85.04,29.877,52.426,29.509 +2020-07-31 21:15:00,83.4,32.773,52.426,29.509 +2020-07-31 21:30:00,80.13,33.031,52.426,29.509 +2020-07-31 21:45:00,79.5,33.88,52.426,29.509 +2020-07-31 22:00:00,75.94,30.871,48.196000000000005,29.509 +2020-07-31 22:15:00,75.61,33.374,48.196000000000005,29.509 +2020-07-31 22:30:00,74.04,33.457,48.196000000000005,29.509 +2020-07-31 22:45:00,74.51,30.945,48.196000000000005,29.509 +2020-07-31 23:00:00,69.29,29.855999999999998,41.71,29.509 +2020-07-31 23:15:00,67.98,27.054000000000002,41.71,29.509 +2020-07-31 23:30:00,67.78,24.331999999999997,41.71,29.509 +2020-07-31 23:45:00,68.07,23.197,41.71,29.509 +2020-08-01 00:00:00,64.84,16.840999999999998,40.227,29.423000000000002 +2020-08-01 00:15:00,65.7,17.429000000000002,40.227,29.423000000000002 +2020-08-01 00:30:00,61.29,16.066,40.227,29.423000000000002 +2020-08-01 00:45:00,63.86,15.87,40.227,29.423000000000002 +2020-08-01 01:00:00,61.89,15.71,36.303000000000004,29.423000000000002 +2020-08-01 01:15:00,63.24,14.94,36.303000000000004,29.423000000000002 +2020-08-01 01:30:00,61.9,13.640999999999998,36.303000000000004,29.423000000000002 +2020-08-01 01:45:00,62.53,14.588,36.303000000000004,29.423000000000002 +2020-08-01 02:00:00,60.34,14.395999999999999,33.849000000000004,29.423000000000002 +2020-08-01 02:15:00,60.96,13.040999999999999,33.849000000000004,29.423000000000002 +2020-08-01 02:30:00,66.99,14.253,33.849000000000004,29.423000000000002 +2020-08-01 02:45:00,68.32,15.014000000000001,33.849000000000004,29.423000000000002 +2020-08-01 03:00:00,65.65,15.258,33.149,29.423000000000002 +2020-08-01 03:15:00,61.57,13.152999999999999,33.149,29.423000000000002 +2020-08-01 03:30:00,61.95,13.040999999999999,33.149,29.423000000000002 +2020-08-01 03:45:00,64.13,14.512,33.149,29.423000000000002 +2020-08-01 04:00:00,63.58,16.715,32.501,29.423000000000002 +2020-08-01 04:15:00,62.36,19.454,32.501,29.423000000000002 +2020-08-01 04:30:00,64.17,16.437,32.501,29.423000000000002 +2020-08-01 04:45:00,69.28,16.136,32.501,29.423000000000002 +2020-08-01 05:00:00,70.79,19.305,31.648000000000003,29.423000000000002 +2020-08-01 05:15:00,66.78,16.058,31.648000000000003,29.423000000000002 +2020-08-01 05:30:00,64.36,13.495999999999999,31.648000000000003,29.423000000000002 +2020-08-01 05:45:00,65.34,14.887,31.648000000000003,29.423000000000002 +2020-08-01 06:00:00,68.08,24.894000000000002,32.552,29.423000000000002 +2020-08-01 06:15:00,71.47,31.201,32.552,29.423000000000002 +2020-08-01 06:30:00,70.7,28.622,32.552,29.423000000000002 +2020-08-01 06:45:00,73.6,28.4,32.552,29.423000000000002 +2020-08-01 07:00:00,80.26,28.034000000000002,35.181999999999995,29.423000000000002 +2020-08-01 07:15:00,81.15,28.61,35.181999999999995,29.423000000000002 +2020-08-01 07:30:00,83.99,26.254,35.181999999999995,29.423000000000002 +2020-08-01 07:45:00,78.02,27.406,35.181999999999995,29.423000000000002 +2020-08-01 08:00:00,78.36,24.79,40.35,29.423000000000002 +2020-08-01 08:15:00,78.58,27.854,40.35,29.423000000000002 +2020-08-01 08:30:00,84.13,28.653000000000002,40.35,29.423000000000002 +2020-08-01 08:45:00,88.25,31.538,40.35,29.423000000000002 +2020-08-01 09:00:00,83.81,27.921,42.292,29.423000000000002 +2020-08-01 09:15:00,77.51,29.616,42.292,29.423000000000002 +2020-08-01 09:30:00,77.4,32.631,42.292,29.423000000000002 +2020-08-01 09:45:00,81.46,35.472,42.292,29.423000000000002 +2020-08-01 10:00:00,88.03,33.78,40.084,29.423000000000002 +2020-08-01 10:15:00,87.71,35.077,40.084,29.423000000000002 +2020-08-01 10:30:00,88.54,35.137,40.084,29.423000000000002 +2020-08-01 10:45:00,81.12,35.558,40.084,29.423000000000002 +2020-08-01 11:00:00,75.49,33.609,36.966,29.423000000000002 +2020-08-01 11:15:00,77.04,34.655,36.966,29.423000000000002 +2020-08-01 11:30:00,73.65,35.507,36.966,29.423000000000002 +2020-08-01 11:45:00,73.41,36.196999999999996,36.966,29.423000000000002 +2020-08-01 12:00:00,72.34,32.466,35.19,29.423000000000002 +2020-08-01 12:15:00,72.7,32.124,35.19,29.423000000000002 +2020-08-01 12:30:00,68.3,30.954,35.19,29.423000000000002 +2020-08-01 12:45:00,72.12,31.738000000000003,35.19,29.423000000000002 +2020-08-01 13:00:00,71.85,31.165,32.277,29.423000000000002 +2020-08-01 13:15:00,73.17,32.038000000000004,32.277,29.423000000000002 +2020-08-01 13:30:00,75.14,31.421,32.277,29.423000000000002 +2020-08-01 13:45:00,78.23,30.410999999999998,32.277,29.423000000000002 +2020-08-01 14:00:00,75.78,31.076999999999998,31.436999999999998,29.423000000000002 +2020-08-01 14:15:00,76.7,29.488000000000003,31.436999999999998,29.423000000000002 +2020-08-01 14:30:00,76.4,29.755,31.436999999999998,29.423000000000002 +2020-08-01 14:45:00,71.54,30.14,31.436999999999998,29.423000000000002 +2020-08-01 15:00:00,72.15,32.229,33.493,29.423000000000002 +2020-08-01 15:15:00,79.53,30.479,33.493,29.423000000000002 +2020-08-01 15:30:00,82.37,28.81,33.493,29.423000000000002 +2020-08-01 15:45:00,82.54,27.113000000000003,33.493,29.423000000000002 +2020-08-01 16:00:00,80.64,30.905,36.593,29.423000000000002 +2020-08-01 16:15:00,78.96,30.311,36.593,29.423000000000002 +2020-08-01 16:30:00,81.42,29.761,36.593,29.423000000000002 +2020-08-01 16:45:00,77.27,26.39,36.593,29.423000000000002 +2020-08-01 17:00:00,78.24,30.271,42.049,29.423000000000002 +2020-08-01 17:15:00,80.69,28.34,42.049,29.423000000000002 +2020-08-01 17:30:00,83.53,27.478,42.049,29.423000000000002 +2020-08-01 17:45:00,81.6,27.182,42.049,29.423000000000002 +2020-08-01 18:00:00,83.2,31.39,43.755,29.423000000000002 +2020-08-01 18:15:00,82.56,31.776,43.755,29.423000000000002 +2020-08-01 18:30:00,82.19,31.188000000000002,43.755,29.423000000000002 +2020-08-01 18:45:00,81.88,31.037,43.755,29.423000000000002 +2020-08-01 19:00:00,79.58,31.397,44.492,29.423000000000002 +2020-08-01 19:15:00,76.37,29.973000000000003,44.492,29.423000000000002 +2020-08-01 19:30:00,75.74,29.653000000000002,44.492,29.423000000000002 +2020-08-01 19:45:00,76.03,29.514,44.492,29.423000000000002 +2020-08-01 20:00:00,78.67,27.618000000000002,40.896,29.423000000000002 +2020-08-01 20:15:00,78.26,27.023000000000003,40.896,29.423000000000002 +2020-08-01 20:30:00,77.78,26.051,40.896,29.423000000000002 +2020-08-01 20:45:00,79.77,27.362,40.896,29.423000000000002 +2020-08-01 21:00:00,74.49,25.883000000000003,39.056,29.423000000000002 +2020-08-01 21:15:00,73.64,28.221,39.056,29.423000000000002 +2020-08-01 21:30:00,71.31,28.568,39.056,29.423000000000002 +2020-08-01 21:45:00,70.86,28.83,39.056,29.423000000000002 +2020-08-01 22:00:00,68.57,26.011,38.478,29.423000000000002 +2020-08-01 22:15:00,67.73,28.296999999999997,38.478,29.423000000000002 +2020-08-01 22:30:00,65.12,27.611,38.478,29.423000000000002 +2020-08-01 22:45:00,65.56,25.581999999999997,38.478,29.423000000000002 +2020-08-01 23:00:00,61.72,23.868000000000002,32.953,29.423000000000002 +2020-08-01 23:15:00,60.35,21.84,32.953,29.423000000000002 +2020-08-01 23:30:00,58.32,21.82,32.953,29.423000000000002 +2020-08-01 23:45:00,57.81,21.265,32.953,29.423000000000002 +2020-08-02 00:00:00,56.36,18.218,28.584,29.423000000000002 +2020-08-02 00:15:00,57.66,17.723,28.584,29.423000000000002 +2020-08-02 00:30:00,57.1,16.248,28.584,29.423000000000002 +2020-08-02 00:45:00,57.12,15.909,28.584,29.423000000000002 +2020-08-02 01:00:00,55.38,16.003,26.419,29.423000000000002 +2020-08-02 01:15:00,55.55,15.054,26.419,29.423000000000002 +2020-08-02 01:30:00,55.72,13.6,26.419,29.423000000000002 +2020-08-02 01:45:00,55.6,14.199000000000002,26.419,29.423000000000002 +2020-08-02 02:00:00,55.11,14.134,25.335,29.423000000000002 +2020-08-02 02:15:00,55.47,13.040999999999999,25.335,29.423000000000002 +2020-08-02 02:30:00,54.77,14.844000000000001,25.335,29.423000000000002 +2020-08-02 02:45:00,55.36,15.297,25.335,29.423000000000002 +2020-08-02 03:00:00,54.8,16.134,24.805,29.423000000000002 +2020-08-02 03:15:00,55.54,14.325,24.805,29.423000000000002 +2020-08-02 03:30:00,55.72,13.217,24.805,29.423000000000002 +2020-08-02 03:45:00,55.21,14.154000000000002,24.805,29.423000000000002 +2020-08-02 04:00:00,58.15,16.34,25.772,29.423000000000002 +2020-08-02 04:15:00,56.39,18.723,25.772,29.423000000000002 +2020-08-02 04:30:00,54.95,16.98,25.772,29.423000000000002 +2020-08-02 04:45:00,55.36,16.218,25.772,29.423000000000002 +2020-08-02 05:00:00,55.17,19.77,25.971999999999998,29.423000000000002 +2020-08-02 05:15:00,54.51,15.937000000000001,25.971999999999998,29.423000000000002 +2020-08-02 05:30:00,54.25,13.040999999999999,25.971999999999998,29.423000000000002 +2020-08-02 05:45:00,55.07,14.138,25.971999999999998,29.423000000000002 +2020-08-02 06:00:00,55.41,21.9,26.026,29.423000000000002 +2020-08-02 06:15:00,55.73,29.107,26.026,29.423000000000002 +2020-08-02 06:30:00,57.85,25.941,26.026,29.423000000000002 +2020-08-02 06:45:00,59.03,24.854,26.026,29.423000000000002 +2020-08-02 07:00:00,59.2,24.644000000000002,27.396,29.423000000000002 +2020-08-02 07:15:00,60.74,23.725,27.396,29.423000000000002 +2020-08-02 07:30:00,63.57,22.7,27.396,29.423000000000002 +2020-08-02 07:45:00,64.87,23.888,27.396,29.423000000000002 +2020-08-02 08:00:00,63.57,21.895,30.791999999999998,29.423000000000002 +2020-08-02 08:15:00,66.35,26.094,30.791999999999998,29.423000000000002 +2020-08-02 08:30:00,66.99,27.593000000000004,30.791999999999998,29.423000000000002 +2020-08-02 08:45:00,68.25,30.224,30.791999999999998,29.423000000000002 +2020-08-02 09:00:00,68.6,26.549,32.482,29.423000000000002 +2020-08-02 09:15:00,67.03,27.708000000000002,32.482,29.423000000000002 +2020-08-02 09:30:00,66.69,31.144000000000002,32.482,29.423000000000002 +2020-08-02 09:45:00,67.87,34.96,32.482,29.423000000000002 +2020-08-02 10:00:00,69.2,33.574,31.951,29.423000000000002 +2020-08-02 10:15:00,67.97,34.938,31.951,29.423000000000002 +2020-08-02 10:30:00,69.5,35.152,31.951,29.423000000000002 +2020-08-02 10:45:00,73.44,36.748000000000005,31.951,29.423000000000002 +2020-08-02 11:00:00,71.52,34.372,33.619,29.423000000000002 +2020-08-02 11:15:00,64.82,34.964,33.619,29.423000000000002 +2020-08-02 11:30:00,60.86,36.402,33.619,29.423000000000002 +2020-08-02 11:45:00,66.18,37.275,33.619,29.423000000000002 +2020-08-02 12:00:00,63.81,34.646,30.975,29.423000000000002 +2020-08-02 12:15:00,60.26,33.494,30.975,29.423000000000002 +2020-08-02 12:30:00,56.84,32.681,30.975,29.423000000000002 +2020-08-02 12:45:00,55.4,32.926,30.975,29.423000000000002 +2020-08-02 13:00:00,53.87,32.108000000000004,27.956999999999997,29.423000000000002 +2020-08-02 13:15:00,58.19,32.109,27.956999999999997,29.423000000000002 +2020-08-02 13:30:00,55.86,30.419,27.956999999999997,29.423000000000002 +2020-08-02 13:45:00,57.45,30.579,27.956999999999997,29.423000000000002 +2020-08-02 14:00:00,56.54,32.385999999999996,25.555999999999997,29.423000000000002 +2020-08-02 14:15:00,55.69,31.107,25.555999999999997,29.423000000000002 +2020-08-02 14:30:00,56.82,30.05,25.555999999999997,29.423000000000002 +2020-08-02 14:45:00,56.34,29.430999999999997,25.555999999999997,29.423000000000002 +2020-08-02 15:00:00,56.78,31.79,26.271,29.423000000000002 +2020-08-02 15:15:00,54.95,29.179000000000002,26.271,29.423000000000002 +2020-08-02 15:30:00,54.84,27.285,26.271,29.423000000000002 +2020-08-02 15:45:00,56.41,25.801,26.271,29.423000000000002 +2020-08-02 16:00:00,59.72,27.843000000000004,30.369,29.423000000000002 +2020-08-02 16:15:00,59.6,27.56,30.369,29.423000000000002 +2020-08-02 16:30:00,61.23,27.973000000000003,30.369,29.423000000000002 +2020-08-02 16:45:00,63.53,24.678,30.369,29.423000000000002 +2020-08-02 17:00:00,67.12,28.910999999999998,38.787,29.423000000000002 +2020-08-02 17:15:00,68.98,28.498,38.787,29.423000000000002 +2020-08-02 17:30:00,70.71,28.406,38.787,29.423000000000002 +2020-08-02 17:45:00,74.99,28.346999999999998,38.787,29.423000000000002 +2020-08-02 18:00:00,74.67,33.192,41.886,29.423000000000002 +2020-08-02 18:15:00,73.6,33.085,41.886,29.423000000000002 +2020-08-02 18:30:00,76.34,32.428000000000004,41.886,29.423000000000002 +2020-08-02 18:45:00,74.12,32.243,41.886,29.423000000000002 +2020-08-02 19:00:00,75.09,34.739000000000004,42.91,29.423000000000002 +2020-08-02 19:15:00,72.97,32.211999999999996,42.91,29.423000000000002 +2020-08-02 19:30:00,73.09,31.666,42.91,29.423000000000002 +2020-08-02 19:45:00,73.57,30.991,42.91,29.423000000000002 +2020-08-02 20:00:00,78.39,29.259,42.148999999999994,29.423000000000002 +2020-08-02 20:15:00,77.61,28.465,42.148999999999994,29.423000000000002 +2020-08-02 20:30:00,77.86,28.169,42.148999999999994,29.423000000000002 +2020-08-02 20:45:00,80.93,27.945999999999998,42.148999999999994,29.423000000000002 +2020-08-02 21:00:00,76.12,26.549,40.955999999999996,29.423000000000002 +2020-08-02 21:15:00,75.92,28.636999999999997,40.955999999999996,29.423000000000002 +2020-08-02 21:30:00,72.29,28.338,40.955999999999996,29.423000000000002 +2020-08-02 21:45:00,71.28,28.91,40.955999999999996,29.423000000000002 +2020-08-02 22:00:00,67.02,28.098000000000003,39.873000000000005,29.423000000000002 +2020-08-02 22:15:00,68.04,28.831,39.873000000000005,29.423000000000002 +2020-08-02 22:30:00,65.78,27.840999999999998,39.873000000000005,29.423000000000002 +2020-08-02 22:45:00,65.72,24.656999999999996,39.873000000000005,29.423000000000002 +2020-08-02 23:00:00,60.72,22.861,35.510999999999996,29.423000000000002 +2020-08-02 23:15:00,63.05,21.92,35.510999999999996,29.423000000000002 +2020-08-02 23:30:00,62.6,21.354,35.510999999999996,29.423000000000002 +2020-08-02 23:45:00,62.86,20.894000000000002,35.510999999999996,29.423000000000002 +2020-08-03 00:00:00,64.33,19.451,33.475,29.535 +2020-08-03 00:15:00,67.22,19.498,33.475,29.535 +2020-08-03 00:30:00,66.41,17.651,33.475,29.535 +2020-08-03 00:45:00,63.17,16.98,33.475,29.535 +2020-08-03 01:00:00,63.68,17.434,33.111,29.535 +2020-08-03 01:15:00,60.61,16.54,33.111,29.535 +2020-08-03 01:30:00,60.33,15.436,33.111,29.535 +2020-08-03 01:45:00,61.13,15.931,33.111,29.535 +2020-08-03 02:00:00,59.92,16.308,32.358000000000004,29.535 +2020-08-03 02:15:00,61.4,14.12,32.358000000000004,29.535 +2020-08-03 02:30:00,61.07,16.077,32.358000000000004,29.535 +2020-08-03 02:45:00,61.46,16.435,32.358000000000004,29.535 +2020-08-03 03:00:00,60.32,17.656,30.779,29.535 +2020-08-03 03:15:00,63.48,16.432000000000002,30.779,29.535 +2020-08-03 03:30:00,63.96,16.000999999999998,30.779,29.535 +2020-08-03 03:45:00,66.5,16.54,30.779,29.535 +2020-08-03 04:00:00,74.97,21.386999999999997,31.416,29.535 +2020-08-03 04:15:00,76.04,26.27,31.416,29.535 +2020-08-03 04:30:00,74.98,23.919,31.416,29.535 +2020-08-03 04:45:00,83.07,23.5,31.416,29.535 +2020-08-03 05:00:00,90.67,32.928000000000004,37.221,29.535 +2020-08-03 05:15:00,89.8,37.603,37.221,29.535 +2020-08-03 05:30:00,91.37,33.244,37.221,29.535 +2020-08-03 05:45:00,93.43,31.784000000000002,37.221,29.535 +2020-08-03 06:00:00,105.8,30.877,51.891000000000005,29.535 +2020-08-03 06:15:00,107.93,30.43,51.891000000000005,29.535 +2020-08-03 06:30:00,104.29,30.522,51.891000000000005,29.535 +2020-08-03 06:45:00,105.53,33.891,51.891000000000005,29.535 +2020-08-03 07:00:00,111.0,33.523,62.282,29.535 +2020-08-03 07:15:00,108.3,34.719,62.282,29.535 +2020-08-03 07:30:00,108.02,32.942,62.282,29.535 +2020-08-03 07:45:00,103.2,34.629,62.282,29.535 +2020-08-03 08:00:00,104.16,30.793000000000003,54.102,29.535 +2020-08-03 08:15:00,105.8,33.993,54.102,29.535 +2020-08-03 08:30:00,107.65,34.896,54.102,29.535 +2020-08-03 08:45:00,105.3,38.001999999999995,54.102,29.535 +2020-08-03 09:00:00,103.61,33.321999999999996,50.917,29.535 +2020-08-03 09:15:00,107.08,33.051,50.917,29.535 +2020-08-03 09:30:00,106.27,35.66,50.917,29.535 +2020-08-03 09:45:00,106.17,37.192,50.917,29.535 +2020-08-03 10:00:00,102.23,36.264,49.718999999999994,29.535 +2020-08-03 10:15:00,106.93,37.5,49.718999999999994,29.535 +2020-08-03 10:30:00,107.96,37.342,49.718999999999994,29.535 +2020-08-03 10:45:00,107.77,37.3,49.718999999999994,29.535 +2020-08-03 11:00:00,100.64,35.437,49.833999999999996,29.535 +2020-08-03 11:15:00,97.07,36.126,49.833999999999996,29.535 +2020-08-03 11:30:00,95.48,38.11,49.833999999999996,29.535 +2020-08-03 11:45:00,95.33,39.504,49.833999999999996,29.535 +2020-08-03 12:00:00,98.79,35.001,47.832,29.535 +2020-08-03 12:15:00,96.36,33.953,47.832,29.535 +2020-08-03 12:30:00,97.53,32.065,47.832,29.535 +2020-08-03 12:45:00,95.9,32.147,47.832,29.535 +2020-08-03 13:00:00,100.77,32.144,48.03,29.535 +2020-08-03 13:15:00,99.22,31.401,48.03,29.535 +2020-08-03 13:30:00,102.37,29.932,48.03,29.535 +2020-08-03 13:45:00,97.29,30.98,48.03,29.535 +2020-08-03 14:00:00,94.86,31.984,48.157,29.535 +2020-08-03 14:15:00,93.05,31.354,48.157,29.535 +2020-08-03 14:30:00,92.91,30.191999999999997,48.157,29.535 +2020-08-03 14:45:00,92.65,31.615,48.157,29.535 +2020-08-03 15:00:00,92.04,33.464,48.897,29.535 +2020-08-03 15:15:00,91.52,30.410999999999998,48.897,29.535 +2020-08-03 15:30:00,90.89,29.355999999999998,48.897,29.535 +2020-08-03 15:45:00,92.82,27.436999999999998,48.897,29.535 +2020-08-03 16:00:00,94.14,30.561,51.446000000000005,29.535 +2020-08-03 16:15:00,94.64,30.415,51.446000000000005,29.535 +2020-08-03 16:30:00,96.56,30.275,51.446000000000005,29.535 +2020-08-03 16:45:00,98.01,27.131999999999998,51.446000000000005,29.535 +2020-08-03 17:00:00,98.65,30.291,57.507,29.535 +2020-08-03 17:15:00,100.17,30.348000000000003,57.507,29.535 +2020-08-03 17:30:00,101.49,29.936999999999998,57.507,29.535 +2020-08-03 17:45:00,102.04,29.642,57.507,29.535 +2020-08-03 18:00:00,103.4,33.454,57.896,29.535 +2020-08-03 18:15:00,101.83,31.758000000000003,57.896,29.535 +2020-08-03 18:30:00,102.94,30.328000000000003,57.896,29.535 +2020-08-03 18:45:00,102.54,33.139,57.896,29.535 +2020-08-03 19:00:00,99.56,35.525,57.891999999999996,29.535 +2020-08-03 19:15:00,96.15,34.346,57.891999999999996,29.535 +2020-08-03 19:30:00,94.72,33.41,57.891999999999996,29.535 +2020-08-03 19:45:00,96.09,32.209,57.891999999999996,29.535 +2020-08-03 20:00:00,96.55,29.432,64.57300000000001,29.535 +2020-08-03 20:15:00,103.85,30.159000000000002,64.57300000000001,29.535 +2020-08-03 20:30:00,102.7,30.487,64.57300000000001,29.535 +2020-08-03 20:45:00,96.84,30.337,64.57300000000001,29.535 +2020-08-03 21:00:00,90.83,28.291999999999998,59.431999999999995,29.535 +2020-08-03 21:15:00,90.21,30.875999999999998,59.431999999999995,29.535 +2020-08-03 21:30:00,85.03,31.019000000000002,59.431999999999995,29.535 +2020-08-03 21:45:00,84.99,31.394000000000002,59.431999999999995,29.535 +2020-08-03 22:00:00,79.53,28.799,51.519,29.535 +2020-08-03 22:15:00,80.49,31.503,51.519,29.535 +2020-08-03 22:30:00,79.92,26.988000000000003,51.519,29.535 +2020-08-03 22:45:00,78.23,24.1,51.519,29.535 +2020-08-03 23:00:00,73.74,22.268,44.501000000000005,29.535 +2020-08-03 23:15:00,72.71,19.594,44.501000000000005,29.535 +2020-08-03 23:30:00,74.86,18.83,44.501000000000005,29.535 +2020-08-03 23:45:00,80.0,17.692,44.501000000000005,29.535 +2020-08-04 00:00:00,77.12,17.405,44.522,29.535 +2020-08-04 00:15:00,74.58,18.18,44.522,29.535 +2020-08-04 00:30:00,69.85,17.092,44.522,29.535 +2020-08-04 00:45:00,70.87,17.262999999999998,44.522,29.535 +2020-08-04 01:00:00,70.62,17.199,41.441,29.535 +2020-08-04 01:15:00,70.7,16.449,41.441,29.535 +2020-08-04 01:30:00,73.19,15.217,41.441,29.535 +2020-08-04 01:45:00,78.29,15.190999999999999,41.441,29.535 +2020-08-04 02:00:00,77.5,15.107999999999999,40.203,29.535 +2020-08-04 02:15:00,71.54,14.061,40.203,29.535 +2020-08-04 02:30:00,71.46,15.665999999999999,40.203,29.535 +2020-08-04 02:45:00,76.65,16.338,40.203,29.535 +2020-08-04 03:00:00,78.03,17.101,39.536,29.535 +2020-08-04 03:15:00,78.15,16.855,39.536,29.535 +2020-08-04 03:30:00,71.82,16.365,39.536,29.535 +2020-08-04 03:45:00,79.75,15.821,39.536,29.535 +2020-08-04 04:00:00,84.05,19.462,40.759,29.535 +2020-08-04 04:15:00,86.03,24.38,40.759,29.535 +2020-08-04 04:30:00,85.13,21.936,40.759,29.535 +2020-08-04 04:45:00,89.94,21.901999999999997,40.759,29.535 +2020-08-04 05:00:00,95.49,32.521,43.623999999999995,29.535 +2020-08-04 05:15:00,96.01,37.814,43.623999999999995,29.535 +2020-08-04 05:30:00,98.12,33.751,43.623999999999995,29.535 +2020-08-04 05:45:00,99.46,31.581,43.623999999999995,29.535 +2020-08-04 06:00:00,100.86,31.744,52.684,29.535 +2020-08-04 06:15:00,104.1,31.389,52.684,29.535 +2020-08-04 06:30:00,107.85,31.221999999999998,52.684,29.535 +2020-08-04 06:45:00,112.25,33.775999999999996,52.684,29.535 +2020-08-04 07:00:00,112.11,33.569,62.676,29.535 +2020-08-04 07:15:00,113.72,34.546,62.676,29.535 +2020-08-04 07:30:00,109.77,32.847,62.676,29.535 +2020-08-04 07:45:00,107.83,33.577,62.676,29.535 +2020-08-04 08:00:00,112.52,29.664,56.161,29.535 +2020-08-04 08:15:00,112.46,32.497,56.161,29.535 +2020-08-04 08:30:00,112.9,33.57,56.161,29.535 +2020-08-04 08:45:00,111.06,35.781,56.161,29.535 +2020-08-04 09:00:00,115.35,31.52,52.132,29.535 +2020-08-04 09:15:00,114.68,30.95,52.132,29.535 +2020-08-04 09:30:00,112.07,34.161,52.132,29.535 +2020-08-04 09:45:00,109.95,37.027,52.132,29.535 +2020-08-04 10:00:00,113.26,34.8,51.032,29.535 +2020-08-04 10:15:00,111.01,35.992,51.032,29.535 +2020-08-04 10:30:00,108.81,35.836,51.032,29.535 +2020-08-04 10:45:00,112.99,36.762,51.032,29.535 +2020-08-04 11:00:00,106.02,34.756,51.085,29.535 +2020-08-04 11:15:00,107.07,35.876999999999995,51.085,29.535 +2020-08-04 11:30:00,108.91,36.829,51.085,29.535 +2020-08-04 11:45:00,110.11,37.693000000000005,51.085,29.535 +2020-08-04 12:00:00,105.38,33.145,49.049,29.535 +2020-08-04 12:15:00,101.69,32.468,49.049,29.535 +2020-08-04 12:30:00,100.66,31.366999999999997,49.049,29.535 +2020-08-04 12:45:00,99.77,32.162,49.049,29.535 +2020-08-04 13:00:00,98.37,31.823,49.722,29.535 +2020-08-04 13:15:00,98.25,32.902,49.722,29.535 +2020-08-04 13:30:00,97.22,31.279,49.722,29.535 +2020-08-04 13:45:00,97.65,31.354,49.722,29.535 +2020-08-04 14:00:00,98.37,32.76,49.565,29.535 +2020-08-04 14:15:00,100.11,31.919,49.565,29.535 +2020-08-04 14:30:00,100.18,31.025,49.565,29.535 +2020-08-04 14:45:00,99.55,31.674,49.565,29.535 +2020-08-04 15:00:00,100.99,33.382,51.108999999999995,29.535 +2020-08-04 15:15:00,98.14,31.217,51.108999999999995,29.535 +2020-08-04 15:30:00,97.4,29.936999999999998,51.108999999999995,29.535 +2020-08-04 15:45:00,98.43,28.403000000000002,51.108999999999995,29.535 +2020-08-04 16:00:00,102.05,30.775,52.725,29.535 +2020-08-04 16:15:00,104.62,30.676,52.725,29.535 +2020-08-04 16:30:00,102.53,30.116999999999997,52.725,29.535 +2020-08-04 16:45:00,103.42,27.68,52.725,29.535 +2020-08-04 17:00:00,104.82,30.979,58.031000000000006,29.535 +2020-08-04 17:15:00,106.88,31.469,58.031000000000006,29.535 +2020-08-04 17:30:00,106.52,30.576,58.031000000000006,29.535 +2020-08-04 17:45:00,106.89,29.989,58.031000000000006,29.535 +2020-08-04 18:00:00,107.37,32.888000000000005,58.338,29.535 +2020-08-04 18:15:00,105.89,32.689,58.338,29.535 +2020-08-04 18:30:00,105.26,31.037,58.338,29.535 +2020-08-04 18:45:00,104.69,33.591,58.338,29.535 +2020-08-04 19:00:00,102.74,34.849000000000004,58.464,29.535 +2020-08-04 19:15:00,99.6,33.861,58.464,29.535 +2020-08-04 19:30:00,98.69,32.769,58.464,29.535 +2020-08-04 19:45:00,99.54,31.89,58.464,29.535 +2020-08-04 20:00:00,98.28,29.48,63.708,29.535 +2020-08-04 20:15:00,104.76,28.862,63.708,29.535 +2020-08-04 20:30:00,104.72,29.14,63.708,29.535 +2020-08-04 20:45:00,102.08,29.421999999999997,63.708,29.535 +2020-08-04 21:00:00,93.21,28.273000000000003,57.06399999999999,29.535 +2020-08-04 21:15:00,91.37,29.372,57.06399999999999,29.535 +2020-08-04 21:30:00,88.94,29.728,57.06399999999999,29.535 +2020-08-04 21:45:00,87.22,30.253,57.06399999999999,29.535 +2020-08-04 22:00:00,81.39,27.693,52.831,29.535 +2020-08-04 22:15:00,90.48,30.084,52.831,29.535 +2020-08-04 22:30:00,86.19,25.816999999999997,52.831,29.535 +2020-08-04 22:45:00,84.07,22.903000000000002,52.831,29.535 +2020-08-04 23:00:00,76.58,20.366,44.717,29.535 +2020-08-04 23:15:00,82.38,19.252,44.717,29.535 +2020-08-04 23:30:00,81.93,18.522000000000002,44.717,29.535 +2020-08-04 23:45:00,82.08,17.589000000000002,44.717,29.535 +2020-08-05 00:00:00,74.43,17.499000000000002,41.263000000000005,29.535 +2020-08-05 00:15:00,72.81,18.273,41.263000000000005,29.535 +2020-08-05 00:30:00,73.78,17.189,41.263000000000005,29.535 +2020-08-05 00:45:00,79.92,17.368,41.263000000000005,29.535 +2020-08-05 01:00:00,79.4,17.293,38.448,29.535 +2020-08-05 01:15:00,79.23,16.547,38.448,29.535 +2020-08-05 01:30:00,73.16,15.324000000000002,38.448,29.535 +2020-08-05 01:45:00,74.56,15.296,38.448,29.535 +2020-08-05 02:00:00,71.2,15.217,36.471,29.535 +2020-08-05 02:15:00,72.53,14.187999999999999,36.471,29.535 +2020-08-05 02:30:00,76.7,15.772,36.471,29.535 +2020-08-05 02:45:00,78.9,16.442999999999998,36.471,29.535 +2020-08-05 03:00:00,76.32,17.195,36.042,29.535 +2020-08-05 03:15:00,71.19,16.965,36.042,29.535 +2020-08-05 03:30:00,73.06,16.482,36.042,29.535 +2020-08-05 03:45:00,74.45,15.94,36.042,29.535 +2020-08-05 04:00:00,76.22,19.583,36.705,29.535 +2020-08-05 04:15:00,79.11,24.499000000000002,36.705,29.535 +2020-08-05 04:30:00,80.91,22.057,36.705,29.535 +2020-08-05 04:45:00,83.59,22.023000000000003,36.705,29.535 +2020-08-05 05:00:00,89.3,32.665,39.716,29.535 +2020-08-05 05:15:00,93.87,37.975,39.716,29.535 +2020-08-05 05:30:00,98.13,33.93,39.716,29.535 +2020-08-05 05:45:00,102.24,31.743000000000002,39.716,29.535 +2020-08-05 06:00:00,110.74,31.885,52.756,29.535 +2020-08-05 06:15:00,113.42,31.543000000000003,52.756,29.535 +2020-08-05 06:30:00,114.26,31.381999999999998,52.756,29.535 +2020-08-05 06:45:00,111.89,33.946999999999996,52.756,29.535 +2020-08-05 07:00:00,112.05,33.738,65.977,29.535 +2020-08-05 07:15:00,111.49,34.732,65.977,29.535 +2020-08-05 07:30:00,110.79,33.048,65.977,29.535 +2020-08-05 07:45:00,115.87,33.79,65.977,29.535 +2020-08-05 08:00:00,116.83,29.88,57.927,29.535 +2020-08-05 08:15:00,115.09,32.696999999999996,57.927,29.535 +2020-08-05 08:30:00,115.44,33.76,57.927,29.535 +2020-08-05 08:45:00,116.68,35.958,57.927,29.535 +2020-08-05 09:00:00,119.16,31.701,54.86,29.535 +2020-08-05 09:15:00,115.56,31.124000000000002,54.86,29.535 +2020-08-05 09:30:00,112.66,34.321999999999996,54.86,29.535 +2020-08-05 09:45:00,127.72,37.173,54.86,29.535 +2020-08-05 10:00:00,125.46,34.949,52.818000000000005,29.535 +2020-08-05 10:15:00,122.4,36.124,52.818000000000005,29.535 +2020-08-05 10:30:00,115.0,35.961999999999996,52.818000000000005,29.535 +2020-08-05 10:45:00,114.33,36.883,52.818000000000005,29.535 +2020-08-05 11:00:00,111.89,34.884,52.937,29.535 +2020-08-05 11:15:00,111.04,36.0,52.937,29.535 +2020-08-05 11:30:00,107.75,36.945,52.937,29.535 +2020-08-05 11:45:00,105.24,37.798,52.937,29.535 +2020-08-05 12:00:00,104.45,33.249,50.826,29.535 +2020-08-05 12:15:00,103.52,32.566,50.826,29.535 +2020-08-05 12:30:00,103.67,31.47,50.826,29.535 +2020-08-05 12:45:00,102.21,32.255,50.826,29.535 +2020-08-05 13:00:00,100.82,31.898000000000003,50.556000000000004,29.535 +2020-08-05 13:15:00,100.96,32.968,50.556000000000004,29.535 +2020-08-05 13:30:00,105.71,31.343000000000004,50.556000000000004,29.535 +2020-08-05 13:45:00,102.53,31.427,50.556000000000004,29.535 +2020-08-05 14:00:00,104.32,32.821,51.188,29.535 +2020-08-05 14:15:00,112.39,31.984,51.188,29.535 +2020-08-05 14:30:00,116.08,31.096999999999998,51.188,29.535 +2020-08-05 14:45:00,116.4,31.75,51.188,29.535 +2020-08-05 15:00:00,114.44,33.433,52.976000000000006,29.535 +2020-08-05 15:15:00,114.65,31.269000000000002,52.976000000000006,29.535 +2020-08-05 15:30:00,115.6,29.997,52.976000000000006,29.535 +2020-08-05 15:45:00,115.27,28.464000000000002,52.976000000000006,29.535 +2020-08-05 16:00:00,113.47,30.823,55.463,29.535 +2020-08-05 16:15:00,110.57,30.73,55.463,29.535 +2020-08-05 16:30:00,109.52,30.18,55.463,29.535 +2020-08-05 16:45:00,111.03,27.772,55.463,29.535 +2020-08-05 17:00:00,109.65,31.051,59.435,29.535 +2020-08-05 17:15:00,106.85,31.566,59.435,29.535 +2020-08-05 17:30:00,106.49,30.684,59.435,29.535 +2020-08-05 17:45:00,109.02,30.124000000000002,59.435,29.535 +2020-08-05 18:00:00,109.21,33.019,61.387,29.535 +2020-08-05 18:15:00,107.26,32.824,61.387,29.535 +2020-08-05 18:30:00,107.93,31.18,61.387,29.535 +2020-08-05 18:45:00,105.44,33.734,61.387,29.535 +2020-08-05 19:00:00,102.48,34.996,63.323,29.535 +2020-08-05 19:15:00,99.33,34.006,63.323,29.535 +2020-08-05 19:30:00,98.06,32.913000000000004,63.323,29.535 +2020-08-05 19:45:00,97.58,32.037,63.323,29.535 +2020-08-05 20:00:00,98.82,29.625999999999998,69.083,29.535 +2020-08-05 20:15:00,99.21,29.009,69.083,29.535 +2020-08-05 20:30:00,100.79,29.276,69.083,29.535 +2020-08-05 20:45:00,97.25,29.539,69.083,29.535 +2020-08-05 21:00:00,94.59,28.393,59.957,29.535 +2020-08-05 21:15:00,93.52,29.486,59.957,29.535 +2020-08-05 21:30:00,89.59,29.833000000000002,59.957,29.535 +2020-08-05 21:45:00,86.86,30.335,59.957,29.535 +2020-08-05 22:00:00,81.98,27.763,53.821000000000005,29.535 +2020-08-05 22:15:00,82.56,30.144000000000002,53.821000000000005,29.535 +2020-08-05 22:30:00,80.52,25.849,53.821000000000005,29.535 +2020-08-05 22:45:00,83.39,22.934,53.821000000000005,29.535 +2020-08-05 23:00:00,76.4,20.426,45.458,29.535 +2020-08-05 23:15:00,76.83,19.312,45.458,29.535 +2020-08-05 23:30:00,76.33,18.596,45.458,29.535 +2020-08-05 23:45:00,75.85,17.664,45.458,29.535 +2020-08-06 00:00:00,71.94,17.595,40.36,29.535 +2020-08-06 00:15:00,72.79,18.368,40.36,29.535 +2020-08-06 00:30:00,71.6,17.289,40.36,29.535 +2020-08-06 00:45:00,72.0,17.474,40.36,29.535 +2020-08-06 01:00:00,70.14,17.387999999999998,38.552,29.535 +2020-08-06 01:15:00,71.15,16.648,38.552,29.535 +2020-08-06 01:30:00,70.1,15.435,38.552,29.535 +2020-08-06 01:45:00,69.93,15.404000000000002,38.552,29.535 +2020-08-06 02:00:00,76.01,15.328,36.895,29.535 +2020-08-06 02:15:00,77.91,14.318,36.895,29.535 +2020-08-06 02:30:00,75.22,15.882,36.895,29.535 +2020-08-06 02:45:00,72.63,16.552,36.895,29.535 +2020-08-06 03:00:00,72.82,17.294,36.565,29.535 +2020-08-06 03:15:00,72.3,17.077,36.565,29.535 +2020-08-06 03:30:00,74.58,16.601,36.565,29.535 +2020-08-06 03:45:00,75.09,16.062,36.565,29.535 +2020-08-06 04:00:00,79.6,19.707,37.263000000000005,29.535 +2020-08-06 04:15:00,86.77,24.622,37.263000000000005,29.535 +2020-08-06 04:30:00,88.17,22.183000000000003,37.263000000000005,29.535 +2020-08-06 04:45:00,90.68,22.151,37.263000000000005,29.535 +2020-08-06 05:00:00,90.92,32.816,40.412,29.535 +2020-08-06 05:15:00,94.47,38.146,40.412,29.535 +2020-08-06 05:30:00,102.91,34.119,40.412,29.535 +2020-08-06 05:45:00,107.32,31.916,40.412,29.535 +2020-08-06 06:00:00,110.3,32.035,49.825,29.535 +2020-08-06 06:15:00,109.16,31.708000000000002,49.825,29.535 +2020-08-06 06:30:00,113.88,31.549,49.825,29.535 +2020-08-06 06:45:00,115.25,34.126,49.825,29.535 +2020-08-06 07:00:00,118.34,33.914,61.082,29.535 +2020-08-06 07:15:00,113.87,34.925,61.082,29.535 +2020-08-06 07:30:00,116.05,33.259,61.082,29.535 +2020-08-06 07:45:00,116.6,34.01,61.082,29.535 +2020-08-06 08:00:00,115.41,30.105,53.961999999999996,29.535 +2020-08-06 08:15:00,108.89,32.903,53.961999999999996,29.535 +2020-08-06 08:30:00,114.46,33.955,53.961999999999996,29.535 +2020-08-06 08:45:00,116.44,36.141999999999996,53.961999999999996,29.535 +2020-08-06 09:00:00,117.7,31.888,50.06100000000001,29.535 +2020-08-06 09:15:00,113.69,31.304000000000002,50.06100000000001,29.535 +2020-08-06 09:30:00,109.27,34.489000000000004,50.06100000000001,29.535 +2020-08-06 09:45:00,108.39,37.323,50.06100000000001,29.535 +2020-08-06 10:00:00,109.59,35.102,47.68,29.535 +2020-08-06 10:15:00,109.22,36.260999999999996,47.68,29.535 +2020-08-06 10:30:00,115.31,36.093,47.68,29.535 +2020-08-06 10:45:00,123.63,37.009,47.68,29.535 +2020-08-06 11:00:00,120.81,35.016999999999996,45.93899999999999,29.535 +2020-08-06 11:15:00,120.53,36.128,45.93899999999999,29.535 +2020-08-06 11:30:00,115.4,37.066,45.93899999999999,29.535 +2020-08-06 11:45:00,114.1,37.907,45.93899999999999,29.535 +2020-08-06 12:00:00,108.48,33.357,43.648999999999994,29.535 +2020-08-06 12:15:00,115.79,32.666,43.648999999999994,29.535 +2020-08-06 12:30:00,114.88,31.578000000000003,43.648999999999994,29.535 +2020-08-06 12:45:00,109.1,32.354,43.648999999999994,29.535 +2020-08-06 13:00:00,105.16,31.976999999999997,42.801,29.535 +2020-08-06 13:15:00,109.12,33.035,42.801,29.535 +2020-08-06 13:30:00,109.71,31.41,42.801,29.535 +2020-08-06 13:45:00,108.45,31.504,42.801,29.535 +2020-08-06 14:00:00,105.05,32.884,43.24,29.535 +2020-08-06 14:15:00,106.1,32.052,43.24,29.535 +2020-08-06 14:30:00,113.49,31.171,43.24,29.535 +2020-08-06 14:45:00,112.04,31.83,43.24,29.535 +2020-08-06 15:00:00,106.97,33.484,45.04600000000001,29.535 +2020-08-06 15:15:00,105.8,31.323,45.04600000000001,29.535 +2020-08-06 15:30:00,104.44,30.059,45.04600000000001,29.535 +2020-08-06 15:45:00,107.22,28.526999999999997,45.04600000000001,29.535 +2020-08-06 16:00:00,109.97,30.872,46.568000000000005,29.535 +2020-08-06 16:15:00,108.43,30.785999999999998,46.568000000000005,29.535 +2020-08-06 16:30:00,109.72,30.246,46.568000000000005,29.535 +2020-08-06 16:45:00,114.1,27.866999999999997,46.568000000000005,29.535 +2020-08-06 17:00:00,114.18,31.127,50.618,29.535 +2020-08-06 17:15:00,107.45,31.666,50.618,29.535 +2020-08-06 17:30:00,110.68,30.795,50.618,29.535 +2020-08-06 17:45:00,113.82,30.261,50.618,29.535 +2020-08-06 18:00:00,115.22,33.153,52.806999999999995,29.535 +2020-08-06 18:15:00,116.03,32.963,52.806999999999995,29.535 +2020-08-06 18:30:00,115.79,31.326999999999998,52.806999999999995,29.535 +2020-08-06 18:45:00,111.98,33.882,52.806999999999995,29.535 +2020-08-06 19:00:00,104.96,35.147,53.464,29.535 +2020-08-06 19:15:00,99.09,34.155,53.464,29.535 +2020-08-06 19:30:00,97.96,33.061,53.464,29.535 +2020-08-06 19:45:00,99.34,32.188,53.464,29.535 +2020-08-06 20:00:00,102.66,29.776999999999997,56.753,29.535 +2020-08-06 20:15:00,99.01,29.160999999999998,56.753,29.535 +2020-08-06 20:30:00,98.36,29.416999999999998,56.753,29.535 +2020-08-06 20:45:00,96.53,29.66,56.753,29.535 +2020-08-06 21:00:00,92.34,28.517,52.506,29.535 +2020-08-06 21:15:00,91.64,29.603,52.506,29.535 +2020-08-06 21:30:00,87.55,29.941999999999997,52.506,29.535 +2020-08-06 21:45:00,86.87,30.42,52.506,29.535 +2020-08-06 22:00:00,82.17,27.836,48.163000000000004,29.535 +2020-08-06 22:15:00,83.32,30.206999999999997,48.163000000000004,29.535 +2020-08-06 22:30:00,79.81,25.884,48.163000000000004,29.535 +2020-08-06 22:45:00,79.96,22.967,48.163000000000004,29.535 +2020-08-06 23:00:00,73.69,20.49,42.379,29.535 +2020-08-06 23:15:00,75.01,19.374000000000002,42.379,29.535 +2020-08-06 23:30:00,75.72,18.672,42.379,29.535 +2020-08-06 23:45:00,76.69,17.742,42.379,29.535 +2020-08-07 00:00:00,71.41,16.029,38.505,29.535 +2020-08-07 00:15:00,70.97,16.997,38.505,29.535 +2020-08-07 00:30:00,66.19,16.194000000000003,38.505,29.535 +2020-08-07 00:45:00,68.12,16.788,38.505,29.535 +2020-08-07 01:00:00,69.02,16.339000000000002,37.004,29.535 +2020-08-07 01:15:00,70.25,14.969000000000001,37.004,29.535 +2020-08-07 01:30:00,70.41,14.469000000000001,37.004,29.535 +2020-08-07 01:45:00,70.59,14.186,37.004,29.535 +2020-08-07 02:00:00,76.79,15.011,36.098,29.535 +2020-08-07 02:15:00,79.54,13.975999999999999,36.098,29.535 +2020-08-07 02:30:00,74.87,16.284000000000002,36.098,29.535 +2020-08-07 02:45:00,70.69,16.291,36.098,29.535 +2020-08-07 03:00:00,70.91,17.83,36.561,29.535 +2020-08-07 03:15:00,70.99,16.409000000000002,36.561,29.535 +2020-08-07 03:30:00,73.56,15.71,36.561,29.535 +2020-08-07 03:45:00,75.0,15.999,36.561,29.535 +2020-08-07 04:00:00,77.55,19.767,37.355,29.535 +2020-08-07 04:15:00,85.83,23.194000000000003,37.355,29.535 +2020-08-07 04:30:00,87.24,21.662,37.355,29.535 +2020-08-07 04:45:00,88.67,21.076,37.355,29.535 +2020-08-07 05:00:00,92.85,31.379,40.285,29.535 +2020-08-07 05:15:00,98.87,37.567,40.285,29.535 +2020-08-07 05:30:00,96.26,33.688,40.285,29.535 +2020-08-07 05:45:00,100.51,31.046999999999997,40.285,29.535 +2020-08-07 06:00:00,106.1,31.334,52.378,29.535 +2020-08-07 06:15:00,108.88,31.254,52.378,29.535 +2020-08-07 06:30:00,111.1,31.092,52.378,29.535 +2020-08-07 06:45:00,113.24,33.475,52.378,29.535 +2020-08-07 07:00:00,114.37,33.924,60.891999999999996,29.535 +2020-08-07 07:15:00,113.51,35.764,60.891999999999996,29.535 +2020-08-07 07:30:00,113.78,32.241,60.891999999999996,29.535 +2020-08-07 07:45:00,114.69,32.854,60.891999999999996,29.535 +2020-08-07 08:00:00,115.11,29.781,53.652,29.535 +2020-08-07 08:15:00,119.6,33.239000000000004,53.652,29.535 +2020-08-07 08:30:00,121.84,34.12,53.652,29.535 +2020-08-07 08:45:00,120.12,36.262,53.652,29.535 +2020-08-07 09:00:00,124.38,29.730999999999998,51.456,29.535 +2020-08-07 09:15:00,127.6,30.976999999999997,51.456,29.535 +2020-08-07 09:30:00,130.39,33.506,51.456,29.535 +2020-08-07 09:45:00,126.25,36.711,51.456,29.535 +2020-08-07 10:00:00,120.83,34.423,49.4,29.535 +2020-08-07 10:15:00,127.56,35.29,49.4,29.535 +2020-08-07 10:30:00,127.41,35.665,49.4,29.535 +2020-08-07 10:45:00,126.26,36.516999999999996,49.4,29.535 +2020-08-07 11:00:00,118.94,34.779,48.773,29.535 +2020-08-07 11:15:00,114.75,34.885,48.773,29.535 +2020-08-07 11:30:00,117.42,35.325,48.773,29.535 +2020-08-07 11:45:00,115.42,35.209,48.773,29.535 +2020-08-07 12:00:00,110.13,30.995,46.033,29.535 +2020-08-07 12:15:00,110.2,29.831999999999997,46.033,29.535 +2020-08-07 12:30:00,109.71,28.854,46.033,29.535 +2020-08-07 12:45:00,110.98,28.804000000000002,46.033,29.535 +2020-08-07 13:00:00,108.18,28.916999999999998,44.38399999999999,29.535 +2020-08-07 13:15:00,108.46,30.076999999999998,44.38399999999999,29.535 +2020-08-07 13:30:00,109.25,29.230999999999998,44.38399999999999,29.535 +2020-08-07 13:45:00,111.29,29.642,44.38399999999999,29.535 +2020-08-07 14:00:00,99.65,30.31,43.162,29.535 +2020-08-07 14:15:00,103.35,29.925,43.162,29.535 +2020-08-07 14:30:00,100.75,30.491,43.162,29.535 +2020-08-07 14:45:00,103.99,30.454,43.162,29.535 +2020-08-07 15:00:00,92.04,32.04,44.91,29.535 +2020-08-07 15:15:00,93.49,29.68,44.91,29.535 +2020-08-07 15:30:00,91.17,27.985,44.91,29.535 +2020-08-07 15:45:00,93.1,27.19,44.91,29.535 +2020-08-07 16:00:00,99.66,28.771,47.489,29.535 +2020-08-07 16:15:00,101.19,29.153000000000002,47.489,29.535 +2020-08-07 16:30:00,102.13,28.436999999999998,47.489,29.535 +2020-08-07 16:45:00,100.1,25.329,47.489,29.535 +2020-08-07 17:00:00,107.59,30.236,52.047,29.535 +2020-08-07 17:15:00,107.4,30.662,52.047,29.535 +2020-08-07 17:30:00,106.27,29.985,52.047,29.535 +2020-08-07 17:45:00,100.88,29.333000000000002,52.047,29.535 +2020-08-07 18:00:00,106.56,32.196,53.306000000000004,29.535 +2020-08-07 18:15:00,103.38,31.123,53.306000000000004,29.535 +2020-08-07 18:30:00,105.2,29.368000000000002,53.306000000000004,29.535 +2020-08-07 18:45:00,105.43,32.33,53.306000000000004,29.535 +2020-08-07 19:00:00,100.82,34.372,53.516000000000005,29.535 +2020-08-07 19:15:00,99.6,33.865,53.516000000000005,29.535 +2020-08-07 19:30:00,101.51,32.855,53.516000000000005,29.535 +2020-08-07 19:45:00,97.37,31.035,53.516000000000005,29.535 +2020-08-07 20:00:00,96.5,28.47,57.88,29.535 +2020-08-07 20:15:00,97.87,28.62,57.88,29.535 +2020-08-07 20:30:00,97.05,28.397,57.88,29.535 +2020-08-07 20:45:00,91.55,27.805,57.88,29.535 +2020-08-07 21:00:00,87.14,27.892,53.32,29.535 +2020-08-07 21:15:00,88.39,30.548000000000002,53.32,29.535 +2020-08-07 21:30:00,86.76,30.733,53.32,29.535 +2020-08-07 21:45:00,86.09,31.351,53.32,29.535 +2020-08-07 22:00:00,78.09,28.561,48.074,29.535 +2020-08-07 22:15:00,76.09,30.691,48.074,29.535 +2020-08-07 22:30:00,77.97,30.533,48.074,29.535 +2020-08-07 22:45:00,79.31,28.241999999999997,48.074,29.535 +2020-08-07 23:00:00,74.19,27.438000000000002,41.306999999999995,29.535 +2020-08-07 23:15:00,68.5,24.93,41.306999999999995,29.535 +2020-08-07 23:30:00,66.0,22.58,41.306999999999995,29.535 +2020-08-07 23:45:00,65.38,21.570999999999998,41.306999999999995,29.535 +2020-08-08 00:00:00,66.74,17.496,40.227,29.423000000000002 +2020-08-08 00:15:00,69.29,18.078,40.227,29.423000000000002 +2020-08-08 00:30:00,68.8,16.746,40.227,29.423000000000002 +2020-08-08 00:45:00,63.95,16.6,40.227,29.423000000000002 +2020-08-08 01:00:00,66.72,16.363,36.303000000000004,29.423000000000002 +2020-08-08 01:15:00,67.67,15.626,36.303000000000004,29.423000000000002 +2020-08-08 01:30:00,68.13,14.392999999999999,36.303000000000004,29.423000000000002 +2020-08-08 01:45:00,65.03,15.319,36.303000000000004,29.423000000000002 +2020-08-08 02:00:00,64.36,15.154000000000002,33.849000000000004,29.423000000000002 +2020-08-08 02:15:00,66.91,13.402000000000001,33.849000000000004,29.423000000000002 +2020-08-08 02:30:00,66.76,14.997,33.849000000000004,29.423000000000002 +2020-08-08 02:45:00,63.89,15.75,33.849000000000004,29.423000000000002 +2020-08-08 03:00:00,58.71,15.923,33.149,29.423000000000002 +2020-08-08 03:15:00,64.73,13.921,33.149,29.423000000000002 +2020-08-08 03:30:00,67.22,13.64,33.149,29.423000000000002 +2020-08-08 03:45:00,67.03,15.345,33.149,29.423000000000002 +2020-08-08 04:00:00,65.26,17.557000000000002,32.501,29.423000000000002 +2020-08-08 04:15:00,60.78,20.284000000000002,32.501,29.423000000000002 +2020-08-08 04:30:00,63.06,17.284000000000002,32.501,29.423000000000002 +2020-08-08 04:45:00,63.74,16.992,32.501,29.423000000000002 +2020-08-08 05:00:00,68.08,20.31,31.648000000000003,29.423000000000002 +2020-08-08 05:15:00,70.17,17.18,31.648000000000003,29.423000000000002 +2020-08-08 05:30:00,69.63,14.745999999999999,31.648000000000003,29.423000000000002 +2020-08-08 05:45:00,69.72,16.029,31.648000000000003,29.423000000000002 +2020-08-08 06:00:00,74.01,25.884,32.552,29.423000000000002 +2020-08-08 06:15:00,76.69,32.289,32.552,29.423000000000002 +2020-08-08 06:30:00,74.92,29.739,32.552,29.423000000000002 +2020-08-08 06:45:00,75.68,29.601,32.552,29.423000000000002 +2020-08-08 07:00:00,78.07,29.214000000000002,35.181999999999995,29.423000000000002 +2020-08-08 07:15:00,77.53,29.912,35.181999999999995,29.423000000000002 +2020-08-08 07:30:00,79.7,27.668000000000003,35.181999999999995,29.423000000000002 +2020-08-08 07:45:00,85.61,28.895,35.181999999999995,29.423000000000002 +2020-08-08 08:00:00,89.39,26.308000000000003,40.35,29.423000000000002 +2020-08-08 08:15:00,89.11,29.252,40.35,29.423000000000002 +2020-08-08 08:30:00,80.69,29.98,40.35,29.423000000000002 +2020-08-08 08:45:00,79.94,32.78,40.35,29.423000000000002 +2020-08-08 09:00:00,81.67,29.186,42.292,29.423000000000002 +2020-08-08 09:15:00,85.15,30.836,42.292,29.423000000000002 +2020-08-08 09:30:00,86.84,33.758,42.292,29.423000000000002 +2020-08-08 09:45:00,85.72,36.493,42.292,29.423000000000002 +2020-08-08 10:00:00,78.03,34.819,40.084,29.423000000000002 +2020-08-08 10:15:00,80.44,36.003,40.084,29.423000000000002 +2020-08-08 10:30:00,87.73,36.022,40.084,29.423000000000002 +2020-08-08 10:45:00,76.01,36.408,40.084,29.423000000000002 +2020-08-08 11:00:00,75.02,34.505,36.966,29.423000000000002 +2020-08-08 11:15:00,71.3,35.515,36.966,29.423000000000002 +2020-08-08 11:30:00,71.44,36.321,36.966,29.423000000000002 +2020-08-08 11:45:00,83.52,36.929,36.966,29.423000000000002 +2020-08-08 12:00:00,78.82,33.196,35.19,29.423000000000002 +2020-08-08 12:15:00,77.58,32.803000000000004,35.19,29.423000000000002 +2020-08-08 12:30:00,68.36,31.677,35.19,29.423000000000002 +2020-08-08 12:45:00,71.92,32.397,35.19,29.423000000000002 +2020-08-08 13:00:00,65.67,31.694000000000003,32.277,29.423000000000002 +2020-08-08 13:15:00,71.96,32.49,32.277,29.423000000000002 +2020-08-08 13:30:00,63.1,31.866,32.277,29.423000000000002 +2020-08-08 13:45:00,64.32,30.926,32.277,29.423000000000002 +2020-08-08 14:00:00,72.95,31.502,31.436999999999998,29.423000000000002 +2020-08-08 14:15:00,65.23,29.944000000000003,31.436999999999998,29.423000000000002 +2020-08-08 14:30:00,63.29,30.256,31.436999999999998,29.423000000000002 +2020-08-08 14:45:00,69.43,30.671999999999997,31.436999999999998,29.423000000000002 +2020-08-08 15:00:00,64.98,32.576,33.493,29.423000000000002 +2020-08-08 15:15:00,62.48,30.843000000000004,33.493,29.423000000000002 +2020-08-08 15:30:00,61.84,29.228,33.493,29.423000000000002 +2020-08-08 15:45:00,64.18,27.535999999999998,33.493,29.423000000000002 +2020-08-08 16:00:00,64.57,31.239,36.593,29.423000000000002 +2020-08-08 16:15:00,66.55,30.691,36.593,29.423000000000002 +2020-08-08 16:30:00,72.26,30.206999999999997,36.593,29.423000000000002 +2020-08-08 16:45:00,72.92,27.033,36.593,29.423000000000002 +2020-08-08 17:00:00,74.06,30.78,42.049,29.423000000000002 +2020-08-08 17:15:00,73.2,29.022,42.049,29.423000000000002 +2020-08-08 17:30:00,75.04,28.235,42.049,29.423000000000002 +2020-08-08 17:45:00,77.03,28.121,42.049,29.423000000000002 +2020-08-08 18:00:00,78.39,32.303000000000004,43.755,29.423000000000002 +2020-08-08 18:15:00,76.69,32.72,43.755,29.423000000000002 +2020-08-08 18:30:00,76.27,32.189,43.755,29.423000000000002 +2020-08-08 18:45:00,76.64,32.038000000000004,43.755,29.423000000000002 +2020-08-08 19:00:00,75.57,32.424,44.492,29.423000000000002 +2020-08-08 19:15:00,72.67,30.984,44.492,29.423000000000002 +2020-08-08 19:30:00,72.39,30.662,44.492,29.423000000000002 +2020-08-08 19:45:00,73.31,30.538,44.492,29.423000000000002 +2020-08-08 20:00:00,75.0,28.638,40.896,29.423000000000002 +2020-08-08 20:15:00,74.88,28.051,40.896,29.423000000000002 +2020-08-08 20:30:00,74.76,27.0,40.896,29.423000000000002 +2020-08-08 20:45:00,74.69,28.18,40.896,29.423000000000002 +2020-08-08 21:00:00,71.99,26.721999999999998,39.056,29.423000000000002 +2020-08-08 21:15:00,71.04,29.015,39.056,29.423000000000002 +2020-08-08 21:30:00,67.87,29.302,39.056,29.423000000000002 +2020-08-08 21:45:00,67.58,29.401,39.056,29.423000000000002 +2020-08-08 22:00:00,63.3,26.502,38.478,29.423000000000002 +2020-08-08 22:15:00,64.97,28.721,38.478,29.423000000000002 +2020-08-08 22:30:00,62.91,27.836,38.478,29.423000000000002 +2020-08-08 22:45:00,62.35,25.795,38.478,29.423000000000002 +2020-08-08 23:00:00,57.64,24.291,32.953,29.423000000000002 +2020-08-08 23:15:00,57.7,22.261999999999997,32.953,29.423000000000002 +2020-08-08 23:30:00,58.28,22.333000000000002,32.953,29.423000000000002 +2020-08-08 23:45:00,58.51,21.79,32.953,29.423000000000002 +2020-08-09 00:00:00,54.08,18.893,28.584,29.423000000000002 +2020-08-09 00:15:00,54.76,18.393,28.584,29.423000000000002 +2020-08-09 00:30:00,54.22,16.949,28.584,29.423000000000002 +2020-08-09 00:45:00,55.07,16.660999999999998,28.584,29.423000000000002 +2020-08-09 01:00:00,51.21,16.673,26.419,29.423000000000002 +2020-08-09 01:15:00,53.23,15.76,26.419,29.423000000000002 +2020-08-09 01:30:00,50.21,14.374,26.419,29.423000000000002 +2020-08-09 01:45:00,52.88,14.954,26.419,29.423000000000002 +2020-08-09 02:00:00,51.33,14.915,25.335,29.423000000000002 +2020-08-09 02:15:00,52.26,13.91,25.335,29.423000000000002 +2020-08-09 02:30:00,51.66,15.61,25.335,29.423000000000002 +2020-08-09 02:45:00,51.22,16.055999999999997,25.335,29.423000000000002 +2020-08-09 03:00:00,51.33,16.819000000000003,24.805,29.423000000000002 +2020-08-09 03:15:00,52.88,15.114,24.805,29.423000000000002 +2020-08-09 03:30:00,52.66,14.052,24.805,29.423000000000002 +2020-08-09 03:45:00,53.13,15.005999999999998,24.805,29.423000000000002 +2020-08-09 04:00:00,53.45,17.209,25.772,29.423000000000002 +2020-08-09 04:15:00,54.38,19.586,25.772,29.423000000000002 +2020-08-09 04:30:00,52.71,17.863,25.772,29.423000000000002 +2020-08-09 04:45:00,52.22,17.111,25.772,29.423000000000002 +2020-08-09 05:00:00,51.09,20.83,25.971999999999998,29.423000000000002 +2020-08-09 05:15:00,51.86,17.137999999999998,25.971999999999998,29.423000000000002 +2020-08-09 05:30:00,51.9,14.347999999999999,25.971999999999998,29.423000000000002 +2020-08-09 05:45:00,52.64,15.338,25.971999999999998,29.423000000000002 +2020-08-09 06:00:00,52.67,22.945,26.026,29.423000000000002 +2020-08-09 06:15:00,53.6,30.255,26.026,29.423000000000002 +2020-08-09 06:30:00,54.27,27.113000000000003,26.026,29.423000000000002 +2020-08-09 06:45:00,55.49,26.105999999999998,26.026,29.423000000000002 +2020-08-09 07:00:00,57.23,25.877,27.396,29.423000000000002 +2020-08-09 07:15:00,57.15,25.078000000000003,27.396,29.423000000000002 +2020-08-09 07:30:00,56.53,24.169,27.396,29.423000000000002 +2020-08-09 07:45:00,56.43,25.428,27.396,29.423000000000002 +2020-08-09 08:00:00,56.12,23.463,30.791999999999998,29.423000000000002 +2020-08-09 08:15:00,57.24,27.535,30.791999999999998,29.423000000000002 +2020-08-09 08:30:00,56.64,28.962,30.791999999999998,29.423000000000002 +2020-08-09 08:45:00,57.8,31.506999999999998,30.791999999999998,29.423000000000002 +2020-08-09 09:00:00,57.9,27.857,32.482,29.423000000000002 +2020-08-09 09:15:00,57.48,28.969,32.482,29.423000000000002 +2020-08-09 09:30:00,53.71,32.31,32.482,29.423000000000002 +2020-08-09 09:45:00,57.49,36.016999999999996,32.482,29.423000000000002 +2020-08-09 10:00:00,58.06,34.648,31.951,29.423000000000002 +2020-08-09 10:15:00,59.54,35.896,31.951,29.423000000000002 +2020-08-09 10:30:00,61.42,36.068000000000005,31.951,29.423000000000002 +2020-08-09 10:45:00,60.84,37.628,31.951,29.423000000000002 +2020-08-09 11:00:00,60.37,35.299,33.619,29.423000000000002 +2020-08-09 11:15:00,61.49,35.853,33.619,29.423000000000002 +2020-08-09 11:30:00,57.13,37.247,33.619,29.423000000000002 +2020-08-09 11:45:00,57.85,38.035,33.619,29.423000000000002 +2020-08-09 12:00:00,53.65,35.400999999999996,30.975,29.423000000000002 +2020-08-09 12:15:00,52.86,34.196999999999996,30.975,29.423000000000002 +2020-08-09 12:30:00,50.62,33.433,30.975,29.423000000000002 +2020-08-09 12:45:00,50.63,33.611999999999995,30.975,29.423000000000002 +2020-08-09 13:00:00,49.19,32.663000000000004,27.956999999999997,29.423000000000002 +2020-08-09 13:15:00,48.43,32.585,27.956999999999997,29.423000000000002 +2020-08-09 13:30:00,48.61,30.886999999999997,27.956999999999997,29.423000000000002 +2020-08-09 13:45:00,49.71,31.119,27.956999999999997,29.423000000000002 +2020-08-09 14:00:00,49.4,32.830999999999996,25.555999999999997,29.423000000000002 +2020-08-09 14:15:00,50.46,31.583000000000002,25.555999999999997,29.423000000000002 +2020-08-09 14:30:00,49.99,30.576,25.555999999999997,29.423000000000002 +2020-08-09 14:45:00,51.47,29.987,25.555999999999997,29.423000000000002 +2020-08-09 15:00:00,52.05,32.154,26.271,29.423000000000002 +2020-08-09 15:15:00,51.23,29.56,26.271,29.423000000000002 +2020-08-09 15:30:00,52.35,27.721999999999998,26.271,29.423000000000002 +2020-08-09 15:45:00,54.8,26.245,26.271,29.423000000000002 +2020-08-09 16:00:00,58.39,28.193,30.369,29.423000000000002 +2020-08-09 16:15:00,59.37,27.954,30.369,29.423000000000002 +2020-08-09 16:30:00,61.94,28.434,30.369,29.423000000000002 +2020-08-09 16:45:00,64.25,25.340999999999998,30.369,29.423000000000002 +2020-08-09 17:00:00,67.55,29.435,38.787,29.423000000000002 +2020-08-09 17:15:00,69.77,29.199,38.787,29.423000000000002 +2020-08-09 17:30:00,74.47,29.183000000000003,38.787,29.423000000000002 +2020-08-09 17:45:00,73.06,29.31,38.787,29.423000000000002 +2020-08-09 18:00:00,76.18,34.126999999999995,41.886,29.423000000000002 +2020-08-09 18:15:00,75.37,34.056,41.886,29.423000000000002 +2020-08-09 18:30:00,78.04,33.457,41.886,29.423000000000002 +2020-08-09 18:45:00,75.37,33.274,41.886,29.423000000000002 +2020-08-09 19:00:00,77.91,35.796,42.91,29.423000000000002 +2020-08-09 19:15:00,76.23,33.253,42.91,29.423000000000002 +2020-08-09 19:30:00,76.04,32.707,42.91,29.423000000000002 +2020-08-09 19:45:00,78.63,32.049,42.91,29.423000000000002 +2020-08-09 20:00:00,79.32,30.315,42.148999999999994,29.423000000000002 +2020-08-09 20:15:00,78.88,29.53,42.148999999999994,29.423000000000002 +2020-08-09 20:30:00,78.51,29.153000000000002,42.148999999999994,29.423000000000002 +2020-08-09 20:45:00,77.97,28.791,42.148999999999994,29.423000000000002 +2020-08-09 21:00:00,76.29,27.416,40.955999999999996,29.423000000000002 +2020-08-09 21:15:00,76.26,29.456999999999997,40.955999999999996,29.423000000000002 +2020-08-09 21:30:00,74.21,29.101,40.955999999999996,29.423000000000002 +2020-08-09 21:45:00,72.91,29.506,40.955999999999996,29.423000000000002 +2020-08-09 22:00:00,68.37,28.61,39.873000000000005,29.423000000000002 +2020-08-09 22:15:00,70.97,29.274,39.873000000000005,29.423000000000002 +2020-08-09 22:30:00,68.0,28.081,39.873000000000005,29.423000000000002 +2020-08-09 22:45:00,68.68,24.886999999999997,39.873000000000005,29.423000000000002 +2020-08-09 23:00:00,64.15,23.305999999999997,35.510999999999996,29.423000000000002 +2020-08-09 23:15:00,65.15,22.361,35.510999999999996,29.423000000000002 +2020-08-09 23:30:00,65.42,21.884,35.510999999999996,29.423000000000002 +2020-08-09 23:45:00,64.78,21.439,35.510999999999996,29.423000000000002 +2020-08-10 00:00:00,64.59,20.146,33.475,29.535 +2020-08-10 00:15:00,67.7,20.188,33.475,29.535 +2020-08-10 00:30:00,67.92,18.373,33.475,29.535 +2020-08-10 00:45:00,66.94,17.753,33.475,29.535 +2020-08-10 01:00:00,60.75,18.12,33.111,29.535 +2020-08-10 01:15:00,62.05,17.266,33.111,29.535 +2020-08-10 01:30:00,61.91,16.230999999999998,33.111,29.535 +2020-08-10 01:45:00,61.67,16.708,33.111,29.535 +2020-08-10 02:00:00,62.03,17.11,32.358000000000004,29.535 +2020-08-10 02:15:00,66.27,15.054,32.358000000000004,29.535 +2020-08-10 02:30:00,69.88,16.866,32.358000000000004,29.535 +2020-08-10 02:45:00,70.55,17.215,32.358000000000004,29.535 +2020-08-10 03:00:00,67.73,18.362000000000002,30.779,29.535 +2020-08-10 03:15:00,64.33,17.243,30.779,29.535 +2020-08-10 03:30:00,68.6,16.858,30.779,29.535 +2020-08-10 03:45:00,68.36,17.41,30.779,29.535 +2020-08-10 04:00:00,72.46,22.284000000000002,31.416,29.535 +2020-08-10 04:15:00,75.4,27.168000000000003,31.416,29.535 +2020-08-10 04:30:00,77.15,24.838,31.416,29.535 +2020-08-10 04:45:00,82.65,24.43,31.416,29.535 +2020-08-10 05:00:00,87.89,34.043,37.221,29.535 +2020-08-10 05:15:00,93.43,38.881,37.221,29.535 +2020-08-10 05:30:00,98.76,34.632,37.221,29.535 +2020-08-10 05:45:00,106.75,33.045,37.221,29.535 +2020-08-10 06:00:00,109.61,31.978,51.891000000000005,29.535 +2020-08-10 06:15:00,110.17,31.636,51.891000000000005,29.535 +2020-08-10 06:30:00,106.16,31.749000000000002,51.891000000000005,29.535 +2020-08-10 06:45:00,105.44,35.194,51.891000000000005,29.535 +2020-08-10 07:00:00,112.69,34.809,62.282,29.535 +2020-08-10 07:15:00,114.13,36.123000000000005,62.282,29.535 +2020-08-10 07:30:00,114.44,34.466,62.282,29.535 +2020-08-10 07:45:00,111.46,36.219,62.282,29.535 +2020-08-10 08:00:00,109.2,32.409,54.102,29.535 +2020-08-10 08:15:00,114.03,35.476,54.102,29.535 +2020-08-10 08:30:00,115.94,36.306999999999995,54.102,29.535 +2020-08-10 08:45:00,113.48,39.326,54.102,29.535 +2020-08-10 09:00:00,109.67,34.673,50.917,29.535 +2020-08-10 09:15:00,113.16,34.354,50.917,29.535 +2020-08-10 09:30:00,114.97,36.866,50.917,29.535 +2020-08-10 09:45:00,114.23,38.284,50.917,29.535 +2020-08-10 10:00:00,110.17,37.372,49.718999999999994,29.535 +2020-08-10 10:15:00,112.96,38.489000000000004,49.718999999999994,29.535 +2020-08-10 10:30:00,113.44,38.289,49.718999999999994,29.535 +2020-08-10 10:45:00,112.68,38.208,49.718999999999994,29.535 +2020-08-10 11:00:00,106.81,36.395,49.833999999999996,29.535 +2020-08-10 11:15:00,101.75,37.046,49.833999999999996,29.535 +2020-08-10 11:30:00,108.76,38.986,49.833999999999996,29.535 +2020-08-10 11:45:00,115.96,40.294000000000004,49.833999999999996,29.535 +2020-08-10 12:00:00,116.04,35.781,47.832,29.535 +2020-08-10 12:15:00,108.93,34.679,47.832,29.535 +2020-08-10 12:30:00,105.17,32.842,47.832,29.535 +2020-08-10 12:45:00,109.13,32.858000000000004,47.832,29.535 +2020-08-10 13:00:00,105.35,32.725,48.03,29.535 +2020-08-10 13:15:00,106.96,31.901999999999997,48.03,29.535 +2020-08-10 13:30:00,112.55,30.421999999999997,48.03,29.535 +2020-08-10 13:45:00,110.89,31.543000000000003,48.03,29.535 +2020-08-10 14:00:00,108.24,32.449,48.157,29.535 +2020-08-10 14:15:00,116.67,31.851,48.157,29.535 +2020-08-10 14:30:00,109.93,30.743000000000002,48.157,29.535 +2020-08-10 14:45:00,100.75,32.195,48.157,29.535 +2020-08-10 15:00:00,102.13,33.843,48.897,29.535 +2020-08-10 15:15:00,101.58,30.81,48.897,29.535 +2020-08-10 15:30:00,103.82,29.811999999999998,48.897,29.535 +2020-08-10 15:45:00,112.32,27.903000000000002,48.897,29.535 +2020-08-10 16:00:00,107.56,30.926,51.446000000000005,29.535 +2020-08-10 16:15:00,110.02,30.826,51.446000000000005,29.535 +2020-08-10 16:30:00,111.09,30.749000000000002,51.446000000000005,29.535 +2020-08-10 16:45:00,111.76,27.815,51.446000000000005,29.535 +2020-08-10 17:00:00,116.67,30.83,57.507,29.535 +2020-08-10 17:15:00,115.62,31.066999999999997,57.507,29.535 +2020-08-10 17:30:00,119.59,30.733,57.507,29.535 +2020-08-10 17:45:00,117.62,30.63,57.507,29.535 +2020-08-10 18:00:00,112.36,34.413000000000004,57.896,29.535 +2020-08-10 18:15:00,111.68,32.756,57.896,29.535 +2020-08-10 18:30:00,111.01,31.386,57.896,29.535 +2020-08-10 18:45:00,118.2,34.199,57.896,29.535 +2020-08-10 19:00:00,115.05,36.61,57.891999999999996,29.535 +2020-08-10 19:15:00,108.14,35.417,57.891999999999996,29.535 +2020-08-10 19:30:00,100.95,34.482,57.891999999999996,29.535 +2020-08-10 19:45:00,107.15,33.301,57.891999999999996,29.535 +2020-08-10 20:00:00,107.38,30.524,64.57300000000001,29.535 +2020-08-10 20:15:00,106.34,31.261,64.57300000000001,29.535 +2020-08-10 20:30:00,101.37,31.505,64.57300000000001,29.535 +2020-08-10 20:45:00,96.58,31.21,64.57300000000001,29.535 +2020-08-10 21:00:00,92.54,29.186,59.431999999999995,29.535 +2020-08-10 21:15:00,98.18,31.721999999999998,59.431999999999995,29.535 +2020-08-10 21:30:00,94.61,31.811,59.431999999999995,29.535 +2020-08-10 21:45:00,89.3,32.016,59.431999999999995,29.535 +2020-08-10 22:00:00,85.65,29.331999999999997,51.519,29.535 +2020-08-10 22:15:00,83.02,31.964000000000002,51.519,29.535 +2020-08-10 22:30:00,84.02,27.245,51.519,29.535 +2020-08-10 22:45:00,86.92,24.348000000000003,51.519,29.535 +2020-08-10 23:00:00,83.27,22.737,44.501000000000005,29.535 +2020-08-10 23:15:00,79.13,20.052,44.501000000000005,29.535 +2020-08-10 23:30:00,77.81,19.378,44.501000000000005,29.535 +2020-08-10 23:45:00,76.11,18.256,44.501000000000005,29.535 +2020-08-11 00:00:00,80.82,18.12,44.522,29.535 +2020-08-11 00:15:00,81.42,18.89,44.522,29.535 +2020-08-11 00:30:00,79.26,17.834,44.522,29.535 +2020-08-11 00:45:00,77.23,18.055999999999997,44.522,29.535 +2020-08-11 01:00:00,77.88,17.902,41.441,29.535 +2020-08-11 01:15:00,81.19,17.195,41.441,29.535 +2020-08-11 01:30:00,80.87,16.033,41.441,29.535 +2020-08-11 01:45:00,77.52,15.99,41.441,29.535 +2020-08-11 02:00:00,76.14,15.933,40.203,29.535 +2020-08-11 02:15:00,80.33,15.019,40.203,29.535 +2020-08-11 02:30:00,80.22,16.476,40.203,29.535 +2020-08-11 02:45:00,76.52,17.139,40.203,29.535 +2020-08-11 03:00:00,76.12,17.828,39.536,29.535 +2020-08-11 03:15:00,81.42,17.687,39.536,29.535 +2020-08-11 03:30:00,82.68,17.243,39.536,29.535 +2020-08-11 03:45:00,81.38,16.709,39.536,29.535 +2020-08-11 04:00:00,84.21,20.386,40.759,29.535 +2020-08-11 04:15:00,83.25,25.311,40.759,29.535 +2020-08-11 04:30:00,87.0,22.891,40.759,29.535 +2020-08-11 04:45:00,96.1,22.866999999999997,40.759,29.535 +2020-08-11 05:00:00,103.35,33.692,43.623999999999995,29.535 +2020-08-11 05:15:00,104.96,39.169000000000004,43.623999999999995,29.535 +2020-08-11 05:30:00,101.93,35.208,43.623999999999995,29.535 +2020-08-11 05:45:00,103.66,32.900999999999996,43.623999999999995,29.535 +2020-08-11 06:00:00,107.81,32.9,52.684,29.535 +2020-08-11 06:15:00,115.41,32.653,52.684,29.535 +2020-08-11 06:30:00,119.45,32.504,52.684,29.535 +2020-08-11 06:45:00,121.63,35.13,52.684,29.535 +2020-08-11 07:00:00,117.33,34.907,62.676,29.535 +2020-08-11 07:15:00,117.63,36.001,62.676,29.535 +2020-08-11 07:30:00,120.47,34.424,62.676,29.535 +2020-08-11 07:45:00,121.97,35.217,62.676,29.535 +2020-08-11 08:00:00,118.67,31.328000000000003,56.161,29.535 +2020-08-11 08:15:00,115.82,34.021,56.161,29.535 +2020-08-11 08:30:00,109.61,35.024,56.161,29.535 +2020-08-11 08:45:00,108.39,37.145,56.161,29.535 +2020-08-11 09:00:00,118.63,32.913000000000004,52.132,29.535 +2020-08-11 09:15:00,124.0,32.294000000000004,52.132,29.535 +2020-08-11 09:30:00,120.89,35.407,52.132,29.535 +2020-08-11 09:45:00,113.74,38.154,52.132,29.535 +2020-08-11 10:00:00,115.04,35.943000000000005,51.032,29.535 +2020-08-11 10:15:00,111.74,37.010999999999996,51.032,29.535 +2020-08-11 10:30:00,117.81,36.813,51.032,29.535 +2020-08-11 10:45:00,120.03,37.7,51.032,29.535 +2020-08-11 11:00:00,116.21,35.745,51.085,29.535 +2020-08-11 11:15:00,109.85,36.827,51.085,29.535 +2020-08-11 11:30:00,112.41,37.735,51.085,29.535 +2020-08-11 11:45:00,117.13,38.514,51.085,29.535 +2020-08-11 12:00:00,121.63,33.95,49.049,29.535 +2020-08-11 12:15:00,122.61,33.218,49.049,29.535 +2020-08-11 12:30:00,116.41,32.172,49.049,29.535 +2020-08-11 12:45:00,106.88,32.898,49.049,29.535 +2020-08-11 13:00:00,103.94,32.428000000000004,49.722,29.535 +2020-08-11 13:15:00,106.12,33.428000000000004,49.722,29.535 +2020-08-11 13:30:00,107.33,31.793000000000003,49.722,29.535 +2020-08-11 13:45:00,105.9,31.94,49.722,29.535 +2020-08-11 14:00:00,104.55,33.245,49.565,29.535 +2020-08-11 14:15:00,111.12,32.436,49.565,29.535 +2020-08-11 14:30:00,106.63,31.599,49.565,29.535 +2020-08-11 14:45:00,112.83,32.278,49.565,29.535 +2020-08-11 15:00:00,106.44,33.777,51.108999999999995,29.535 +2020-08-11 15:15:00,107.53,31.633000000000003,51.108999999999995,29.535 +2020-08-11 15:30:00,111.68,30.412,51.108999999999995,29.535 +2020-08-11 15:45:00,112.97,28.89,51.108999999999995,29.535 +2020-08-11 16:00:00,110.35,31.155,52.725,29.535 +2020-08-11 16:15:00,106.75,31.103,52.725,29.535 +2020-08-11 16:30:00,107.28,30.604,52.725,29.535 +2020-08-11 16:45:00,113.29,28.383000000000003,52.725,29.535 +2020-08-11 17:00:00,120.26,31.531999999999996,58.031000000000006,29.535 +2020-08-11 17:15:00,116.61,32.205,58.031000000000006,29.535 +2020-08-11 17:30:00,113.1,31.391,58.031000000000006,29.535 +2020-08-11 17:45:00,115.09,31.000999999999998,58.031000000000006,29.535 +2020-08-11 18:00:00,111.56,33.868,58.338,29.535 +2020-08-11 18:15:00,114.45,33.714,58.338,29.535 +2020-08-11 18:30:00,115.07,32.123000000000005,58.338,29.535 +2020-08-11 18:45:00,115.29,34.679,58.338,29.535 +2020-08-11 19:00:00,107.15,35.964,58.464,29.535 +2020-08-11 19:15:00,108.56,34.964,58.464,29.535 +2020-08-11 19:30:00,109.56,33.873000000000005,58.464,29.535 +2020-08-11 19:45:00,111.07,33.014,58.464,29.535 +2020-08-11 20:00:00,104.95,30.608,63.708,29.535 +2020-08-11 20:15:00,102.1,30.0,63.708,29.535 +2020-08-11 20:30:00,97.33,30.191999999999997,63.708,29.535 +2020-08-11 20:45:00,97.81,30.324,63.708,29.535 +2020-08-11 21:00:00,95.02,29.195,57.06399999999999,29.535 +2020-08-11 21:15:00,98.79,30.244,57.06399999999999,29.535 +2020-08-11 21:30:00,96.16,30.55,57.06399999999999,29.535 +2020-08-11 21:45:00,94.75,30.9,57.06399999999999,29.535 +2020-08-11 22:00:00,85.73,28.247,52.831,29.535 +2020-08-11 22:15:00,90.12,30.564,52.831,29.535 +2020-08-11 22:30:00,87.77,26.09,52.831,29.535 +2020-08-11 22:45:00,86.92,23.166999999999998,52.831,29.535 +2020-08-11 23:00:00,83.18,20.858,44.717,29.535 +2020-08-11 23:15:00,84.73,19.727,44.717,29.535 +2020-08-11 23:30:00,83.67,19.087,44.717,29.535 +2020-08-11 23:45:00,76.9,18.173,44.717,29.535 +2020-08-12 00:00:00,72.5,18.233,41.263000000000005,29.535 +2020-08-12 00:15:00,78.82,19.003,41.263000000000005,29.535 +2020-08-12 00:30:00,80.76,17.951,41.263000000000005,29.535 +2020-08-12 00:45:00,81.39,18.18,41.263000000000005,29.535 +2020-08-12 01:00:00,78.93,18.011,38.448,29.535 +2020-08-12 01:15:00,81.15,17.312,38.448,29.535 +2020-08-12 01:30:00,80.17,16.162,38.448,29.535 +2020-08-12 01:45:00,77.33,16.117,38.448,29.535 +2020-08-12 02:00:00,73.77,16.063,36.471,29.535 +2020-08-12 02:15:00,73.65,15.169,36.471,29.535 +2020-08-12 02:30:00,80.17,16.605,36.471,29.535 +2020-08-12 02:45:00,80.73,17.266,36.471,29.535 +2020-08-12 03:00:00,81.84,17.944000000000003,36.042,29.535 +2020-08-12 03:15:00,75.9,17.819000000000003,36.042,29.535 +2020-08-12 03:30:00,82.63,17.381,36.042,29.535 +2020-08-12 03:45:00,85.64,16.847,36.042,29.535 +2020-08-12 04:00:00,87.96,20.533,36.705,29.535 +2020-08-12 04:15:00,83.3,25.464000000000002,36.705,29.535 +2020-08-12 04:30:00,94.27,23.048000000000002,36.705,29.535 +2020-08-12 04:45:00,96.6,23.026,36.705,29.535 +2020-08-12 05:00:00,101.67,33.89,39.716,29.535 +2020-08-12 05:15:00,102.94,39.406,39.716,29.535 +2020-08-12 05:30:00,106.43,35.455,39.716,29.535 +2020-08-12 05:45:00,111.7,33.123000000000005,39.716,29.535 +2020-08-12 06:00:00,115.63,33.095,52.756,29.535 +2020-08-12 06:15:00,114.21,32.867,52.756,29.535 +2020-08-12 06:30:00,115.31,32.718,52.756,29.535 +2020-08-12 06:45:00,119.39,35.351,52.756,29.535 +2020-08-12 07:00:00,120.77,35.128,65.977,29.535 +2020-08-12 07:15:00,115.6,36.236999999999995,65.977,29.535 +2020-08-12 07:30:00,114.05,34.679,65.977,29.535 +2020-08-12 07:45:00,117.2,35.479,65.977,29.535 +2020-08-12 08:00:00,118.01,31.593000000000004,57.927,29.535 +2020-08-12 08:15:00,113.31,34.262,57.927,29.535 +2020-08-12 08:30:00,111.75,35.255,57.927,29.535 +2020-08-12 08:45:00,110.43,37.361999999999995,57.927,29.535 +2020-08-12 09:00:00,116.17,33.135,54.86,29.535 +2020-08-12 09:15:00,116.01,32.508,54.86,29.535 +2020-08-12 09:30:00,116.37,35.607,54.86,29.535 +2020-08-12 09:45:00,112.57,38.335,54.86,29.535 +2020-08-12 10:00:00,113.53,36.125,52.818000000000005,29.535 +2020-08-12 10:15:00,118.87,37.175,52.818000000000005,29.535 +2020-08-12 10:30:00,114.46,36.969,52.818000000000005,29.535 +2020-08-12 10:45:00,110.49,37.849000000000004,52.818000000000005,29.535 +2020-08-12 11:00:00,116.41,35.904,52.937,29.535 +2020-08-12 11:15:00,112.99,36.979,52.937,29.535 +2020-08-12 11:30:00,106.78,37.882,52.937,29.535 +2020-08-12 11:45:00,114.37,38.647,52.937,29.535 +2020-08-12 12:00:00,116.44,34.079,50.826,29.535 +2020-08-12 12:15:00,114.58,33.338,50.826,29.535 +2020-08-12 12:30:00,119.4,32.302,50.826,29.535 +2020-08-12 12:45:00,114.81,33.016999999999996,50.826,29.535 +2020-08-12 13:00:00,111.49,32.529,50.556000000000004,29.535 +2020-08-12 13:15:00,104.65,33.516999999999996,50.556000000000004,29.535 +2020-08-12 13:30:00,100.7,31.879,50.556000000000004,29.535 +2020-08-12 13:45:00,102.39,32.037,50.556000000000004,29.535 +2020-08-12 14:00:00,103.75,33.325,51.188,29.535 +2020-08-12 14:15:00,103.76,32.522,51.188,29.535 +2020-08-12 14:30:00,107.42,31.695999999999998,51.188,29.535 +2020-08-12 14:45:00,105.01,32.378,51.188,29.535 +2020-08-12 15:00:00,100.4,33.842,52.976000000000006,29.535 +2020-08-12 15:15:00,103.59,31.701999999999998,52.976000000000006,29.535 +2020-08-12 15:30:00,101.61,30.491,52.976000000000006,29.535 +2020-08-12 15:45:00,103.92,28.971999999999998,52.976000000000006,29.535 +2020-08-12 16:00:00,102.77,31.217,55.463,29.535 +2020-08-12 16:15:00,107.41,31.173000000000002,55.463,29.535 +2020-08-12 16:30:00,108.87,30.682,55.463,29.535 +2020-08-12 16:45:00,109.71,28.494,55.463,29.535 +2020-08-12 17:00:00,108.65,31.62,59.435,29.535 +2020-08-12 17:15:00,108.44,32.32,59.435,29.535 +2020-08-12 17:30:00,108.62,31.518,59.435,29.535 +2020-08-12 17:45:00,109.31,31.159000000000002,59.435,29.535 +2020-08-12 18:00:00,109.78,34.02,61.387,29.535 +2020-08-12 18:15:00,107.43,33.876,61.387,29.535 +2020-08-12 18:30:00,107.41,32.295,61.387,29.535 +2020-08-12 18:45:00,106.93,34.85,61.387,29.535 +2020-08-12 19:00:00,105.24,36.139,63.323,29.535 +2020-08-12 19:15:00,101.29,35.138000000000005,63.323,29.535 +2020-08-12 19:30:00,101.31,34.048,63.323,29.535 +2020-08-12 19:45:00,105.17,33.194,63.323,29.535 +2020-08-12 20:00:00,102.58,30.789,69.083,29.535 +2020-08-12 20:15:00,100.04,30.183000000000003,69.083,29.535 +2020-08-12 20:30:00,99.46,30.362,69.083,29.535 +2020-08-12 20:45:00,97.18,30.468000000000004,69.083,29.535 +2020-08-12 21:00:00,93.2,29.343000000000004,59.957,29.535 +2020-08-12 21:15:00,90.83,30.383000000000003,59.957,29.535 +2020-08-12 21:30:00,86.78,30.683000000000003,59.957,29.535 +2020-08-12 21:45:00,87.17,31.006,59.957,29.535 +2020-08-12 22:00:00,81.89,28.338,53.821000000000005,29.535 +2020-08-12 22:15:00,83.57,30.643,53.821000000000005,29.535 +2020-08-12 22:30:00,80.19,26.136999999999997,53.821000000000005,29.535 +2020-08-12 22:45:00,79.2,23.215,53.821000000000005,29.535 +2020-08-12 23:00:00,76.03,20.941,45.458,29.535 +2020-08-12 23:15:00,75.7,19.805,45.458,29.535 +2020-08-12 23:30:00,76.14,19.178,45.458,29.535 +2020-08-12 23:45:00,76.61,18.267,45.458,29.535 +2020-08-13 00:00:00,72.42,18.349,40.36,29.535 +2020-08-13 00:15:00,73.49,19.119,40.36,29.535 +2020-08-13 00:30:00,73.4,18.072,40.36,29.535 +2020-08-13 00:45:00,73.58,18.308,40.36,29.535 +2020-08-13 01:00:00,73.1,18.123,38.552,29.535 +2020-08-13 01:15:00,73.24,17.432000000000002,38.552,29.535 +2020-08-13 01:30:00,72.48,16.293,38.552,29.535 +2020-08-13 01:45:00,73.22,16.247,38.552,29.535 +2020-08-13 02:00:00,71.87,16.195999999999998,36.895,29.535 +2020-08-13 02:15:00,75.03,15.322000000000001,36.895,29.535 +2020-08-13 02:30:00,78.27,16.736,36.895,29.535 +2020-08-13 02:45:00,80.01,17.394000000000002,36.895,29.535 +2020-08-13 03:00:00,77.38,18.062,36.565,29.535 +2020-08-13 03:15:00,72.6,17.952,36.565,29.535 +2020-08-13 03:30:00,74.33,17.521,36.565,29.535 +2020-08-13 03:45:00,79.52,16.986,36.565,29.535 +2020-08-13 04:00:00,85.0,20.684,37.263000000000005,29.535 +2020-08-13 04:15:00,91.08,25.62,37.263000000000005,29.535 +2020-08-13 04:30:00,94.95,23.21,37.263000000000005,29.535 +2020-08-13 04:45:00,93.59,23.19,37.263000000000005,29.535 +2020-08-13 05:00:00,98.02,34.096,40.412,29.535 +2020-08-13 05:15:00,100.02,39.655,40.412,29.535 +2020-08-13 05:30:00,106.59,35.711,40.412,29.535 +2020-08-13 05:45:00,112.6,33.354,40.412,29.535 +2020-08-13 06:00:00,116.48,33.299,49.825,29.535 +2020-08-13 06:15:00,113.4,33.088,49.825,29.535 +2020-08-13 06:30:00,111.91,32.939,49.825,29.535 +2020-08-13 06:45:00,111.96,35.58,49.825,29.535 +2020-08-13 07:00:00,114.86,35.355,61.082,29.535 +2020-08-13 07:15:00,116.59,36.48,61.082,29.535 +2020-08-13 07:30:00,119.63,34.942,61.082,29.535 +2020-08-13 07:45:00,118.33,35.748000000000005,61.082,29.535 +2020-08-13 08:00:00,111.91,31.864,53.961999999999996,29.535 +2020-08-13 08:15:00,110.29,34.508,53.961999999999996,29.535 +2020-08-13 08:30:00,109.63,35.491,53.961999999999996,29.535 +2020-08-13 08:45:00,114.21,37.586,53.961999999999996,29.535 +2020-08-13 09:00:00,116.43,33.364000000000004,50.06100000000001,29.535 +2020-08-13 09:15:00,117.8,32.728,50.06100000000001,29.535 +2020-08-13 09:30:00,114.9,35.812,50.06100000000001,29.535 +2020-08-13 09:45:00,115.89,38.521,50.06100000000001,29.535 +2020-08-13 10:00:00,115.79,36.312,47.68,29.535 +2020-08-13 10:15:00,109.66,37.342,47.68,29.535 +2020-08-13 10:30:00,115.53,37.13,47.68,29.535 +2020-08-13 10:45:00,114.31,38.004,47.68,29.535 +2020-08-13 11:00:00,112.62,36.067,45.93899999999999,29.535 +2020-08-13 11:15:00,112.44,37.135999999999996,45.93899999999999,29.535 +2020-08-13 11:30:00,112.77,38.033,45.93899999999999,29.535 +2020-08-13 11:45:00,109.6,38.785,45.93899999999999,29.535 +2020-08-13 12:00:00,110.46,34.21,43.648999999999994,29.535 +2020-08-13 12:15:00,110.53,33.461,43.648999999999994,29.535 +2020-08-13 12:30:00,110.63,32.435,43.648999999999994,29.535 +2020-08-13 12:45:00,107.32,33.141,43.648999999999994,29.535 +2020-08-13 13:00:00,111.0,32.635,42.801,29.535 +2020-08-13 13:15:00,110.62,33.609,42.801,29.535 +2020-08-13 13:30:00,110.8,31.969,42.801,29.535 +2020-08-13 13:45:00,106.12,32.137,42.801,29.535 +2020-08-13 14:00:00,106.62,33.408,43.24,29.535 +2020-08-13 14:15:00,110.75,32.61,43.24,29.535 +2020-08-13 14:30:00,108.44,31.795,43.24,29.535 +2020-08-13 14:45:00,108.43,32.482,43.24,29.535 +2020-08-13 15:00:00,100.47,33.91,45.04600000000001,29.535 +2020-08-13 15:15:00,100.61,31.774,45.04600000000001,29.535 +2020-08-13 15:30:00,104.49,30.573,45.04600000000001,29.535 +2020-08-13 15:45:00,105.82,29.055999999999997,45.04600000000001,29.535 +2020-08-13 16:00:00,111.96,31.281999999999996,46.568000000000005,29.535 +2020-08-13 16:15:00,112.43,31.245,46.568000000000005,29.535 +2020-08-13 16:30:00,113.32,30.761,46.568000000000005,29.535 +2020-08-13 16:45:00,111.17,28.608,46.568000000000005,29.535 +2020-08-13 17:00:00,120.16,31.709,50.618,29.535 +2020-08-13 17:15:00,119.4,32.437,50.618,29.535 +2020-08-13 17:30:00,121.21,31.648000000000003,50.618,29.535 +2020-08-13 17:45:00,113.4,31.320999999999998,50.618,29.535 +2020-08-13 18:00:00,115.18,34.176,52.806999999999995,29.535 +2020-08-13 18:15:00,116.91,34.041,52.806999999999995,29.535 +2020-08-13 18:30:00,119.58,32.469,52.806999999999995,29.535 +2020-08-13 18:45:00,114.31,35.025,52.806999999999995,29.535 +2020-08-13 19:00:00,107.96,36.318000000000005,53.464,29.535 +2020-08-13 19:15:00,104.43,35.316,53.464,29.535 +2020-08-13 19:30:00,104.41,34.227,53.464,29.535 +2020-08-13 19:45:00,110.75,33.378,53.464,29.535 +2020-08-13 20:00:00,110.35,30.975,56.753,29.535 +2020-08-13 20:15:00,104.81,30.371,56.753,29.535 +2020-08-13 20:30:00,100.88,30.537,56.753,29.535 +2020-08-13 20:45:00,98.91,30.616,56.753,29.535 +2020-08-13 21:00:00,94.78,29.493000000000002,52.506,29.535 +2020-08-13 21:15:00,93.57,30.526,52.506,29.535 +2020-08-13 21:30:00,89.55,30.82,52.506,29.535 +2020-08-13 21:45:00,89.48,31.116999999999997,52.506,29.535 +2020-08-13 22:00:00,84.47,28.432,48.163000000000004,29.535 +2020-08-13 22:15:00,85.66,30.724,48.163000000000004,29.535 +2020-08-13 22:30:00,83.52,26.188000000000002,48.163000000000004,29.535 +2020-08-13 22:45:00,82.68,23.265,48.163000000000004,29.535 +2020-08-13 23:00:00,78.63,21.028000000000002,42.379,29.535 +2020-08-13 23:15:00,79.67,19.885,42.379,29.535 +2020-08-13 23:30:00,78.45,19.271,42.379,29.535 +2020-08-13 23:45:00,79.55,18.363,42.379,29.535 +2020-08-14 00:00:00,77.38,16.802,38.505,29.535 +2020-08-14 00:15:00,77.2,17.767,38.505,29.535 +2020-08-14 00:30:00,76.42,16.995,38.505,29.535 +2020-08-14 00:45:00,77.01,17.641,38.505,29.535 +2020-08-14 01:00:00,75.04,17.09,37.004,29.535 +2020-08-14 01:15:00,76.59,15.772,37.004,29.535 +2020-08-14 01:30:00,76.72,15.347000000000001,37.004,29.535 +2020-08-14 01:45:00,76.98,15.050999999999998,37.004,29.535 +2020-08-14 02:00:00,75.7,15.899000000000001,36.098,29.535 +2020-08-14 02:15:00,76.98,15.003,36.098,29.535 +2020-08-14 02:30:00,76.36,17.160999999999998,36.098,29.535 +2020-08-14 02:45:00,77.05,17.155,36.098,29.535 +2020-08-14 03:00:00,76.85,18.619,36.561,29.535 +2020-08-14 03:15:00,76.89,17.305,36.561,29.535 +2020-08-14 03:30:00,77.81,16.651,36.561,29.535 +2020-08-14 03:45:00,81.89,16.94,36.561,29.535 +2020-08-14 04:00:00,84.07,20.77,37.355,29.535 +2020-08-14 04:15:00,91.01,24.226,37.355,29.535 +2020-08-14 04:30:00,96.53,22.723000000000003,37.355,29.535 +2020-08-14 04:45:00,98.47,22.149,37.355,29.535 +2020-08-14 05:00:00,97.75,32.713,40.285,29.535 +2020-08-14 05:15:00,106.42,39.152,40.285,29.535 +2020-08-14 05:30:00,111.47,35.349000000000004,40.285,29.535 +2020-08-14 05:45:00,114.34,32.543,40.285,29.535 +2020-08-14 06:00:00,117.13,32.652,52.378,29.535 +2020-08-14 06:15:00,115.21,32.692,52.378,29.535 +2020-08-14 06:30:00,119.17,32.536,52.378,29.535 +2020-08-14 06:45:00,121.31,34.979,52.378,29.535 +2020-08-14 07:00:00,122.16,35.415,60.891999999999996,29.535 +2020-08-14 07:15:00,114.97,37.369,60.891999999999996,29.535 +2020-08-14 07:30:00,119.05,33.976,60.891999999999996,29.535 +2020-08-14 07:45:00,119.76,34.639,60.891999999999996,29.535 +2020-08-14 08:00:00,121.92,31.587,53.652,29.535 +2020-08-14 08:15:00,117.57,34.885,53.652,29.535 +2020-08-14 08:30:00,116.45,35.696,53.652,29.535 +2020-08-14 08:45:00,119.87,37.746,53.652,29.535 +2020-08-14 09:00:00,122.22,31.248,51.456,29.535 +2020-08-14 09:15:00,121.31,32.442,51.456,29.535 +2020-08-14 09:30:00,117.74,34.867,51.456,29.535 +2020-08-14 09:45:00,114.72,37.943000000000005,51.456,29.535 +2020-08-14 10:00:00,116.88,35.665,49.4,29.535 +2020-08-14 10:15:00,122.22,36.4,49.4,29.535 +2020-08-14 10:30:00,123.37,36.731,49.4,29.535 +2020-08-14 10:45:00,118.57,37.541,49.4,29.535 +2020-08-14 11:00:00,116.16,35.859,48.773,29.535 +2020-08-14 11:15:00,114.64,35.923,48.773,29.535 +2020-08-14 11:30:00,114.36,36.323,48.773,29.535 +2020-08-14 11:45:00,109.72,36.116,48.773,29.535 +2020-08-14 12:00:00,109.46,31.871,46.033,29.535 +2020-08-14 12:15:00,106.86,30.65,46.033,29.535 +2020-08-14 12:30:00,104.86,29.738000000000003,46.033,29.535 +2020-08-14 12:45:00,104.8,29.616,46.033,29.535 +2020-08-14 13:00:00,101.33,29.599,44.38399999999999,29.535 +2020-08-14 13:15:00,101.51,30.674,44.38399999999999,29.535 +2020-08-14 13:30:00,101.84,29.811999999999998,44.38399999999999,29.535 +2020-08-14 13:45:00,104.46,30.298000000000002,44.38399999999999,29.535 +2020-08-14 14:00:00,98.79,30.854,43.162,29.535 +2020-08-14 14:15:00,100.52,30.504,43.162,29.535 +2020-08-14 14:30:00,104.56,31.139,43.162,29.535 +2020-08-14 14:45:00,107.41,31.128,43.162,29.535 +2020-08-14 15:00:00,107.75,32.481,44.91,29.535 +2020-08-14 15:15:00,107.7,30.148000000000003,44.91,29.535 +2020-08-14 15:30:00,103.14,28.518,44.91,29.535 +2020-08-14 15:45:00,104.08,27.74,44.91,29.535 +2020-08-14 16:00:00,108.58,29.195,47.489,29.535 +2020-08-14 16:15:00,109.61,29.625999999999998,47.489,29.535 +2020-08-14 16:30:00,105.67,28.965999999999998,47.489,29.535 +2020-08-14 16:45:00,106.48,26.09,47.489,29.535 +2020-08-14 17:00:00,106.27,30.831999999999997,52.047,29.535 +2020-08-14 17:15:00,112.92,31.45,52.047,29.535 +2020-08-14 17:30:00,117.17,30.857,52.047,29.535 +2020-08-14 17:45:00,114.87,30.416999999999998,52.047,29.535 +2020-08-14 18:00:00,113.07,33.241,53.306000000000004,29.535 +2020-08-14 18:15:00,108.05,32.227,53.306000000000004,29.535 +2020-08-14 18:30:00,110.51,30.538,53.306000000000004,29.535 +2020-08-14 18:45:00,114.14,33.5,53.306000000000004,29.535 +2020-08-14 19:00:00,111.52,35.571,53.516000000000005,29.535 +2020-08-14 19:15:00,103.77,35.055,53.516000000000005,29.535 +2020-08-14 19:30:00,100.59,34.052,53.516000000000005,29.535 +2020-08-14 19:45:00,102.62,32.256,53.516000000000005,29.535 +2020-08-14 20:00:00,101.7,29.701999999999998,57.88,29.535 +2020-08-14 20:15:00,100.14,29.865,57.88,29.535 +2020-08-14 20:30:00,104.22,29.55,57.88,29.535 +2020-08-14 20:45:00,102.33,28.785999999999998,57.88,29.535 +2020-08-14 21:00:00,93.47,28.896,53.32,29.535 +2020-08-14 21:15:00,89.73,31.496,53.32,29.535 +2020-08-14 21:30:00,84.2,31.639,53.32,29.535 +2020-08-14 21:45:00,90.61,32.071999999999996,53.32,29.535 +2020-08-14 22:00:00,87.69,29.177,48.074,29.535 +2020-08-14 22:15:00,85.9,31.226999999999997,48.074,29.535 +2020-08-14 22:30:00,80.19,30.853,48.074,29.535 +2020-08-14 22:45:00,79.07,28.558000000000003,48.074,29.535 +2020-08-14 23:00:00,72.75,27.998,41.306999999999995,29.535 +2020-08-14 23:15:00,73.35,25.458000000000002,41.306999999999995,29.535 +2020-08-14 23:30:00,77.1,23.195,41.306999999999995,29.535 +2020-08-14 23:45:00,80.37,22.21,41.306999999999995,29.535 +2020-08-15 00:00:00,78.66,18.289,40.227,29.423000000000002 +2020-08-15 00:15:00,78.01,18.867,40.227,29.423000000000002 +2020-08-15 00:30:00,77.59,17.567,40.227,29.423000000000002 +2020-08-15 00:45:00,78.28,17.474,40.227,29.423000000000002 +2020-08-15 01:00:00,75.62,17.129,36.303000000000004,29.423000000000002 +2020-08-15 01:15:00,73.05,16.448,36.303000000000004,29.423000000000002 +2020-08-15 01:30:00,72.17,15.292,36.303000000000004,29.423000000000002 +2020-08-15 01:45:00,76.06,16.205,36.303000000000004,29.423000000000002 +2020-08-15 02:00:00,74.7,16.063,33.849000000000004,29.423000000000002 +2020-08-15 02:15:00,74.19,14.452,33.849000000000004,29.423000000000002 +2020-08-15 02:30:00,72.6,15.894,33.849000000000004,29.423000000000002 +2020-08-15 02:45:00,74.73,16.635,33.849000000000004,29.423000000000002 +2020-08-15 03:00:00,74.32,16.73,33.149,29.423000000000002 +2020-08-15 03:15:00,71.83,14.837,33.149,29.423000000000002 +2020-08-15 03:30:00,66.95,14.600999999999999,33.149,29.423000000000002 +2020-08-15 03:45:00,67.51,16.303,33.149,29.423000000000002 +2020-08-15 04:00:00,68.28,18.586,32.501,29.423000000000002 +2020-08-15 04:15:00,68.18,21.346999999999998,32.501,29.423000000000002 +2020-08-15 04:30:00,67.99,18.38,32.501,29.423000000000002 +2020-08-15 04:45:00,69.92,18.101,32.501,29.423000000000002 +2020-08-15 05:00:00,68.61,21.697,31.648000000000003,29.423000000000002 +2020-08-15 05:15:00,71.82,18.840999999999998,31.648000000000003,29.423000000000002 +2020-08-15 05:30:00,69.58,16.473,31.648000000000003,29.423000000000002 +2020-08-15 05:45:00,70.22,17.582,31.648000000000003,29.423000000000002 +2020-08-15 06:00:00,73.17,27.255,32.552,29.423000000000002 +2020-08-15 06:15:00,75.29,33.784,32.552,29.423000000000002 +2020-08-15 06:30:00,78.84,31.236,32.552,29.423000000000002 +2020-08-15 06:45:00,86.96,31.153000000000002,32.552,29.423000000000002 +2020-08-15 07:00:00,88.59,30.756,35.181999999999995,29.423000000000002 +2020-08-15 07:15:00,88.61,31.564,35.181999999999995,29.423000000000002 +2020-08-15 07:30:00,88.1,29.455,35.181999999999995,29.423000000000002 +2020-08-15 07:45:00,94.77,30.729,35.181999999999995,29.423000000000002 +2020-08-15 08:00:00,96.75,28.160999999999998,40.35,29.423000000000002 +2020-08-15 08:15:00,93.18,30.936999999999998,40.35,29.423000000000002 +2020-08-15 08:30:00,93.98,31.596,40.35,29.423000000000002 +2020-08-15 08:45:00,95.48,34.302,40.35,29.423000000000002 +2020-08-15 09:00:00,93.38,30.744,42.292,29.423000000000002 +2020-08-15 09:15:00,91.59,32.339,42.292,29.423000000000002 +2020-08-15 09:30:00,92.73,35.157,42.292,29.423000000000002 +2020-08-15 09:45:00,96.95,37.759,42.292,29.423000000000002 +2020-08-15 10:00:00,100.16,36.094,40.084,29.423000000000002 +2020-08-15 10:15:00,101.04,37.143,40.084,29.423000000000002 +2020-08-15 10:30:00,99.67,37.117,40.084,29.423000000000002 +2020-08-15 10:45:00,99.38,37.459,40.084,29.423000000000002 +2020-08-15 11:00:00,95.9,35.616,36.966,29.423000000000002 +2020-08-15 11:15:00,93.51,36.58,36.966,29.423000000000002 +2020-08-15 11:30:00,89.93,37.348,36.966,29.423000000000002 +2020-08-15 11:45:00,86.8,37.865,36.966,29.423000000000002 +2020-08-15 12:00:00,82.81,34.096,35.19,29.423000000000002 +2020-08-15 12:15:00,81.87,33.643,35.19,29.423000000000002 +2020-08-15 12:30:00,80.39,32.586999999999996,35.19,29.423000000000002 +2020-08-15 12:45:00,72.88,33.234,35.19,29.423000000000002 +2020-08-15 13:00:00,68.23,32.400999999999996,32.277,29.423000000000002 +2020-08-15 13:15:00,68.49,33.111999999999995,32.277,29.423000000000002 +2020-08-15 13:30:00,72.94,32.469,32.277,29.423000000000002 +2020-08-15 13:45:00,73.41,31.604,32.277,29.423000000000002 +2020-08-15 14:00:00,67.3,32.065,31.436999999999998,29.423000000000002 +2020-08-15 14:15:00,71.1,30.541999999999998,31.436999999999998,29.423000000000002 +2020-08-15 14:30:00,67.23,30.926,31.436999999999998,29.423000000000002 +2020-08-15 14:45:00,66.15,31.37,31.436999999999998,29.423000000000002 +2020-08-15 15:00:00,64.93,33.033,33.493,29.423000000000002 +2020-08-15 15:15:00,67.48,31.328000000000003,33.493,29.423000000000002 +2020-08-15 15:30:00,66.52,29.779,33.493,29.423000000000002 +2020-08-15 15:45:00,68.11,28.105999999999998,33.493,29.423000000000002 +2020-08-15 16:00:00,71.24,31.679000000000002,36.593,29.423000000000002 +2020-08-15 16:15:00,71.7,31.18,36.593,29.423000000000002 +2020-08-15 16:30:00,73.28,30.749000000000002,36.593,29.423000000000002 +2020-08-15 16:45:00,76.09,27.814,36.593,29.423000000000002 +2020-08-15 17:00:00,77.79,31.39,42.049,29.423000000000002 +2020-08-15 17:15:00,78.28,29.826,42.049,29.423000000000002 +2020-08-15 17:30:00,80.48,29.125999999999998,42.049,29.423000000000002 +2020-08-15 17:45:00,81.56,29.226,42.049,29.423000000000002 +2020-08-15 18:00:00,82.67,33.369,43.755,29.423000000000002 +2020-08-15 18:15:00,81.18,33.85,43.755,29.423000000000002 +2020-08-15 18:30:00,80.1,33.385,43.755,29.423000000000002 +2020-08-15 18:45:00,80.79,33.236,43.755,29.423000000000002 +2020-08-15 19:00:00,78.86,33.652,44.492,29.423000000000002 +2020-08-15 19:15:00,76.55,32.202,44.492,29.423000000000002 +2020-08-15 19:30:00,77.59,31.889,44.492,29.423000000000002 +2020-08-15 19:45:00,80.08,31.791,44.492,29.423000000000002 +2020-08-15 20:00:00,79.74,29.905,40.896,29.423000000000002 +2020-08-15 20:15:00,80.99,29.331,40.896,29.423000000000002 +2020-08-15 20:30:00,76.08,28.186999999999998,40.896,29.423000000000002 +2020-08-15 20:45:00,75.31,29.188000000000002,40.896,29.423000000000002 +2020-08-15 21:00:00,72.65,27.752,39.056,29.423000000000002 +2020-08-15 21:15:00,72.67,29.988000000000003,39.056,29.423000000000002 +2020-08-15 21:30:00,70.17,30.236,39.056,29.423000000000002 +2020-08-15 21:45:00,69.86,30.145,39.056,29.423000000000002 +2020-08-15 22:00:00,66.08,27.138,38.478,29.423000000000002 +2020-08-15 22:15:00,67.02,29.274,38.478,29.423000000000002 +2020-08-15 22:30:00,64.4,28.171999999999997,38.478,29.423000000000002 +2020-08-15 22:45:00,64.41,26.127,38.478,29.423000000000002 +2020-08-15 23:00:00,59.88,24.873,32.953,29.423000000000002 +2020-08-15 23:15:00,61.24,22.807,32.953,29.423000000000002 +2020-08-15 23:30:00,60.61,22.965,32.953,29.423000000000002 +2020-08-15 23:45:00,59.27,22.448,32.953,29.423000000000002 +2020-08-16 00:00:00,56.4,20.963,28.584,29.423000000000002 +2020-08-16 00:15:00,57.15,20.25,28.584,29.423000000000002 +2020-08-16 00:30:00,56.4,19.059,28.584,29.423000000000002 +2020-08-16 00:45:00,56.75,18.803,28.584,29.423000000000002 +2020-08-16 01:00:00,54.08,18.761,26.419,29.423000000000002 +2020-08-16 01:15:00,55.73,17.887999999999998,26.419,29.423000000000002 +2020-08-16 01:30:00,54.82,16.56,26.419,29.423000000000002 +2020-08-16 01:45:00,55.19,16.852,26.419,29.423000000000002 +2020-08-16 02:00:00,53.79,16.772000000000002,25.335,29.423000000000002 +2020-08-16 02:15:00,54.64,15.895999999999999,25.335,29.423000000000002 +2020-08-16 02:30:00,54.21,17.325,25.335,29.423000000000002 +2020-08-16 02:45:00,53.98,17.839000000000002,25.335,29.423000000000002 +2020-08-16 03:00:00,53.46,18.679000000000002,24.805,29.423000000000002 +2020-08-16 03:15:00,54.06,16.922,24.805,29.423000000000002 +2020-08-16 03:30:00,54.48,15.995999999999999,24.805,29.423000000000002 +2020-08-16 03:45:00,55.19,17.086,24.805,29.423000000000002 +2020-08-16 04:00:00,55.99,19.613,25.772,29.423000000000002 +2020-08-16 04:15:00,57.26,22.346,25.772,29.423000000000002 +2020-08-16 04:30:00,54.82,20.494,25.772,29.423000000000002 +2020-08-16 04:45:00,56.06,19.851,25.772,29.423000000000002 +2020-08-16 05:00:00,55.21,23.886,25.971999999999998,29.423000000000002 +2020-08-16 05:15:00,55.74,20.704,25.971999999999998,29.423000000000002 +2020-08-16 05:30:00,55.42,17.433,25.971999999999998,29.423000000000002 +2020-08-16 05:45:00,56.8,18.361,25.971999999999998,29.423000000000002 +2020-08-16 06:00:00,55.15,26.846999999999998,26.026,29.423000000000002 +2020-08-16 06:15:00,58.89,34.753,26.026,29.423000000000002 +2020-08-16 06:30:00,59.5,31.089000000000002,26.026,29.423000000000002 +2020-08-16 06:45:00,60.53,29.549,26.026,29.423000000000002 +2020-08-16 07:00:00,61.9,29.266,27.396,29.423000000000002 +2020-08-16 07:15:00,61.93,28.503,27.396,29.423000000000002 +2020-08-16 07:30:00,61.72,27.575,27.396,29.423000000000002 +2020-08-16 07:45:00,59.55,28.915,27.396,29.423000000000002 +2020-08-16 08:00:00,62.74,27.895,30.791999999999998,29.423000000000002 +2020-08-16 08:15:00,61.33,31.752,30.791999999999998,29.423000000000002 +2020-08-16 08:30:00,60.91,32.825,30.791999999999998,29.423000000000002 +2020-08-16 08:45:00,60.73,35.29,30.791999999999998,29.423000000000002 +2020-08-16 09:00:00,60.52,30.888,32.482,29.423000000000002 +2020-08-16 09:15:00,61.23,31.75,32.482,29.423000000000002 +2020-08-16 09:30:00,61.34,34.828,32.482,29.423000000000002 +2020-08-16 09:45:00,61.67,38.303000000000004,32.482,29.423000000000002 +2020-08-16 10:00:00,59.03,35.681,31.951,29.423000000000002 +2020-08-16 10:15:00,63.77,36.964,31.951,29.423000000000002 +2020-08-16 10:30:00,64.3,37.236,31.951,29.423000000000002 +2020-08-16 10:45:00,64.55,39.031,31.951,29.423000000000002 +2020-08-16 11:00:00,62.19,37.029,33.619,29.423000000000002 +2020-08-16 11:15:00,61.81,37.51,33.619,29.423000000000002 +2020-08-16 11:30:00,60.1,38.786,33.619,29.423000000000002 +2020-08-16 11:45:00,59.33,39.66,33.619,29.423000000000002 +2020-08-16 12:00:00,56.59,37.803000000000004,30.975,29.423000000000002 +2020-08-16 12:15:00,56.52,36.787,30.975,29.423000000000002 +2020-08-16 12:30:00,56.09,36.008,30.975,29.423000000000002 +2020-08-16 12:45:00,54.79,36.227,30.975,29.423000000000002 +2020-08-16 13:00:00,51.28,35.288000000000004,27.956999999999997,29.423000000000002 +2020-08-16 13:15:00,55.09,35.214,27.956999999999997,29.423000000000002 +2020-08-16 13:30:00,52.21,33.672,27.956999999999997,29.423000000000002 +2020-08-16 13:45:00,54.84,33.584,27.956999999999997,29.423000000000002 +2020-08-16 14:00:00,55.31,35.247,25.555999999999997,29.423000000000002 +2020-08-16 14:15:00,56.62,34.028,25.555999999999997,29.423000000000002 +2020-08-16 14:30:00,54.58,32.937,25.555999999999997,29.423000000000002 +2020-08-16 14:45:00,56.86,32.461,25.555999999999997,29.423000000000002 +2020-08-16 15:00:00,58.29,34.168,26.271,29.423000000000002 +2020-08-16 15:15:00,58.87,31.699,26.271,29.423000000000002 +2020-08-16 15:30:00,54.96,29.926,26.271,29.423000000000002 +2020-08-16 15:45:00,59.3,28.335,26.271,29.423000000000002 +2020-08-16 16:00:00,63.28,30.392,30.369,29.423000000000002 +2020-08-16 16:15:00,64.29,30.148000000000003,30.369,29.423000000000002 +2020-08-16 16:30:00,66.51,30.853,30.369,29.423000000000002 +2020-08-16 16:45:00,70.7,27.897,30.369,29.423000000000002 +2020-08-16 17:00:00,74.15,31.488000000000003,38.787,29.423000000000002 +2020-08-16 17:15:00,75.05,31.449,38.787,29.423000000000002 +2020-08-16 17:30:00,76.55,31.503,38.787,29.423000000000002 +2020-08-16 17:45:00,77.73,31.592,38.787,29.423000000000002 +2020-08-16 18:00:00,80.1,35.961,41.886,29.423000000000002 +2020-08-16 18:15:00,79.15,35.951,41.886,29.423000000000002 +2020-08-16 18:30:00,78.57,35.23,41.886,29.423000000000002 +2020-08-16 18:45:00,78.39,35.485,41.886,29.423000000000002 +2020-08-16 19:00:00,77.11,37.847,42.91,29.423000000000002 +2020-08-16 19:15:00,78.29,35.399,42.91,29.423000000000002 +2020-08-16 19:30:00,80.1,34.857,42.91,29.423000000000002 +2020-08-16 19:45:00,82.78,34.594,42.91,29.423000000000002 +2020-08-16 20:00:00,82.35,32.407,42.148999999999994,29.423000000000002 +2020-08-16 20:15:00,81.74,32.199,42.148999999999994,29.423000000000002 +2020-08-16 20:30:00,81.4,31.858,42.148999999999994,29.423000000000002 +2020-08-16 20:45:00,81.03,31.601,42.148999999999994,29.423000000000002 +2020-08-16 21:00:00,79.1,29.82,40.955999999999996,29.423000000000002 +2020-08-16 21:15:00,80.28,32.325,40.955999999999996,29.423000000000002 +2020-08-16 21:30:00,78.42,32.105,40.955999999999996,29.423000000000002 +2020-08-16 21:45:00,77.85,32.394,40.955999999999996,29.423000000000002 +2020-08-16 22:00:00,73.71,31.552,39.873000000000005,29.423000000000002 +2020-08-16 22:15:00,74.13,31.956,39.873000000000005,29.423000000000002 +2020-08-16 22:30:00,70.68,30.46,39.873000000000005,29.423000000000002 +2020-08-16 22:45:00,73.48,27.304000000000002,39.873000000000005,29.423000000000002 +2020-08-16 23:00:00,66.63,25.803,35.510999999999996,29.423000000000002 +2020-08-16 23:15:00,68.8,24.945999999999998,35.510999999999996,29.423000000000002 +2020-08-16 23:30:00,67.36,24.513,35.510999999999996,29.423000000000002 +2020-08-16 23:45:00,68.31,24.014,35.510999999999996,29.423000000000002 +2020-08-17 00:00:00,67.08,22.375,33.475,29.535 +2020-08-17 00:15:00,66.94,22.315,33.475,29.535 +2020-08-17 00:30:00,66.43,20.779,33.475,29.535 +2020-08-17 00:45:00,66.96,20.184,33.475,29.535 +2020-08-17 01:00:00,64.35,20.474,33.111,29.535 +2020-08-17 01:15:00,65.92,19.635,33.111,29.535 +2020-08-17 01:30:00,65.15,18.643,33.111,29.535 +2020-08-17 01:45:00,64.74,18.845,33.111,29.535 +2020-08-17 02:00:00,65.98,19.177,32.358000000000004,29.535 +2020-08-17 02:15:00,72.77,17.374000000000002,32.358000000000004,29.535 +2020-08-17 02:30:00,73.53,18.923,32.358000000000004,29.535 +2020-08-17 02:45:00,69.28,19.314,32.358000000000004,29.535 +2020-08-17 03:00:00,66.89,20.573,30.779,29.535 +2020-08-17 03:15:00,67.8,19.445999999999998,30.779,29.535 +2020-08-17 03:30:00,71.43,19.149,30.779,29.535 +2020-08-17 03:45:00,74.75,19.83,30.779,29.535 +2020-08-17 04:00:00,80.78,25.086,31.416,29.535 +2020-08-17 04:15:00,87.43,30.389,31.416,29.535 +2020-08-17 04:30:00,90.67,28.061,31.416,29.535 +2020-08-17 04:45:00,90.09,27.750999999999998,31.416,29.535 +2020-08-17 05:00:00,93.62,38.103,37.221,29.535 +2020-08-17 05:15:00,96.83,43.934,37.221,29.535 +2020-08-17 05:30:00,105.37,39.284,37.221,29.535 +2020-08-17 05:45:00,108.9,37.507,37.221,29.535 +2020-08-17 06:00:00,113.83,36.976,51.891000000000005,29.535 +2020-08-17 06:15:00,111.93,36.961,51.891000000000005,29.535 +2020-08-17 06:30:00,116.33,36.743,51.891000000000005,29.535 +2020-08-17 06:45:00,116.15,39.816,51.891000000000005,29.535 +2020-08-17 07:00:00,119.7,39.711999999999996,62.282,29.535 +2020-08-17 07:15:00,114.94,40.998000000000005,62.282,29.535 +2020-08-17 07:30:00,113.97,39.330999999999996,62.282,29.535 +2020-08-17 07:45:00,116.01,40.996,62.282,29.535 +2020-08-17 08:00:00,116.0,38.075,54.102,29.535 +2020-08-17 08:15:00,113.19,40.864000000000004,54.102,29.535 +2020-08-17 08:30:00,108.1,41.148,54.102,29.535 +2020-08-17 08:45:00,111.95,43.861000000000004,54.102,29.535 +2020-08-17 09:00:00,116.11,38.529,50.917,29.535 +2020-08-17 09:15:00,113.89,37.845,50.917,29.535 +2020-08-17 09:30:00,111.12,40.088,50.917,29.535 +2020-08-17 09:45:00,106.99,41.456,50.917,29.535 +2020-08-17 10:00:00,109.37,39.195,49.718999999999994,29.535 +2020-08-17 10:15:00,108.32,40.332,49.718999999999994,29.535 +2020-08-17 10:30:00,115.64,40.201,49.718999999999994,29.535 +2020-08-17 10:45:00,113.32,40.523,49.718999999999994,29.535 +2020-08-17 11:00:00,109.23,38.788000000000004,49.833999999999996,29.535 +2020-08-17 11:15:00,106.15,39.465,49.833999999999996,29.535 +2020-08-17 11:30:00,106.16,41.331,49.833999999999996,29.535 +2020-08-17 11:45:00,112.52,42.663999999999994,49.833999999999996,29.535 +2020-08-17 12:00:00,113.63,39.105,47.832,29.535 +2020-08-17 12:15:00,106.33,38.181,47.832,29.535 +2020-08-17 12:30:00,107.11,36.42,47.832,29.535 +2020-08-17 12:45:00,107.04,36.57,47.832,29.535 +2020-08-17 13:00:00,103.37,36.479,48.03,29.535 +2020-08-17 13:15:00,102.48,35.629,48.03,29.535 +2020-08-17 13:30:00,104.21,34.259,48.03,29.535 +2020-08-17 13:45:00,102.22,35.001,48.03,29.535 +2020-08-17 14:00:00,103.51,35.816,48.157,29.535 +2020-08-17 14:15:00,99.38,35.17,48.157,29.535 +2020-08-17 14:30:00,99.26,33.96,48.157,29.535 +2020-08-17 14:45:00,99.3,35.389,48.157,29.535 +2020-08-17 15:00:00,102.77,36.656,48.897,29.535 +2020-08-17 15:15:00,101.97,33.701,48.897,29.535 +2020-08-17 15:30:00,103.47,32.678000000000004,48.897,29.535 +2020-08-17 15:45:00,104.76,30.66,48.897,29.535 +2020-08-17 16:00:00,106.74,33.74,51.446000000000005,29.535 +2020-08-17 16:15:00,109.18,33.586,51.446000000000005,29.535 +2020-08-17 16:30:00,111.89,33.715,51.446000000000005,29.535 +2020-08-17 16:45:00,110.39,30.851,51.446000000000005,29.535 +2020-08-17 17:00:00,109.89,33.382,57.507,29.535 +2020-08-17 17:15:00,108.72,33.742,57.507,29.535 +2020-08-17 17:30:00,109.15,33.471,57.507,29.535 +2020-08-17 17:45:00,108.54,33.272,57.507,29.535 +2020-08-17 18:00:00,108.29,36.677,57.896,29.535 +2020-08-17 18:15:00,107.32,35.067,57.896,29.535 +2020-08-17 18:30:00,107.79,33.650999999999996,57.896,29.535 +2020-08-17 18:45:00,106.79,36.765,57.896,29.535 +2020-08-17 19:00:00,113.59,38.936,57.891999999999996,29.535 +2020-08-17 19:15:00,107.67,37.709,57.891999999999996,29.535 +2020-08-17 19:30:00,110.58,36.824,57.891999999999996,29.535 +2020-08-17 19:45:00,109.62,36.025999999999996,57.891999999999996,29.535 +2020-08-17 20:00:00,102.56,32.74,64.57300000000001,29.535 +2020-08-17 20:15:00,100.21,33.866,64.57300000000001,29.535 +2020-08-17 20:30:00,98.27,34.029,64.57300000000001,29.535 +2020-08-17 20:45:00,99.3,33.9,64.57300000000001,29.535 +2020-08-17 21:00:00,95.28,31.535,59.431999999999995,29.535 +2020-08-17 21:15:00,96.5,34.459,59.431999999999995,29.535 +2020-08-17 21:30:00,93.09,34.631,59.431999999999995,29.535 +2020-08-17 21:45:00,92.08,34.704,59.431999999999995,29.535 +2020-08-17 22:00:00,89.32,32.049,51.519,29.535 +2020-08-17 22:15:00,87.02,34.274,51.519,29.535 +2020-08-17 22:30:00,82.96,29.033,51.519,29.535 +2020-08-17 22:45:00,87.26,25.968000000000004,51.519,29.535 +2020-08-17 23:00:00,83.17,24.506999999999998,44.501000000000005,29.535 +2020-08-17 23:15:00,84.93,22.086,44.501000000000005,29.535 +2020-08-17 23:30:00,79.26,21.566999999999997,44.501000000000005,29.535 +2020-08-17 23:45:00,75.42,20.524,44.501000000000005,29.535 +2020-08-18 00:00:00,80.51,20.454,44.522,29.535 +2020-08-18 00:15:00,81.43,21.15,44.522,29.535 +2020-08-18 00:30:00,80.49,20.289,44.522,29.535 +2020-08-18 00:45:00,75.85,20.444000000000003,44.522,29.535 +2020-08-18 01:00:00,72.88,20.229,41.441,29.535 +2020-08-18 01:15:00,73.3,19.511,41.441,29.535 +2020-08-18 01:30:00,75.87,18.41,41.441,29.535 +2020-08-18 01:45:00,80.87,18.136,41.441,29.535 +2020-08-18 02:00:00,81.98,18.032,40.203,29.535 +2020-08-18 02:15:00,79.19,17.307000000000002,40.203,29.535 +2020-08-18 02:30:00,76.72,18.492,40.203,29.535 +2020-08-18 02:45:00,82.03,19.179000000000002,40.203,29.535 +2020-08-18 03:00:00,82.91,19.949,39.536,29.535 +2020-08-18 03:15:00,82.37,19.711,39.536,29.535 +2020-08-18 03:30:00,78.01,19.379,39.536,29.535 +2020-08-18 03:45:00,83.18,19.037,39.536,29.535 +2020-08-18 04:00:00,90.43,23.16,40.759,29.535 +2020-08-18 04:15:00,92.1,28.493000000000002,40.759,29.535 +2020-08-18 04:30:00,89.48,26.066999999999997,40.759,29.535 +2020-08-18 04:45:00,92.67,26.177,40.759,29.535 +2020-08-18 05:00:00,97.83,37.921,43.623999999999995,29.535 +2020-08-18 05:15:00,105.65,44.367,43.623999999999995,29.535 +2020-08-18 05:30:00,111.86,39.913000000000004,43.623999999999995,29.535 +2020-08-18 05:45:00,115.65,37.446,43.623999999999995,29.535 +2020-08-18 06:00:00,116.29,37.865,52.684,29.535 +2020-08-18 06:15:00,117.43,38.018,52.684,29.535 +2020-08-18 06:30:00,123.82,37.52,52.684,29.535 +2020-08-18 06:45:00,129.27,39.798,52.684,29.535 +2020-08-18 07:00:00,135.91,39.836,62.676,29.535 +2020-08-18 07:15:00,126.32,40.907,62.676,29.535 +2020-08-18 07:30:00,127.7,39.289,62.676,29.535 +2020-08-18 07:45:00,125.98,40.064,62.676,29.535 +2020-08-18 08:00:00,127.08,37.076,56.161,29.535 +2020-08-18 08:15:00,131.88,39.455,56.161,29.535 +2020-08-18 08:30:00,134.91,39.891,56.161,29.535 +2020-08-18 08:45:00,131.78,41.751000000000005,56.161,29.535 +2020-08-18 09:00:00,132.31,36.759,52.132,29.535 +2020-08-18 09:15:00,134.14,35.897,52.132,29.535 +2020-08-18 09:30:00,140.01,38.738,52.132,29.535 +2020-08-18 09:45:00,140.34,41.32,52.132,29.535 +2020-08-18 10:00:00,137.81,37.816,51.032,29.535 +2020-08-18 10:15:00,133.5,38.835,51.032,29.535 +2020-08-18 10:30:00,134.66,38.716,51.032,29.535 +2020-08-18 10:45:00,131.74,39.949,51.032,29.535 +2020-08-18 11:00:00,125.16,38.181,51.085,29.535 +2020-08-18 11:15:00,124.46,39.236,51.085,29.535 +2020-08-18 11:30:00,124.13,40.074,51.085,29.535 +2020-08-18 11:45:00,115.02,40.953,51.085,29.535 +2020-08-18 12:00:00,109.2,37.262,49.049,29.535 +2020-08-18 12:15:00,108.88,36.66,49.049,29.535 +2020-08-18 12:30:00,112.98,35.681999999999995,49.049,29.535 +2020-08-18 12:45:00,115.32,36.479,49.049,29.535 +2020-08-18 13:00:00,108.39,36.041,49.722,29.535 +2020-08-18 13:15:00,111.75,36.867,49.722,29.535 +2020-08-18 13:30:00,111.55,35.42,49.722,29.535 +2020-08-18 13:45:00,107.18,35.269,49.722,29.535 +2020-08-18 14:00:00,114.92,36.473,49.565,29.535 +2020-08-18 14:15:00,117.53,35.641999999999996,49.565,29.535 +2020-08-18 14:30:00,117.41,34.726,49.565,29.535 +2020-08-18 14:45:00,113.97,35.425,49.565,29.535 +2020-08-18 15:00:00,104.47,36.525,51.108999999999995,29.535 +2020-08-18 15:15:00,107.86,34.426,51.108999999999995,29.535 +2020-08-18 15:30:00,116.75,33.21,51.108999999999995,29.535 +2020-08-18 15:45:00,114.65,31.535999999999998,51.108999999999995,29.535 +2020-08-18 16:00:00,112.4,33.915,52.725,29.535 +2020-08-18 16:15:00,114.87,33.833,52.725,29.535 +2020-08-18 16:30:00,114.02,33.6,52.725,29.535 +2020-08-18 16:45:00,116.97,31.426,52.725,29.535 +2020-08-18 17:00:00,113.73,34.107,58.031000000000006,29.535 +2020-08-18 17:15:00,110.05,34.88,58.031000000000006,29.535 +2020-08-18 17:30:00,118.5,34.19,58.031000000000006,29.535 +2020-08-18 17:45:00,119.33,33.718,58.031000000000006,29.535 +2020-08-18 18:00:00,122.4,36.251999999999995,58.338,29.535 +2020-08-18 18:15:00,114.26,36.035,58.338,29.535 +2020-08-18 18:30:00,115.94,34.396,58.338,29.535 +2020-08-18 18:45:00,118.08,37.306999999999995,58.338,29.535 +2020-08-18 19:00:00,107.79,38.418,58.464,29.535 +2020-08-18 19:15:00,104.16,37.361,58.464,29.535 +2020-08-18 19:30:00,111.36,36.297,58.464,29.535 +2020-08-18 19:45:00,113.13,35.806,58.464,29.535 +2020-08-18 20:00:00,106.38,32.879,63.708,29.535 +2020-08-18 20:15:00,103.17,32.701,63.708,29.535 +2020-08-18 20:30:00,98.53,32.858000000000004,63.708,29.535 +2020-08-18 20:45:00,97.08,33.101,63.708,29.535 +2020-08-18 21:00:00,89.74,31.555,57.06399999999999,29.535 +2020-08-18 21:15:00,91.57,33.103,57.06399999999999,29.535 +2020-08-18 21:30:00,86.58,33.452,57.06399999999999,29.535 +2020-08-18 21:45:00,86.69,33.669000000000004,57.06399999999999,29.535 +2020-08-18 22:00:00,80.92,31.107,52.831,29.535 +2020-08-18 22:15:00,81.51,33.018,52.831,29.535 +2020-08-18 22:30:00,80.13,28.011999999999997,52.831,29.535 +2020-08-18 22:45:00,79.13,24.936999999999998,52.831,29.535 +2020-08-18 23:00:00,72.87,22.809,44.717,29.535 +2020-08-18 23:15:00,76.22,21.831999999999997,44.717,29.535 +2020-08-18 23:30:00,77.08,21.331,44.717,29.535 +2020-08-18 23:45:00,78.95,20.47,44.717,29.535 +2020-08-19 00:00:00,75.55,20.588,41.263000000000005,29.535 +2020-08-19 00:15:00,74.25,21.284000000000002,41.263000000000005,29.535 +2020-08-19 00:30:00,73.14,20.428,41.263000000000005,29.535 +2020-08-19 00:45:00,73.85,20.59,41.263000000000005,29.535 +2020-08-19 01:00:00,72.97,20.358,38.448,29.535 +2020-08-19 01:15:00,75.56,19.652,38.448,29.535 +2020-08-19 01:30:00,70.9,18.563,38.448,29.535 +2020-08-19 01:45:00,73.95,18.288,38.448,29.535 +2020-08-19 02:00:00,73.83,18.187,36.471,29.535 +2020-08-19 02:15:00,75.11,17.482,36.471,29.535 +2020-08-19 02:30:00,74.21,18.646,36.471,29.535 +2020-08-19 02:45:00,74.47,19.331,36.471,29.535 +2020-08-19 03:00:00,76.06,20.089000000000002,36.042,29.535 +2020-08-19 03:15:00,77.07,19.866,36.042,29.535 +2020-08-19 03:30:00,74.69,19.54,36.042,29.535 +2020-08-19 03:45:00,84.15,19.194000000000003,36.042,29.535 +2020-08-19 04:00:00,90.54,23.335,36.705,29.535 +2020-08-19 04:15:00,91.52,28.679000000000002,36.705,29.535 +2020-08-19 04:30:00,88.4,26.26,36.705,29.535 +2020-08-19 04:45:00,91.16,26.372,36.705,29.535 +2020-08-19 05:00:00,95.33,38.171,39.716,29.535 +2020-08-19 05:15:00,104.66,44.675,39.716,29.535 +2020-08-19 05:30:00,106.17,40.223,39.716,29.535 +2020-08-19 05:45:00,113.8,37.723,39.716,29.535 +2020-08-19 06:00:00,118.61,38.113,52.756,29.535 +2020-08-19 06:15:00,117.99,38.286,52.756,29.535 +2020-08-19 06:30:00,114.51,37.786,52.756,29.535 +2020-08-19 06:45:00,111.03,40.067,52.756,29.535 +2020-08-19 07:00:00,120.0,40.104,65.977,29.535 +2020-08-19 07:15:00,121.82,41.191,65.977,29.535 +2020-08-19 07:30:00,118.1,39.595,65.977,29.535 +2020-08-19 07:45:00,117.26,40.374,65.977,29.535 +2020-08-19 08:00:00,114.13,37.389,57.927,29.535 +2020-08-19 08:15:00,117.26,39.741,57.927,29.535 +2020-08-19 08:30:00,120.28,40.171,57.927,29.535 +2020-08-19 08:45:00,118.51,42.016999999999996,57.927,29.535 +2020-08-19 09:00:00,117.01,37.033,54.86,29.535 +2020-08-19 09:15:00,114.75,36.162,54.86,29.535 +2020-08-19 09:30:00,117.13,38.986999999999995,54.86,29.535 +2020-08-19 09:45:00,112.52,41.547,54.86,29.535 +2020-08-19 10:00:00,114.95,38.043,52.818000000000005,29.535 +2020-08-19 10:15:00,116.13,39.04,52.818000000000005,29.535 +2020-08-19 10:30:00,116.65,38.913000000000004,52.818000000000005,29.535 +2020-08-19 10:45:00,111.6,40.138000000000005,52.818000000000005,29.535 +2020-08-19 11:00:00,110.58,38.38,52.937,29.535 +2020-08-19 11:15:00,112.5,39.426,52.937,29.535 +2020-08-19 11:30:00,112.13,40.26,52.937,29.535 +2020-08-19 11:45:00,111.52,41.125,52.937,29.535 +2020-08-19 12:00:00,106.13,37.423,50.826,29.535 +2020-08-19 12:15:00,106.08,36.811,50.826,29.535 +2020-08-19 12:30:00,106.22,35.847,50.826,29.535 +2020-08-19 12:45:00,115.53,36.633,50.826,29.535 +2020-08-19 13:00:00,113.75,36.177,50.556000000000004,29.535 +2020-08-19 13:15:00,112.48,36.993,50.556000000000004,29.535 +2020-08-19 13:30:00,105.73,35.543,50.556000000000004,29.535 +2020-08-19 13:45:00,105.62,35.400999999999996,50.556000000000004,29.535 +2020-08-19 14:00:00,109.57,36.584,51.188,29.535 +2020-08-19 14:15:00,112.24,35.759,51.188,29.535 +2020-08-19 14:30:00,108.23,34.856,51.188,29.535 +2020-08-19 14:45:00,107.5,35.559,51.188,29.535 +2020-08-19 15:00:00,109.94,36.618,52.976000000000006,29.535 +2020-08-19 15:15:00,110.19,34.525999999999996,52.976000000000006,29.535 +2020-08-19 15:30:00,105.86,33.321999999999996,52.976000000000006,29.535 +2020-08-19 15:45:00,109.9,31.653000000000002,52.976000000000006,29.535 +2020-08-19 16:00:00,110.95,34.01,55.463,29.535 +2020-08-19 16:15:00,112.44,33.936,55.463,29.535 +2020-08-19 16:30:00,109.15,33.707,55.463,29.535 +2020-08-19 16:45:00,107.57,31.572,55.463,29.535 +2020-08-19 17:00:00,115.26,34.226,59.435,29.535 +2020-08-19 17:15:00,118.06,35.025,59.435,29.535 +2020-08-19 17:30:00,118.86,34.346,59.435,29.535 +2020-08-19 17:45:00,116.17,33.907,59.435,29.535 +2020-08-19 18:00:00,112.79,36.433,61.387,29.535 +2020-08-19 18:15:00,115.1,36.225,61.387,29.535 +2020-08-19 18:30:00,118.21,34.598,61.387,29.535 +2020-08-19 18:45:00,118.6,37.507,61.387,29.535 +2020-08-19 19:00:00,113.59,38.624,63.323,29.535 +2020-08-19 19:15:00,106.04,37.567,63.323,29.535 +2020-08-19 19:30:00,112.3,36.503,63.323,29.535 +2020-08-19 19:45:00,112.18,36.016,63.323,29.535 +2020-08-19 20:00:00,107.02,33.094,69.083,29.535 +2020-08-19 20:15:00,107.02,32.918,69.083,29.535 +2020-08-19 20:30:00,100.41,33.061,69.083,29.535 +2020-08-19 20:45:00,98.9,33.274,69.083,29.535 +2020-08-19 21:00:00,94.63,31.729,59.957,29.535 +2020-08-19 21:15:00,94.3,33.27,59.957,29.535 +2020-08-19 21:30:00,91.09,33.616,59.957,29.535 +2020-08-19 21:45:00,88.92,33.804,59.957,29.535 +2020-08-19 22:00:00,81.63,31.223000000000003,53.821000000000005,29.535 +2020-08-19 22:15:00,84.95,33.12,53.821000000000005,29.535 +2020-08-19 22:30:00,83.56,28.084,53.821000000000005,29.535 +2020-08-19 22:45:00,83.98,25.009,53.821000000000005,29.535 +2020-08-19 23:00:00,74.72,22.921,45.458,29.535 +2020-08-19 23:15:00,78.64,21.933000000000003,45.458,29.535 +2020-08-19 23:30:00,74.14,21.443,45.458,29.535 +2020-08-19 23:45:00,79.63,20.586,45.458,29.535 +2020-08-20 00:00:00,73.64,20.724,40.36,29.535 +2020-08-20 00:15:00,75.13,21.421,40.36,29.535 +2020-08-20 00:30:00,70.59,20.57,40.36,29.535 +2020-08-20 00:45:00,74.45,20.739,40.36,29.535 +2020-08-20 01:00:00,72.22,20.49,38.552,29.535 +2020-08-20 01:15:00,74.16,19.795,38.552,29.535 +2020-08-20 01:30:00,75.17,18.719,38.552,29.535 +2020-08-20 01:45:00,81.58,18.444000000000003,38.552,29.535 +2020-08-20 02:00:00,80.48,18.346,36.895,29.535 +2020-08-20 02:15:00,77.16,17.660999999999998,36.895,29.535 +2020-08-20 02:30:00,74.84,18.802,36.895,29.535 +2020-08-20 02:45:00,76.18,19.485,36.895,29.535 +2020-08-20 03:00:00,77.16,20.230999999999998,36.565,29.535 +2020-08-20 03:15:00,76.8,20.024,36.565,29.535 +2020-08-20 03:30:00,77.13,19.705,36.565,29.535 +2020-08-20 03:45:00,81.08,19.352999999999998,36.565,29.535 +2020-08-20 04:00:00,82.53,23.513,37.263000000000005,29.535 +2020-08-20 04:15:00,85.3,28.87,37.263000000000005,29.535 +2020-08-20 04:30:00,89.78,26.456,37.263000000000005,29.535 +2020-08-20 04:45:00,92.89,26.572,37.263000000000005,29.535 +2020-08-20 05:00:00,100.42,38.428000000000004,40.412,29.535 +2020-08-20 05:15:00,97.56,44.992,40.412,29.535 +2020-08-20 05:30:00,104.41,40.54,40.412,29.535 +2020-08-20 05:45:00,114.19,38.008,40.412,29.535 +2020-08-20 06:00:00,118.96,38.368,49.825,29.535 +2020-08-20 06:15:00,118.57,38.561,49.825,29.535 +2020-08-20 06:30:00,115.56,38.058,49.825,29.535 +2020-08-20 06:45:00,116.37,40.343,49.825,29.535 +2020-08-20 07:00:00,117.94,40.379,61.082,29.535 +2020-08-20 07:15:00,121.74,41.48,61.082,29.535 +2020-08-20 07:30:00,117.74,39.907,61.082,29.535 +2020-08-20 07:45:00,117.37,40.691,61.082,29.535 +2020-08-20 08:00:00,119.5,37.708,53.961999999999996,29.535 +2020-08-20 08:15:00,119.23,40.032,53.961999999999996,29.535 +2020-08-20 08:30:00,117.61,40.457,53.961999999999996,29.535 +2020-08-20 08:45:00,119.25,42.288999999999994,53.961999999999996,29.535 +2020-08-20 09:00:00,121.78,37.311,50.06100000000001,29.535 +2020-08-20 09:15:00,121.69,36.431999999999995,50.06100000000001,29.535 +2020-08-20 09:30:00,113.46,39.24,50.06100000000001,29.535 +2020-08-20 09:45:00,113.9,41.778,50.06100000000001,29.535 +2020-08-20 10:00:00,119.89,38.274,47.68,29.535 +2020-08-20 10:15:00,119.66,39.247,47.68,29.535 +2020-08-20 10:30:00,118.06,39.114000000000004,47.68,29.535 +2020-08-20 10:45:00,111.47,40.330999999999996,47.68,29.535 +2020-08-20 11:00:00,110.59,38.582,45.93899999999999,29.535 +2020-08-20 11:15:00,113.71,39.62,45.93899999999999,29.535 +2020-08-20 11:30:00,113.77,40.45,45.93899999999999,29.535 +2020-08-20 11:45:00,113.94,41.301,45.93899999999999,29.535 +2020-08-20 12:00:00,110.52,37.588,43.648999999999994,29.535 +2020-08-20 12:15:00,113.88,36.965,43.648999999999994,29.535 +2020-08-20 12:30:00,114.93,36.016,43.648999999999994,29.535 +2020-08-20 12:45:00,114.05,36.791,43.648999999999994,29.535 +2020-08-20 13:00:00,113.61,36.317,42.801,29.535 +2020-08-20 13:15:00,115.95,37.122,42.801,29.535 +2020-08-20 13:30:00,127.37,35.669000000000004,42.801,29.535 +2020-08-20 13:45:00,125.7,35.538000000000004,42.801,29.535 +2020-08-20 14:00:00,120.24,36.696999999999996,43.24,29.535 +2020-08-20 14:15:00,116.22,35.878,43.24,29.535 +2020-08-20 14:30:00,117.36,34.991,43.24,29.535 +2020-08-20 14:45:00,120.32,35.696,43.24,29.535 +2020-08-20 15:00:00,118.94,36.713,45.04600000000001,29.535 +2020-08-20 15:15:00,113.51,34.626999999999995,45.04600000000001,29.535 +2020-08-20 15:30:00,112.59,33.437,45.04600000000001,29.535 +2020-08-20 15:45:00,117.13,31.774,45.04600000000001,29.535 +2020-08-20 16:00:00,112.87,34.106,46.568000000000005,29.535 +2020-08-20 16:15:00,107.6,34.041,46.568000000000005,29.535 +2020-08-20 16:30:00,113.61,33.817,46.568000000000005,29.535 +2020-08-20 16:45:00,113.75,31.721,46.568000000000005,29.535 +2020-08-20 17:00:00,116.5,34.345,50.618,29.535 +2020-08-20 17:15:00,117.63,35.172,50.618,29.535 +2020-08-20 17:30:00,118.34,34.506,50.618,29.535 +2020-08-20 17:45:00,121.4,34.099000000000004,50.618,29.535 +2020-08-20 18:00:00,121.03,36.616,52.806999999999995,29.535 +2020-08-20 18:15:00,118.78,36.42,52.806999999999995,29.535 +2020-08-20 18:30:00,118.33,34.802,52.806999999999995,29.535 +2020-08-20 18:45:00,113.93,37.711,52.806999999999995,29.535 +2020-08-20 19:00:00,110.67,38.834,53.464,29.535 +2020-08-20 19:15:00,108.18,37.775999999999996,53.464,29.535 +2020-08-20 19:30:00,108.15,36.714,53.464,29.535 +2020-08-20 19:45:00,108.46,36.23,53.464,29.535 +2020-08-20 20:00:00,104.07,33.314,56.753,29.535 +2020-08-20 20:15:00,102.81,33.14,56.753,29.535 +2020-08-20 20:30:00,101.22,33.266999999999996,56.753,29.535 +2020-08-20 20:45:00,100.86,33.449,56.753,29.535 +2020-08-20 21:00:00,93.78,31.909000000000002,52.506,29.535 +2020-08-20 21:15:00,95.92,33.44,52.506,29.535 +2020-08-20 21:30:00,88.69,33.784,52.506,29.535 +2020-08-20 21:45:00,90.87,33.941,52.506,29.535 +2020-08-20 22:00:00,83.08,31.343000000000004,48.163000000000004,29.535 +2020-08-20 22:15:00,86.43,33.224000000000004,48.163000000000004,29.535 +2020-08-20 22:30:00,83.99,28.158,48.163000000000004,29.535 +2020-08-20 22:45:00,83.77,25.084,48.163000000000004,29.535 +2020-08-20 23:00:00,75.0,23.035,42.379,29.535 +2020-08-20 23:15:00,77.96,22.035,42.379,29.535 +2020-08-20 23:30:00,75.78,21.557,42.379,29.535 +2020-08-20 23:45:00,78.91,20.704,42.379,29.535 +2020-08-21 00:00:00,75.42,19.239,38.505,29.535 +2020-08-21 00:15:00,75.15,20.131,38.505,29.535 +2020-08-21 00:30:00,73.55,19.534000000000002,38.505,29.535 +2020-08-21 00:45:00,74.33,20.096,38.505,29.535 +2020-08-21 01:00:00,73.08,19.484,37.004,29.535 +2020-08-21 01:15:00,73.78,18.242,37.004,29.535 +2020-08-21 01:30:00,73.77,17.834,37.004,29.535 +2020-08-21 01:45:00,80.71,17.328,37.004,29.535 +2020-08-21 02:00:00,81.21,18.083,36.098,29.535 +2020-08-21 02:15:00,77.73,17.374000000000002,36.098,29.535 +2020-08-21 02:30:00,74.39,19.24,36.098,29.535 +2020-08-21 02:45:00,75.1,19.294,36.098,29.535 +2020-08-21 03:00:00,77.31,20.749000000000002,36.561,29.535 +2020-08-21 03:15:00,77.03,19.445,36.561,29.535 +2020-08-21 03:30:00,76.96,18.914,36.561,29.535 +2020-08-21 03:45:00,83.38,19.355,36.561,29.535 +2020-08-21 04:00:00,88.24,23.659000000000002,37.355,29.535 +2020-08-21 04:15:00,91.71,27.607,37.355,29.535 +2020-08-21 04:30:00,90.26,26.066,37.355,29.535 +2020-08-21 04:45:00,91.87,25.607,37.355,29.535 +2020-08-21 05:00:00,99.2,37.098,40.285,29.535 +2020-08-21 05:15:00,102.18,44.583,40.285,29.535 +2020-08-21 05:30:00,106.7,40.306,40.285,29.535 +2020-08-21 05:45:00,115.67,37.338,40.285,29.535 +2020-08-21 06:00:00,123.11,37.869,52.378,29.535 +2020-08-21 06:15:00,120.91,38.234,52.378,29.535 +2020-08-21 06:30:00,123.41,37.684,52.378,29.535 +2020-08-21 06:45:00,127.16,39.852,52.378,29.535 +2020-08-21 07:00:00,129.29,40.475,60.891999999999996,29.535 +2020-08-21 07:15:00,124.88,42.409,60.891999999999996,29.535 +2020-08-21 07:30:00,117.88,39.092,60.891999999999996,29.535 +2020-08-21 07:45:00,116.37,39.691,60.891999999999996,29.535 +2020-08-21 08:00:00,118.09,37.425,53.652,29.535 +2020-08-21 08:15:00,119.83,40.330999999999996,53.652,29.535 +2020-08-21 08:30:00,122.73,40.656,53.652,29.535 +2020-08-21 08:45:00,121.5,42.353,53.652,29.535 +2020-08-21 09:00:00,118.6,35.285,51.456,29.535 +2020-08-21 09:15:00,120.12,36.135999999999996,51.456,29.535 +2020-08-21 09:30:00,126.02,38.305,51.456,29.535 +2020-08-21 09:45:00,122.6,41.172,51.456,29.535 +2020-08-21 10:00:00,120.93,37.529,49.4,29.535 +2020-08-21 10:15:00,121.21,38.27,49.4,29.535 +2020-08-21 10:30:00,124.91,38.634,49.4,29.535 +2020-08-21 10:45:00,126.82,39.759,49.4,29.535 +2020-08-21 11:00:00,123.43,38.259,48.773,29.535 +2020-08-21 11:15:00,121.31,38.306,48.773,29.535 +2020-08-21 11:30:00,114.97,38.778,48.773,29.535 +2020-08-21 11:45:00,117.42,38.736,48.773,29.535 +2020-08-21 12:00:00,122.33,35.384,46.033,29.535 +2020-08-21 12:15:00,112.57,34.199,46.033,29.535 +2020-08-21 12:30:00,103.79,33.37,46.033,29.535 +2020-08-21 12:45:00,111.23,33.400999999999996,46.033,29.535 +2020-08-21 13:00:00,109.6,33.441,44.38399999999999,29.535 +2020-08-21 13:15:00,108.67,34.389,44.38399999999999,29.535 +2020-08-21 13:30:00,105.43,33.662,44.38399999999999,29.535 +2020-08-21 13:45:00,105.72,33.823,44.38399999999999,29.535 +2020-08-21 14:00:00,109.3,34.238,43.162,29.535 +2020-08-21 14:15:00,119.63,33.829,43.162,29.535 +2020-08-21 14:30:00,116.81,34.333,43.162,29.535 +2020-08-21 14:45:00,115.99,34.4,43.162,29.535 +2020-08-21 15:00:00,108.46,35.311,44.91,29.535 +2020-08-21 15:15:00,102.35,33.022,44.91,29.535 +2020-08-21 15:30:00,102.98,31.35,44.91,29.535 +2020-08-21 15:45:00,109.7,30.392,44.91,29.535 +2020-08-21 16:00:00,115.52,31.933000000000003,47.489,29.535 +2020-08-21 16:15:00,117.53,32.324,47.489,29.535 +2020-08-21 16:30:00,114.19,31.936,47.489,29.535 +2020-08-21 16:45:00,114.63,29.162,47.489,29.535 +2020-08-21 17:00:00,114.87,33.342,52.047,29.535 +2020-08-21 17:15:00,112.6,34.046,52.047,29.535 +2020-08-21 17:30:00,111.56,33.547,52.047,29.535 +2020-08-21 17:45:00,119.81,33.025999999999996,52.047,29.535 +2020-08-21 18:00:00,118.61,35.549,53.306000000000004,29.535 +2020-08-21 18:15:00,111.71,34.506,53.306000000000004,29.535 +2020-08-21 18:30:00,109.02,32.798,53.306000000000004,29.535 +2020-08-21 18:45:00,109.32,36.089,53.306000000000004,29.535 +2020-08-21 19:00:00,104.75,37.998000000000005,53.516000000000005,29.535 +2020-08-21 19:15:00,103.22,37.463,53.516000000000005,29.535 +2020-08-21 19:30:00,105.19,36.463,53.516000000000005,29.535 +2020-08-21 19:45:00,109.91,35.064,53.516000000000005,29.535 +2020-08-21 20:00:00,108.19,32.015,57.88,29.535 +2020-08-21 20:15:00,105.08,32.567,57.88,29.535 +2020-08-21 20:30:00,99.01,32.235,57.88,29.535 +2020-08-21 20:45:00,95.41,31.636,57.88,29.535 +2020-08-21 21:00:00,90.21,31.29,53.32,29.535 +2020-08-21 21:15:00,89.55,34.330999999999996,53.32,29.535 +2020-08-21 21:30:00,92.66,34.534,53.32,29.535 +2020-08-21 21:45:00,91.64,34.841,53.32,29.535 +2020-08-21 22:00:00,87.68,32.082,48.074,29.535 +2020-08-21 22:15:00,84.08,33.724000000000004,48.074,29.535 +2020-08-21 22:30:00,86.02,32.883,48.074,29.535 +2020-08-21 22:45:00,85.3,30.55,48.074,29.535 +2020-08-21 23:00:00,77.83,30.086,41.306999999999995,29.535 +2020-08-21 23:15:00,71.95,27.67,41.306999999999995,29.535 +2020-08-21 23:30:00,73.74,25.559,41.306999999999995,29.535 +2020-08-21 23:45:00,71.29,24.608,41.306999999999995,29.535 +2020-08-22 00:00:00,69.81,20.534000000000002,40.227,29.423000000000002 +2020-08-22 00:15:00,69.02,20.857,40.227,29.423000000000002 +2020-08-22 00:30:00,67.89,19.826,40.227,29.423000000000002 +2020-08-22 00:45:00,72.34,19.723,40.227,29.423000000000002 +2020-08-22 01:00:00,75.79,19.339000000000002,36.303000000000004,29.423000000000002 +2020-08-22 01:15:00,76.02,18.655,36.303000000000004,29.423000000000002 +2020-08-22 01:30:00,75.61,17.533,36.303000000000004,29.423000000000002 +2020-08-22 01:45:00,73.35,18.16,36.303000000000004,29.423000000000002 +2020-08-22 02:00:00,74.97,18.018,33.849000000000004,29.423000000000002 +2020-08-22 02:15:00,75.4,16.616,33.849000000000004,29.423000000000002 +2020-08-22 02:30:00,69.28,17.753,33.849000000000004,29.423000000000002 +2020-08-22 02:45:00,66.05,18.517,33.849000000000004,29.423000000000002 +2020-08-22 03:00:00,72.84,18.707,33.149,29.423000000000002 +2020-08-22 03:15:00,73.03,16.8,33.149,29.423000000000002 +2020-08-22 03:30:00,67.68,16.592,33.149,29.423000000000002 +2020-08-22 03:45:00,66.88,18.373,33.149,29.423000000000002 +2020-08-22 04:00:00,69.12,21.037,32.501,29.423000000000002 +2020-08-22 04:15:00,67.9,24.219,32.501,29.423000000000002 +2020-08-22 04:30:00,68.89,21.195999999999998,32.501,29.423000000000002 +2020-08-22 04:45:00,75.53,20.994,32.501,29.423000000000002 +2020-08-22 05:00:00,76.39,25.175,31.648000000000003,29.423000000000002 +2020-08-22 05:15:00,77.34,23.013,31.648000000000003,29.423000000000002 +2020-08-22 05:30:00,70.85,20.104,31.648000000000003,29.423000000000002 +2020-08-22 05:45:00,75.88,21.084,31.648000000000003,29.423000000000002 +2020-08-22 06:00:00,79.9,31.51,32.552,29.423000000000002 +2020-08-22 06:15:00,81.93,38.769,32.552,29.423000000000002 +2020-08-22 06:30:00,78.71,35.705999999999996,32.552,29.423000000000002 +2020-08-22 06:45:00,78.42,35.068000000000005,32.552,29.423000000000002 +2020-08-22 07:00:00,81.49,34.53,35.181999999999995,29.423000000000002 +2020-08-22 07:15:00,83.52,35.321999999999996,35.181999999999995,29.423000000000002 +2020-08-22 07:30:00,88.71,33.359,35.181999999999995,29.423000000000002 +2020-08-22 07:45:00,83.28,34.743,35.181999999999995,29.423000000000002 +2020-08-22 08:00:00,87.36,33.074,40.35,29.423000000000002 +2020-08-22 08:15:00,83.56,35.699,40.35,29.423000000000002 +2020-08-22 08:30:00,83.76,35.982,40.35,29.423000000000002 +2020-08-22 08:45:00,86.8,38.474000000000004,40.35,29.423000000000002 +2020-08-22 09:00:00,91.83,34.203,42.292,29.423000000000002 +2020-08-22 09:15:00,94.77,35.471,42.292,29.423000000000002 +2020-08-22 09:30:00,93.76,38.058,42.292,29.423000000000002 +2020-08-22 09:45:00,86.84,40.493,42.292,29.423000000000002 +2020-08-22 10:00:00,92.6,37.429,40.084,29.423000000000002 +2020-08-22 10:15:00,82.43,38.467,40.084,29.423000000000002 +2020-08-22 10:30:00,84.17,38.509,40.084,29.423000000000002 +2020-08-22 10:45:00,83.75,39.282,40.084,29.423000000000002 +2020-08-22 11:00:00,82.45,37.674,36.966,29.423000000000002 +2020-08-22 11:15:00,78.36,38.518,36.966,29.423000000000002 +2020-08-22 11:30:00,80.55,39.266,36.966,29.423000000000002 +2020-08-22 11:45:00,84.46,39.841,36.966,29.423000000000002 +2020-08-22 12:00:00,80.83,36.912,35.19,29.423000000000002 +2020-08-22 12:15:00,81.73,36.481,35.19,29.423000000000002 +2020-08-22 12:30:00,83.66,35.539,35.19,29.423000000000002 +2020-08-22 12:45:00,83.6,36.244,35.19,29.423000000000002 +2020-08-22 13:00:00,80.22,35.459,32.277,29.423000000000002 +2020-08-22 13:15:00,81.83,35.946,32.277,29.423000000000002 +2020-08-22 13:30:00,79.68,35.398,32.277,29.423000000000002 +2020-08-22 13:45:00,76.05,34.327,32.277,29.423000000000002 +2020-08-22 14:00:00,76.01,34.762,31.436999999999998,29.423000000000002 +2020-08-22 14:15:00,76.05,33.229,31.436999999999998,29.423000000000002 +2020-08-22 14:30:00,73.17,33.400999999999996,31.436999999999998,29.423000000000002 +2020-08-22 14:45:00,73.18,33.904,31.436999999999998,29.423000000000002 +2020-08-22 15:00:00,73.62,35.177,33.493,29.423000000000002 +2020-08-22 15:15:00,75.64,33.522,33.493,29.423000000000002 +2020-08-22 15:30:00,75.93,32.013000000000005,33.493,29.423000000000002 +2020-08-22 15:45:00,75.66,30.217,33.493,29.423000000000002 +2020-08-22 16:00:00,74.81,33.667,36.593,29.423000000000002 +2020-08-22 16:15:00,75.25,33.234,36.593,29.423000000000002 +2020-08-22 16:30:00,75.96,33.058,36.593,29.423000000000002 +2020-08-22 16:45:00,78.59,30.281,36.593,29.423000000000002 +2020-08-22 17:00:00,80.01,33.37,42.049,29.423000000000002 +2020-08-22 17:15:00,81.17,32.102,42.049,29.423000000000002 +2020-08-22 17:30:00,80.84,31.496,42.049,29.423000000000002 +2020-08-22 17:45:00,79.99,31.468000000000004,42.049,29.423000000000002 +2020-08-22 18:00:00,83.24,35.213,43.755,29.423000000000002 +2020-08-22 18:15:00,78.95,35.67,43.755,29.423000000000002 +2020-08-22 18:30:00,78.74,35.183,43.755,29.423000000000002 +2020-08-22 18:45:00,79.68,35.374,43.755,29.423000000000002 +2020-08-22 19:00:00,79.36,35.782,44.492,29.423000000000002 +2020-08-22 19:15:00,78.0,34.344,44.492,29.423000000000002 +2020-08-22 19:30:00,78.76,34.03,44.492,29.423000000000002 +2020-08-22 19:45:00,78.12,34.227,44.492,29.423000000000002 +2020-08-22 20:00:00,75.97,31.935,40.896,29.423000000000002 +2020-08-22 20:15:00,75.27,31.884,40.896,29.423000000000002 +2020-08-22 20:30:00,77.26,30.746,40.896,29.423000000000002 +2020-08-22 20:45:00,74.04,31.799,40.896,29.423000000000002 +2020-08-22 21:00:00,72.99,30.090999999999998,39.056,29.423000000000002 +2020-08-22 21:15:00,69.37,32.803000000000004,39.056,29.423000000000002 +2020-08-22 21:30:00,65.99,33.161,39.056,29.423000000000002 +2020-08-22 21:45:00,65.64,32.949,39.056,29.423000000000002 +2020-08-22 22:00:00,62.66,30.133000000000003,38.478,29.423000000000002 +2020-08-22 22:15:00,62.96,31.954,38.478,29.423000000000002 +2020-08-22 22:30:00,60.65,30.666999999999998,38.478,29.423000000000002 +2020-08-22 22:45:00,60.45,28.648000000000003,38.478,29.423000000000002 +2020-08-22 23:00:00,57.7,27.596,32.953,29.423000000000002 +2020-08-22 23:15:00,56.86,25.565,32.953,29.423000000000002 +2020-08-22 23:30:00,56.41,25.7,32.953,29.423000000000002 +2020-08-22 23:45:00,56.12,25.105,32.953,29.423000000000002 +2020-08-23 00:00:00,54.05,21.918000000000003,28.584,29.423000000000002 +2020-08-23 00:15:00,54.14,21.205,28.584,29.423000000000002 +2020-08-23 00:30:00,51.82,20.051,28.584,29.423000000000002 +2020-08-23 00:45:00,52.75,19.846,28.584,29.423000000000002 +2020-08-23 01:00:00,50.79,19.685,26.419,29.423000000000002 +2020-08-23 01:15:00,51.91,18.892,26.419,29.423000000000002 +2020-08-23 01:30:00,52.0,17.651,26.419,29.423000000000002 +2020-08-23 01:45:00,51.21,17.939,26.419,29.423000000000002 +2020-08-23 02:00:00,49.98,17.88,25.335,29.423000000000002 +2020-08-23 02:15:00,49.28,17.148,25.335,29.423000000000002 +2020-08-23 02:30:00,49.62,18.421,25.335,29.423000000000002 +2020-08-23 02:45:00,50.01,18.914,25.335,29.423000000000002 +2020-08-23 03:00:00,49.63,19.677,24.805,29.423000000000002 +2020-08-23 03:15:00,50.61,18.027,24.805,29.423000000000002 +2020-08-23 03:30:00,51.05,17.143,24.805,29.423000000000002 +2020-08-23 03:45:00,51.04,18.202,24.805,29.423000000000002 +2020-08-23 04:00:00,52.83,20.862,25.772,29.423000000000002 +2020-08-23 04:15:00,53.3,23.68,25.772,29.423000000000002 +2020-08-23 04:30:00,53.83,21.873,25.772,29.423000000000002 +2020-08-23 04:45:00,55.23,21.249000000000002,25.772,29.423000000000002 +2020-08-23 05:00:00,53.76,25.686999999999998,25.971999999999998,29.423000000000002 +2020-08-23 05:15:00,53.31,22.923000000000002,25.971999999999998,29.423000000000002 +2020-08-23 05:30:00,52.98,19.655,25.971999999999998,29.423000000000002 +2020-08-23 05:45:00,54.2,20.352,25.971999999999998,29.423000000000002 +2020-08-23 06:00:00,55.52,28.631999999999998,26.026,29.423000000000002 +2020-08-23 06:15:00,56.17,36.676,26.026,29.423000000000002 +2020-08-23 06:30:00,57.6,32.996,26.026,29.423000000000002 +2020-08-23 06:45:00,58.37,31.48,26.026,29.423000000000002 +2020-08-23 07:00:00,60.01,31.19,27.396,29.423000000000002 +2020-08-23 07:15:00,59.93,30.531,27.396,29.423000000000002 +2020-08-23 07:30:00,61.88,29.76,27.396,29.423000000000002 +2020-08-23 07:45:00,62.61,31.128,27.396,29.423000000000002 +2020-08-23 08:00:00,63.28,30.125999999999998,30.791999999999998,29.423000000000002 +2020-08-23 08:15:00,61.72,33.788000000000004,30.791999999999998,29.423000000000002 +2020-08-23 08:30:00,60.05,34.823,30.791999999999998,29.423000000000002 +2020-08-23 08:45:00,60.37,37.19,30.791999999999998,29.423000000000002 +2020-08-23 09:00:00,57.88,32.838,32.482,29.423000000000002 +2020-08-23 09:15:00,58.54,33.64,32.482,29.423000000000002 +2020-08-23 09:30:00,61.36,36.603,32.482,29.423000000000002 +2020-08-23 09:45:00,64.5,39.924,32.482,29.423000000000002 +2020-08-23 10:00:00,65.45,37.297,31.951,29.423000000000002 +2020-08-23 10:15:00,63.8,38.42,31.951,29.423000000000002 +2020-08-23 10:30:00,69.36,38.641,31.951,29.423000000000002 +2020-08-23 10:45:00,69.5,40.378,31.951,29.423000000000002 +2020-08-23 11:00:00,68.42,38.444,33.619,29.423000000000002 +2020-08-23 11:15:00,68.97,38.867,33.619,29.423000000000002 +2020-08-23 11:30:00,65.45,40.113,33.619,29.423000000000002 +2020-08-23 11:45:00,65.73,40.89,33.619,29.423000000000002 +2020-08-23 12:00:00,62.07,38.953,30.975,29.423000000000002 +2020-08-23 12:15:00,60.77,37.865,30.975,29.423000000000002 +2020-08-23 12:30:00,57.24,37.19,30.975,29.423000000000002 +2020-08-23 12:45:00,59.1,37.333,30.975,29.423000000000002 +2020-08-23 13:00:00,54.41,36.266999999999996,27.956999999999997,29.423000000000002 +2020-08-23 13:15:00,56.3,36.116,27.956999999999997,29.423000000000002 +2020-08-23 13:30:00,54.14,34.552,27.956999999999997,29.423000000000002 +2020-08-23 13:45:00,57.34,34.536,27.956999999999997,29.423000000000002 +2020-08-23 14:00:00,54.95,36.039,25.555999999999997,29.423000000000002 +2020-08-23 14:15:00,55.93,34.866,25.555999999999997,29.423000000000002 +2020-08-23 14:30:00,53.34,33.878,25.555999999999997,29.423000000000002 +2020-08-23 14:45:00,52.83,33.415,25.555999999999997,29.423000000000002 +2020-08-23 15:00:00,52.56,34.832,26.271,29.423000000000002 +2020-08-23 15:15:00,52.26,32.414,26.271,29.423000000000002 +2020-08-23 15:30:00,53.99,30.730999999999998,26.271,29.423000000000002 +2020-08-23 15:45:00,55.72,29.178,26.271,29.423000000000002 +2020-08-23 16:00:00,60.31,31.070999999999998,30.369,29.423000000000002 +2020-08-23 16:15:00,60.83,30.884,30.369,29.423000000000002 +2020-08-23 16:30:00,63.75,31.62,30.369,29.423000000000002 +2020-08-23 16:45:00,67.7,28.939,30.369,29.423000000000002 +2020-08-23 17:00:00,71.4,32.325,38.787,29.423000000000002 +2020-08-23 17:15:00,73.13,32.479,38.787,29.423000000000002 +2020-08-23 17:30:00,74.84,32.615,38.787,29.423000000000002 +2020-08-23 17:45:00,76.19,32.934,38.787,29.423000000000002 +2020-08-23 18:00:00,78.8,37.244,41.886,29.423000000000002 +2020-08-23 18:15:00,78.18,37.311,41.886,29.423000000000002 +2020-08-23 18:30:00,76.17,36.661,41.886,29.423000000000002 +2020-08-23 18:45:00,77.31,36.91,41.886,29.423000000000002 +2020-08-23 19:00:00,79.28,39.316,42.91,29.423000000000002 +2020-08-23 19:15:00,79.78,36.865,42.91,29.423000000000002 +2020-08-23 19:30:00,81.08,36.332,42.91,29.423000000000002 +2020-08-23 19:45:00,80.3,36.093,42.91,29.423000000000002 +2020-08-23 20:00:00,79.94,33.946999999999996,42.148999999999994,29.423000000000002 +2020-08-23 20:15:00,79.46,33.751,42.148999999999994,29.423000000000002 +2020-08-23 20:30:00,78.65,33.303000000000004,42.148999999999994,29.423000000000002 +2020-08-23 20:45:00,78.43,32.828,42.148999999999994,29.423000000000002 +2020-08-23 21:00:00,77.51,31.070999999999998,40.955999999999996,29.423000000000002 +2020-08-23 21:15:00,78.17,33.514,40.955999999999996,29.423000000000002 +2020-08-23 21:30:00,74.81,33.278,40.955999999999996,29.423000000000002 +2020-08-23 21:45:00,74.82,33.356,40.955999999999996,29.423000000000002 +2020-08-23 22:00:00,70.62,32.385999999999996,39.873000000000005,29.423000000000002 +2020-08-23 22:15:00,71.54,32.687,39.873000000000005,29.423000000000002 +2020-08-23 22:30:00,70.08,30.976999999999997,39.873000000000005,29.423000000000002 +2020-08-23 22:45:00,70.5,27.828000000000003,39.873000000000005,29.423000000000002 +2020-08-23 23:00:00,65.77,26.604,35.510999999999996,29.423000000000002 +2020-08-23 23:15:00,68.03,25.668000000000003,35.510999999999996,29.423000000000002 +2020-08-23 23:30:00,68.09,25.31,35.510999999999996,29.423000000000002 +2020-08-23 23:45:00,67.17,24.844,35.510999999999996,29.423000000000002 +2020-08-24 00:00:00,61.92,23.346999999999998,33.475,29.535 +2020-08-24 00:15:00,61.51,23.287,33.475,29.535 +2020-08-24 00:30:00,62.23,21.79,33.475,29.535 +2020-08-24 00:45:00,64.91,21.246,33.475,29.535 +2020-08-24 01:00:00,63.88,21.413,33.111,29.535 +2020-08-24 01:15:00,64.28,20.656,33.111,29.535 +2020-08-24 01:30:00,63.48,19.754,33.111,29.535 +2020-08-24 01:45:00,64.2,19.951,33.111,29.535 +2020-08-24 02:00:00,63.59,20.304000000000002,32.358000000000004,29.535 +2020-08-24 02:15:00,65.17,18.646,32.358000000000004,29.535 +2020-08-24 02:30:00,66.78,20.039,32.358000000000004,29.535 +2020-08-24 02:45:00,69.86,20.409000000000002,32.358000000000004,29.535 +2020-08-24 03:00:00,74.42,21.589000000000002,30.779,29.535 +2020-08-24 03:15:00,75.12,20.570999999999998,30.779,29.535 +2020-08-24 03:30:00,69.75,20.315,30.779,29.535 +2020-08-24 03:45:00,71.44,20.962,30.779,29.535 +2020-08-24 04:00:00,77.38,26.358,31.416,29.535 +2020-08-24 04:15:00,84.0,31.753,31.416,29.535 +2020-08-24 04:30:00,88.97,29.471,31.416,29.535 +2020-08-24 04:45:00,90.63,29.182,31.416,29.535 +2020-08-24 05:00:00,92.59,39.953,37.221,29.535 +2020-08-24 05:15:00,98.51,46.222,37.221,29.535 +2020-08-24 05:30:00,97.7,41.567,37.221,29.535 +2020-08-24 05:45:00,106.99,39.55,37.221,29.535 +2020-08-24 06:00:00,114.75,38.809,51.891000000000005,29.535 +2020-08-24 06:15:00,115.62,38.935,51.891000000000005,29.535 +2020-08-24 06:30:00,114.57,38.699,51.891000000000005,29.535 +2020-08-24 06:45:00,111.02,41.791000000000004,51.891000000000005,29.535 +2020-08-24 07:00:00,116.17,41.681999999999995,62.282,29.535 +2020-08-24 07:15:00,117.57,43.071000000000005,62.282,29.535 +2020-08-24 07:30:00,116.26,41.563,62.282,29.535 +2020-08-24 07:45:00,111.86,43.251999999999995,62.282,29.535 +2020-08-24 08:00:00,112.24,40.347,54.102,29.535 +2020-08-24 08:15:00,110.91,42.93600000000001,54.102,29.535 +2020-08-24 08:30:00,107.9,43.183,54.102,29.535 +2020-08-24 08:45:00,106.7,45.798,54.102,29.535 +2020-08-24 09:00:00,103.97,40.516999999999996,50.917,29.535 +2020-08-24 09:15:00,112.8,39.77,50.917,29.535 +2020-08-24 09:30:00,109.89,41.897,50.917,29.535 +2020-08-24 09:45:00,115.07,43.108000000000004,50.917,29.535 +2020-08-24 10:00:00,107.02,40.842,49.718999999999994,29.535 +2020-08-24 10:15:00,116.52,41.815,49.718999999999994,29.535 +2020-08-24 10:30:00,119.9,41.635,49.718999999999994,29.535 +2020-08-24 10:45:00,119.31,41.896,49.718999999999994,29.535 +2020-08-24 11:00:00,116.15,40.231,49.833999999999996,29.535 +2020-08-24 11:15:00,121.04,40.848,49.833999999999996,29.535 +2020-08-24 11:30:00,121.33,42.687,49.833999999999996,29.535 +2020-08-24 11:45:00,120.83,43.92,49.833999999999996,29.535 +2020-08-24 12:00:00,115.38,40.277,47.832,29.535 +2020-08-24 12:15:00,118.08,39.281,47.832,29.535 +2020-08-24 12:30:00,110.62,37.626999999999995,47.832,29.535 +2020-08-24 12:45:00,109.16,37.7,47.832,29.535 +2020-08-24 13:00:00,106.38,37.482,48.03,29.535 +2020-08-24 13:15:00,100.21,36.554,48.03,29.535 +2020-08-24 13:30:00,103.29,35.159,48.03,29.535 +2020-08-24 13:45:00,105.95,35.975,48.03,29.535 +2020-08-24 14:00:00,103.3,36.626999999999995,48.157,29.535 +2020-08-24 14:15:00,99.66,36.027,48.157,29.535 +2020-08-24 14:30:00,103.17,34.923,48.157,29.535 +2020-08-24 14:45:00,110.25,36.366,48.157,29.535 +2020-08-24 15:00:00,115.96,37.335,48.897,29.535 +2020-08-24 15:15:00,112.33,34.433,48.897,29.535 +2020-08-24 15:30:00,104.1,33.501,48.897,29.535 +2020-08-24 15:45:00,99.37,31.523000000000003,48.897,29.535 +2020-08-24 16:00:00,108.59,34.434,51.446000000000005,29.535 +2020-08-24 16:15:00,110.63,34.338,51.446000000000005,29.535 +2020-08-24 16:30:00,113.83,34.496,51.446000000000005,29.535 +2020-08-24 16:45:00,113.22,31.912,51.446000000000005,29.535 +2020-08-24 17:00:00,108.34,34.234,57.507,29.535 +2020-08-24 17:15:00,106.39,34.789,57.507,29.535 +2020-08-24 17:30:00,107.38,34.6,57.507,29.535 +2020-08-24 17:45:00,112.93,34.635999999999996,57.507,29.535 +2020-08-24 18:00:00,113.44,37.981,57.896,29.535 +2020-08-24 18:15:00,110.9,36.451,57.896,29.535 +2020-08-24 18:30:00,108.43,35.108000000000004,57.896,29.535 +2020-08-24 18:45:00,106.18,38.216,57.896,29.535 +2020-08-24 19:00:00,105.21,40.431,57.891999999999996,29.535 +2020-08-24 19:15:00,109.56,39.202,57.891999999999996,29.535 +2020-08-24 19:30:00,106.94,38.328,57.891999999999996,29.535 +2020-08-24 19:45:00,101.03,37.554,57.891999999999996,29.535 +2020-08-24 20:00:00,100.29,34.311,64.57300000000001,29.535 +2020-08-24 20:15:00,98.55,35.45,64.57300000000001,29.535 +2020-08-24 20:30:00,97.25,35.505,64.57300000000001,29.535 +2020-08-24 20:45:00,101.29,35.153,64.57300000000001,29.535 +2020-08-24 21:00:00,99.14,32.809,59.431999999999995,29.535 +2020-08-24 21:15:00,93.54,35.671,59.431999999999995,29.535 +2020-08-24 21:30:00,84.27,35.83,59.431999999999995,29.535 +2020-08-24 21:45:00,83.92,35.689,59.431999999999995,29.535 +2020-08-24 22:00:00,75.13,32.900999999999996,51.519,29.535 +2020-08-24 22:15:00,79.97,35.023,51.519,29.535 +2020-08-24 22:30:00,74.93,29.565,51.519,29.535 +2020-08-24 22:45:00,75.97,26.509,51.519,29.535 +2020-08-24 23:00:00,72.01,25.331,44.501000000000005,29.535 +2020-08-24 23:15:00,73.98,22.824,44.501000000000005,29.535 +2020-08-24 23:30:00,73.76,22.38,44.501000000000005,29.535 +2020-08-24 23:45:00,72.64,21.371,44.501000000000005,29.535 +2020-08-25 00:00:00,71.72,21.445,44.522,29.535 +2020-08-25 00:15:00,71.62,22.142,44.522,29.535 +2020-08-25 00:30:00,70.03,21.316999999999997,44.522,29.535 +2020-08-25 00:45:00,71.88,21.523000000000003,44.522,29.535 +2020-08-25 01:00:00,71.19,21.183000000000003,41.441,29.535 +2020-08-25 01:15:00,72.36,20.55,41.441,29.535 +2020-08-25 01:30:00,70.95,19.538,41.441,29.535 +2020-08-25 01:45:00,71.76,19.262,41.441,29.535 +2020-08-25 02:00:00,70.7,19.179000000000002,40.203,29.535 +2020-08-25 02:15:00,69.84,18.6,40.203,29.535 +2020-08-25 02:30:00,70.82,19.628,40.203,29.535 +2020-08-25 02:45:00,70.92,20.293,40.203,29.535 +2020-08-25 03:00:00,72.33,20.983,39.536,29.535 +2020-08-25 03:15:00,72.62,20.854,39.536,29.535 +2020-08-25 03:30:00,75.29,20.564,39.536,29.535 +2020-08-25 03:45:00,77.05,20.184,39.536,29.535 +2020-08-25 04:00:00,84.41,24.456,40.759,29.535 +2020-08-25 04:15:00,89.97,29.886,40.759,29.535 +2020-08-25 04:30:00,92.14,27.509,40.759,29.535 +2020-08-25 04:45:00,92.03,27.641,40.759,29.535 +2020-08-25 05:00:00,99.0,39.819,43.623999999999995,29.535 +2020-08-25 05:15:00,107.55,46.723,43.623999999999995,29.535 +2020-08-25 05:30:00,109.53,42.256,43.623999999999995,29.535 +2020-08-25 05:45:00,114.17,39.54,43.623999999999995,29.535 +2020-08-25 06:00:00,113.12,39.746,52.684,29.535 +2020-08-25 06:15:00,114.58,40.044000000000004,52.684,29.535 +2020-08-25 06:30:00,116.2,39.524,52.684,29.535 +2020-08-25 06:45:00,116.67,41.817,52.684,29.535 +2020-08-25 07:00:00,123.26,41.85,62.676,29.535 +2020-08-25 07:15:00,127.76,43.023,62.676,29.535 +2020-08-25 07:30:00,128.42,41.568000000000005,62.676,29.535 +2020-08-25 07:45:00,121.39,42.361999999999995,62.676,29.535 +2020-08-25 08:00:00,117.12,39.391,56.161,29.535 +2020-08-25 08:15:00,112.74,41.563,56.161,29.535 +2020-08-25 08:30:00,113.5,41.963,56.161,29.535 +2020-08-25 08:45:00,116.16,43.723,56.161,29.535 +2020-08-25 09:00:00,118.01,38.784,52.132,29.535 +2020-08-25 09:15:00,126.14,37.858000000000004,52.132,29.535 +2020-08-25 09:30:00,124.08,40.582,52.132,29.535 +2020-08-25 09:45:00,118.84,43.004,52.132,29.535 +2020-08-25 10:00:00,117.53,39.493,51.032,29.535 +2020-08-25 10:15:00,121.68,40.346,51.032,29.535 +2020-08-25 10:30:00,120.94,40.177,51.032,29.535 +2020-08-25 10:45:00,113.26,41.349,51.032,29.535 +2020-08-25 11:00:00,109.61,39.652,51.085,29.535 +2020-08-25 11:15:00,117.1,40.645,51.085,29.535 +2020-08-25 11:30:00,122.66,41.457,51.085,29.535 +2020-08-25 11:45:00,124.89,42.236000000000004,51.085,29.535 +2020-08-25 12:00:00,115.6,38.455,49.049,29.535 +2020-08-25 12:15:00,113.66,37.78,49.049,29.535 +2020-08-25 12:30:00,120.05,36.912,49.049,29.535 +2020-08-25 12:45:00,118.16,37.631,49.049,29.535 +2020-08-25 13:00:00,117.18,37.067,49.722,29.535 +2020-08-25 13:15:00,108.95,37.815,49.722,29.535 +2020-08-25 13:30:00,104.14,36.343,49.722,29.535 +2020-08-25 13:45:00,104.85,36.264,49.722,29.535 +2020-08-25 14:00:00,109.46,37.303000000000004,49.565,29.535 +2020-08-25 14:15:00,108.21,36.518,49.565,29.535 +2020-08-25 14:30:00,106.98,35.711,49.565,29.535 +2020-08-25 14:45:00,101.38,36.424,49.565,29.535 +2020-08-25 15:00:00,98.49,37.22,51.108999999999995,29.535 +2020-08-25 15:15:00,103.6,35.174,51.108999999999995,29.535 +2020-08-25 15:30:00,106.69,34.051,51.108999999999995,29.535 +2020-08-25 15:45:00,107.37,32.419000000000004,51.108999999999995,29.535 +2020-08-25 16:00:00,106.02,34.625,52.725,29.535 +2020-08-25 16:15:00,105.04,34.601,52.725,29.535 +2020-08-25 16:30:00,111.46,34.395,52.725,29.535 +2020-08-25 16:45:00,113.59,32.505,52.725,29.535 +2020-08-25 17:00:00,115.04,34.974000000000004,58.031000000000006,29.535 +2020-08-25 17:15:00,109.32,35.943000000000005,58.031000000000006,29.535 +2020-08-25 17:30:00,115.28,35.338,58.031000000000006,29.535 +2020-08-25 17:45:00,115.65,35.104,58.031000000000006,29.535 +2020-08-25 18:00:00,116.18,37.575,58.338,29.535 +2020-08-25 18:15:00,111.28,37.442,58.338,29.535 +2020-08-25 18:30:00,110.08,35.876999999999995,58.338,29.535 +2020-08-25 18:45:00,108.95,38.782,58.338,29.535 +2020-08-25 19:00:00,112.98,39.938,58.464,29.535 +2020-08-25 19:15:00,115.45,38.88,58.464,29.535 +2020-08-25 19:30:00,112.47,37.827,58.464,29.535 +2020-08-25 19:45:00,107.22,37.361999999999995,58.464,29.535 +2020-08-25 20:00:00,101.92,34.48,63.708,29.535 +2020-08-25 20:15:00,107.45,34.316,63.708,29.535 +2020-08-25 20:30:00,106.27,34.363,63.708,29.535 +2020-08-25 20:45:00,103.99,34.378,63.708,29.535 +2020-08-25 21:00:00,94.4,32.854,57.06399999999999,29.535 +2020-08-25 21:15:00,95.17,34.338,57.06399999999999,29.535 +2020-08-25 21:30:00,89.29,34.676,57.06399999999999,29.535 +2020-08-25 21:45:00,87.19,34.677,57.06399999999999,29.535 +2020-08-25 22:00:00,78.39,31.979,52.831,29.535 +2020-08-25 22:15:00,81.0,33.783,52.831,29.535 +2020-08-25 22:30:00,76.91,28.561,52.831,29.535 +2020-08-25 22:45:00,80.86,25.493000000000002,52.831,29.535 +2020-08-25 23:00:00,74.5,23.654,44.717,29.535 +2020-08-25 23:15:00,72.88,22.587,44.717,29.535 +2020-08-25 23:30:00,72.05,22.16,44.717,29.535 +2020-08-25 23:45:00,72.32,21.335,44.717,29.535 +2020-08-26 00:00:00,71.27,21.596,41.263000000000005,29.535 +2020-08-26 00:15:00,72.44,22.294,41.263000000000005,29.535 +2020-08-26 00:30:00,72.68,21.475,41.263000000000005,29.535 +2020-08-26 00:45:00,73.43,21.688000000000002,41.263000000000005,29.535 +2020-08-26 01:00:00,69.7,21.326,38.448,29.535 +2020-08-26 01:15:00,71.13,20.708000000000002,38.448,29.535 +2020-08-26 01:30:00,67.88,19.711,38.448,29.535 +2020-08-26 01:45:00,71.52,19.434,38.448,29.535 +2020-08-26 02:00:00,69.26,19.352999999999998,36.471,29.535 +2020-08-26 02:15:00,69.62,18.797,36.471,29.535 +2020-08-26 02:30:00,70.17,19.801,36.471,29.535 +2020-08-26 02:45:00,71.2,20.463,36.471,29.535 +2020-08-26 03:00:00,72.48,21.142,36.042,29.535 +2020-08-26 03:15:00,72.93,21.028000000000002,36.042,29.535 +2020-08-26 03:30:00,74.6,20.743000000000002,36.042,29.535 +2020-08-26 03:45:00,77.43,20.357,36.042,29.535 +2020-08-26 04:00:00,82.27,24.653000000000002,36.705,29.535 +2020-08-26 04:15:00,88.85,30.103,36.705,29.535 +2020-08-26 04:30:00,91.76,27.734,36.705,29.535 +2020-08-26 04:45:00,93.35,27.868000000000002,36.705,29.535 +2020-08-26 05:00:00,101.37,40.118,39.716,29.535 +2020-08-26 05:15:00,108.33,47.097,39.716,29.535 +2020-08-26 05:30:00,110.12,42.623000000000005,39.716,29.535 +2020-08-26 05:45:00,111.53,39.868,39.716,29.535 +2020-08-26 06:00:00,112.68,40.042,52.756,29.535 +2020-08-26 06:15:00,116.55,40.361999999999995,52.756,29.535 +2020-08-26 06:30:00,120.01,39.836999999999996,52.756,29.535 +2020-08-26 06:45:00,121.27,42.13,52.756,29.535 +2020-08-26 07:00:00,119.57,42.163000000000004,65.977,29.535 +2020-08-26 07:15:00,114.72,43.35,65.977,29.535 +2020-08-26 07:30:00,121.57,41.919,65.977,29.535 +2020-08-26 07:45:00,118.18,42.715,65.977,29.535 +2020-08-26 08:00:00,118.81,39.744,57.927,29.535 +2020-08-26 08:15:00,116.2,41.883,57.927,29.535 +2020-08-26 08:30:00,118.47,42.278,57.927,29.535 +2020-08-26 08:45:00,117.16,44.023999999999994,57.927,29.535 +2020-08-26 09:00:00,113.77,39.093,54.86,29.535 +2020-08-26 09:15:00,109.86,38.158,54.86,29.535 +2020-08-26 09:30:00,114.74,40.864000000000004,54.86,29.535 +2020-08-26 09:45:00,115.54,43.261,54.86,29.535 +2020-08-26 10:00:00,115.19,39.749,52.818000000000005,29.535 +2020-08-26 10:15:00,109.01,40.578,52.818000000000005,29.535 +2020-08-26 10:30:00,111.28,40.4,52.818000000000005,29.535 +2020-08-26 10:45:00,114.2,41.563,52.818000000000005,29.535 +2020-08-26 11:00:00,112.86,39.876,52.937,29.535 +2020-08-26 11:15:00,109.67,40.861,52.937,29.535 +2020-08-26 11:30:00,106.25,41.67,52.937,29.535 +2020-08-26 11:45:00,111.83,42.434,52.937,29.535 +2020-08-26 12:00:00,109.68,38.638000000000005,50.826,29.535 +2020-08-26 12:15:00,109.43,37.951,50.826,29.535 +2020-08-26 12:30:00,102.87,37.102,50.826,29.535 +2020-08-26 12:45:00,108.4,37.808,50.826,29.535 +2020-08-26 13:00:00,105.9,37.226,50.556000000000004,29.535 +2020-08-26 13:15:00,105.95,37.961999999999996,50.556000000000004,29.535 +2020-08-26 13:30:00,101.59,36.486999999999995,50.556000000000004,29.535 +2020-08-26 13:45:00,99.54,36.418,50.556000000000004,29.535 +2020-08-26 14:00:00,100.94,37.431999999999995,51.188,29.535 +2020-08-26 14:15:00,105.78,36.654,51.188,29.535 +2020-08-26 14:30:00,108.59,35.864000000000004,51.188,29.535 +2020-08-26 14:45:00,109.21,36.579,51.188,29.535 +2020-08-26 15:00:00,104.05,37.327,52.976000000000006,29.535 +2020-08-26 15:15:00,107.09,35.291,52.976000000000006,29.535 +2020-08-26 15:30:00,108.02,34.181,52.976000000000006,29.535 +2020-08-26 15:45:00,108.26,32.556,52.976000000000006,29.535 +2020-08-26 16:00:00,107.58,34.734,55.463,29.535 +2020-08-26 16:15:00,106.64,34.72,55.463,29.535 +2020-08-26 16:30:00,106.57,34.516,55.463,29.535 +2020-08-26 16:45:00,108.55,32.669000000000004,55.463,29.535 +2020-08-26 17:00:00,113.88,35.105,59.435,29.535 +2020-08-26 17:15:00,118.13,36.104,59.435,29.535 +2020-08-26 17:30:00,118.87,35.510999999999996,59.435,29.535 +2020-08-26 17:45:00,113.11,35.313,59.435,29.535 +2020-08-26 18:00:00,113.43,37.775,61.387,29.535 +2020-08-26 18:15:00,112.92,37.656,61.387,29.535 +2020-08-26 18:30:00,117.05,36.104,61.387,29.535 +2020-08-26 18:45:00,113.66,39.007,61.387,29.535 +2020-08-26 19:00:00,116.87,40.169000000000004,63.323,29.535 +2020-08-26 19:15:00,114.49,39.111,63.323,29.535 +2020-08-26 19:30:00,115.09,38.061,63.323,29.535 +2020-08-26 19:45:00,114.82,37.6,63.323,29.535 +2020-08-26 20:00:00,107.23,34.726,69.083,29.535 +2020-08-26 20:15:00,102.55,34.564,69.083,29.535 +2020-08-26 20:30:00,99.74,34.594,69.083,29.535 +2020-08-26 20:45:00,99.86,34.573,69.083,29.535 +2020-08-26 21:00:00,92.47,33.052,59.957,29.535 +2020-08-26 21:15:00,89.95,34.527,59.957,29.535 +2020-08-26 21:30:00,86.5,34.865,59.957,29.535 +2020-08-26 21:45:00,86.61,34.833,59.957,29.535 +2020-08-26 22:00:00,81.97,32.114000000000004,53.821000000000005,29.535 +2020-08-26 22:15:00,83.28,33.902,53.821000000000005,29.535 +2020-08-26 22:30:00,79.53,28.649,53.821000000000005,29.535 +2020-08-26 22:45:00,84.8,25.583000000000002,53.821000000000005,29.535 +2020-08-26 23:00:00,81.23,23.787,45.458,29.535 +2020-08-26 23:15:00,79.13,22.703000000000003,45.458,29.535 +2020-08-26 23:30:00,78.99,22.287,45.458,29.535 +2020-08-26 23:45:00,78.9,21.468000000000004,45.458,29.535 +2020-08-27 00:00:00,71.33,21.749000000000002,40.36,29.535 +2020-08-27 00:15:00,73.36,22.448,40.36,29.535 +2020-08-27 00:30:00,73.38,21.634,40.36,29.535 +2020-08-27 00:45:00,73.37,21.854,40.36,29.535 +2020-08-27 01:00:00,70.34,21.473000000000003,38.552,29.535 +2020-08-27 01:15:00,71.82,20.868000000000002,38.552,29.535 +2020-08-27 01:30:00,71.13,19.885,38.552,29.535 +2020-08-27 01:45:00,71.82,19.609,38.552,29.535 +2020-08-27 02:00:00,69.67,19.53,36.895,29.535 +2020-08-27 02:15:00,71.68,18.995,36.895,29.535 +2020-08-27 02:30:00,71.41,19.977,36.895,29.535 +2020-08-27 02:45:00,72.88,20.635,36.895,29.535 +2020-08-27 03:00:00,74.73,21.303,36.565,29.535 +2020-08-27 03:15:00,73.5,21.204,36.565,29.535 +2020-08-27 03:30:00,74.7,20.924,36.565,29.535 +2020-08-27 03:45:00,77.44,20.531,36.565,29.535 +2020-08-27 04:00:00,80.24,24.855,37.263000000000005,29.535 +2020-08-27 04:15:00,84.26,30.322,37.263000000000005,29.535 +2020-08-27 04:30:00,88.93,27.961,37.263000000000005,29.535 +2020-08-27 04:45:00,92.35,28.099,37.263000000000005,29.535 +2020-08-27 05:00:00,100.04,40.422,40.412,29.535 +2020-08-27 05:15:00,101.99,47.481,40.412,29.535 +2020-08-27 05:30:00,103.56,43.0,40.412,29.535 +2020-08-27 05:45:00,108.21,40.204,40.412,29.535 +2020-08-27 06:00:00,111.6,40.345,49.825,29.535 +2020-08-27 06:15:00,115.35,40.686,49.825,29.535 +2020-08-27 06:30:00,124.04,40.156,49.825,29.535 +2020-08-27 06:45:00,126.88,42.449,49.825,29.535 +2020-08-27 07:00:00,129.89,42.482,61.082,29.535 +2020-08-27 07:15:00,127.93,43.683,61.082,29.535 +2020-08-27 07:30:00,132.59,42.276,61.082,29.535 +2020-08-27 07:45:00,134.35,43.071999999999996,61.082,29.535 +2020-08-27 08:00:00,130.7,40.103,53.961999999999996,29.535 +2020-08-27 08:15:00,129.47,42.208999999999996,53.961999999999996,29.535 +2020-08-27 08:30:00,135.31,42.599,53.961999999999996,29.535 +2020-08-27 08:45:00,130.08,44.33,53.961999999999996,29.535 +2020-08-27 09:00:00,132.01,39.407,50.06100000000001,29.535 +2020-08-27 09:15:00,137.15,38.461999999999996,50.06100000000001,29.535 +2020-08-27 09:30:00,136.56,41.151,50.06100000000001,29.535 +2020-08-27 09:45:00,138.15,43.523999999999994,50.06100000000001,29.535 +2020-08-27 10:00:00,140.11,40.01,47.68,29.535 +2020-08-27 10:15:00,137.51,40.811,47.68,29.535 +2020-08-27 10:30:00,134.24,40.628,47.68,29.535 +2020-08-27 10:45:00,138.17,41.781000000000006,47.68,29.535 +2020-08-27 11:00:00,132.89,40.105,45.93899999999999,29.535 +2020-08-27 11:15:00,126.1,41.08,45.93899999999999,29.535 +2020-08-27 11:30:00,131.52,41.886,45.93899999999999,29.535 +2020-08-27 11:45:00,134.76,42.635,45.93899999999999,29.535 +2020-08-27 12:00:00,133.16,38.823,43.648999999999994,29.535 +2020-08-27 12:15:00,130.96,38.125,43.648999999999994,29.535 +2020-08-27 12:30:00,125.67,37.294000000000004,43.648999999999994,29.535 +2020-08-27 12:45:00,134.33,37.988,43.648999999999994,29.535 +2020-08-27 13:00:00,127.85,37.389,42.801,29.535 +2020-08-27 13:15:00,124.91,38.113,42.801,29.535 +2020-08-27 13:30:00,127.6,36.634,42.801,29.535 +2020-08-27 13:45:00,132.81,36.576,42.801,29.535 +2020-08-27 14:00:00,129.82,37.563,43.24,29.535 +2020-08-27 14:15:00,126.07,36.792,43.24,29.535 +2020-08-27 14:30:00,121.33,36.021,43.24,29.535 +2020-08-27 14:45:00,126.96,36.736999999999995,43.24,29.535 +2020-08-27 15:00:00,125.31,37.437,45.04600000000001,29.535 +2020-08-27 15:15:00,123.64,35.409,45.04600000000001,29.535 +2020-08-27 15:30:00,117.09,34.314,45.04600000000001,29.535 +2020-08-27 15:45:00,113.24,32.696,45.04600000000001,29.535 +2020-08-27 16:00:00,115.1,34.846,46.568000000000005,29.535 +2020-08-27 16:15:00,117.47,34.841,46.568000000000005,29.535 +2020-08-27 16:30:00,113.23,34.639,46.568000000000005,29.535 +2020-08-27 16:45:00,113.72,32.836999999999996,46.568000000000005,29.535 +2020-08-27 17:00:00,116.96,35.239000000000004,50.618,29.535 +2020-08-27 17:15:00,113.6,36.266999999999996,50.618,29.535 +2020-08-27 17:30:00,111.58,35.686,50.618,29.535 +2020-08-27 17:45:00,112.19,35.525999999999996,50.618,29.535 +2020-08-27 18:00:00,111.35,37.977,52.806999999999995,29.535 +2020-08-27 18:15:00,110.44,37.874,52.806999999999995,29.535 +2020-08-27 18:30:00,108.59,36.330999999999996,52.806999999999995,29.535 +2020-08-27 18:45:00,108.94,39.234,52.806999999999995,29.535 +2020-08-27 19:00:00,111.09,40.403,53.464,29.535 +2020-08-27 19:15:00,108.1,39.346,53.464,29.535 +2020-08-27 19:30:00,108.89,38.298,53.464,29.535 +2020-08-27 19:45:00,104.8,37.842,53.464,29.535 +2020-08-27 20:00:00,99.49,34.976,56.753,29.535 +2020-08-27 20:15:00,100.21,34.816,56.753,29.535 +2020-08-27 20:30:00,96.74,34.83,56.753,29.535 +2020-08-27 20:45:00,94.89,34.772,56.753,29.535 +2020-08-27 21:00:00,91.41,33.254,52.506,29.535 +2020-08-27 21:15:00,90.08,34.719,52.506,29.535 +2020-08-27 21:30:00,85.55,35.056999999999995,52.506,29.535 +2020-08-27 21:45:00,85.17,34.993,52.506,29.535 +2020-08-27 22:00:00,80.57,32.251999999999995,48.163000000000004,29.535 +2020-08-27 22:15:00,82.4,34.023,48.163000000000004,29.535 +2020-08-27 22:30:00,79.2,28.738000000000003,48.163000000000004,29.535 +2020-08-27 22:45:00,79.7,25.674,48.163000000000004,29.535 +2020-08-27 23:00:00,75.57,23.923000000000002,42.379,29.535 +2020-08-27 23:15:00,76.68,22.822,42.379,29.535 +2020-08-27 23:30:00,75.21,22.416,42.379,29.535 +2020-08-27 23:45:00,74.58,21.603,42.379,29.535 +2020-08-28 00:00:00,72.43,20.281,38.505,29.535 +2020-08-28 00:15:00,71.75,21.175,38.505,29.535 +2020-08-28 00:30:00,72.78,20.615,38.505,29.535 +2020-08-28 00:45:00,72.99,21.226999999999997,38.505,29.535 +2020-08-28 01:00:00,70.23,20.479,37.004,29.535 +2020-08-28 01:15:00,72.14,19.332,37.004,29.535 +2020-08-28 01:30:00,71.17,19.017,37.004,29.535 +2020-08-28 01:45:00,72.62,18.511,37.004,29.535 +2020-08-28 02:00:00,71.55,19.285,36.098,29.535 +2020-08-28 02:15:00,72.3,18.726,36.098,29.535 +2020-08-28 02:30:00,72.07,20.434,36.098,29.535 +2020-08-28 02:45:00,72.86,20.463,36.098,29.535 +2020-08-28 03:00:00,73.05,21.838,36.561,29.535 +2020-08-28 03:15:00,75.07,20.644000000000002,36.561,29.535 +2020-08-28 03:30:00,74.45,20.151,36.561,29.535 +2020-08-28 03:45:00,79.36,20.548000000000002,36.561,29.535 +2020-08-28 04:00:00,87.62,25.022,37.355,29.535 +2020-08-28 04:15:00,89.23,29.087,37.355,29.535 +2020-08-28 04:30:00,93.88,27.601,37.355,29.535 +2020-08-28 04:45:00,94.42,27.164,37.355,29.535 +2020-08-28 05:00:00,99.29,39.138000000000005,40.285,29.535 +2020-08-28 05:15:00,100.91,47.137,40.285,29.535 +2020-08-28 05:30:00,109.34,42.821999999999996,40.285,29.535 +2020-08-28 05:45:00,112.16,39.583,40.285,29.535 +2020-08-28 06:00:00,116.49,39.891999999999996,52.378,29.535 +2020-08-28 06:15:00,113.8,40.41,52.378,29.535 +2020-08-28 06:30:00,110.25,39.828,52.378,29.535 +2020-08-28 06:45:00,110.75,42.0,52.378,29.535 +2020-08-28 07:00:00,116.02,42.622,60.891999999999996,29.535 +2020-08-28 07:15:00,113.76,44.653,60.891999999999996,29.535 +2020-08-28 07:30:00,113.32,41.505,60.891999999999996,29.535 +2020-08-28 07:45:00,116.11,42.113,60.891999999999996,29.535 +2020-08-28 08:00:00,110.53,39.859,53.652,29.535 +2020-08-28 08:15:00,110.47,42.542,53.652,29.535 +2020-08-28 08:30:00,118.74,42.833,53.652,29.535 +2020-08-28 08:45:00,118.78,44.428000000000004,53.652,29.535 +2020-08-28 09:00:00,114.6,37.416,51.456,29.535 +2020-08-28 09:15:00,109.6,38.2,51.456,29.535 +2020-08-28 09:30:00,106.67,40.25,51.456,29.535 +2020-08-28 09:45:00,107.3,42.948,51.456,29.535 +2020-08-28 10:00:00,110.6,39.293,49.4,29.535 +2020-08-28 10:15:00,108.84,39.86,49.4,29.535 +2020-08-28 10:30:00,109.67,40.172,49.4,29.535 +2020-08-28 10:45:00,104.36,41.235,49.4,29.535 +2020-08-28 11:00:00,105.31,39.806999999999995,48.773,29.535 +2020-08-28 11:15:00,104.1,39.791,48.773,29.535 +2020-08-28 11:30:00,102.59,40.239000000000004,48.773,29.535 +2020-08-28 11:45:00,100.47,40.095,48.773,29.535 +2020-08-28 12:00:00,97.94,36.64,46.033,29.535 +2020-08-28 12:15:00,100.85,35.379,46.033,29.535 +2020-08-28 12:30:00,98.11,34.671,46.033,29.535 +2020-08-28 12:45:00,98.83,34.62,46.033,29.535 +2020-08-28 13:00:00,96.79,34.536,44.38399999999999,29.535 +2020-08-28 13:15:00,98.89,35.403,44.38399999999999,29.535 +2020-08-28 13:30:00,109.7,34.647,44.38399999999999,29.535 +2020-08-28 13:45:00,107.01,34.881,44.38399999999999,29.535 +2020-08-28 14:00:00,107.86,35.121,43.162,29.535 +2020-08-28 14:15:00,106.56,34.760999999999996,43.162,29.535 +2020-08-28 14:30:00,110.38,35.385,43.162,29.535 +2020-08-28 14:45:00,113.12,35.463,43.162,29.535 +2020-08-28 15:00:00,113.59,36.05,44.91,29.535 +2020-08-28 15:15:00,109.31,33.82,44.91,29.535 +2020-08-28 15:30:00,109.09,32.246,44.91,29.535 +2020-08-28 15:45:00,113.5,31.334,44.91,29.535 +2020-08-28 16:00:00,117.73,32.688,47.489,29.535 +2020-08-28 16:15:00,114.05,33.139,47.489,29.535 +2020-08-28 16:30:00,111.88,32.772,47.489,29.535 +2020-08-28 16:45:00,110.29,30.296,47.489,29.535 +2020-08-28 17:00:00,116.92,34.25,52.047,29.535 +2020-08-28 17:15:00,116.73,35.156,52.047,29.535 +2020-08-28 17:30:00,116.42,34.746,52.047,29.535 +2020-08-28 17:45:00,110.79,34.473,52.047,29.535 +2020-08-28 18:00:00,112.92,36.928000000000004,53.306000000000004,29.535 +2020-08-28 18:15:00,115.02,35.982,53.306000000000004,29.535 +2020-08-28 18:30:00,113.25,34.352,53.306000000000004,29.535 +2020-08-28 18:45:00,112.51,37.635,53.306000000000004,29.535 +2020-08-28 19:00:00,107.58,39.59,53.516000000000005,29.535 +2020-08-28 19:15:00,106.68,39.058,53.516000000000005,29.535 +2020-08-28 19:30:00,106.55,38.073,53.516000000000005,29.535 +2020-08-28 19:45:00,110.05,36.702,53.516000000000005,29.535 +2020-08-28 20:00:00,105.55,33.705999999999996,57.88,29.535 +2020-08-28 20:15:00,103.72,34.273,57.88,29.535 +2020-08-28 20:30:00,95.59,33.826,57.88,29.535 +2020-08-28 20:45:00,90.62,32.982,57.88,29.535 +2020-08-28 21:00:00,84.68,32.658,53.32,29.535 +2020-08-28 21:15:00,83.65,35.631,53.32,29.535 +2020-08-28 21:30:00,80.52,35.832,53.32,29.535 +2020-08-28 21:45:00,82.9,35.915,53.32,29.535 +2020-08-28 22:00:00,83.63,33.01,48.074,29.535 +2020-08-28 22:15:00,83.65,34.539,48.074,29.535 +2020-08-28 22:30:00,78.8,33.479,48.074,29.535 +2020-08-28 22:45:00,76.63,31.156999999999996,48.074,29.535 +2020-08-28 23:00:00,75.72,30.994,41.306999999999995,29.535 +2020-08-28 23:15:00,75.8,28.473000000000003,41.306999999999995,29.535 +2020-08-28 23:30:00,73.3,26.433000000000003,41.306999999999995,29.535 +2020-08-28 23:45:00,66.16,25.524,41.306999999999995,29.535 +2020-08-29 00:00:00,64.44,21.593000000000004,40.227,29.423000000000002 +2020-08-29 00:15:00,71.06,21.918000000000003,40.227,29.423000000000002 +2020-08-29 00:30:00,70.31,20.924,40.227,29.423000000000002 +2020-08-29 00:45:00,70.07,20.871,40.227,29.423000000000002 +2020-08-29 01:00:00,63.31,20.348,36.303000000000004,29.423000000000002 +2020-08-29 01:15:00,64.56,19.761,36.303000000000004,29.423000000000002 +2020-08-29 01:30:00,62.06,18.733,36.303000000000004,29.423000000000002 +2020-08-29 01:45:00,61.97,19.362000000000002,36.303000000000004,29.423000000000002 +2020-08-29 02:00:00,61.2,19.239,33.849000000000004,29.423000000000002 +2020-08-29 02:15:00,65.22,17.988,33.849000000000004,29.423000000000002 +2020-08-29 02:30:00,67.93,18.965,33.849000000000004,29.423000000000002 +2020-08-29 02:45:00,67.63,19.703,33.849000000000004,29.423000000000002 +2020-08-29 03:00:00,62.88,19.814,33.149,29.423000000000002 +2020-08-29 03:15:00,60.75,18.016,33.149,29.423000000000002 +2020-08-29 03:30:00,64.25,17.847,33.149,29.423000000000002 +2020-08-29 03:45:00,69.62,19.58,33.149,29.423000000000002 +2020-08-29 04:00:00,70.43,22.421999999999997,32.501,29.423000000000002 +2020-08-29 04:15:00,67.71,25.729,32.501,29.423000000000002 +2020-08-29 04:30:00,64.26,22.76,32.501,29.423000000000002 +2020-08-29 04:45:00,66.86,22.581,32.501,29.423000000000002 +2020-08-29 05:00:00,72.03,27.261,31.648000000000003,29.423000000000002 +2020-08-29 05:15:00,74.87,25.631,31.648000000000003,29.423000000000002 +2020-08-29 05:30:00,74.46,22.678,31.648000000000003,29.423000000000002 +2020-08-29 05:45:00,72.84,23.379,31.648000000000003,29.423000000000002 +2020-08-29 06:00:00,73.13,33.579,32.552,29.423000000000002 +2020-08-29 06:15:00,77.89,40.993,32.552,29.423000000000002 +2020-08-29 06:30:00,78.78,37.895,32.552,29.423000000000002 +2020-08-29 06:45:00,77.89,37.258,32.552,29.423000000000002 +2020-08-29 07:00:00,77.07,36.72,35.181999999999995,29.423000000000002 +2020-08-29 07:15:00,78.08,37.609,35.181999999999995,29.423000000000002 +2020-08-29 07:30:00,79.75,35.816,35.181999999999995,29.423000000000002 +2020-08-29 07:45:00,82.27,37.204,35.181999999999995,29.423000000000002 +2020-08-29 08:00:00,84.1,35.546,40.35,29.423000000000002 +2020-08-29 08:15:00,84.31,37.943000000000005,40.35,29.423000000000002 +2020-08-29 08:30:00,85.25,38.193000000000005,40.35,29.423000000000002 +2020-08-29 08:45:00,85.89,40.580999999999996,40.35,29.423000000000002 +2020-08-29 09:00:00,89.71,36.368,42.292,29.423000000000002 +2020-08-29 09:15:00,89.66,37.569,42.292,29.423000000000002 +2020-08-29 09:30:00,85.99,40.035,42.292,29.423000000000002 +2020-08-29 09:45:00,80.14,42.298,42.292,29.423000000000002 +2020-08-29 10:00:00,80.79,39.221,40.084,29.423000000000002 +2020-08-29 10:15:00,88.58,40.082,40.084,29.423000000000002 +2020-08-29 10:30:00,90.14,40.073,40.084,29.423000000000002 +2020-08-29 10:45:00,90.37,40.782,40.084,29.423000000000002 +2020-08-29 11:00:00,89.97,39.248000000000005,36.966,29.423000000000002 +2020-08-29 11:15:00,91.84,40.025999999999996,36.966,29.423000000000002 +2020-08-29 11:30:00,89.82,40.753,36.966,29.423000000000002 +2020-08-29 11:45:00,88.72,41.224,36.966,29.423000000000002 +2020-08-29 12:00:00,86.32,38.188,35.19,29.423000000000002 +2020-08-29 12:15:00,84.51,37.68,35.19,29.423000000000002 +2020-08-29 12:30:00,82.92,36.863,35.19,29.423000000000002 +2020-08-29 12:45:00,85.5,37.486,35.19,29.423000000000002 +2020-08-29 13:00:00,81.37,36.576,32.277,29.423000000000002 +2020-08-29 13:15:00,82.13,36.982,32.277,29.423000000000002 +2020-08-29 13:30:00,81.68,36.403,32.277,29.423000000000002 +2020-08-29 13:45:00,81.28,35.406,32.277,29.423000000000002 +2020-08-29 14:00:00,79.6,35.664,31.436999999999998,29.423000000000002 +2020-08-29 14:15:00,79.02,34.179,31.436999999999998,29.423000000000002 +2020-08-29 14:30:00,76.91,34.474000000000004,31.436999999999998,29.423000000000002 +2020-08-29 14:45:00,76.5,34.986999999999995,31.436999999999998,29.423000000000002 +2020-08-29 15:00:00,76.4,35.93,33.493,29.423000000000002 +2020-08-29 15:15:00,77.73,34.336,33.493,29.423000000000002 +2020-08-29 15:30:00,78.14,32.926,33.493,29.423000000000002 +2020-08-29 15:45:00,77.73,31.178,33.493,29.423000000000002 +2020-08-29 16:00:00,78.32,34.436,36.593,29.423000000000002 +2020-08-29 16:15:00,79.7,34.065,36.593,29.423000000000002 +2020-08-29 16:30:00,80.81,33.906,36.593,29.423000000000002 +2020-08-29 16:45:00,82.06,31.432,36.593,29.423000000000002 +2020-08-29 17:00:00,84.59,34.291,42.049,29.423000000000002 +2020-08-29 17:15:00,86.73,33.227,42.049,29.423000000000002 +2020-08-29 17:30:00,85.87,32.711,42.049,29.423000000000002 +2020-08-29 17:45:00,83.17,32.935,42.049,29.423000000000002 +2020-08-29 18:00:00,84.18,36.61,43.755,29.423000000000002 +2020-08-29 18:15:00,83.2,37.168,43.755,29.423000000000002 +2020-08-29 18:30:00,83.27,36.759,43.755,29.423000000000002 +2020-08-29 18:45:00,84.31,36.943000000000005,43.755,29.423000000000002 +2020-08-29 19:00:00,89.05,37.398,44.492,29.423000000000002 +2020-08-29 19:15:00,84.66,35.963,44.492,29.423000000000002 +2020-08-29 19:30:00,86.21,35.665,44.492,29.423000000000002 +2020-08-29 19:45:00,81.85,35.891999999999996,44.492,29.423000000000002 +2020-08-29 20:00:00,77.43,33.655,40.896,29.423000000000002 +2020-08-29 20:15:00,77.44,33.62,40.896,29.423000000000002 +2020-08-29 20:30:00,75.73,32.365,40.896,29.423000000000002 +2020-08-29 20:45:00,76.44,33.168,40.896,29.423000000000002 +2020-08-29 21:00:00,73.3,31.482,39.056,29.423000000000002 +2020-08-29 21:15:00,72.72,34.125,39.056,29.423000000000002 +2020-08-29 21:30:00,70.23,34.483000000000004,39.056,29.423000000000002 +2020-08-29 21:45:00,69.8,34.045,39.056,29.423000000000002 +2020-08-29 22:00:00,66.22,31.079,38.478,29.423000000000002 +2020-08-29 22:15:00,65.01,32.786,38.478,29.423000000000002 +2020-08-29 22:30:00,63.02,31.276999999999997,38.478,29.423000000000002 +2020-08-29 22:45:00,61.99,29.272,38.478,29.423000000000002 +2020-08-29 23:00:00,58.73,28.525,32.953,29.423000000000002 +2020-08-29 23:15:00,57.9,26.384,32.953,29.423000000000002 +2020-08-29 23:30:00,57.04,26.589000000000002,32.953,29.423000000000002 +2020-08-29 23:45:00,56.91,26.037,32.953,29.423000000000002 +2020-08-30 00:00:00,54.27,22.994,28.584,29.423000000000002 +2020-08-30 00:15:00,55.13,22.281999999999996,28.584,29.423000000000002 +2020-08-30 00:30:00,50.94,21.165,28.584,29.423000000000002 +2020-08-30 00:45:00,53.35,21.011,28.584,29.423000000000002 +2020-08-30 01:00:00,51.34,20.706999999999997,26.419,29.423000000000002 +2020-08-30 01:15:00,52.9,20.012999999999998,26.419,29.423000000000002 +2020-08-30 01:30:00,52.18,18.868,26.419,29.423000000000002 +2020-08-30 01:45:00,51.93,19.159000000000002,26.419,29.423000000000002 +2020-08-30 02:00:00,52.18,19.118,25.335,29.423000000000002 +2020-08-30 02:15:00,51.62,18.54,25.335,29.423000000000002 +2020-08-30 02:30:00,51.01,19.651,25.335,29.423000000000002 +2020-08-30 02:45:00,50.76,20.117,25.335,29.423000000000002 +2020-08-30 03:00:00,51.01,20.8,24.805,29.423000000000002 +2020-08-30 03:15:00,51.32,19.261,24.805,29.423000000000002 +2020-08-30 03:30:00,52.27,18.414,24.805,29.423000000000002 +2020-08-30 03:45:00,53.2,19.423,24.805,29.423000000000002 +2020-08-30 04:00:00,53.28,22.268,25.772,29.423000000000002 +2020-08-30 04:15:00,53.79,25.218000000000004,25.772,29.423000000000002 +2020-08-30 04:30:00,54.34,23.467,25.772,29.423000000000002 +2020-08-30 04:45:00,55.42,22.866,25.772,29.423000000000002 +2020-08-30 05:00:00,56.16,27.819000000000003,25.971999999999998,29.423000000000002 +2020-08-30 05:15:00,56.99,25.605999999999998,25.971999999999998,29.423000000000002 +2020-08-30 05:30:00,56.61,22.284000000000002,25.971999999999998,29.423000000000002 +2020-08-30 05:45:00,57.04,22.694000000000003,25.971999999999998,29.423000000000002 +2020-08-30 06:00:00,58.54,30.746,26.026,29.423000000000002 +2020-08-30 06:15:00,59.88,38.948,26.026,29.423000000000002 +2020-08-30 06:30:00,61.66,35.231,26.026,29.423000000000002 +2020-08-30 06:45:00,63.97,33.71,26.026,29.423000000000002 +2020-08-30 07:00:00,66.86,33.422,27.396,29.423000000000002 +2020-08-30 07:15:00,68.13,32.858000000000004,27.396,29.423000000000002 +2020-08-30 07:30:00,70.07,32.260999999999996,27.396,29.423000000000002 +2020-08-30 07:45:00,71.87,33.628,27.396,29.423000000000002 +2020-08-30 08:00:00,74.54,32.635,30.791999999999998,29.423000000000002 +2020-08-30 08:15:00,76.67,36.064,30.791999999999998,29.423000000000002 +2020-08-30 08:30:00,77.37,37.067,30.791999999999998,29.423000000000002 +2020-08-30 08:45:00,78.29,39.330999999999996,30.791999999999998,29.423000000000002 +2020-08-30 09:00:00,78.71,35.037,32.482,29.423000000000002 +2020-08-30 09:15:00,79.95,35.771,32.482,29.423000000000002 +2020-08-30 09:30:00,80.79,38.611999999999995,32.482,29.423000000000002 +2020-08-30 09:45:00,83.14,41.757,32.482,29.423000000000002 +2020-08-30 10:00:00,84.85,39.117,31.951,29.423000000000002 +2020-08-30 10:15:00,87.31,40.061,31.951,29.423000000000002 +2020-08-30 10:30:00,88.3,40.231,31.951,29.423000000000002 +2020-08-30 10:45:00,87.52,41.902,31.951,29.423000000000002 +2020-08-30 11:00:00,84.25,40.043,33.619,29.423000000000002 +2020-08-30 11:15:00,83.28,40.399,33.619,29.423000000000002 +2020-08-30 11:30:00,81.94,41.626000000000005,33.619,29.423000000000002 +2020-08-30 11:45:00,80.98,42.297,33.619,29.423000000000002 +2020-08-30 12:00:00,78.49,40.249,30.975,29.423000000000002 +2020-08-30 12:15:00,77.41,39.084,30.975,29.423000000000002 +2020-08-30 12:30:00,75.82,38.535,30.975,29.423000000000002 +2020-08-30 12:45:00,78.16,38.595,30.975,29.423000000000002 +2020-08-30 13:00:00,72.08,37.405,27.956999999999997,29.423000000000002 +2020-08-30 13:15:00,72.23,37.171,27.956999999999997,29.423000000000002 +2020-08-30 13:30:00,71.97,35.578,27.956999999999997,29.423000000000002 +2020-08-30 13:45:00,72.4,35.635,27.956999999999997,29.423000000000002 +2020-08-30 14:00:00,70.92,36.957,25.555999999999997,29.423000000000002 +2020-08-30 14:15:00,70.88,35.834,25.555999999999997,29.423000000000002 +2020-08-30 14:30:00,70.54,34.972,25.555999999999997,29.423000000000002 +2020-08-30 14:45:00,70.65,34.519,25.555999999999997,29.423000000000002 +2020-08-30 15:00:00,71.32,35.598,26.271,29.423000000000002 +2020-08-30 15:15:00,71.25,33.243,26.271,29.423000000000002 +2020-08-30 15:30:00,71.19,31.660999999999998,26.271,29.423000000000002 +2020-08-30 15:45:00,71.97,30.158,26.271,29.423000000000002 +2020-08-30 16:00:00,73.4,31.854,30.369,29.423000000000002 +2020-08-30 16:15:00,74.06,31.729,30.369,29.423000000000002 +2020-08-30 16:30:00,75.86,32.481,30.369,29.423000000000002 +2020-08-30 16:45:00,78.3,30.105999999999998,30.369,29.423000000000002 +2020-08-30 17:00:00,79.85,33.259,38.787,29.423000000000002 +2020-08-30 17:15:00,80.1,33.619,38.787,29.423000000000002 +2020-08-30 17:30:00,81.58,33.845,38.787,29.423000000000002 +2020-08-30 17:45:00,80.78,34.421,38.787,29.423000000000002 +2020-08-30 18:00:00,83.14,38.659,41.886,29.423000000000002 +2020-08-30 18:15:00,80.9,38.830999999999996,41.886,29.423000000000002 +2020-08-30 18:30:00,80.98,38.26,41.886,29.423000000000002 +2020-08-30 18:45:00,80.43,38.503,41.886,29.423000000000002 +2020-08-30 19:00:00,86.05,40.954,42.91,29.423000000000002 +2020-08-30 19:15:00,82.33,38.507,42.91,29.423000000000002 +2020-08-30 19:30:00,83.91,37.993,42.91,29.423000000000002 +2020-08-30 19:45:00,80.57,37.784,42.91,29.423000000000002 +2020-08-30 20:00:00,78.83,35.695,42.148999999999994,29.423000000000002 +2020-08-30 20:15:00,79.05,35.516,42.148999999999994,29.423000000000002 +2020-08-30 20:30:00,78.41,34.949,42.148999999999994,29.423000000000002 +2020-08-30 20:45:00,79.19,34.219,42.148999999999994,29.423000000000002 +2020-08-30 21:00:00,77.65,32.483000000000004,40.955999999999996,29.423000000000002 +2020-08-30 21:15:00,78.79,34.856,40.955999999999996,29.423000000000002 +2020-08-30 21:30:00,75.78,34.623000000000005,40.955999999999996,29.423000000000002 +2020-08-30 21:45:00,75.79,34.472,40.955999999999996,29.423000000000002 +2020-08-30 22:00:00,72.02,33.349000000000004,39.873000000000005,29.423000000000002 +2020-08-30 22:15:00,72.93,33.534,39.873000000000005,29.423000000000002 +2020-08-30 22:30:00,71.7,31.603,39.873000000000005,29.423000000000002 +2020-08-30 22:45:00,70.88,28.467,39.873000000000005,29.423000000000002 +2020-08-30 23:00:00,66.13,27.553,35.510999999999996,29.423000000000002 +2020-08-30 23:15:00,67.65,26.502,35.510999999999996,29.423000000000002 +2020-08-30 23:30:00,68.19,26.213,35.510999999999996,29.423000000000002 +2020-08-30 23:45:00,66.64,25.791,35.510999999999996,29.423000000000002 +2020-08-31 00:00:00,63.09,24.439,33.475,29.535 +2020-08-31 00:15:00,65.41,24.381,33.475,29.535 +2020-08-31 00:30:00,65.2,22.921,33.475,29.535 +2020-08-31 00:45:00,65.56,22.427,33.475,29.535 +2020-08-31 01:00:00,62.92,22.448,33.111,29.535 +2020-08-31 01:15:00,64.41,21.793000000000003,33.111,29.535 +2020-08-31 01:30:00,64.04,20.988000000000003,33.111,29.535 +2020-08-31 01:45:00,65.01,21.19,33.111,29.535 +2020-08-31 02:00:00,62.9,21.559,32.358000000000004,29.535 +2020-08-31 02:15:00,64.34,20.055999999999997,32.358000000000004,29.535 +2020-08-31 02:30:00,64.2,21.287,32.358000000000004,29.535 +2020-08-31 02:45:00,64.04,21.629,32.358000000000004,29.535 +2020-08-31 03:00:00,65.64,22.729,30.779,29.535 +2020-08-31 03:15:00,66.35,21.822,30.779,29.535 +2020-08-31 03:30:00,72.4,21.601999999999997,30.779,29.535 +2020-08-31 03:45:00,75.77,22.195,30.779,29.535 +2020-08-31 04:00:00,80.59,27.787,31.416,29.535 +2020-08-31 04:15:00,79.36,33.317,31.416,29.535 +2020-08-31 04:30:00,88.89,31.094,31.416,29.535 +2020-08-31 04:45:00,94.27,30.829,31.416,29.535 +2020-08-31 05:00:00,100.31,42.129,37.221,29.535 +2020-08-31 05:15:00,101.92,48.968,37.221,29.535 +2020-08-31 05:30:00,101.22,44.25,37.221,29.535 +2020-08-31 05:45:00,104.81,41.94,37.221,29.535 +2020-08-31 06:00:00,108.02,40.968,51.891000000000005,29.535 +2020-08-31 06:15:00,111.44,41.255,51.891000000000005,29.535 +2020-08-31 06:30:00,112.93,40.977,51.891000000000005,29.535 +2020-08-31 06:45:00,115.62,44.062,51.891000000000005,29.535 +2020-08-31 07:00:00,119.0,43.956,62.282,29.535 +2020-08-31 07:15:00,119.27,45.438,62.282,29.535 +2020-08-31 07:30:00,120.08,44.106,62.282,29.535 +2020-08-31 07:45:00,119.77,45.791000000000004,62.282,29.535 +2020-08-31 08:00:00,121.76,42.894,54.102,29.535 +2020-08-31 08:15:00,123.35,45.245,54.102,29.535 +2020-08-31 08:30:00,124.12,45.458999999999996,54.102,29.535 +2020-08-31 08:45:00,125.71,47.97,54.102,29.535 +2020-08-31 09:00:00,129.33,42.748999999999995,50.917,29.535 +2020-08-31 09:15:00,126.56,41.933,50.917,29.535 +2020-08-31 09:30:00,126.46,43.938,50.917,29.535 +2020-08-31 09:45:00,127.36,44.971000000000004,50.917,29.535 +2020-08-31 10:00:00,128.24,42.68899999999999,49.718999999999994,29.535 +2020-08-31 10:15:00,131.55,43.481,49.718999999999994,29.535 +2020-08-31 10:30:00,130.34,43.248000000000005,49.718999999999994,29.535 +2020-08-31 10:45:00,123.89,43.443000000000005,49.718999999999994,29.535 +2020-08-31 11:00:00,124.43,41.855,49.833999999999996,29.535 +2020-08-31 11:15:00,124.83,42.403,49.833999999999996,29.535 +2020-08-31 11:30:00,129.95,44.224,49.833999999999996,29.535 +2020-08-31 11:45:00,125.36,45.352,49.833999999999996,29.535 +2020-08-31 12:00:00,121.07,41.592,47.832,29.535 +2020-08-31 12:15:00,122.98,40.518,47.832,29.535 +2020-08-31 12:30:00,117.12,38.993,47.832,29.535 +2020-08-31 12:45:00,116.46,38.983000000000004,47.832,29.535 +2020-08-31 13:00:00,120.35,38.641,48.03,29.535 +2020-08-31 13:15:00,120.22,37.631,48.03,29.535 +2020-08-31 13:30:00,118.44,36.205,48.03,29.535 +2020-08-31 13:45:00,111.87,37.094,48.03,29.535 +2020-08-31 14:00:00,111.57,37.562,48.157,29.535 +2020-08-31 14:15:00,114.04,37.012,48.157,29.535 +2020-08-31 14:30:00,113.71,36.038000000000004,48.157,29.535 +2020-08-31 14:45:00,116.13,37.49,48.157,29.535 +2020-08-31 15:00:00,113.44,38.115,48.897,29.535 +2020-08-31 15:15:00,111.84,35.278,48.897,29.535 +2020-08-31 15:30:00,110.24,34.448,48.897,29.535 +2020-08-31 15:45:00,108.51,32.522,48.897,29.535 +2020-08-31 16:00:00,104.99,35.231,51.446000000000005,29.535 +2020-08-31 16:15:00,104.55,35.198,51.446000000000005,29.535 +2020-08-31 16:30:00,108.33,35.369,51.446000000000005,29.535 +2020-08-31 16:45:00,108.42,33.097,51.446000000000005,29.535 +2020-08-31 17:00:00,109.57,35.18,57.507,29.535 +2020-08-31 17:15:00,108.31,35.944,57.507,29.535 +2020-08-31 17:30:00,111.74,35.846,57.507,29.535 +2020-08-31 17:45:00,110.77,36.141999999999996,57.507,29.535 +2020-08-31 18:00:00,113.05,39.413000000000004,57.896,29.535 +2020-08-31 18:15:00,109.66,37.992,57.896,29.535 +2020-08-31 18:30:00,110.56,36.729,57.896,29.535 +2020-08-31 18:45:00,108.24,39.83,57.896,29.535 +2020-08-31 19:00:00,109.87,42.091,57.891999999999996,29.535 +2020-08-31 19:15:00,105.78,40.868,57.891999999999996,29.535 +2020-08-31 19:30:00,103.3,40.012,57.891999999999996,29.535 +2020-08-31 19:45:00,105.3,39.27,57.891999999999996,29.535 +2020-08-31 20:00:00,98.65,36.086999999999996,64.57300000000001,29.535 +2020-08-31 20:15:00,99.73,37.242,64.57300000000001,29.535 +2020-08-31 20:30:00,98.19,37.177,64.57300000000001,29.535 +2020-08-31 20:45:00,98.28,36.565,64.57300000000001,29.535 +2020-08-31 21:00:00,93.39,34.244,59.431999999999995,29.535 +2020-08-31 21:15:00,93.97,37.034,59.431999999999995,29.535 +2020-08-31 21:30:00,89.46,37.196999999999996,59.431999999999995,29.535 +2020-08-31 21:45:00,86.99,36.826,59.431999999999995,29.535 +2020-08-31 22:00:00,86.09,33.882,51.519,29.535 +2020-08-31 22:15:00,89.99,35.885999999999996,51.519,29.535 +2020-08-31 22:30:00,88.27,30.206,51.519,29.535 +2020-08-31 22:45:00,83.31,27.164,51.519,29.535 +2020-08-31 23:00:00,75.5,26.299,44.501000000000005,29.535 +2020-08-31 23:15:00,75.51,23.673000000000002,44.501000000000005,29.535 +2020-08-31 23:30:00,75.27,23.296999999999997,44.501000000000005,29.535 +2020-08-31 23:45:00,75.09,22.335,44.501000000000005,29.535 +2020-09-01 00:00:00,73.47,29.916999999999998,44.438,29.93 +2020-09-01 00:15:00,81.26,30.489,44.438,29.93 +2020-09-01 00:30:00,72.65,30.061,44.438,29.93 +2020-09-01 00:45:00,74.24,30.002,44.438,29.93 +2020-09-01 01:00:00,75.98,29.759,41.468999999999994,29.93 +2020-09-01 01:15:00,79.49,28.94,41.468999999999994,29.93 +2020-09-01 01:30:00,80.77,27.840999999999998,41.468999999999994,29.93 +2020-09-01 01:45:00,80.77,26.919,41.468999999999994,29.93 +2020-09-01 02:00:00,76.57,26.625,39.708,29.93 +2020-09-01 02:15:00,74.41,26.226,39.708,29.93 +2020-09-01 02:30:00,73.57,26.895,39.708,29.93 +2020-09-01 02:45:00,75.06,27.743000000000002,39.708,29.93 +2020-09-01 03:00:00,75.97,28.845,38.919000000000004,29.93 +2020-09-01 03:15:00,76.67,28.206,38.919000000000004,29.93 +2020-09-01 03:30:00,77.0,28.11,38.919000000000004,29.93 +2020-09-01 03:45:00,80.42,28.19,38.919000000000004,29.93 +2020-09-01 04:00:00,83.6,34.74,40.092,29.93 +2020-09-01 04:15:00,85.52,41.981,40.092,29.93 +2020-09-01 04:30:00,93.85,39.315,40.092,29.93 +2020-09-01 04:45:00,102.83,39.861999999999995,40.092,29.93 +2020-09-01 05:00:00,110.21,57.162,43.713,29.93 +2020-09-01 05:15:00,108.06,68.223,43.713,29.93 +2020-09-01 05:30:00,106.25,62.226000000000006,43.713,29.93 +2020-09-01 05:45:00,108.65,58.465,43.713,29.93 +2020-09-01 06:00:00,117.46,58.838,56.033,29.93 +2020-09-01 06:15:00,114.57,60.111000000000004,56.033,29.93 +2020-09-01 06:30:00,114.76,58.723,56.033,29.93 +2020-09-01 06:45:00,117.5,60.365,56.033,29.93 +2020-09-01 07:00:00,120.97,58.69,66.003,29.93 +2020-09-01 07:15:00,121.58,59.825,66.003,29.93 +2020-09-01 07:30:00,122.12,58.056000000000004,66.003,29.93 +2020-09-01 07:45:00,122.42,58.925,66.003,29.93 +2020-09-01 08:00:00,123.81,57.608999999999995,57.474,29.93 +2020-09-01 08:15:00,119.75,59.684,57.474,29.93 +2020-09-01 08:30:00,121.34,58.928999999999995,57.474,29.93 +2020-09-01 08:45:00,119.7,60.385,57.474,29.93 +2020-09-01 09:00:00,119.63,56.747,51.928000000000004,29.93 +2020-09-01 09:15:00,114.95,55.32899999999999,51.928000000000004,29.93 +2020-09-01 09:30:00,111.22,57.449,51.928000000000004,29.93 +2020-09-01 09:45:00,112.13,59.055,51.928000000000004,29.93 +2020-09-01 10:00:00,110.13,55.532,49.46,29.93 +2020-09-01 10:15:00,111.52,56.278999999999996,49.46,29.93 +2020-09-01 10:30:00,112.58,56.043,49.46,29.93 +2020-09-01 10:45:00,113.39,57.13399999999999,49.46,29.93 +2020-09-01 11:00:00,106.2,53.576,48.206,29.93 +2020-09-01 11:15:00,104.73,54.661,48.206,29.93 +2020-09-01 11:30:00,106.63,55.326,48.206,29.93 +2020-09-01 11:45:00,108.22,56.138000000000005,48.206,29.93 +2020-09-01 12:00:00,105.4,52.413999999999994,46.285,29.93 +2020-09-01 12:15:00,110.05,52.071000000000005,46.285,29.93 +2020-09-01 12:30:00,108.08,51.176,46.285,29.93 +2020-09-01 12:45:00,105.77,52.277,46.285,29.93 +2020-09-01 13:00:00,110.03,51.369,46.861999999999995,29.93 +2020-09-01 13:15:00,106.07,51.993,46.861999999999995,29.93 +2020-09-01 13:30:00,107.01,50.818000000000005,46.861999999999995,29.93 +2020-09-01 13:45:00,108.05,49.903999999999996,46.861999999999995,29.93 +2020-09-01 14:00:00,104.78,51.108000000000004,46.488,29.93 +2020-09-01 14:15:00,107.92,50.152,46.488,29.93 +2020-09-01 14:30:00,103.2,48.902,46.488,29.93 +2020-09-01 14:45:00,103.58,49.669,46.488,29.93 +2020-09-01 15:00:00,101.89,50.393,48.442,29.93 +2020-09-01 15:15:00,103.13,48.303000000000004,48.442,29.93 +2020-09-01 15:30:00,103.38,47.102,48.442,29.93 +2020-09-01 15:45:00,102.77,44.833999999999996,48.442,29.93 +2020-09-01 16:00:00,102.66,47.282,50.397,29.93 +2020-09-01 16:15:00,105.53,47.086000000000006,50.397,29.93 +2020-09-01 16:30:00,106.51,47.457,50.397,29.93 +2020-09-01 16:45:00,108.89,45.013999999999996,50.397,29.93 +2020-09-01 17:00:00,109.84,46.915,56.668,29.93 +2020-09-01 17:15:00,109.18,47.858000000000004,56.668,29.93 +2020-09-01 17:30:00,109.98,47.298,56.668,29.93 +2020-09-01 17:45:00,112.11,46.278999999999996,56.668,29.93 +2020-09-01 18:00:00,113.7,48.562,57.957,29.93 +2020-09-01 18:15:00,109.54,48.016999999999996,57.957,29.93 +2020-09-01 18:30:00,111.45,46.04,57.957,29.93 +2020-09-01 18:45:00,109.99,50.2,57.957,29.93 +2020-09-01 19:00:00,111.38,52.652,57.056000000000004,29.93 +2020-09-01 19:15:00,107.84,51.383,57.056000000000004,29.93 +2020-09-01 19:30:00,107.01,50.34,57.056000000000004,29.93 +2020-09-01 19:45:00,106.18,50.461999999999996,57.056000000000004,29.93 +2020-09-01 20:00:00,100.69,48.705,64.156,29.93 +2020-09-01 20:15:00,100.76,48.184,64.156,29.93 +2020-09-01 20:30:00,98.36,47.782,64.156,29.93 +2020-09-01 20:45:00,97.15,47.833,64.156,29.93 +2020-09-01 21:00:00,91.14,46.413000000000004,56.507,29.93 +2020-09-01 21:15:00,91.05,48.013000000000005,56.507,29.93 +2020-09-01 21:30:00,87.36,47.721000000000004,56.507,29.93 +2020-09-01 21:45:00,85.97,47.453,56.507,29.93 +2020-09-01 22:00:00,81.23,45.082,50.728,29.93 +2020-09-01 22:15:00,80.37,45.82899999999999,50.728,29.93 +2020-09-01 22:30:00,79.33,38.537,50.728,29.93 +2020-09-01 22:45:00,78.66,35.022,50.728,29.93 +2020-09-01 23:00:00,74.04,31.743000000000002,43.556999999999995,29.93 +2020-09-01 23:15:00,74.63,31.009,43.556999999999995,29.93 +2020-09-01 23:30:00,74.95,30.879,43.556999999999995,29.93 +2020-09-01 23:45:00,73.76,30.048000000000002,43.556999999999995,29.93 +2020-09-02 00:00:00,70.09,30.121,41.151,29.93 +2020-09-02 00:15:00,71.22,30.691,41.151,29.93 +2020-09-02 00:30:00,70.52,30.272,41.151,29.93 +2020-09-02 00:45:00,70.04,30.22,41.151,29.93 +2020-09-02 01:00:00,70.4,29.961,37.763000000000005,29.93 +2020-09-02 01:15:00,71.55,29.159000000000002,37.763000000000005,29.93 +2020-09-02 01:30:00,70.54,28.076999999999998,37.763000000000005,29.93 +2020-09-02 01:45:00,71.31,27.156,37.763000000000005,29.93 +2020-09-02 02:00:00,70.05,26.865,35.615,29.93 +2020-09-02 02:15:00,70.33,26.489,35.615,29.93 +2020-09-02 02:30:00,70.42,27.131999999999998,35.615,29.93 +2020-09-02 02:45:00,71.01,27.975,35.615,29.93 +2020-09-02 03:00:00,71.66,29.066,35.153,29.93 +2020-09-02 03:15:00,73.29,28.445,35.153,29.93 +2020-09-02 03:30:00,74.2,28.353,35.153,29.93 +2020-09-02 03:45:00,76.12,28.423000000000002,35.153,29.93 +2020-09-02 04:00:00,79.71,35.003,36.203,29.93 +2020-09-02 04:15:00,83.25,42.268,36.203,29.93 +2020-09-02 04:30:00,93.94,39.609,36.203,29.93 +2020-09-02 04:45:00,98.95,40.161,36.203,29.93 +2020-09-02 05:00:00,102.22,57.54600000000001,39.922,29.93 +2020-09-02 05:15:00,99.73,68.696,39.922,29.93 +2020-09-02 05:30:00,104.42,62.69,39.922,29.93 +2020-09-02 05:45:00,106.12,58.883,39.922,29.93 +2020-09-02 06:00:00,110.69,59.221000000000004,56.443999999999996,29.93 +2020-09-02 06:15:00,110.55,60.515,56.443999999999996,29.93 +2020-09-02 06:30:00,112.02,59.126999999999995,56.443999999999996,29.93 +2020-09-02 06:45:00,111.67,60.766000000000005,56.443999999999996,29.93 +2020-09-02 07:00:00,114.69,59.091,68.683,29.93 +2020-09-02 07:15:00,114.16,60.243,68.683,29.93 +2020-09-02 07:30:00,112.53,58.504,68.683,29.93 +2020-09-02 07:45:00,112.99,59.375,68.683,29.93 +2020-09-02 08:00:00,112.66,58.06399999999999,59.003,29.93 +2020-09-02 08:15:00,112.55,60.106,59.003,29.93 +2020-09-02 08:30:00,111.76,59.354,59.003,29.93 +2020-09-02 08:45:00,108.42,60.795,59.003,29.93 +2020-09-02 09:00:00,107.65,57.165,56.21,29.93 +2020-09-02 09:15:00,106.69,55.736000000000004,56.21,29.93 +2020-09-02 09:30:00,106.79,57.836000000000006,56.21,29.93 +2020-09-02 09:45:00,106.64,59.413999999999994,56.21,29.93 +2020-09-02 10:00:00,106.81,55.888999999999996,52.358999999999995,29.93 +2020-09-02 10:15:00,107.49,56.603,52.358999999999995,29.93 +2020-09-02 10:30:00,104.67,56.357,52.358999999999995,29.93 +2020-09-02 10:45:00,104.68,57.435,52.358999999999995,29.93 +2020-09-02 11:00:00,102.67,53.89,51.161,29.93 +2020-09-02 11:15:00,101.54,54.961000000000006,51.161,29.93 +2020-09-02 11:30:00,105.83,55.623999999999995,51.161,29.93 +2020-09-02 11:45:00,103.98,56.419,51.161,29.93 +2020-09-02 12:00:00,99.78,52.676,49.119,29.93 +2020-09-02 12:15:00,101.12,52.318999999999996,49.119,29.93 +2020-09-02 12:30:00,101.87,51.45,49.119,29.93 +2020-09-02 12:45:00,104.35,52.54,49.119,29.93 +2020-09-02 13:00:00,100.22,51.608000000000004,49.187,29.93 +2020-09-02 13:15:00,101.85,52.223,49.187,29.93 +2020-09-02 13:30:00,99.0,51.04600000000001,49.187,29.93 +2020-09-02 13:45:00,102.96,50.141999999999996,49.187,29.93 +2020-09-02 14:00:00,102.01,51.306999999999995,49.787,29.93 +2020-09-02 14:15:00,102.18,50.363,49.787,29.93 +2020-09-02 14:30:00,100.6,49.137,49.787,29.93 +2020-09-02 14:45:00,103.38,49.903,49.787,29.93 +2020-09-02 15:00:00,106.74,50.576,51.458999999999996,29.93 +2020-09-02 15:15:00,101.18,48.5,51.458999999999996,29.93 +2020-09-02 15:30:00,102.11,47.323,51.458999999999996,29.93 +2020-09-02 15:45:00,100.59,45.065,51.458999999999996,29.93 +2020-09-02 16:00:00,102.98,47.48,53.663000000000004,29.93 +2020-09-02 16:15:00,103.91,47.295,53.663000000000004,29.93 +2020-09-02 16:30:00,108.38,47.666000000000004,53.663000000000004,29.93 +2020-09-02 16:45:00,107.21,45.275,53.663000000000004,29.93 +2020-09-02 17:00:00,108.81,47.14,58.183,29.93 +2020-09-02 17:15:00,109.2,48.111000000000004,58.183,29.93 +2020-09-02 17:30:00,109.84,47.558,58.183,29.93 +2020-09-02 17:45:00,111.27,46.574,58.183,29.93 +2020-09-02 18:00:00,113.28,48.843,60.141000000000005,29.93 +2020-09-02 18:15:00,110.36,48.303999999999995,60.141000000000005,29.93 +2020-09-02 18:30:00,108.48,46.339,60.141000000000005,29.93 +2020-09-02 18:45:00,112.25,50.495,60.141000000000005,29.93 +2020-09-02 19:00:00,116.4,52.958999999999996,60.582,29.93 +2020-09-02 19:15:00,114.79,51.688,60.582,29.93 +2020-09-02 19:30:00,113.75,50.644,60.582,29.93 +2020-09-02 19:45:00,114.25,50.766000000000005,60.582,29.93 +2020-09-02 20:00:00,110.94,49.023,66.61,29.93 +2020-09-02 20:15:00,106.94,48.503,66.61,29.93 +2020-09-02 20:30:00,102.41,48.07899999999999,66.61,29.93 +2020-09-02 20:45:00,98.12,48.092,66.61,29.93 +2020-09-02 21:00:00,91.53,46.675,57.658,29.93 +2020-09-02 21:15:00,91.25,48.266000000000005,57.658,29.93 +2020-09-02 21:30:00,86.71,47.976000000000006,57.658,29.93 +2020-09-02 21:45:00,86.21,47.672,57.658,29.93 +2020-09-02 22:00:00,80.75,45.28,51.81,29.93 +2020-09-02 22:15:00,81.19,46.005,51.81,29.93 +2020-09-02 22:30:00,80.24,38.691,51.81,29.93 +2020-09-02 22:45:00,83.69,35.178000000000004,51.81,29.93 +2020-09-02 23:00:00,82.31,31.945,42.93600000000001,29.93 +2020-09-02 23:15:00,84.53,31.188000000000002,42.93600000000001,29.93 +2020-09-02 23:30:00,80.72,31.066,42.93600000000001,29.93 +2020-09-02 23:45:00,74.93,30.238000000000003,42.93600000000001,29.93 +2020-09-03 00:00:00,78.78,30.328000000000003,39.211,29.93 +2020-09-03 00:15:00,80.39,30.897,39.211,29.93 +2020-09-03 00:30:00,80.32,30.485,39.211,29.93 +2020-09-03 00:45:00,73.95,30.44,39.211,29.93 +2020-09-03 01:00:00,73.78,30.165,37.607,29.93 +2020-09-03 01:15:00,79.05,29.381,37.607,29.93 +2020-09-03 01:30:00,79.16,28.316,37.607,29.93 +2020-09-03 01:45:00,76.51,27.395,37.607,29.93 +2020-09-03 02:00:00,74.11,27.108,36.44,29.93 +2020-09-03 02:15:00,80.14,26.754,36.44,29.93 +2020-09-03 02:30:00,79.59,27.372,36.44,29.93 +2020-09-03 02:45:00,78.57,28.211,36.44,29.93 +2020-09-03 03:00:00,73.82,29.289,36.116,29.93 +2020-09-03 03:15:00,72.93,28.685,36.116,29.93 +2020-09-03 03:30:00,74.34,28.599,36.116,29.93 +2020-09-03 03:45:00,77.3,28.656999999999996,36.116,29.93 +2020-09-03 04:00:00,81.78,35.27,37.398,29.93 +2020-09-03 04:15:00,82.8,42.558,37.398,29.93 +2020-09-03 04:30:00,91.14,39.907,37.398,29.93 +2020-09-03 04:45:00,98.67,40.465,37.398,29.93 +2020-09-03 05:00:00,108.57,57.937,41.776,29.93 +2020-09-03 05:15:00,103.7,69.179,41.776,29.93 +2020-09-03 05:30:00,104.33,63.162,41.776,29.93 +2020-09-03 05:45:00,105.67,59.306999999999995,41.776,29.93 +2020-09-03 06:00:00,110.59,59.608999999999995,55.61,29.93 +2020-09-03 06:15:00,111.02,60.928000000000004,55.61,29.93 +2020-09-03 06:30:00,112.63,59.537,55.61,29.93 +2020-09-03 06:45:00,113.01,61.175,55.61,29.93 +2020-09-03 07:00:00,115.07,59.497,67.13600000000001,29.93 +2020-09-03 07:15:00,114.55,60.665,67.13600000000001,29.93 +2020-09-03 07:30:00,112.42,58.958,67.13600000000001,29.93 +2020-09-03 07:45:00,112.27,59.832,67.13600000000001,29.93 +2020-09-03 08:00:00,109.14,58.523999999999994,57.55,29.93 +2020-09-03 08:15:00,107.8,60.532,57.55,29.93 +2020-09-03 08:30:00,111.96,59.786,57.55,29.93 +2020-09-03 08:45:00,108.99,61.208,57.55,29.93 +2020-09-03 09:00:00,108.66,57.586000000000006,52.931999999999995,29.93 +2020-09-03 09:15:00,108.25,56.148999999999994,52.931999999999995,29.93 +2020-09-03 09:30:00,107.97,58.228,52.931999999999995,29.93 +2020-09-03 09:45:00,106.96,59.778,52.931999999999995,29.93 +2020-09-03 10:00:00,107.82,56.248999999999995,50.36600000000001,29.93 +2020-09-03 10:15:00,109.16,56.93,50.36600000000001,29.93 +2020-09-03 10:30:00,108.59,56.674,50.36600000000001,29.93 +2020-09-03 10:45:00,107.7,57.74,50.36600000000001,29.93 +2020-09-03 11:00:00,107.03,54.208,47.893,29.93 +2020-09-03 11:15:00,107.11,55.266000000000005,47.893,29.93 +2020-09-03 11:30:00,109.86,55.926,47.893,29.93 +2020-09-03 11:45:00,106.11,56.703,47.893,29.93 +2020-09-03 12:00:00,104.22,52.94,45.271,29.93 +2020-09-03 12:15:00,106.28,52.57,45.271,29.93 +2020-09-03 12:30:00,105.6,51.727,45.271,29.93 +2020-09-03 12:45:00,104.71,52.805,45.271,29.93 +2020-09-03 13:00:00,106.15,51.85,44.351000000000006,29.93 +2020-09-03 13:15:00,107.6,52.457,44.351000000000006,29.93 +2020-09-03 13:30:00,113.46,51.277,44.351000000000006,29.93 +2020-09-03 13:45:00,112.08,50.383,44.351000000000006,29.93 +2020-09-03 14:00:00,113.14,51.511,44.99,29.93 +2020-09-03 14:15:00,108.1,50.577,44.99,29.93 +2020-09-03 14:30:00,104.21,49.376000000000005,44.99,29.93 +2020-09-03 14:45:00,103.23,50.141000000000005,44.99,29.93 +2020-09-03 15:00:00,107.19,50.763000000000005,46.869,29.93 +2020-09-03 15:15:00,106.83,48.701,46.869,29.93 +2020-09-03 15:30:00,105.79,47.54600000000001,46.869,29.93 +2020-09-03 15:45:00,104.72,45.301,46.869,29.93 +2020-09-03 16:00:00,105.33,47.681000000000004,48.902,29.93 +2020-09-03 16:15:00,105.72,47.508,48.902,29.93 +2020-09-03 16:30:00,107.33,47.879,48.902,29.93 +2020-09-03 16:45:00,110.5,45.538999999999994,48.902,29.93 +2020-09-03 17:00:00,112.54,47.36600000000001,53.244,29.93 +2020-09-03 17:15:00,111.29,48.365,53.244,29.93 +2020-09-03 17:30:00,110.8,47.821999999999996,53.244,29.93 +2020-09-03 17:45:00,111.93,46.872,53.244,29.93 +2020-09-03 18:00:00,112.15,49.126999999999995,54.343999999999994,29.93 +2020-09-03 18:15:00,110.62,48.596000000000004,54.343999999999994,29.93 +2020-09-03 18:30:00,112.94,46.643,54.343999999999994,29.93 +2020-09-03 18:45:00,111.47,50.794,54.343999999999994,29.93 +2020-09-03 19:00:00,112.83,53.269,54.332,29.93 +2020-09-03 19:15:00,109.18,51.998000000000005,54.332,29.93 +2020-09-03 19:30:00,109.17,50.953,54.332,29.93 +2020-09-03 19:45:00,112.48,51.073,54.332,29.93 +2020-09-03 20:00:00,107.89,49.346000000000004,58.06,29.93 +2020-09-03 20:15:00,107.07,48.826,58.06,29.93 +2020-09-03 20:30:00,101.06,48.38,58.06,29.93 +2020-09-03 20:45:00,99.03,48.354,58.06,29.93 +2020-09-03 21:00:00,91.24,46.941,52.411,29.93 +2020-09-03 21:15:00,98.02,48.522,52.411,29.93 +2020-09-03 21:30:00,96.57,48.235,52.411,29.93 +2020-09-03 21:45:00,96.41,47.895,52.411,29.93 +2020-09-03 22:00:00,88.52,45.481,47.148999999999994,29.93 +2020-09-03 22:15:00,84.04,46.18600000000001,47.148999999999994,29.93 +2020-09-03 22:30:00,85.59,38.847,47.148999999999994,29.93 +2020-09-03 22:45:00,88.8,35.336999999999996,47.148999999999994,29.93 +2020-09-03 23:00:00,84.03,32.149,40.814,29.93 +2020-09-03 23:15:00,81.23,31.37,40.814,29.93 +2020-09-03 23:30:00,78.97,31.255,40.814,29.93 +2020-09-03 23:45:00,84.43,30.430999999999997,40.814,29.93 +2020-09-04 00:00:00,79.88,28.809,39.153,29.93 +2020-09-04 00:15:00,81.93,29.595,39.153,29.93 +2020-09-04 00:30:00,76.83,29.414,39.153,29.93 +2020-09-04 00:45:00,81.19,29.766,39.153,29.93 +2020-09-04 01:00:00,78.59,29.094,37.228,29.93 +2020-09-04 01:15:00,75.78,27.914,37.228,29.93 +2020-09-04 01:30:00,74.53,27.471,37.228,29.93 +2020-09-04 01:45:00,76.53,26.340999999999998,37.228,29.93 +2020-09-04 02:00:00,79.34,26.897,35.851,29.93 +2020-09-04 02:15:00,80.68,26.505,35.851,29.93 +2020-09-04 02:30:00,79.68,27.898000000000003,35.851,29.93 +2020-09-04 02:45:00,72.95,28.131,35.851,29.93 +2020-09-04 03:00:00,71.7,29.756,36.54,29.93 +2020-09-04 03:15:00,72.28,28.221,36.54,29.93 +2020-09-04 03:30:00,76.49,27.927,36.54,29.93 +2020-09-04 03:45:00,83.91,28.791999999999998,36.54,29.93 +2020-09-04 04:00:00,87.81,35.59,37.578,29.93 +2020-09-04 04:15:00,87.55,41.49100000000001,37.578,29.93 +2020-09-04 04:30:00,92.55,39.719,37.578,29.93 +2020-09-04 04:45:00,98.47,39.561,37.578,29.93 +2020-09-04 05:00:00,103.34,56.532,40.387,29.93 +2020-09-04 05:15:00,107.52,68.905,40.387,29.93 +2020-09-04 05:30:00,105.74,63.184,40.387,29.93 +2020-09-04 05:45:00,109.74,58.878,40.387,29.93 +2020-09-04 06:00:00,115.98,59.403999999999996,54.668,29.93 +2020-09-04 06:15:00,115.47,60.705,54.668,29.93 +2020-09-04 06:30:00,117.38,59.156000000000006,54.668,29.93 +2020-09-04 06:45:00,119.84,60.883,54.668,29.93 +2020-09-04 07:00:00,121.76,59.673,63.971000000000004,29.93 +2020-09-04 07:15:00,124.37,61.793,63.971000000000004,29.93 +2020-09-04 07:30:00,124.71,58.375,63.971000000000004,29.93 +2020-09-04 07:45:00,124.2,58.946000000000005,63.971000000000004,29.93 +2020-09-04 08:00:00,125.37,58.18,56.042,29.93 +2020-09-04 08:15:00,124.6,60.688,56.042,29.93 +2020-09-04 08:30:00,128.49,59.994,56.042,29.93 +2020-09-04 08:45:00,128.74,61.058,56.042,29.93 +2020-09-04 09:00:00,130.79,55.483000000000004,52.832,29.93 +2020-09-04 09:15:00,128.22,55.792,52.832,29.93 +2020-09-04 09:30:00,125.85,57.18899999999999,52.832,29.93 +2020-09-04 09:45:00,122.8,59.033,52.832,29.93 +2020-09-04 10:00:00,123.79,55.185,50.044,29.93 +2020-09-04 10:15:00,126.55,55.742,50.044,29.93 +2020-09-04 10:30:00,128.28,55.945,50.044,29.93 +2020-09-04 10:45:00,124.38,56.851000000000006,50.044,29.93 +2020-09-04 11:00:00,124.72,53.549,49.06100000000001,29.93 +2020-09-04 11:15:00,126.1,53.519,49.06100000000001,29.93 +2020-09-04 11:30:00,129.93,54.115,49.06100000000001,29.93 +2020-09-04 11:45:00,120.43,54.05,49.06100000000001,29.93 +2020-09-04 12:00:00,116.34,50.803999999999995,45.595,29.93 +2020-09-04 12:15:00,122.71,49.553999999999995,45.595,29.93 +2020-09-04 12:30:00,109.57,48.861999999999995,45.595,29.93 +2020-09-04 12:45:00,103.66,49.325,45.595,29.93 +2020-09-04 13:00:00,100.92,48.995,43.218,29.93 +2020-09-04 13:15:00,101.16,49.873000000000005,43.218,29.93 +2020-09-04 13:30:00,100.93,49.382,43.218,29.93 +2020-09-04 13:45:00,100.61,48.751999999999995,43.218,29.93 +2020-09-04 14:00:00,99.88,48.977,41.926,29.93 +2020-09-04 14:15:00,100.39,48.406000000000006,41.926,29.93 +2020-09-04 14:30:00,103.67,48.613,41.926,29.93 +2020-09-04 14:45:00,105.03,48.823,41.926,29.93 +2020-09-04 15:00:00,106.17,49.254,43.79,29.93 +2020-09-04 15:15:00,101.95,46.93600000000001,43.79,29.93 +2020-09-04 15:30:00,95.93,45.091,43.79,29.93 +2020-09-04 15:45:00,97.23,43.538999999999994,43.79,29.93 +2020-09-04 16:00:00,99.92,44.988,45.895,29.93 +2020-09-04 16:15:00,100.88,45.3,45.895,29.93 +2020-09-04 16:30:00,104.55,45.525,45.895,29.93 +2020-09-04 16:45:00,106.79,42.534,45.895,29.93 +2020-09-04 17:00:00,106.67,45.867,51.36,29.93 +2020-09-04 17:15:00,105.13,46.685,51.36,29.93 +2020-09-04 17:30:00,106.1,46.251999999999995,51.36,29.93 +2020-09-04 17:45:00,108.53,45.159,51.36,29.93 +2020-09-04 18:00:00,108.52,47.55,52.985,29.93 +2020-09-04 18:15:00,105.88,46.141999999999996,52.985,29.93 +2020-09-04 18:30:00,108.88,44.162,52.985,29.93 +2020-09-04 18:45:00,110.64,48.68,52.985,29.93 +2020-09-04 19:00:00,111.33,52.07,52.602,29.93 +2020-09-04 19:15:00,112.44,51.495,52.602,29.93 +2020-09-04 19:30:00,112.3,50.453,52.602,29.93 +2020-09-04 19:45:00,106.77,49.611999999999995,52.602,29.93 +2020-09-04 20:00:00,98.61,47.763000000000005,58.063,29.93 +2020-09-04 20:15:00,99.3,47.946999999999996,58.063,29.93 +2020-09-04 20:30:00,95.69,47.044,58.063,29.93 +2020-09-04 20:45:00,93.25,46.33,58.063,29.93 +2020-09-04 21:00:00,88.17,46.15,50.135,29.93 +2020-09-04 21:15:00,88.23,49.258,50.135,29.93 +2020-09-04 21:30:00,85.09,48.846000000000004,50.135,29.93 +2020-09-04 21:45:00,85.78,48.718999999999994,50.135,29.93 +2020-09-04 22:00:00,82.3,46.283,45.165,29.93 +2020-09-04 22:15:00,81.47,46.729,45.165,29.93 +2020-09-04 22:30:00,77.2,44.387,45.165,29.93 +2020-09-04 22:45:00,79.3,42.086000000000006,45.165,29.93 +2020-09-04 23:00:00,74.88,40.385,39.121,29.93 +2020-09-04 23:15:00,70.48,37.946999999999996,39.121,29.93 +2020-09-04 23:30:00,72.77,36.041,39.121,29.93 +2020-09-04 23:45:00,73.16,35.028,39.121,29.93 +2020-09-05 00:00:00,69.6,29.750999999999998,38.49,29.816 +2020-09-05 00:15:00,65.9,29.398000000000003,38.49,29.816 +2020-09-05 00:30:00,61.59,28.978,38.49,29.816 +2020-09-05 00:45:00,64.02,28.78,38.49,29.816 +2020-09-05 01:00:00,62.98,28.416999999999998,34.5,29.816 +2020-09-05 01:15:00,69.08,27.651,34.5,29.816 +2020-09-05 01:30:00,69.9,26.445999999999998,34.5,29.816 +2020-09-05 01:45:00,65.62,26.391,34.5,29.816 +2020-09-05 02:00:00,59.22,26.165,32.236,29.816 +2020-09-05 02:15:00,61.62,25.051,32.236,29.816 +2020-09-05 02:30:00,60.74,25.576999999999998,32.236,29.816 +2020-09-05 02:45:00,61.81,26.514,32.236,29.816 +2020-09-05 03:00:00,59.94,26.993000000000002,32.067,29.816 +2020-09-05 03:15:00,61.24,24.705,32.067,29.816 +2020-09-05 03:30:00,61.65,24.504,32.067,29.816 +2020-09-05 03:45:00,61.59,26.679000000000002,32.067,29.816 +2020-09-05 04:00:00,61.57,31.316999999999997,33.071,29.816 +2020-09-05 04:15:00,59.69,36.134,33.071,29.816 +2020-09-05 04:30:00,62.02,32.619,33.071,29.816 +2020-09-05 04:45:00,63.77,32.647,33.071,29.816 +2020-09-05 05:00:00,64.24,40.606,33.014,29.816 +2020-09-05 05:15:00,65.87,41.105,33.014,29.816 +2020-09-05 05:30:00,65.26,36.77,33.014,29.816 +2020-09-05 05:45:00,64.15,37.029,33.014,29.816 +2020-09-05 06:00:00,67.9,49.608999999999995,34.628,29.816 +2020-09-05 06:15:00,70.61,59.795,34.628,29.816 +2020-09-05 06:30:00,71.88,55.083,34.628,29.816 +2020-09-05 06:45:00,72.73,52.895,34.628,29.816 +2020-09-05 07:00:00,75.06,49.843999999999994,38.871,29.816 +2020-09-05 07:15:00,73.03,50.681999999999995,38.871,29.816 +2020-09-05 07:30:00,77.02,48.961999999999996,38.871,29.816 +2020-09-05 07:45:00,79.22,50.842,38.871,29.816 +2020-09-05 08:00:00,77.29,51.131,43.293,29.816 +2020-09-05 08:15:00,78.24,53.879,43.293,29.816 +2020-09-05 08:30:00,76.23,53.385,43.293,29.816 +2020-09-05 08:45:00,75.72,55.662,43.293,29.816 +2020-09-05 09:00:00,74.97,53.013000000000005,44.559,29.816 +2020-09-05 09:15:00,72.64,53.833,44.559,29.816 +2020-09-05 09:30:00,73.09,55.76,44.559,29.816 +2020-09-05 09:45:00,75.25,57.203,44.559,29.816 +2020-09-05 10:00:00,74.29,53.878,42.091,29.816 +2020-09-05 10:15:00,73.61,54.733000000000004,42.091,29.816 +2020-09-05 10:30:00,73.64,54.651,42.091,29.816 +2020-09-05 10:45:00,71.59,55.418,42.091,29.816 +2020-09-05 11:00:00,70.86,52.035,38.505,29.816 +2020-09-05 11:15:00,67.38,52.648,38.505,29.816 +2020-09-05 11:30:00,67.78,53.333999999999996,38.505,29.816 +2020-09-05 11:45:00,67.79,53.699,38.505,29.816 +2020-09-05 12:00:00,64.86,50.608999999999995,35.388000000000005,29.816 +2020-09-05 12:15:00,64.27,50.17,35.388000000000005,29.816 +2020-09-05 12:30:00,61.73,49.437,35.388000000000005,29.816 +2020-09-05 12:45:00,61.93,50.407,35.388000000000005,29.816 +2020-09-05 13:00:00,57.25,49.278999999999996,31.355999999999998,29.816 +2020-09-05 13:15:00,60.01,49.393,31.355999999999998,29.816 +2020-09-05 13:30:00,60.1,48.998000000000005,31.355999999999998,29.816 +2020-09-05 13:45:00,58.58,47.281000000000006,31.355999999999998,29.816 +2020-09-05 14:00:00,62.4,47.692,30.522,29.816 +2020-09-05 14:15:00,62.32,45.977,30.522,29.816 +2020-09-05 14:30:00,69.06,45.586000000000006,30.522,29.816 +2020-09-05 14:45:00,67.31,46.24,30.522,29.816 +2020-09-05 15:00:00,64.95,47.103,34.36,29.816 +2020-09-05 15:15:00,68.02,45.523,34.36,29.816 +2020-09-05 15:30:00,73.3,44.092,34.36,29.816 +2020-09-05 15:45:00,66.23,41.758,34.36,29.816 +2020-09-05 16:00:00,70.0,44.927,39.507,29.816 +2020-09-05 16:15:00,70.69,44.593999999999994,39.507,29.816 +2020-09-05 16:30:00,72.97,45.01,39.507,29.816 +2020-09-05 16:45:00,75.43,42.166000000000004,39.507,29.816 +2020-09-05 17:00:00,78.45,44.356,47.151,29.816 +2020-09-05 17:15:00,79.25,43.55,47.151,29.816 +2020-09-05 17:30:00,80.34,42.998000000000005,47.151,29.816 +2020-09-05 17:45:00,81.44,42.316,47.151,29.816 +2020-09-05 18:00:00,84.42,45.887,50.303999999999995,29.816 +2020-09-05 18:15:00,82.25,46.185,50.303999999999995,29.816 +2020-09-05 18:30:00,82.55,45.576,50.303999999999995,29.816 +2020-09-05 18:45:00,85.27,46.61600000000001,50.303999999999995,29.816 +2020-09-05 19:00:00,83.52,48.75899999999999,50.622,29.816 +2020-09-05 19:15:00,83.04,47.233000000000004,50.622,29.816 +2020-09-05 19:30:00,81.56,46.961999999999996,50.622,29.816 +2020-09-05 19:45:00,80.53,47.653,50.622,29.816 +2020-09-05 20:00:00,77.83,46.8,45.391000000000005,29.816 +2020-09-05 20:15:00,76.29,46.696000000000005,45.391000000000005,29.816 +2020-09-05 20:30:00,75.74,44.957,45.391000000000005,29.816 +2020-09-05 20:45:00,75.02,45.777,45.391000000000005,29.816 +2020-09-05 21:00:00,71.28,44.6,39.98,29.816 +2020-09-05 21:15:00,70.5,47.446000000000005,39.98,29.816 +2020-09-05 21:30:00,67.18,47.358999999999995,39.98,29.816 +2020-09-05 21:45:00,67.05,46.666000000000004,39.98,29.816 +2020-09-05 22:00:00,64.24,44.354,37.53,29.816 +2020-09-05 22:15:00,62.85,45.309,37.53,29.816 +2020-09-05 22:30:00,60.44,43.391000000000005,37.53,29.816 +2020-09-05 22:45:00,60.44,41.656000000000006,37.53,29.816 +2020-09-05 23:00:00,55.7,39.704,30.97,29.816 +2020-09-05 23:15:00,55.88,37.408,30.97,29.816 +2020-09-05 23:30:00,55.87,37.45,30.97,29.816 +2020-09-05 23:45:00,54.85,36.455,30.97,29.816 +2020-09-06 00:00:00,51.94,31.230999999999998,27.24,29.816 +2020-09-06 00:15:00,52.98,29.822,27.24,29.816 +2020-09-06 00:30:00,51.93,29.235,27.24,29.816 +2020-09-06 00:45:00,52.47,29.033,27.24,29.816 +2020-09-06 01:00:00,50.9,28.868000000000002,25.662,29.816 +2020-09-06 01:15:00,51.69,28.144000000000002,25.662,29.816 +2020-09-06 01:30:00,51.06,26.9,25.662,29.816 +2020-09-06 01:45:00,51.75,26.471999999999998,25.662,29.816 +2020-09-06 02:00:00,50.22,26.226,25.67,29.816 +2020-09-06 02:15:00,50.83,25.649,25.67,29.816 +2020-09-06 02:30:00,50.93,26.423000000000002,25.67,29.816 +2020-09-06 02:45:00,51.01,27.158,25.67,29.816 +2020-09-06 03:00:00,50.72,28.236,24.258000000000003,29.816 +2020-09-06 03:15:00,51.86,26.129,24.258000000000003,29.816 +2020-09-06 03:30:00,51.93,25.463,24.258000000000003,29.816 +2020-09-06 03:45:00,52.05,26.918000000000003,24.258000000000003,29.816 +2020-09-06 04:00:00,52.55,31.531,25.051,29.816 +2020-09-06 04:15:00,52.95,35.867,25.051,29.816 +2020-09-06 04:30:00,53.59,33.545,25.051,29.816 +2020-09-06 04:45:00,54.33,33.202,25.051,29.816 +2020-09-06 05:00:00,56.46,40.988,25.145,29.816 +2020-09-06 05:15:00,55.34,40.624,25.145,29.816 +2020-09-06 05:30:00,53.86,35.900999999999996,25.145,29.816 +2020-09-06 05:45:00,55.65,35.897,25.145,29.816 +2020-09-06 06:00:00,56.53,46.333999999999996,26.371,29.816 +2020-09-06 06:15:00,57.41,57.077,26.371,29.816 +2020-09-06 06:30:00,58.46,51.619,26.371,29.816 +2020-09-06 06:45:00,60.16,48.426,26.371,29.816 +2020-09-06 07:00:00,60.1,45.915,28.756999999999998,29.816 +2020-09-06 07:15:00,60.26,45.225,28.756999999999998,29.816 +2020-09-06 07:30:00,65.45,44.508,28.756999999999998,29.816 +2020-09-06 07:45:00,66.55,46.265,28.756999999999998,29.816 +2020-09-06 08:00:00,66.48,47.442,32.82,29.816 +2020-09-06 08:15:00,65.64,51.174,32.82,29.816 +2020-09-06 08:30:00,58.67,51.653,32.82,29.816 +2020-09-06 08:45:00,61.25,54.093,32.82,29.816 +2020-09-06 09:00:00,63.13,51.301,35.534,29.816 +2020-09-06 09:15:00,57.61,51.75,35.534,29.816 +2020-09-06 09:30:00,58.16,54.012,35.534,29.816 +2020-09-06 09:45:00,60.38,56.286,35.534,29.816 +2020-09-06 10:00:00,61.07,53.742,35.925,29.816 +2020-09-06 10:15:00,60.78,54.75,35.925,29.816 +2020-09-06 10:30:00,63.8,54.928000000000004,35.925,29.816 +2020-09-06 10:45:00,61.65,56.318000000000005,35.925,29.816 +2020-09-06 11:00:00,61.14,52.763999999999996,37.056,29.816 +2020-09-06 11:15:00,57.92,52.993,37.056,29.816 +2020-09-06 11:30:00,60.04,54.018,37.056,29.816 +2020-09-06 11:45:00,59.7,54.666000000000004,37.056,29.816 +2020-09-06 12:00:00,59.47,52.391999999999996,33.124,29.816 +2020-09-06 12:15:00,59.46,51.644,33.124,29.816 +2020-09-06 12:30:00,57.73,50.933,33.124,29.816 +2020-09-06 12:45:00,56.0,51.214,33.124,29.816 +2020-09-06 13:00:00,53.2,49.711000000000006,29.874000000000002,29.816 +2020-09-06 13:15:00,54.82,49.708,29.874000000000002,29.816 +2020-09-06 13:30:00,54.67,48.316,29.874000000000002,29.816 +2020-09-06 13:45:00,53.78,47.49100000000001,29.874000000000002,29.816 +2020-09-06 14:00:00,52.66,48.938,27.302,29.816 +2020-09-06 14:15:00,55.42,47.761,27.302,29.816 +2020-09-06 14:30:00,57.66,46.483000000000004,27.302,29.816 +2020-09-06 14:45:00,57.04,46.153,27.302,29.816 +2020-09-06 15:00:00,57.93,46.895,27.642,29.816 +2020-09-06 15:15:00,56.35,44.723,27.642,29.816 +2020-09-06 15:30:00,55.04,43.233000000000004,27.642,29.816 +2020-09-06 15:45:00,55.71,41.247,27.642,29.816 +2020-09-06 16:00:00,59.68,43.17,31.945999999999998,29.816 +2020-09-06 16:15:00,60.8,42.937,31.945999999999998,29.816 +2020-09-06 16:30:00,66.16,44.26,31.945999999999998,29.816 +2020-09-06 16:45:00,71.81,41.54,31.945999999999998,29.816 +2020-09-06 17:00:00,76.25,44.00899999999999,40.387,29.816 +2020-09-06 17:15:00,77.49,44.511,40.387,29.816 +2020-09-06 17:30:00,78.34,44.708,40.387,29.816 +2020-09-06 17:45:00,74.93,44.711000000000006,40.387,29.816 +2020-09-06 18:00:00,79.12,48.699,44.575,29.816 +2020-09-06 18:15:00,78.19,48.806999999999995,44.575,29.816 +2020-09-06 18:30:00,80.41,47.744,44.575,29.816 +2020-09-06 18:45:00,80.48,49.102,44.575,29.816 +2020-09-06 19:00:00,92.24,53.111000000000004,45.623999999999995,29.816 +2020-09-06 19:15:00,91.93,50.673,45.623999999999995,29.816 +2020-09-06 19:30:00,89.23,50.163999999999994,45.623999999999995,29.816 +2020-09-06 19:45:00,85.71,50.617,45.623999999999995,29.816 +2020-09-06 20:00:00,81.21,49.949,44.583999999999996,29.816 +2020-09-06 20:15:00,87.39,49.841,44.583999999999996,29.816 +2020-09-06 20:30:00,86.81,48.938,44.583999999999996,29.816 +2020-09-06 20:45:00,86.6,48.099,44.583999999999996,29.816 +2020-09-06 21:00:00,78.52,46.498999999999995,39.732,29.816 +2020-09-06 21:15:00,79.0,48.997,39.732,29.816 +2020-09-06 21:30:00,80.14,48.376999999999995,39.732,29.816 +2020-09-06 21:45:00,77.29,47.978,39.732,29.816 +2020-09-06 22:00:00,73.53,47.293,38.571,29.816 +2020-09-06 22:15:00,73.0,46.657,38.571,29.816 +2020-09-06 22:30:00,71.65,43.901,38.571,29.816 +2020-09-06 22:45:00,71.15,40.95,38.571,29.816 +2020-09-06 23:00:00,65.54,38.457,33.121,29.816 +2020-09-06 23:15:00,66.93,37.468,33.121,29.816 +2020-09-06 23:30:00,66.52,37.135999999999996,33.121,29.816 +2020-09-06 23:45:00,66.55,36.387,33.121,29.816 +2020-09-07 00:00:00,64.09,33.271,32.506,29.93 +2020-09-07 00:15:00,66.1,32.906,32.506,29.93 +2020-09-07 00:30:00,65.16,32.003,32.506,29.93 +2020-09-07 00:45:00,72.59,31.397,32.506,29.93 +2020-09-07 01:00:00,71.19,31.555999999999997,31.121,29.93 +2020-09-07 01:15:00,72.55,30.802,31.121,29.93 +2020-09-07 01:30:00,70.95,29.899,31.121,29.93 +2020-09-07 01:45:00,71.59,29.4,31.121,29.93 +2020-09-07 02:00:00,71.83,29.554000000000002,29.605999999999998,29.93 +2020-09-07 02:15:00,71.29,28.28,29.605999999999998,29.93 +2020-09-07 02:30:00,65.32,29.213,29.605999999999998,29.93 +2020-09-07 02:45:00,68.55,29.741,29.605999999999998,29.93 +2020-09-07 03:00:00,73.65,31.401,28.124000000000002,29.93 +2020-09-07 03:15:00,75.4,30.138,28.124000000000002,29.93 +2020-09-07 03:30:00,77.07,30.045,28.124000000000002,29.93 +2020-09-07 03:45:00,72.74,31.016,28.124000000000002,29.93 +2020-09-07 04:00:00,78.77,38.909,29.743000000000002,29.93 +2020-09-07 04:15:00,78.27,46.333999999999996,29.743000000000002,29.93 +2020-09-07 04:30:00,81.92,43.856,29.743000000000002,29.93 +2020-09-07 04:45:00,87.93,43.864,29.743000000000002,29.93 +2020-09-07 05:00:00,95.33,59.738,36.191,29.93 +2020-09-07 05:15:00,99.43,70.842,36.191,29.93 +2020-09-07 05:30:00,100.39,64.77199999999999,36.191,29.93 +2020-09-07 05:45:00,103.16,61.369,36.191,29.93 +2020-09-07 06:00:00,107.79,60.716,55.277,29.93 +2020-09-07 06:15:00,107.35,61.775,55.277,29.93 +2020-09-07 06:30:00,111.41,60.729,55.277,29.93 +2020-09-07 06:45:00,110.88,63.191,55.277,29.93 +2020-09-07 07:00:00,111.5,61.396,65.697,29.93 +2020-09-07 07:15:00,111.03,62.872,65.697,29.93 +2020-09-07 07:30:00,108.26,61.34,65.697,29.93 +2020-09-07 07:45:00,107.84,63.056999999999995,65.697,29.93 +2020-09-07 08:00:00,106.73,61.815,57.028,29.93 +2020-09-07 08:15:00,106.0,64.19800000000001,57.028,29.93 +2020-09-07 08:30:00,105.72,63.345,57.028,29.93 +2020-09-07 08:45:00,105.41,65.547,57.028,29.93 +2020-09-07 09:00:00,105.06,61.758,52.633,29.93 +2020-09-07 09:15:00,103.6,60.206,52.633,29.93 +2020-09-07 09:30:00,104.14,61.515,52.633,29.93 +2020-09-07 09:45:00,108.19,61.792,52.633,29.93 +2020-09-07 10:00:00,105.88,59.531000000000006,50.647,29.93 +2020-09-07 10:15:00,107.1,60.333999999999996,50.647,29.93 +2020-09-07 10:30:00,106.5,59.99,50.647,29.93 +2020-09-07 10:45:00,110.53,60.085,50.647,29.93 +2020-09-07 11:00:00,112.98,56.413000000000004,50.245,29.93 +2020-09-07 11:15:00,107.64,57.105,50.245,29.93 +2020-09-07 11:30:00,101.36,58.91,50.245,29.93 +2020-09-07 11:45:00,103.67,59.924,50.245,29.93 +2020-09-07 12:00:00,108.34,56.413000000000004,46.956,29.93 +2020-09-07 12:15:00,106.87,55.748000000000005,46.956,29.93 +2020-09-07 12:30:00,97.46,54.169,46.956,29.93 +2020-09-07 12:45:00,99.84,54.635,46.956,29.93 +2020-09-07 13:00:00,97.74,53.949,47.383,29.93 +2020-09-07 13:15:00,100.52,52.99100000000001,47.383,29.93 +2020-09-07 13:30:00,97.51,51.676,47.383,29.93 +2020-09-07 13:45:00,96.25,51.64,47.383,29.93 +2020-09-07 14:00:00,98.27,52.184,47.1,29.93 +2020-09-07 14:15:00,99.93,51.449,47.1,29.93 +2020-09-07 14:30:00,104.1,49.985,47.1,29.93 +2020-09-07 14:45:00,107.04,51.446999999999996,47.1,29.93 +2020-09-07 15:00:00,106.36,52.048,49.355,29.93 +2020-09-07 15:15:00,104.11,49.193999999999996,49.355,29.93 +2020-09-07 15:30:00,105.03,48.29,49.355,29.93 +2020-09-07 15:45:00,108.61,45.835,49.355,29.93 +2020-09-07 16:00:00,109.53,48.643,52.14,29.93 +2020-09-07 16:15:00,109.89,48.38399999999999,52.14,29.93 +2020-09-07 16:30:00,113.1,49.004,52.14,29.93 +2020-09-07 16:45:00,113.41,46.208,52.14,29.93 +2020-09-07 17:00:00,113.76,47.629,58.705,29.93 +2020-09-07 17:15:00,113.0,48.368,58.705,29.93 +2020-09-07 17:30:00,115.63,48.17,58.705,29.93 +2020-09-07 17:45:00,114.27,47.673,58.705,29.93 +2020-09-07 18:00:00,115.1,50.723,59.153,29.93 +2020-09-07 18:15:00,112.76,48.966,59.153,29.93 +2020-09-07 18:30:00,113.45,47.331,59.153,29.93 +2020-09-07 18:45:00,118.05,51.548,59.153,29.93 +2020-09-07 19:00:00,114.98,55.098,61.483000000000004,29.93 +2020-09-07 19:15:00,112.15,53.696999999999996,61.483000000000004,29.93 +2020-09-07 19:30:00,113.66,52.918,61.483000000000004,29.93 +2020-09-07 19:45:00,113.35,52.733000000000004,61.483000000000004,29.93 +2020-09-07 20:00:00,109.69,50.699,67.55,29.93 +2020-09-07 20:15:00,107.25,51.534,67.55,29.93 +2020-09-07 20:30:00,106.23,50.851000000000006,67.55,29.93 +2020-09-07 20:45:00,103.57,50.35,67.55,29.93 +2020-09-07 21:00:00,96.79,48.262,60.026,29.93 +2020-09-07 21:15:00,92.0,51.003,60.026,29.93 +2020-09-07 21:30:00,87.06,50.658,60.026,29.93 +2020-09-07 21:45:00,91.31,49.968999999999994,60.026,29.93 +2020-09-07 22:00:00,88.7,47.123000000000005,52.736999999999995,29.93 +2020-09-07 22:15:00,87.47,48.068999999999996,52.736999999999995,29.93 +2020-09-07 22:30:00,82.02,40.363,52.736999999999995,29.93 +2020-09-07 22:45:00,84.25,36.84,52.736999999999995,29.93 +2020-09-07 23:00:00,81.67,34.525999999999996,44.408,29.93 +2020-09-07 23:15:00,82.18,32.357,44.408,29.93 +2020-09-07 23:30:00,79.67,32.315,44.408,29.93 +2020-09-07 23:45:00,80.93,31.396,44.408,29.93 +2020-09-08 00:00:00,78.69,31.4,44.438,29.93 +2020-09-08 00:15:00,78.24,31.965,44.438,29.93 +2020-09-08 00:30:00,74.74,31.59,44.438,29.93 +2020-09-08 00:45:00,78.91,31.578000000000003,44.438,29.93 +2020-09-08 01:00:00,78.69,31.218000000000004,41.468999999999994,29.93 +2020-09-08 01:15:00,79.73,30.53,41.468999999999994,29.93 +2020-09-08 01:30:00,74.2,29.549,41.468999999999994,29.93 +2020-09-08 01:45:00,79.22,28.631999999999998,41.468999999999994,29.93 +2020-09-08 02:00:00,78.78,28.364,39.708,29.93 +2020-09-08 02:15:00,79.67,28.124000000000002,39.708,29.93 +2020-09-08 02:30:00,78.51,28.616999999999997,39.708,29.93 +2020-09-08 02:45:00,78.4,29.432,39.708,29.93 +2020-09-08 03:00:00,81.3,30.445999999999998,38.919000000000004,29.93 +2020-09-08 03:15:00,79.9,29.929000000000002,38.919000000000004,29.93 +2020-09-08 03:30:00,77.62,29.871,38.919000000000004,29.93 +2020-09-08 03:45:00,81.23,29.865,38.919000000000004,29.93 +2020-09-08 04:00:00,88.53,36.65,40.092,29.93 +2020-09-08 04:15:00,91.69,44.075,40.092,29.93 +2020-09-08 04:30:00,94.06,41.461999999999996,40.092,29.93 +2020-09-08 04:45:00,99.19,42.047,40.092,29.93 +2020-09-08 05:00:00,107.52,59.983000000000004,43.713,29.93 +2020-09-08 05:15:00,111.02,71.717,43.713,29.93 +2020-09-08 05:30:00,106.44,65.628,43.713,29.93 +2020-09-08 05:45:00,110.66,61.523,43.713,29.93 +2020-09-08 06:00:00,113.9,61.646,56.033,29.93 +2020-09-08 06:15:00,116.71,63.085,56.033,29.93 +2020-09-08 06:30:00,119.58,61.678000000000004,56.033,29.93 +2020-09-08 06:45:00,119.15,63.302,56.033,29.93 +2020-09-08 07:00:00,121.33,61.621,66.003,29.93 +2020-09-08 07:15:00,120.99,62.865,66.003,29.93 +2020-09-08 07:30:00,121.51,61.317,66.003,29.93 +2020-09-08 07:45:00,119.76,62.193999999999996,66.003,29.93 +2020-09-08 08:00:00,113.06,60.905,57.474,29.93 +2020-09-08 08:15:00,112.42,62.732,57.474,29.93 +2020-09-08 08:30:00,112.09,62.012,57.474,29.93 +2020-09-08 08:45:00,114.08,63.347,57.474,29.93 +2020-09-08 09:00:00,111.56,59.766999999999996,51.928000000000004,29.93 +2020-09-08 09:15:00,113.34,58.276,51.928000000000004,29.93 +2020-09-08 09:30:00,112.99,60.253,51.928000000000004,29.93 +2020-09-08 09:45:00,116.22,61.656000000000006,51.928000000000004,29.93 +2020-09-08 10:00:00,117.84,58.111000000000004,49.46,29.93 +2020-09-08 10:15:00,109.46,58.625,49.46,29.93 +2020-09-08 10:30:00,109.25,58.316,49.46,29.93 +2020-09-08 10:45:00,109.93,59.315,49.46,29.93 +2020-09-08 11:00:00,105.93,55.848,48.206,29.93 +2020-09-08 11:15:00,109.06,56.84,48.206,29.93 +2020-09-08 11:30:00,107.95,57.488,48.206,29.93 +2020-09-08 11:45:00,108.6,58.178000000000004,48.206,29.93 +2020-09-08 12:00:00,108.71,54.305,46.285,29.93 +2020-09-08 12:15:00,108.48,53.868,46.285,29.93 +2020-09-08 12:30:00,108.83,53.159,46.285,29.93 +2020-09-08 12:45:00,105.3,54.181999999999995,46.285,29.93 +2020-09-08 13:00:00,104.99,53.108000000000004,46.861999999999995,29.93 +2020-09-08 13:15:00,107.88,53.677,46.861999999999995,29.93 +2020-09-08 13:30:00,110.14,52.479,46.861999999999995,29.93 +2020-09-08 13:45:00,107.86,51.63,46.861999999999995,29.93 +2020-09-08 14:00:00,108.5,52.566,46.488,29.93 +2020-09-08 14:15:00,109.29,51.687,46.488,29.93 +2020-09-08 14:30:00,103.98,50.617,46.488,29.93 +2020-09-08 14:45:00,103.36,51.375,46.488,29.93 +2020-09-08 15:00:00,104.44,51.73,48.442,29.93 +2020-09-08 15:15:00,102.88,49.745,48.442,29.93 +2020-09-08 15:30:00,100.95,48.707,48.442,29.93 +2020-09-08 15:45:00,101.93,46.519,48.442,29.93 +2020-09-08 16:00:00,106.74,48.718999999999994,50.397,29.93 +2020-09-08 16:15:00,109.62,48.608000000000004,50.397,29.93 +2020-09-08 16:30:00,111.41,48.976000000000006,50.397,29.93 +2020-09-08 16:45:00,111.89,46.9,50.397,29.93 +2020-09-08 17:00:00,114.63,48.536,56.668,29.93 +2020-09-08 17:15:00,112.41,49.675,56.668,29.93 +2020-09-08 17:30:00,113.49,49.18,56.668,29.93 +2020-09-08 17:45:00,115.51,48.411,56.668,29.93 +2020-09-08 18:00:00,115.96,50.586999999999996,57.957,29.93 +2020-09-08 18:15:00,112.95,50.101000000000006,57.957,29.93 +2020-09-08 18:30:00,113.99,48.21,57.957,29.93 +2020-09-08 18:45:00,116.35,52.343,57.957,29.93 +2020-09-08 19:00:00,120.65,54.869,57.056000000000004,29.93 +2020-09-08 19:15:00,118.5,53.596000000000004,57.056000000000004,29.93 +2020-09-08 19:30:00,120.53,52.551,57.056000000000004,29.93 +2020-09-08 19:45:00,111.59,52.668,57.056000000000004,29.93 +2020-09-08 20:00:00,103.31,51.019,64.156,29.93 +2020-09-08 20:15:00,102.93,50.501000000000005,64.156,29.93 +2020-09-08 20:30:00,100.46,49.946999999999996,64.156,29.93 +2020-09-08 20:45:00,102.55,49.711000000000006,64.156,29.93 +2020-09-08 21:00:00,101.45,48.318000000000005,56.507,29.93 +2020-09-08 21:15:00,102.7,49.847,56.507,29.93 +2020-09-08 21:30:00,97.23,49.58,56.507,29.93 +2020-09-08 21:45:00,96.21,49.055,56.507,29.93 +2020-09-08 22:00:00,91.16,46.526,50.728,29.93 +2020-09-08 22:15:00,91.57,47.123000000000005,50.728,29.93 +2020-09-08 22:30:00,84.85,39.665,50.728,29.93 +2020-09-08 22:45:00,86.91,36.171,50.728,29.93 +2020-09-08 23:00:00,84.69,33.22,43.556999999999995,29.93 +2020-09-08 23:15:00,85.78,32.317,43.556999999999995,29.93 +2020-09-08 23:30:00,82.52,32.239000000000004,43.556999999999995,29.93 +2020-09-08 23:45:00,85.59,31.436999999999998,43.556999999999995,29.93 +2020-09-09 00:00:00,82.31,31.622,41.151,29.93 +2020-09-09 00:15:00,83.13,32.187,41.151,29.93 +2020-09-09 00:30:00,79.51,31.819000000000003,41.151,29.93 +2020-09-09 00:45:00,77.56,31.813000000000002,41.151,29.93 +2020-09-09 01:00:00,79.6,31.435,37.763000000000005,29.93 +2020-09-09 01:15:00,81.91,30.766,37.763000000000005,29.93 +2020-09-09 01:30:00,82.15,29.803,37.763000000000005,29.93 +2020-09-09 01:45:00,79.99,28.886999999999997,37.763000000000005,29.93 +2020-09-09 02:00:00,74.52,28.623,35.615,29.93 +2020-09-09 02:15:00,74.8,28.405,35.615,29.93 +2020-09-09 02:30:00,81.69,28.874000000000002,35.615,29.93 +2020-09-09 02:45:00,82.63,29.683000000000003,35.615,29.93 +2020-09-09 03:00:00,83.44,30.685,35.153,29.93 +2020-09-09 03:15:00,78.79,30.186,35.153,29.93 +2020-09-09 03:30:00,84.28,30.133000000000003,35.153,29.93 +2020-09-09 03:45:00,86.29,30.113000000000003,35.153,29.93 +2020-09-09 04:00:00,89.48,36.935,36.203,29.93 +2020-09-09 04:15:00,89.42,44.39,36.203,29.93 +2020-09-09 04:30:00,94.57,41.785,36.203,29.93 +2020-09-09 04:45:00,102.54,42.376000000000005,36.203,29.93 +2020-09-09 05:00:00,108.61,60.409,39.922,29.93 +2020-09-09 05:15:00,107.95,72.247,39.922,29.93 +2020-09-09 05:30:00,111.12,66.142,39.922,29.93 +2020-09-09 05:45:00,111.45,61.985,39.922,29.93 +2020-09-09 06:00:00,113.78,62.07,56.443999999999996,29.93 +2020-09-09 06:15:00,116.37,63.535,56.443999999999996,29.93 +2020-09-09 06:30:00,119.03,62.123000000000005,56.443999999999996,29.93 +2020-09-09 06:45:00,118.81,63.743,56.443999999999996,29.93 +2020-09-09 07:00:00,118.73,62.062,68.683,29.93 +2020-09-09 07:15:00,118.39,63.321000000000005,68.683,29.93 +2020-09-09 07:30:00,116.09,61.806000000000004,68.683,29.93 +2020-09-09 07:45:00,114.53,62.681999999999995,68.683,29.93 +2020-09-09 08:00:00,113.91,61.396,59.003,29.93 +2020-09-09 08:15:00,112.48,63.184,59.003,29.93 +2020-09-09 08:30:00,113.23,62.471000000000004,59.003,29.93 +2020-09-09 08:45:00,114.44,63.788000000000004,59.003,29.93 +2020-09-09 09:00:00,111.57,60.216,56.21,29.93 +2020-09-09 09:15:00,109.34,58.714,56.21,29.93 +2020-09-09 09:30:00,109.66,60.67100000000001,56.21,29.93 +2020-09-09 09:45:00,109.6,62.044,56.21,29.93 +2020-09-09 10:00:00,108.97,58.495,52.358999999999995,29.93 +2020-09-09 10:15:00,108.59,58.974,52.358999999999995,29.93 +2020-09-09 10:30:00,107.81,58.653,52.358999999999995,29.93 +2020-09-09 10:45:00,107.09,59.638999999999996,52.358999999999995,29.93 +2020-09-09 11:00:00,105.96,56.187,51.161,29.93 +2020-09-09 11:15:00,104.7,57.163999999999994,51.161,29.93 +2020-09-09 11:30:00,105.82,57.81,51.161,29.93 +2020-09-09 11:45:00,102.87,58.482,51.161,29.93 +2020-09-09 12:00:00,103.56,54.586000000000006,49.119,29.93 +2020-09-09 12:15:00,100.18,54.136,49.119,29.93 +2020-09-09 12:30:00,98.72,53.456,49.119,29.93 +2020-09-09 12:45:00,100.89,54.467,49.119,29.93 +2020-09-09 13:00:00,101.22,53.369,49.187,29.93 +2020-09-09 13:15:00,101.0,53.93,49.187,29.93 +2020-09-09 13:30:00,103.88,52.728,49.187,29.93 +2020-09-09 13:45:00,102.94,51.888000000000005,49.187,29.93 +2020-09-09 14:00:00,103.48,52.784,49.787,29.93 +2020-09-09 14:15:00,104.49,51.917,49.787,29.93 +2020-09-09 14:30:00,101.7,50.873999999999995,49.787,29.93 +2020-09-09 14:45:00,101.43,51.63,49.787,29.93 +2020-09-09 15:00:00,101.46,51.931000000000004,51.458999999999996,29.93 +2020-09-09 15:15:00,101.99,49.961000000000006,51.458999999999996,29.93 +2020-09-09 15:30:00,102.58,48.946999999999996,51.458999999999996,29.93 +2020-09-09 15:45:00,104.51,46.772,51.458999999999996,29.93 +2020-09-09 16:00:00,104.61,48.935,53.663000000000004,29.93 +2020-09-09 16:15:00,109.4,48.835,53.663000000000004,29.93 +2020-09-09 16:30:00,108.58,49.201,53.663000000000004,29.93 +2020-09-09 16:45:00,109.96,47.181000000000004,53.663000000000004,29.93 +2020-09-09 17:00:00,112.76,48.776,58.183,29.93 +2020-09-09 17:15:00,112.11,49.945,58.183,29.93 +2020-09-09 17:30:00,115.42,49.458999999999996,58.183,29.93 +2020-09-09 17:45:00,113.9,48.728,58.183,29.93 +2020-09-09 18:00:00,112.53,50.887,60.141000000000005,29.93 +2020-09-09 18:15:00,111.03,50.411,60.141000000000005,29.93 +2020-09-09 18:30:00,112.22,48.534,60.141000000000005,29.93 +2020-09-09 18:45:00,115.87,52.662,60.141000000000005,29.93 +2020-09-09 19:00:00,114.45,55.199,60.582,29.93 +2020-09-09 19:15:00,114.54,53.925,60.582,29.93 +2020-09-09 19:30:00,114.12,52.881,60.582,29.93 +2020-09-09 19:45:00,114.17,52.997,60.582,29.93 +2020-09-09 20:00:00,107.24,51.36600000000001,66.61,29.93 +2020-09-09 20:15:00,100.92,50.848,66.61,29.93 +2020-09-09 20:30:00,105.27,50.271,66.61,29.93 +2020-09-09 20:45:00,105.34,49.992,66.61,29.93 +2020-09-09 21:00:00,100.98,48.603,57.658,29.93 +2020-09-09 21:15:00,97.14,50.12,57.658,29.93 +2020-09-09 21:30:00,95.75,49.858999999999995,57.658,29.93 +2020-09-09 21:45:00,95.25,49.29600000000001,57.658,29.93 +2020-09-09 22:00:00,92.5,46.744,51.81,29.93 +2020-09-09 22:15:00,86.93,47.318000000000005,51.81,29.93 +2020-09-09 22:30:00,87.86,39.836,51.81,29.93 +2020-09-09 22:45:00,86.09,36.346,51.81,29.93 +2020-09-09 23:00:00,84.21,33.443000000000005,42.93600000000001,29.93 +2020-09-09 23:15:00,82.99,32.514,42.93600000000001,29.93 +2020-09-09 23:30:00,83.34,32.443000000000005,42.93600000000001,29.93 +2020-09-09 23:45:00,84.51,31.645,42.93600000000001,29.93 +2020-09-10 00:00:00,80.33,31.846,39.211,29.93 +2020-09-10 00:15:00,77.06,32.409,39.211,29.93 +2020-09-10 00:30:00,80.13,32.05,39.211,29.93 +2020-09-10 00:45:00,81.28,32.049,39.211,29.93 +2020-09-10 01:00:00,81.02,31.653000000000002,37.607,29.93 +2020-09-10 01:15:00,75.14,31.005,37.607,29.93 +2020-09-10 01:30:00,73.91,30.059,37.607,29.93 +2020-09-10 01:45:00,77.05,29.145,37.607,29.93 +2020-09-10 02:00:00,80.23,28.884,36.44,29.93 +2020-09-10 02:15:00,81.87,28.69,36.44,29.93 +2020-09-10 02:30:00,73.49,29.133000000000003,36.44,29.93 +2020-09-10 02:45:00,74.47,29.936999999999998,36.44,29.93 +2020-09-10 03:00:00,77.1,30.927,36.116,29.93 +2020-09-10 03:15:00,80.63,30.445,36.116,29.93 +2020-09-10 03:30:00,83.84,30.397,36.116,29.93 +2020-09-10 03:45:00,83.29,30.363000000000003,36.116,29.93 +2020-09-10 04:00:00,83.53,37.223,37.398,29.93 +2020-09-10 04:15:00,89.58,44.708,37.398,29.93 +2020-09-10 04:30:00,94.75,42.11,37.398,29.93 +2020-09-10 04:45:00,100.81,42.708999999999996,37.398,29.93 +2020-09-10 05:00:00,101.58,60.841,41.776,29.93 +2020-09-10 05:15:00,104.3,72.78699999999999,41.776,29.93 +2020-09-10 05:30:00,107.36,66.663,41.776,29.93 +2020-09-10 05:45:00,107.9,62.453,41.776,29.93 +2020-09-10 06:00:00,114.18,62.5,55.61,29.93 +2020-09-10 06:15:00,116.19,63.99,55.61,29.93 +2020-09-10 06:30:00,117.06,62.575,55.61,29.93 +2020-09-10 06:45:00,118.62,64.19,55.61,29.93 +2020-09-10 07:00:00,119.66,62.50899999999999,67.13600000000001,29.93 +2020-09-10 07:15:00,118.33,63.782,67.13600000000001,29.93 +2020-09-10 07:30:00,118.98,62.299,67.13600000000001,29.93 +2020-09-10 07:45:00,118.75,63.175,67.13600000000001,29.93 +2020-09-10 08:00:00,117.68,61.891999999999996,57.55,29.93 +2020-09-10 08:15:00,118.15,63.641000000000005,57.55,29.93 +2020-09-10 08:30:00,118.58,62.933,57.55,29.93 +2020-09-10 08:45:00,119.2,64.232,57.55,29.93 +2020-09-10 09:00:00,121.14,60.669,52.931999999999995,29.93 +2020-09-10 09:15:00,118.91,59.157,52.931999999999995,29.93 +2020-09-10 09:30:00,120.24,61.093,52.931999999999995,29.93 +2020-09-10 09:45:00,124.85,62.435,52.931999999999995,29.93 +2020-09-10 10:00:00,125.58,58.882,50.36600000000001,29.93 +2020-09-10 10:15:00,124.15,59.327,50.36600000000001,29.93 +2020-09-10 10:30:00,125.44,58.995,50.36600000000001,29.93 +2020-09-10 10:45:00,121.44,59.967,50.36600000000001,29.93 +2020-09-10 11:00:00,119.2,56.528,47.893,29.93 +2020-09-10 11:15:00,121.84,57.49100000000001,47.893,29.93 +2020-09-10 11:30:00,126.86,58.136,47.893,29.93 +2020-09-10 11:45:00,126.23,58.79,47.893,29.93 +2020-09-10 12:00:00,125.52,54.871,45.271,29.93 +2020-09-10 12:15:00,125.28,54.406000000000006,45.271,29.93 +2020-09-10 12:30:00,120.23,53.754,45.271,29.93 +2020-09-10 12:45:00,121.25,54.754,45.271,29.93 +2020-09-10 13:00:00,119.37,53.633,44.351000000000006,29.93 +2020-09-10 13:15:00,120.66,54.18600000000001,44.351000000000006,29.93 +2020-09-10 13:30:00,119.3,52.98,44.351000000000006,29.93 +2020-09-10 13:45:00,117.09,52.148,44.351000000000006,29.93 +2020-09-10 14:00:00,117.58,53.005,44.99,29.93 +2020-09-10 14:15:00,117.93,52.148999999999994,44.99,29.93 +2020-09-10 14:30:00,115.71,51.13399999999999,44.99,29.93 +2020-09-10 14:45:00,114.86,51.888000000000005,44.99,29.93 +2020-09-10 15:00:00,113.58,52.13399999999999,46.869,29.93 +2020-09-10 15:15:00,112.65,50.178999999999995,46.869,29.93 +2020-09-10 15:30:00,113.09,49.191,46.869,29.93 +2020-09-10 15:45:00,113.8,47.027,46.869,29.93 +2020-09-10 16:00:00,114.36,49.151,48.902,29.93 +2020-09-10 16:15:00,116.87,49.066,48.902,29.93 +2020-09-10 16:30:00,117.85,49.43,48.902,29.93 +2020-09-10 16:45:00,119.64,47.464,48.902,29.93 +2020-09-10 17:00:00,120.81,49.018,53.244,29.93 +2020-09-10 17:15:00,118.06,50.216,53.244,29.93 +2020-09-10 17:30:00,118.85,49.74100000000001,53.244,29.93 +2020-09-10 17:45:00,118.18,49.047,53.244,29.93 +2020-09-10 18:00:00,119.17,51.18899999999999,54.343999999999994,29.93 +2020-09-10 18:15:00,118.52,50.725,54.343999999999994,29.93 +2020-09-10 18:30:00,117.98,48.86,54.343999999999994,29.93 +2020-09-10 18:45:00,117.71,52.985,54.343999999999994,29.93 +2020-09-10 19:00:00,118.08,55.531000000000006,54.332,29.93 +2020-09-10 19:15:00,117.58,54.257,54.332,29.93 +2020-09-10 19:30:00,115.86,53.214,54.332,29.93 +2020-09-10 19:45:00,111.56,53.33,54.332,29.93 +2020-09-10 20:00:00,103.85,51.715,58.06,29.93 +2020-09-10 20:15:00,104.58,51.196999999999996,58.06,29.93 +2020-09-10 20:30:00,99.86,50.598,58.06,29.93 +2020-09-10 20:45:00,103.55,50.276,58.06,29.93 +2020-09-10 21:00:00,99.44,48.89,52.411,29.93 +2020-09-10 21:15:00,100.62,50.397,52.411,29.93 +2020-09-10 21:30:00,92.72,50.141000000000005,52.411,29.93 +2020-09-10 21:45:00,88.73,49.54,52.411,29.93 +2020-09-10 22:00:00,87.31,46.963,47.148999999999994,29.93 +2020-09-10 22:15:00,88.58,47.515,47.148999999999994,29.93 +2020-09-10 22:30:00,84.17,40.009,47.148999999999994,29.93 +2020-09-10 22:45:00,83.22,36.524,47.148999999999994,29.93 +2020-09-10 23:00:00,80.89,33.669000000000004,40.814,29.93 +2020-09-10 23:15:00,82.71,32.713,40.814,29.93 +2020-09-10 23:30:00,78.26,32.649,40.814,29.93 +2020-09-10 23:45:00,82.96,31.855999999999998,40.814,29.93 +2020-09-11 00:00:00,75.6,30.344,39.153,29.93 +2020-09-11 00:15:00,80.38,31.125,39.153,29.93 +2020-09-11 00:30:00,80.19,30.996,39.153,29.93 +2020-09-11 00:45:00,79.37,31.391,39.153,29.93 +2020-09-11 01:00:00,72.6,30.596999999999998,37.228,29.93 +2020-09-11 01:15:00,79.38,29.555,37.228,29.93 +2020-09-11 01:30:00,77.58,29.232,37.228,29.93 +2020-09-11 01:45:00,80.0,28.109,37.228,29.93 +2020-09-11 02:00:00,76.16,28.691,35.851,29.93 +2020-09-11 02:15:00,80.66,28.458000000000002,35.851,29.93 +2020-09-11 02:30:00,81.2,29.677,35.851,29.93 +2020-09-11 02:45:00,80.38,29.875999999999998,35.851,29.93 +2020-09-11 03:00:00,76.4,31.41,36.54,29.93 +2020-09-11 03:15:00,80.73,29.999000000000002,36.54,29.93 +2020-09-11 03:30:00,84.41,29.741999999999997,36.54,29.93 +2020-09-11 03:45:00,81.82,30.514,36.54,29.93 +2020-09-11 04:00:00,85.33,37.564,37.578,29.93 +2020-09-11 04:15:00,91.54,43.666000000000004,37.578,29.93 +2020-09-11 04:30:00,94.67,41.95,37.578,29.93 +2020-09-11 04:45:00,96.14,41.833,37.578,29.93 +2020-09-11 05:00:00,101.04,59.476000000000006,40.387,29.93 +2020-09-11 05:15:00,102.73,72.569,40.387,29.93 +2020-09-11 05:30:00,108.87,66.734,40.387,29.93 +2020-09-11 05:45:00,111.53,62.067,40.387,29.93 +2020-09-11 06:00:00,116.13,62.336000000000006,54.668,29.93 +2020-09-11 06:15:00,118.68,63.81100000000001,54.668,29.93 +2020-09-11 06:30:00,120.17,62.233999999999995,54.668,29.93 +2020-09-11 06:45:00,123.12,63.937,54.668,29.93 +2020-09-11 07:00:00,125.11,62.724,63.971000000000004,29.93 +2020-09-11 07:15:00,125.24,64.94800000000001,63.971000000000004,29.93 +2020-09-11 07:30:00,125.46,61.755,63.971000000000004,29.93 +2020-09-11 07:45:00,125.76,62.323,63.971000000000004,29.93 +2020-09-11 08:00:00,124.69,61.582,56.042,29.93 +2020-09-11 08:15:00,125.79,63.826,56.042,29.93 +2020-09-11 08:30:00,127.99,63.174,56.042,29.93 +2020-09-11 08:45:00,128.03,64.111,56.042,29.93 +2020-09-11 09:00:00,127.4,58.596000000000004,52.832,29.93 +2020-09-11 09:15:00,127.15,58.831,52.832,29.93 +2020-09-11 09:30:00,126.24,60.083999999999996,52.832,29.93 +2020-09-11 09:45:00,126.73,61.718,52.832,29.93 +2020-09-11 10:00:00,123.95,57.843999999999994,50.044,29.93 +2020-09-11 10:15:00,126.91,58.161,50.044,29.93 +2020-09-11 10:30:00,126.92,58.288000000000004,50.044,29.93 +2020-09-11 10:45:00,126.94,59.099,50.044,29.93 +2020-09-11 11:00:00,124.25,55.891999999999996,49.06100000000001,29.93 +2020-09-11 11:15:00,122.43,55.766000000000005,49.06100000000001,29.93 +2020-09-11 11:30:00,123.76,56.348,49.06100000000001,29.93 +2020-09-11 11:45:00,121.22,56.158,49.06100000000001,29.93 +2020-09-11 12:00:00,117.99,52.754,45.595,29.93 +2020-09-11 12:15:00,118.71,51.409,45.595,29.93 +2020-09-11 12:30:00,115.33,50.912,45.595,29.93 +2020-09-11 12:45:00,114.1,51.29600000000001,45.595,29.93 +2020-09-11 13:00:00,113.23,50.799,43.218,29.93 +2020-09-11 13:15:00,112.25,51.623000000000005,43.218,29.93 +2020-09-11 13:30:00,110.79,51.105,43.218,29.93 +2020-09-11 13:45:00,111.31,50.538000000000004,43.218,29.93 +2020-09-11 14:00:00,110.24,50.486999999999995,41.926,29.93 +2020-09-11 14:15:00,112.95,49.996,41.926,29.93 +2020-09-11 14:30:00,108.86,50.391999999999996,41.926,29.93 +2020-09-11 14:45:00,112.25,50.591,41.926,29.93 +2020-09-11 15:00:00,111.4,50.64,43.79,29.93 +2020-09-11 15:15:00,111.65,48.431000000000004,43.79,29.93 +2020-09-11 15:30:00,110.79,46.753,43.79,29.93 +2020-09-11 15:45:00,107.67,45.287,43.79,29.93 +2020-09-11 16:00:00,107.07,46.474,45.895,29.93 +2020-09-11 16:15:00,108.07,46.873999999999995,45.895,29.93 +2020-09-11 16:30:00,106.95,47.09,45.895,29.93 +2020-09-11 16:45:00,105.52,44.477,45.895,29.93 +2020-09-11 17:00:00,109.06,47.535,51.36,29.93 +2020-09-11 17:15:00,109.74,48.553000000000004,51.36,29.93 +2020-09-11 17:30:00,110.3,48.187,51.36,29.93 +2020-09-11 17:45:00,111.89,47.353,51.36,29.93 +2020-09-11 18:00:00,111.62,49.632,52.985,29.93 +2020-09-11 18:15:00,109.54,48.294,52.985,29.93 +2020-09-11 18:30:00,111.46,46.401,52.985,29.93 +2020-09-11 18:45:00,114.19,50.891999999999996,52.985,29.93 +2020-09-11 19:00:00,112.04,54.356,52.602,29.93 +2020-09-11 19:15:00,108.7,53.778,52.602,29.93 +2020-09-11 19:30:00,105.58,52.738,52.602,29.93 +2020-09-11 19:45:00,103.63,51.891999999999996,52.602,29.93 +2020-09-11 20:00:00,98.96,50.158,58.063,29.93 +2020-09-11 20:15:00,100.7,50.345,58.063,29.93 +2020-09-11 20:30:00,97.79,49.286,58.063,29.93 +2020-09-11 20:45:00,98.82,48.273999999999994,58.063,29.93 +2020-09-11 21:00:00,92.16,48.119,50.135,29.93 +2020-09-11 21:15:00,87.15,51.151,50.135,29.93 +2020-09-11 21:30:00,82.01,50.773999999999994,50.135,29.93 +2020-09-11 21:45:00,83.79,50.385,50.135,29.93 +2020-09-11 22:00:00,84.45,47.785,45.165,29.93 +2020-09-11 22:15:00,82.84,48.075,45.165,29.93 +2020-09-11 22:30:00,74.05,45.567,45.165,29.93 +2020-09-11 22:45:00,75.88,43.292,45.165,29.93 +2020-09-11 23:00:00,67.88,41.926,39.121,29.93 +2020-09-11 23:15:00,66.06,39.306999999999995,39.121,29.93 +2020-09-11 23:30:00,65.9,37.449,39.121,29.93 +2020-09-11 23:45:00,73.95,36.47,39.121,29.93 +2020-09-12 00:00:00,72.49,31.303,38.49,29.816 +2020-09-12 00:15:00,70.54,30.943,38.49,29.816 +2020-09-12 00:30:00,63.74,30.576,38.49,29.816 +2020-09-12 00:45:00,64.17,30.421999999999997,38.49,29.816 +2020-09-12 01:00:00,63.03,29.933000000000003,34.5,29.816 +2020-09-12 01:15:00,61.92,29.307,34.5,29.816 +2020-09-12 01:30:00,61.64,28.223000000000003,34.5,29.816 +2020-09-12 01:45:00,61.96,28.176,34.5,29.816 +2020-09-12 02:00:00,62.24,27.976,32.236,29.816 +2020-09-12 02:15:00,67.5,27.022,32.236,29.816 +2020-09-12 02:30:00,67.15,27.374000000000002,32.236,29.816 +2020-09-12 02:45:00,66.48,28.275,32.236,29.816 +2020-09-12 03:00:00,65.68,28.665,32.067,29.816 +2020-09-12 03:15:00,61.69,26.500999999999998,32.067,29.816 +2020-09-12 03:30:00,61.21,26.336,32.067,29.816 +2020-09-12 03:45:00,61.06,28.415,32.067,29.816 +2020-09-12 04:00:00,62.25,33.311,33.071,29.816 +2020-09-12 04:15:00,61.64,38.334,33.071,29.816 +2020-09-12 04:30:00,61.43,34.878,33.071,29.816 +2020-09-12 04:45:00,64.14,34.945,33.071,29.816 +2020-09-12 05:00:00,66.46,43.589,33.014,29.816 +2020-09-12 05:15:00,68.23,44.825,33.014,29.816 +2020-09-12 05:30:00,66.76,40.367,33.014,29.816 +2020-09-12 05:45:00,68.02,40.258,33.014,29.816 +2020-09-12 06:00:00,70.3,52.581,34.628,29.816 +2020-09-12 06:15:00,72.97,62.941,34.628,29.816 +2020-09-12 06:30:00,74.15,58.201,34.628,29.816 +2020-09-12 06:45:00,76.25,55.983999999999995,34.628,29.816 +2020-09-12 07:00:00,77.74,52.931999999999995,38.871,29.816 +2020-09-12 07:15:00,80.76,53.872,38.871,29.816 +2020-09-12 07:30:00,82.04,52.38,38.871,29.816 +2020-09-12 07:45:00,79.99,54.251999999999995,38.871,29.816 +2020-09-12 08:00:00,80.65,54.566,43.293,29.816 +2020-09-12 08:15:00,84.33,57.047,43.293,29.816 +2020-09-12 08:30:00,80.92,56.593,43.293,29.816 +2020-09-12 08:45:00,82.18,58.745,43.293,29.816 +2020-09-12 09:00:00,73.56,56.155,44.559,29.816 +2020-09-12 09:15:00,72.42,56.9,44.559,29.816 +2020-09-12 09:30:00,72.7,58.683,44.559,29.816 +2020-09-12 09:45:00,77.44,59.915,44.559,29.816 +2020-09-12 10:00:00,76.92,56.563,42.091,29.816 +2020-09-12 10:15:00,82.93,57.177,42.091,29.816 +2020-09-12 10:30:00,75.13,57.018,42.091,29.816 +2020-09-12 10:45:00,78.26,57.68899999999999,42.091,29.816 +2020-09-12 11:00:00,75.56,54.4,38.505,29.816 +2020-09-12 11:15:00,72.28,54.917,38.505,29.816 +2020-09-12 11:30:00,70.41,55.589,38.505,29.816 +2020-09-12 11:45:00,69.55,55.831,38.505,29.816 +2020-09-12 12:00:00,69.43,52.578,35.388000000000005,29.816 +2020-09-12 12:15:00,67.6,52.043,35.388000000000005,29.816 +2020-09-12 12:30:00,66.35,51.507,35.388000000000005,29.816 +2020-09-12 12:45:00,66.07,52.397,35.388000000000005,29.816 +2020-09-12 13:00:00,63.61,51.104,31.355999999999998,29.816 +2020-09-12 13:15:00,68.82,51.163999999999994,31.355999999999998,29.816 +2020-09-12 13:30:00,66.78,50.739,31.355999999999998,29.816 +2020-09-12 13:45:00,66.36,49.083999999999996,31.355999999999998,29.816 +2020-09-12 14:00:00,68.36,49.22,30.522,29.816 +2020-09-12 14:15:00,69.67,47.583999999999996,30.522,29.816 +2020-09-12 14:30:00,66.25,47.385,30.522,29.816 +2020-09-12 14:45:00,67.5,48.027,30.522,29.816 +2020-09-12 15:00:00,68.44,48.504,34.36,29.816 +2020-09-12 15:15:00,69.12,47.034,34.36,29.816 +2020-09-12 15:30:00,69.44,45.773,34.36,29.816 +2020-09-12 15:45:00,70.61,43.525,34.36,29.816 +2020-09-12 16:00:00,73.84,46.43,39.507,29.816 +2020-09-12 16:15:00,75.23,46.185,39.507,29.816 +2020-09-12 16:30:00,75.45,46.589,39.507,29.816 +2020-09-12 16:45:00,77.86,44.126999999999995,39.507,29.816 +2020-09-12 17:00:00,81.34,46.038000000000004,47.151,29.816 +2020-09-12 17:15:00,84.36,45.433,47.151,29.816 +2020-09-12 17:30:00,82.72,44.951,47.151,29.816 +2020-09-12 17:45:00,83.58,44.531000000000006,47.151,29.816 +2020-09-12 18:00:00,85.61,47.988,50.303999999999995,29.816 +2020-09-12 18:15:00,84.62,48.356,50.303999999999995,29.816 +2020-09-12 18:30:00,89.46,47.836999999999996,50.303999999999995,29.816 +2020-09-12 18:45:00,89.86,48.85,50.303999999999995,29.816 +2020-09-12 19:00:00,88.15,51.066,50.622,29.816 +2020-09-12 19:15:00,83.92,49.538000000000004,50.622,29.816 +2020-09-12 19:30:00,83.24,49.269,50.622,29.816 +2020-09-12 19:45:00,82.65,49.957,50.622,29.816 +2020-09-12 20:00:00,80.31,49.22,45.391000000000005,29.816 +2020-09-12 20:15:00,77.48,49.12,45.391000000000005,29.816 +2020-09-12 20:30:00,76.12,47.223,45.391000000000005,29.816 +2020-09-12 20:45:00,75.87,47.74100000000001,45.391000000000005,29.816 +2020-09-12 21:00:00,71.68,46.589,39.98,29.816 +2020-09-12 21:15:00,70.68,49.358999999999995,39.98,29.816 +2020-09-12 21:30:00,68.86,49.309,39.98,29.816 +2020-09-12 21:45:00,68.45,48.352,39.98,29.816 +2020-09-12 22:00:00,64.74,45.873999999999995,37.53,29.816 +2020-09-12 22:15:00,65.48,46.672,37.53,29.816 +2020-09-12 22:30:00,62.36,44.59,37.53,29.816 +2020-09-12 22:45:00,62.8,42.88,37.53,29.816 +2020-09-12 23:00:00,57.88,41.266999999999996,30.97,29.816 +2020-09-12 23:15:00,56.65,38.785,30.97,29.816 +2020-09-12 23:30:00,58.13,38.875,30.97,29.816 +2020-09-12 23:45:00,57.91,37.913000000000004,30.97,29.816 +2020-09-13 00:00:00,53.28,32.799,27.24,29.816 +2020-09-13 00:15:00,53.54,31.384,27.24,29.816 +2020-09-13 00:30:00,53.48,30.849,27.24,29.816 +2020-09-13 00:45:00,53.88,30.689,27.24,29.816 +2020-09-13 01:00:00,51.24,30.398000000000003,25.662,29.816 +2020-09-13 01:15:00,52.62,29.816,25.662,29.816 +2020-09-13 01:30:00,52.7,28.694000000000003,25.662,29.816 +2020-09-13 01:45:00,52.44,28.274,25.662,29.816 +2020-09-13 02:00:00,53.25,28.054000000000002,25.67,29.816 +2020-09-13 02:15:00,52.44,27.636999999999997,25.67,29.816 +2020-09-13 02:30:00,51.82,28.238000000000003,25.67,29.816 +2020-09-13 02:45:00,51.81,28.935,25.67,29.816 +2020-09-13 03:00:00,50.96,29.924,24.258000000000003,29.816 +2020-09-13 03:15:00,52.6,27.941,24.258000000000003,29.816 +2020-09-13 03:30:00,52.63,27.311,24.258000000000003,29.816 +2020-09-13 03:45:00,52.9,28.666999999999998,24.258000000000003,29.816 +2020-09-13 04:00:00,53.7,33.546,25.051,29.816 +2020-09-13 04:15:00,54.59,38.092,25.051,29.816 +2020-09-13 04:30:00,54.4,35.829,25.051,29.816 +2020-09-13 04:45:00,55.85,35.528,25.051,29.816 +2020-09-13 05:00:00,57.42,44.011,25.145,29.816 +2020-09-13 05:15:00,58.05,44.397,25.145,29.816 +2020-09-13 05:30:00,55.99,39.544000000000004,25.145,29.816 +2020-09-13 05:45:00,56.86,39.168,25.145,29.816 +2020-09-13 06:00:00,57.81,49.345,26.371,29.816 +2020-09-13 06:15:00,58.65,60.265,26.371,29.816 +2020-09-13 06:30:00,58.81,54.775,26.371,29.816 +2020-09-13 06:45:00,60.74,51.551,26.371,29.816 +2020-09-13 07:00:00,61.74,49.041000000000004,28.756999999999998,29.816 +2020-09-13 07:15:00,61.61,48.45,28.756999999999998,29.816 +2020-09-13 07:30:00,61.01,47.961999999999996,28.756999999999998,29.816 +2020-09-13 07:45:00,61.51,49.708999999999996,28.756999999999998,29.816 +2020-09-13 08:00:00,61.6,50.91,32.82,29.816 +2020-09-13 08:15:00,63.67,54.369,32.82,29.816 +2020-09-13 08:30:00,61.43,54.89,32.82,29.816 +2020-09-13 08:45:00,60.33,57.203,32.82,29.816 +2020-09-13 09:00:00,58.83,54.472,35.534,29.816 +2020-09-13 09:15:00,60.35,54.845,35.534,29.816 +2020-09-13 09:30:00,60.42,56.964,35.534,29.816 +2020-09-13 09:45:00,60.83,59.023999999999994,35.534,29.816 +2020-09-13 10:00:00,61.52,56.451,35.925,29.816 +2020-09-13 10:15:00,63.03,57.215,35.925,29.816 +2020-09-13 10:30:00,64.13,57.315,35.925,29.816 +2020-09-13 10:45:00,64.9,58.61,35.925,29.816 +2020-09-13 11:00:00,61.16,55.151,37.056,29.816 +2020-09-13 11:15:00,60.3,55.281000000000006,37.056,29.816 +2020-09-13 11:30:00,56.6,56.294,37.056,29.816 +2020-09-13 11:45:00,58.49,56.818000000000005,37.056,29.816 +2020-09-13 12:00:00,52.37,54.376999999999995,33.124,29.816 +2020-09-13 12:15:00,54.75,53.535,33.124,29.816 +2020-09-13 12:30:00,50.06,53.025,33.124,29.816 +2020-09-13 12:45:00,50.17,53.224,33.124,29.816 +2020-09-13 13:00:00,49.32,51.556999999999995,29.874000000000002,29.816 +2020-09-13 13:15:00,49.67,51.497,29.874000000000002,29.816 +2020-09-13 13:30:00,48.44,50.077,29.874000000000002,29.816 +2020-09-13 13:45:00,49.63,49.31399999999999,29.874000000000002,29.816 +2020-09-13 14:00:00,49.39,50.481,27.302,29.816 +2020-09-13 14:15:00,50.02,49.385,27.302,29.816 +2020-09-13 14:30:00,50.96,48.302,27.302,29.816 +2020-09-13 14:45:00,52.31,47.96,27.302,29.816 +2020-09-13 15:00:00,52.47,48.31100000000001,27.642,29.816 +2020-09-13 15:15:00,56.04,46.251000000000005,27.642,29.816 +2020-09-13 15:30:00,55.91,44.931000000000004,27.642,29.816 +2020-09-13 15:45:00,58.62,43.033,27.642,29.816 +2020-09-13 16:00:00,63.63,44.687,31.945999999999998,29.816 +2020-09-13 16:15:00,63.63,44.543,31.945999999999998,29.816 +2020-09-13 16:30:00,67.25,45.853,31.945999999999998,29.816 +2020-09-13 16:45:00,69.54,43.519,31.945999999999998,29.816 +2020-09-13 17:00:00,73.29,45.705,40.387,29.816 +2020-09-13 17:15:00,75.48,46.41,40.387,29.816 +2020-09-13 17:30:00,76.49,46.678000000000004,40.387,29.816 +2020-09-13 17:45:00,80.06,46.943999999999996,40.387,29.816 +2020-09-13 18:00:00,80.24,50.817,44.575,29.816 +2020-09-13 18:15:00,80.31,50.998000000000005,44.575,29.816 +2020-09-13 18:30:00,86.23,50.026,44.575,29.816 +2020-09-13 18:45:00,84.95,51.357,44.575,29.816 +2020-09-13 19:00:00,83.91,55.43899999999999,45.623999999999995,29.816 +2020-09-13 19:15:00,82.55,53.001000000000005,45.623999999999995,29.816 +2020-09-13 19:30:00,81.33,52.494,45.623999999999995,29.816 +2020-09-13 19:45:00,82.57,52.943999999999996,45.623999999999995,29.816 +2020-09-13 20:00:00,82.59,52.394,44.583999999999996,29.816 +2020-09-13 20:15:00,85.52,52.29,44.583999999999996,29.816 +2020-09-13 20:30:00,81.65,51.228,44.583999999999996,29.816 +2020-09-13 20:45:00,80.24,50.083999999999996,44.583999999999996,29.816 +2020-09-13 21:00:00,74.96,48.508,39.732,29.816 +2020-09-13 21:15:00,80.14,50.928000000000004,39.732,29.816 +2020-09-13 21:30:00,82.33,50.347,39.732,29.816 +2020-09-13 21:45:00,81.42,49.684,39.732,29.816 +2020-09-13 22:00:00,76.5,48.83,38.571,29.816 +2020-09-13 22:15:00,71.7,48.037,38.571,29.816 +2020-09-13 22:30:00,69.2,45.117,38.571,29.816 +2020-09-13 22:45:00,69.21,42.193000000000005,38.571,29.816 +2020-09-13 23:00:00,66.31,40.039,33.121,29.816 +2020-09-13 23:15:00,68.89,38.861,33.121,29.816 +2020-09-13 23:30:00,67.54,38.577,33.121,29.816 +2020-09-13 23:45:00,72.63,37.861999999999995,33.121,29.816 +2020-09-14 00:00:00,70.49,34.855,32.506,29.93 +2020-09-14 00:15:00,71.68,34.483000000000004,32.506,29.93 +2020-09-14 00:30:00,66.33,33.631,32.506,29.93 +2020-09-14 00:45:00,63.56,33.067,32.506,29.93 +2020-09-14 01:00:00,65.34,33.099000000000004,31.121,29.93 +2020-09-14 01:15:00,71.25,32.488,31.121,29.93 +2020-09-14 01:30:00,71.36,31.708000000000002,31.121,29.93 +2020-09-14 01:45:00,70.39,31.218000000000004,31.121,29.93 +2020-09-14 02:00:00,67.0,31.398000000000003,29.605999999999998,29.93 +2020-09-14 02:15:00,72.09,30.285,29.605999999999998,29.93 +2020-09-14 02:30:00,73.13,31.045,29.605999999999998,29.93 +2020-09-14 02:45:00,72.63,31.535,29.605999999999998,29.93 +2020-09-14 03:00:00,70.81,33.106,28.124000000000002,29.93 +2020-09-14 03:15:00,74.52,31.967,28.124000000000002,29.93 +2020-09-14 03:30:00,76.44,31.909000000000002,28.124000000000002,29.93 +2020-09-14 03:45:00,75.06,32.779,28.124000000000002,29.93 +2020-09-14 04:00:00,77.33,40.942,29.743000000000002,29.93 +2020-09-14 04:15:00,83.88,48.583999999999996,29.743000000000002,29.93 +2020-09-14 04:30:00,89.01,46.166000000000004,29.743000000000002,29.93 +2020-09-14 04:45:00,92.93,46.215,29.743000000000002,29.93 +2020-09-14 05:00:00,94.93,62.798,36.191,29.93 +2020-09-14 05:15:00,104.51,74.667,36.191,29.93 +2020-09-14 05:30:00,102.86,68.46,36.191,29.93 +2020-09-14 05:45:00,103.19,64.678,36.191,29.93 +2020-09-14 06:00:00,108.82,63.763999999999996,55.277,29.93 +2020-09-14 06:15:00,111.8,65.002,55.277,29.93 +2020-09-14 06:30:00,112.82,63.923,55.277,29.93 +2020-09-14 06:45:00,111.4,66.351,55.277,29.93 +2020-09-14 07:00:00,115.98,64.559,65.697,29.93 +2020-09-14 07:15:00,110.78,66.132,65.697,29.93 +2020-09-14 07:30:00,110.77,64.83,65.697,29.93 +2020-09-14 07:45:00,108.81,66.53399999999999,65.697,29.93 +2020-09-14 08:00:00,107.66,65.313,57.028,29.93 +2020-09-14 08:15:00,106.67,67.42,57.028,29.93 +2020-09-14 08:30:00,111.75,66.61,57.028,29.93 +2020-09-14 08:45:00,114.96,68.684,57.028,29.93 +2020-09-14 09:00:00,107.61,64.957,52.633,29.93 +2020-09-14 09:15:00,104.76,63.328,52.633,29.93 +2020-09-14 09:30:00,104.17,64.493,52.633,29.93 +2020-09-14 09:45:00,103.52,64.554,52.633,29.93 +2020-09-14 10:00:00,104.08,62.263000000000005,50.647,29.93 +2020-09-14 10:15:00,103.09,62.821000000000005,50.647,29.93 +2020-09-14 10:30:00,103.8,62.4,50.647,29.93 +2020-09-14 10:45:00,103.53,62.397,50.647,29.93 +2020-09-14 11:00:00,102.76,58.821999999999996,50.245,29.93 +2020-09-14 11:15:00,103.69,59.413000000000004,50.245,29.93 +2020-09-14 11:30:00,102.69,61.208,50.245,29.93 +2020-09-14 11:45:00,105.2,62.097,50.245,29.93 +2020-09-14 12:00:00,101.81,58.416000000000004,46.956,29.93 +2020-09-14 12:15:00,103.71,57.656000000000006,46.956,29.93 +2020-09-14 12:30:00,101.04,56.28,46.956,29.93 +2020-09-14 12:45:00,103.68,56.665,46.956,29.93 +2020-09-14 13:00:00,99.52,55.81399999999999,47.383,29.93 +2020-09-14 13:15:00,98.82,54.8,47.383,29.93 +2020-09-14 13:30:00,100.67,53.45399999999999,47.383,29.93 +2020-09-14 13:45:00,102.23,53.481,47.383,29.93 +2020-09-14 14:00:00,102.81,53.742,47.1,29.93 +2020-09-14 14:15:00,100.22,53.089,47.1,29.93 +2020-09-14 14:30:00,100.3,51.821999999999996,47.1,29.93 +2020-09-14 14:45:00,98.73,53.272,47.1,29.93 +2020-09-14 15:00:00,99.67,53.479,49.355,29.93 +2020-09-14 15:15:00,101.22,50.736999999999995,49.355,29.93 +2020-09-14 15:30:00,103.35,50.004,49.355,29.93 +2020-09-14 15:45:00,102.28,47.64,49.355,29.93 +2020-09-14 16:00:00,103.7,50.174,52.14,29.93 +2020-09-14 16:15:00,104.69,50.006,52.14,29.93 +2020-09-14 16:30:00,107.28,50.61,52.14,29.93 +2020-09-14 16:45:00,109.54,48.20399999999999,52.14,29.93 +2020-09-14 17:00:00,111.52,49.339,58.705,29.93 +2020-09-14 17:15:00,109.57,50.282,58.705,29.93 +2020-09-14 17:30:00,112.39,50.155,58.705,29.93 +2020-09-14 17:45:00,111.73,49.925,58.705,29.93 +2020-09-14 18:00:00,113.13,52.857,59.153,29.93 +2020-09-14 18:15:00,111.59,51.177,59.153,29.93 +2020-09-14 18:30:00,117.13,49.633,59.153,29.93 +2020-09-14 18:45:00,115.67,53.824,59.153,29.93 +2020-09-14 19:00:00,112.01,57.446000000000005,61.483000000000004,29.93 +2020-09-14 19:15:00,107.89,56.045,61.483000000000004,29.93 +2020-09-14 19:30:00,105.2,55.27,61.483000000000004,29.93 +2020-09-14 19:45:00,108.61,55.082,61.483000000000004,29.93 +2020-09-14 20:00:00,105.34,53.168,67.55,29.93 +2020-09-14 20:15:00,106.11,54.007,67.55,29.93 +2020-09-14 20:30:00,103.61,53.163000000000004,67.55,29.93 +2020-09-14 20:45:00,94.43,52.355,67.55,29.93 +2020-09-14 21:00:00,92.43,50.291000000000004,60.026,29.93 +2020-09-14 21:15:00,89.42,52.953,60.026,29.93 +2020-09-14 21:30:00,85.77,52.648999999999994,60.026,29.93 +2020-09-14 21:45:00,91.92,51.695,60.026,29.93 +2020-09-14 22:00:00,88.36,48.677,52.736999999999995,29.93 +2020-09-14 22:15:00,88.46,49.465,52.736999999999995,29.93 +2020-09-14 22:30:00,82.45,41.596000000000004,52.736999999999995,29.93 +2020-09-14 22:45:00,82.32,38.1,52.736999999999995,29.93 +2020-09-14 23:00:00,80.56,36.128,44.408,29.93 +2020-09-14 23:15:00,81.23,33.766,44.408,29.93 +2020-09-14 23:30:00,76.93,33.77,44.408,29.93 +2020-09-14 23:45:00,75.97,32.887,44.408,29.93 +2020-09-15 00:00:00,76.99,32.999,44.438,29.93 +2020-09-15 00:15:00,79.26,33.558,44.438,29.93 +2020-09-15 00:30:00,78.82,33.234,44.438,29.93 +2020-09-15 00:45:00,74.27,33.263000000000005,44.438,29.93 +2020-09-15 01:00:00,70.44,32.773,41.468999999999994,29.93 +2020-09-15 01:15:00,74.86,32.23,41.468999999999994,29.93 +2020-09-15 01:30:00,77.73,31.374000000000002,41.468999999999994,29.93 +2020-09-15 01:45:00,78.5,30.465999999999998,41.468999999999994,29.93 +2020-09-15 02:00:00,77.58,30.223000000000003,39.708,29.93 +2020-09-15 02:15:00,76.77,30.145,39.708,29.93 +2020-09-15 02:30:00,78.83,30.465,39.708,29.93 +2020-09-15 02:45:00,78.93,31.241,39.708,29.93 +2020-09-15 03:00:00,79.89,32.166,38.919000000000004,29.93 +2020-09-15 03:15:00,80.14,31.774,38.919000000000004,29.93 +2020-09-15 03:30:00,82.91,31.75,38.919000000000004,29.93 +2020-09-15 03:45:00,83.91,31.641,38.919000000000004,29.93 +2020-09-15 04:00:00,85.18,38.702,40.092,29.93 +2020-09-15 04:15:00,89.49,46.347,40.092,29.93 +2020-09-15 04:30:00,94.24,43.79600000000001,40.092,29.93 +2020-09-15 04:45:00,96.41,44.424,40.092,29.93 +2020-09-15 05:00:00,99.04,63.07899999999999,43.713,29.93 +2020-09-15 05:15:00,103.98,75.593,43.713,29.93 +2020-09-15 05:30:00,107.4,69.36,43.713,29.93 +2020-09-15 05:45:00,109.39,64.872,43.713,29.93 +2020-09-15 06:00:00,117.19,64.73100000000001,56.033,29.93 +2020-09-15 06:15:00,114.09,66.351,56.033,29.93 +2020-09-15 06:30:00,115.14,64.90899999999999,56.033,29.93 +2020-09-15 06:45:00,116.49,66.49600000000001,56.033,29.93 +2020-09-15 07:00:00,118.41,64.818,66.003,29.93 +2020-09-15 07:15:00,117.26,66.15899999999999,66.003,29.93 +2020-09-15 07:30:00,117.65,64.84100000000001,66.003,29.93 +2020-09-15 07:45:00,116.28,65.70100000000001,66.003,29.93 +2020-09-15 08:00:00,113.85,64.434,57.474,29.93 +2020-09-15 08:15:00,110.41,65.98100000000001,57.474,29.93 +2020-09-15 08:30:00,110.67,65.303,57.474,29.93 +2020-09-15 08:45:00,110.98,66.51,57.474,29.93 +2020-09-15 09:00:00,110.26,62.99100000000001,51.928000000000004,29.93 +2020-09-15 09:15:00,109.79,61.425,51.928000000000004,29.93 +2020-09-15 09:30:00,108.62,63.258,51.928000000000004,29.93 +2020-09-15 09:45:00,108.66,64.442,51.928000000000004,29.93 +2020-09-15 10:00:00,108.71,60.86600000000001,49.46,29.93 +2020-09-15 10:15:00,108.89,61.133,49.46,29.93 +2020-09-15 10:30:00,108.34,60.746,49.46,29.93 +2020-09-15 10:45:00,108.25,61.647,49.46,29.93 +2020-09-15 11:00:00,106.43,58.276,48.206,29.93 +2020-09-15 11:15:00,104.48,59.166000000000004,48.206,29.93 +2020-09-15 11:30:00,106.87,59.805,48.206,29.93 +2020-09-15 11:45:00,104.72,60.371,48.206,29.93 +2020-09-15 12:00:00,102.62,56.325,46.285,29.93 +2020-09-15 12:15:00,101.2,55.792,46.285,29.93 +2020-09-15 12:30:00,102.37,55.288999999999994,46.285,29.93 +2020-09-15 12:45:00,101.53,56.23,46.285,29.93 +2020-09-15 13:00:00,101.21,54.992,46.861999999999995,29.93 +2020-09-15 13:15:00,100.74,55.506,46.861999999999995,29.93 +2020-09-15 13:30:00,100.23,54.276,46.861999999999995,29.93 +2020-09-15 13:45:00,102.07,53.488,46.861999999999995,29.93 +2020-09-15 14:00:00,100.98,54.14,46.488,29.93 +2020-09-15 14:15:00,101.37,53.342,46.488,29.93 +2020-09-15 14:30:00,102.21,52.472,46.488,29.93 +2020-09-15 14:45:00,104.2,53.218,46.488,29.93 +2020-09-15 15:00:00,103.31,53.176,48.442,29.93 +2020-09-15 15:15:00,105.66,51.303999999999995,48.442,29.93 +2020-09-15 15:30:00,104.22,50.43899999999999,48.442,29.93 +2020-09-15 15:45:00,106.45,48.342,48.442,29.93 +2020-09-15 16:00:00,107.2,50.266000000000005,50.397,29.93 +2020-09-15 16:15:00,108.47,50.245,50.397,29.93 +2020-09-15 16:30:00,111.24,50.597,50.397,29.93 +2020-09-15 16:45:00,112.38,48.913000000000004,50.397,29.93 +2020-09-15 17:00:00,118.39,50.25899999999999,56.668,29.93 +2020-09-15 17:15:00,113.96,51.603,56.668,29.93 +2020-09-15 17:30:00,113.72,51.18,56.668,29.93 +2020-09-15 17:45:00,114.84,50.68,56.668,29.93 +2020-09-15 18:00:00,117.58,52.738,57.957,29.93 +2020-09-15 18:15:00,116.18,52.331,57.957,29.93 +2020-09-15 18:30:00,120.29,50.532,57.957,29.93 +2020-09-15 18:45:00,119.15,54.638999999999996,57.957,29.93 +2020-09-15 19:00:00,120.04,57.236000000000004,57.056000000000004,29.93 +2020-09-15 19:15:00,118.63,55.964,57.056000000000004,29.93 +2020-09-15 19:30:00,113.73,54.924,57.056000000000004,29.93 +2020-09-15 19:45:00,113.54,55.038000000000004,57.056000000000004,29.93 +2020-09-15 20:00:00,105.26,53.512,64.156,29.93 +2020-09-15 20:15:00,101.39,52.998000000000005,64.156,29.93 +2020-09-15 20:30:00,98.17,52.282,64.156,29.93 +2020-09-15 20:45:00,97.7,51.735,64.156,29.93 +2020-09-15 21:00:00,98.43,50.36600000000001,56.507,29.93 +2020-09-15 21:15:00,100.0,51.815,56.507,29.93 +2020-09-15 21:30:00,94.23,51.591,56.507,29.93 +2020-09-15 21:45:00,89.6,50.799,56.507,29.93 +2020-09-15 22:00:00,85.63,48.097,50.728,29.93 +2020-09-15 22:15:00,88.34,48.534,50.728,29.93 +2020-09-15 22:30:00,87.42,40.913000000000004,50.728,29.93 +2020-09-15 22:45:00,85.3,37.449,50.728,29.93 +2020-09-15 23:00:00,78.95,34.841,43.556999999999995,29.93 +2020-09-15 23:15:00,83.3,33.741,43.556999999999995,29.93 +2020-09-15 23:30:00,83.87,33.709,43.556999999999995,29.93 +2020-09-15 23:45:00,78.6,32.943000000000005,43.556999999999995,29.93 +2020-09-16 00:00:00,73.44,39.084,41.151,29.93 +2020-09-16 00:15:00,73.98,39.830999999999996,41.151,29.93 +2020-09-16 00:30:00,78.99,39.635,41.151,29.93 +2020-09-16 00:45:00,80.82,39.476,41.151,29.93 +2020-09-16 01:00:00,76.35,39.616,37.763000000000005,29.93 +2020-09-16 01:15:00,73.59,38.859,37.763000000000005,29.93 +2020-09-16 01:30:00,72.26,37.896,37.763000000000005,29.93 +2020-09-16 01:45:00,78.96,37.031,37.763000000000005,29.93 +2020-09-16 02:00:00,78.44,37.302,35.615,29.93 +2020-09-16 02:15:00,78.12,37.166,35.615,29.93 +2020-09-16 02:30:00,72.51,37.736,35.615,29.93 +2020-09-16 02:45:00,78.41,38.519,35.615,29.93 +2020-09-16 03:00:00,82.31,40.568000000000005,35.153,29.93 +2020-09-16 03:15:00,81.48,40.628,35.153,29.93 +2020-09-16 03:30:00,78.89,40.606,35.153,29.93 +2020-09-16 03:45:00,80.9,41.105,35.153,29.93 +2020-09-16 04:00:00,85.15,49.181000000000004,36.203,29.93 +2020-09-16 04:15:00,89.89,57.271,36.203,29.93 +2020-09-16 04:30:00,92.92,55.732,36.203,29.93 +2020-09-16 04:45:00,92.18,56.861000000000004,36.203,29.93 +2020-09-16 05:00:00,99.03,78.215,39.922,29.93 +2020-09-16 05:15:00,104.76,95.447,39.922,29.93 +2020-09-16 05:30:00,108.0,89.35799999999999,39.922,29.93 +2020-09-16 05:45:00,109.51,83.431,39.922,29.93 +2020-09-16 06:00:00,113.65,83.573,56.443999999999996,29.93 +2020-09-16 06:15:00,114.5,86.17,56.443999999999996,29.93 +2020-09-16 06:30:00,115.4,84.446,56.443999999999996,29.93 +2020-09-16 06:45:00,117.08,85.50399999999999,56.443999999999996,29.93 +2020-09-16 07:00:00,119.77,85.01899999999999,68.683,29.93 +2020-09-16 07:15:00,119.16,86.70100000000001,68.683,29.93 +2020-09-16 07:30:00,124.41,85.609,68.683,29.93 +2020-09-16 07:45:00,120.27,86.053,68.683,29.93 +2020-09-16 08:00:00,119.32,82.478,59.003,29.93 +2020-09-16 08:15:00,118.97,83.669,59.003,29.93 +2020-09-16 08:30:00,117.59,81.854,59.003,29.93 +2020-09-16 08:45:00,117.4,82.26899999999999,59.003,29.93 +2020-09-16 09:00:00,118.31,76.666,56.21,29.93 +2020-09-16 09:15:00,120.11,74.795,56.21,29.93 +2020-09-16 09:30:00,122.52,75.825,56.21,29.93 +2020-09-16 09:45:00,124.42,76.199,56.21,29.93 +2020-09-16 10:00:00,126.1,73.703,52.358999999999995,29.93 +2020-09-16 10:15:00,126.69,73.807,52.358999999999995,29.93 +2020-09-16 10:30:00,121.23,73.306,52.358999999999995,29.93 +2020-09-16 10:45:00,118.65,73.608,52.358999999999995,29.93 +2020-09-16 11:00:00,109.55,71.77600000000001,51.161,29.93 +2020-09-16 11:15:00,111.88,72.722,51.161,29.93 +2020-09-16 11:30:00,117.41,73.042,51.161,29.93 +2020-09-16 11:45:00,116.4,72.767,51.161,29.93 +2020-09-16 12:00:00,107.59,70.317,49.119,29.93 +2020-09-16 12:15:00,105.17,70.104,49.119,29.93 +2020-09-16 12:30:00,102.46,69.05199999999999,49.119,29.93 +2020-09-16 12:45:00,103.14,69.616,49.119,29.93 +2020-09-16 13:00:00,101.75,69.403,49.187,29.93 +2020-09-16 13:15:00,101.95,69.571,49.187,29.93 +2020-09-16 13:30:00,104.52,68.53399999999999,49.187,29.93 +2020-09-16 13:45:00,101.25,67.885,49.187,29.93 +2020-09-16 14:00:00,102.1,68.471,49.787,29.93 +2020-09-16 14:15:00,103.0,68.127,49.787,29.93 +2020-09-16 14:30:00,103.44,66.956,49.787,29.93 +2020-09-16 14:45:00,104.37,67.27600000000001,49.787,29.93 +2020-09-16 15:00:00,102.18,67.498,51.458999999999996,29.93 +2020-09-16 15:15:00,104.63,66.195,51.458999999999996,29.93 +2020-09-16 15:30:00,104.49,65.661,51.458999999999996,29.93 +2020-09-16 15:45:00,106.34,64.396,51.458999999999996,29.93 +2020-09-16 16:00:00,111.51,65.649,53.663000000000004,29.93 +2020-09-16 16:15:00,108.92,65.133,53.663000000000004,29.93 +2020-09-16 16:30:00,110.62,65.763,53.663000000000004,29.93 +2020-09-16 16:45:00,111.54,63.458999999999996,53.663000000000004,29.93 +2020-09-16 17:00:00,116.11,64.80199999999999,58.183,29.93 +2020-09-16 17:15:00,114.55,65.96,58.183,29.93 +2020-09-16 17:30:00,114.66,65.595,58.183,29.93 +2020-09-16 17:45:00,115.07,65.561,58.183,29.93 +2020-09-16 18:00:00,117.2,65.617,60.141000000000005,29.93 +2020-09-16 18:15:00,116.17,65.419,60.141000000000005,29.93 +2020-09-16 18:30:00,119.01,64.142,60.141000000000005,29.93 +2020-09-16 18:45:00,118.27,68.15,60.141000000000005,29.93 +2020-09-16 19:00:00,116.67,67.969,60.582,29.93 +2020-09-16 19:15:00,116.96,66.834,60.582,29.93 +2020-09-16 19:30:00,113.33,66.178,60.582,29.93 +2020-09-16 19:45:00,115.63,66.4,60.582,29.93 +2020-09-16 20:00:00,104.66,64.4,66.61,29.93 +2020-09-16 20:15:00,107.17,62.825,66.61,29.93 +2020-09-16 20:30:00,104.72,61.527,66.61,29.93 +2020-09-16 20:45:00,104.85,61.146,66.61,29.93 +2020-09-16 21:00:00,98.44,59.425,57.658,29.93 +2020-09-16 21:15:00,92.27,60.768,57.658,29.93 +2020-09-16 21:30:00,95.86,59.723,57.658,29.93 +2020-09-16 21:45:00,94.6,58.926,57.658,29.93 +2020-09-16 22:00:00,90.23,57.67,51.81,29.93 +2020-09-16 22:15:00,84.5,56.826,51.81,29.93 +2020-09-16 22:30:00,82.73,48.071000000000005,51.81,29.93 +2020-09-16 22:45:00,80.31,44.38,51.81,29.93 +2020-09-16 23:00:00,82.5,40.444,42.93600000000001,29.93 +2020-09-16 23:15:00,82.69,40.001999999999995,42.93600000000001,29.93 +2020-09-16 23:30:00,82.91,40.023,42.93600000000001,29.93 +2020-09-16 23:45:00,78.13,39.402,42.93600000000001,29.93 +2020-09-17 00:00:00,79.44,39.347,39.211,29.93 +2020-09-17 00:15:00,80.59,40.09,39.211,29.93 +2020-09-17 00:30:00,78.36,39.903,39.211,29.93 +2020-09-17 00:45:00,75.57,39.747,39.211,29.93 +2020-09-17 01:00:00,79.12,39.88,37.607,29.93 +2020-09-17 01:15:00,78.2,39.145,37.607,29.93 +2020-09-17 01:30:00,77.23,38.2,37.607,29.93 +2020-09-17 01:45:00,74.58,37.333,37.607,29.93 +2020-09-17 02:00:00,78.04,37.611,36.44,29.93 +2020-09-17 02:15:00,79.78,37.494,36.44,29.93 +2020-09-17 02:30:00,76.24,38.041,36.44,29.93 +2020-09-17 02:45:00,75.01,38.82,36.44,29.93 +2020-09-17 03:00:00,77.9,40.855,36.116,29.93 +2020-09-17 03:15:00,79.65,40.934,36.116,29.93 +2020-09-17 03:30:00,81.81,40.917,36.116,29.93 +2020-09-17 03:45:00,78.48,41.401,36.116,29.93 +2020-09-17 04:00:00,87.76,49.50899999999999,37.398,29.93 +2020-09-17 04:15:00,91.21,57.629,37.398,29.93 +2020-09-17 04:30:00,95.34,56.093999999999994,37.398,29.93 +2020-09-17 04:45:00,94.05,57.232,37.398,29.93 +2020-09-17 05:00:00,98.21,78.67699999999999,41.776,29.93 +2020-09-17 05:15:00,104.48,96.00299999999999,41.776,29.93 +2020-09-17 05:30:00,108.42,89.899,41.776,29.93 +2020-09-17 05:45:00,108.7,83.925,41.776,29.93 +2020-09-17 06:00:00,114.23,84.03399999999999,55.61,29.93 +2020-09-17 06:15:00,113.86,86.65299999999999,55.61,29.93 +2020-09-17 06:30:00,116.05,84.93299999999999,55.61,29.93 +2020-09-17 06:45:00,116.34,85.98899999999999,55.61,29.93 +2020-09-17 07:00:00,117.73,85.50200000000001,67.13600000000001,29.93 +2020-09-17 07:15:00,117.01,87.2,67.13600000000001,29.93 +2020-09-17 07:30:00,115.49,86.14299999999999,67.13600000000001,29.93 +2020-09-17 07:45:00,114.64,86.59,67.13600000000001,29.93 +2020-09-17 08:00:00,113.27,83.023,57.55,29.93 +2020-09-17 08:15:00,113.0,84.181,57.55,29.93 +2020-09-17 08:30:00,112.72,82.383,57.55,29.93 +2020-09-17 08:45:00,115.23,82.779,57.55,29.93 +2020-09-17 09:00:00,116.21,77.181,52.931999999999995,29.93 +2020-09-17 09:15:00,118.42,75.3,52.931999999999995,29.93 +2020-09-17 09:30:00,115.37,76.311,52.931999999999995,29.93 +2020-09-17 09:45:00,110.86,76.655,52.931999999999995,29.93 +2020-09-17 10:00:00,114.03,74.156,50.36600000000001,29.93 +2020-09-17 10:15:00,117.04,74.223,50.36600000000001,29.93 +2020-09-17 10:30:00,119.88,73.707,50.36600000000001,29.93 +2020-09-17 10:45:00,114.08,73.994,50.36600000000001,29.93 +2020-09-17 11:00:00,112.85,72.17399999999999,47.893,29.93 +2020-09-17 11:15:00,111.3,73.104,47.893,29.93 +2020-09-17 11:30:00,112.41,73.421,47.893,29.93 +2020-09-17 11:45:00,109.95,73.13,47.893,29.93 +2020-09-17 12:00:00,109.44,70.656,45.271,29.93 +2020-09-17 12:15:00,107.34,70.431,45.271,29.93 +2020-09-17 12:30:00,105.72,69.41,45.271,29.93 +2020-09-17 12:45:00,114.51,69.967,45.271,29.93 +2020-09-17 13:00:00,108.61,69.725,44.351000000000006,29.93 +2020-09-17 13:15:00,107.32,69.892,44.351000000000006,29.93 +2020-09-17 13:30:00,104.8,68.85300000000001,44.351000000000006,29.93 +2020-09-17 13:45:00,105.93,68.209,44.351000000000006,29.93 +2020-09-17 14:00:00,105.83,68.747,44.99,29.93 +2020-09-17 14:15:00,108.76,68.418,44.99,29.93 +2020-09-17 14:30:00,104.88,67.279,44.99,29.93 +2020-09-17 14:45:00,103.7,67.595,44.99,29.93 +2020-09-17 15:00:00,105.59,67.77199999999999,46.869,29.93 +2020-09-17 15:15:00,105.16,66.488,46.869,29.93 +2020-09-17 15:30:00,103.15,65.985,46.869,29.93 +2020-09-17 15:45:00,107.04,64.735,46.869,29.93 +2020-09-17 16:00:00,107.68,65.949,48.902,29.93 +2020-09-17 16:15:00,110.33,65.449,48.902,29.93 +2020-09-17 16:30:00,109.86,66.07600000000001,48.902,29.93 +2020-09-17 16:45:00,111.84,63.827,48.902,29.93 +2020-09-17 17:00:00,114.46,65.133,53.244,29.93 +2020-09-17 17:15:00,113.14,66.313,53.244,29.93 +2020-09-17 17:30:00,113.05,65.952,53.244,29.93 +2020-09-17 17:45:00,114.69,65.947,53.244,29.93 +2020-09-17 18:00:00,115.77,65.985,54.343999999999994,29.93 +2020-09-17 18:15:00,118.59,65.783,54.343999999999994,29.93 +2020-09-17 18:30:00,118.36,64.518,54.343999999999994,29.93 +2020-09-17 18:45:00,120.85,68.518,54.343999999999994,29.93 +2020-09-17 19:00:00,115.71,68.35,54.332,29.93 +2020-09-17 19:15:00,116.59,67.211,54.332,29.93 +2020-09-17 19:30:00,109.14,66.551,54.332,29.93 +2020-09-17 19:45:00,110.22,66.765,54.332,29.93 +2020-09-17 20:00:00,101.81,64.786,58.06,29.93 +2020-09-17 20:15:00,102.55,63.208,58.06,29.93 +2020-09-17 20:30:00,100.73,61.886,58.06,29.93 +2020-09-17 20:45:00,100.68,61.467,58.06,29.93 +2020-09-17 21:00:00,95.01,59.748999999999995,52.411,29.93 +2020-09-17 21:15:00,100.41,61.083,52.411,29.93 +2020-09-17 21:30:00,97.41,60.045,52.411,29.93 +2020-09-17 21:45:00,94.7,59.213,52.411,29.93 +2020-09-17 22:00:00,85.44,57.93899999999999,47.148999999999994,29.93 +2020-09-17 22:15:00,89.95,57.071000000000005,47.148999999999994,29.93 +2020-09-17 22:30:00,89.79,48.309,47.148999999999994,29.93 +2020-09-17 22:45:00,89.53,44.622,47.148999999999994,29.93 +2020-09-17 23:00:00,81.01,40.726,40.814,29.93 +2020-09-17 23:15:00,79.55,40.256,40.814,29.93 +2020-09-17 23:30:00,83.76,40.281,40.814,29.93 +2020-09-17 23:45:00,83.95,39.659,40.814,29.93 +2020-09-18 00:00:00,80.29,38.004,39.153,29.93 +2020-09-18 00:15:00,74.72,38.957,39.153,29.93 +2020-09-18 00:30:00,73.6,38.939,39.153,29.93 +2020-09-18 00:45:00,72.77,39.126,39.153,29.93 +2020-09-18 01:00:00,71.06,38.888000000000005,37.228,29.93 +2020-09-18 01:15:00,72.86,37.991,37.228,29.93 +2020-09-18 01:30:00,71.99,37.521,37.228,29.93 +2020-09-18 01:45:00,72.65,36.501999999999995,37.228,29.93 +2020-09-18 02:00:00,72.83,37.49,35.851,29.93 +2020-09-18 02:15:00,78.45,37.321999999999996,35.851,29.93 +2020-09-18 02:30:00,72.91,38.597,35.851,29.93 +2020-09-18 02:45:00,73.98,38.884,35.851,29.93 +2020-09-18 03:00:00,74.75,41.191,36.54,29.93 +2020-09-18 03:15:00,77.42,40.675,36.54,29.93 +2020-09-18 03:30:00,77.21,40.485,36.54,29.93 +2020-09-18 03:45:00,81.51,41.681000000000004,36.54,29.93 +2020-09-18 04:00:00,90.84,49.988,37.578,29.93 +2020-09-18 04:15:00,91.02,56.937,37.578,29.93 +2020-09-18 04:30:00,86.07,56.161,37.578,29.93 +2020-09-18 04:45:00,92.31,56.513999999999996,37.578,29.93 +2020-09-18 05:00:00,108.17,77.343,40.387,29.93 +2020-09-18 05:15:00,112.85,95.874,40.387,29.93 +2020-09-18 05:30:00,108.76,90.18299999999999,40.387,29.93 +2020-09-18 05:45:00,109.88,83.81,40.387,29.93 +2020-09-18 06:00:00,117.15,84.181,54.668,29.93 +2020-09-18 06:15:00,115.51,86.54899999999999,54.668,29.93 +2020-09-18 06:30:00,115.18,84.565,54.668,29.93 +2020-09-18 06:45:00,116.95,85.958,54.668,29.93 +2020-09-18 07:00:00,118.68,85.72399999999999,63.971000000000004,29.93 +2020-09-18 07:15:00,118.95,88.38600000000001,63.971000000000004,29.93 +2020-09-18 07:30:00,116.43,85.897,63.971000000000004,29.93 +2020-09-18 07:45:00,116.62,85.943,63.971000000000004,29.93 +2020-09-18 08:00:00,114.28,82.63,56.042,29.93 +2020-09-18 08:15:00,112.83,84.12299999999999,56.042,29.93 +2020-09-18 08:30:00,114.36,82.556,56.042,29.93 +2020-09-18 08:45:00,115.55,82.37,56.042,29.93 +2020-09-18 09:00:00,114.17,75.26,52.832,29.93 +2020-09-18 09:15:00,111.77,74.903,52.832,29.93 +2020-09-18 09:30:00,111.15,75.277,52.832,29.93 +2020-09-18 09:45:00,111.61,75.834,52.832,29.93 +2020-09-18 10:00:00,107.89,72.842,50.044,29.93 +2020-09-18 10:15:00,109.25,72.935,50.044,29.93 +2020-09-18 10:30:00,109.02,72.765,50.044,29.93 +2020-09-18 10:45:00,107.6,72.832,50.044,29.93 +2020-09-18 11:00:00,107.22,71.185,49.06100000000001,29.93 +2020-09-18 11:15:00,115.43,71.064,49.06100000000001,29.93 +2020-09-18 11:30:00,114.09,71.685,49.06100000000001,29.93 +2020-09-18 11:45:00,112.5,70.729,49.06100000000001,29.93 +2020-09-18 12:00:00,109.27,68.899,45.595,29.93 +2020-09-18 12:15:00,107.26,67.53399999999999,45.595,29.93 +2020-09-18 12:30:00,111.87,66.678,45.595,29.93 +2020-09-18 12:45:00,112.45,66.866,45.595,29.93 +2020-09-18 13:00:00,107.19,67.291,43.218,29.93 +2020-09-18 13:15:00,111.3,67.846,43.218,29.93 +2020-09-18 13:30:00,109.47,67.354,43.218,29.93 +2020-09-18 13:45:00,112.2,66.903,43.218,29.93 +2020-09-18 14:00:00,112.75,66.477,41.926,29.93 +2020-09-18 14:15:00,113.18,66.402,41.926,29.93 +2020-09-18 14:30:00,113.34,66.488,41.926,29.93 +2020-09-18 14:45:00,112.16,66.43,41.926,29.93 +2020-09-18 15:00:00,107.96,66.34899999999999,43.79,29.93 +2020-09-18 15:15:00,108.95,64.778,43.79,29.93 +2020-09-18 15:30:00,111.25,63.426,43.79,29.93 +2020-09-18 15:45:00,111.89,62.758,43.79,29.93 +2020-09-18 16:00:00,114.19,63.008,45.895,29.93 +2020-09-18 16:15:00,112.94,62.958,45.895,29.93 +2020-09-18 16:30:00,113.82,63.486000000000004,45.895,29.93 +2020-09-18 16:45:00,116.38,60.707,45.895,29.93 +2020-09-18 17:00:00,117.22,63.245,51.36,29.93 +2020-09-18 17:15:00,116.42,64.20100000000001,51.36,29.93 +2020-09-18 17:30:00,115.99,63.861000000000004,51.36,29.93 +2020-09-18 17:45:00,115.41,63.695,51.36,29.93 +2020-09-18 18:00:00,117.73,64.012,52.985,29.93 +2020-09-18 18:15:00,116.7,63.016000000000005,52.985,29.93 +2020-09-18 18:30:00,116.11,61.803000000000004,52.985,29.93 +2020-09-18 18:45:00,115.8,66.096,52.985,29.93 +2020-09-18 19:00:00,113.5,66.872,52.602,29.93 +2020-09-18 19:15:00,108.61,66.538,52.602,29.93 +2020-09-18 19:30:00,106.11,65.805,52.602,29.93 +2020-09-18 19:45:00,104.55,65.138,52.602,29.93 +2020-09-18 20:00:00,95.47,63.067,58.063,29.93 +2020-09-18 20:15:00,94.24,62.063,58.063,29.93 +2020-09-18 20:30:00,92.29,60.351000000000006,58.063,29.93 +2020-09-18 20:45:00,95.23,59.466,58.063,29.93 +2020-09-18 21:00:00,87.91,58.849,50.135,29.93 +2020-09-18 21:15:00,85.57,61.511,50.135,29.93 +2020-09-18 21:30:00,78.26,60.382,50.135,29.93 +2020-09-18 21:45:00,84.38,59.817,50.135,29.93 +2020-09-18 22:00:00,79.5,58.698,45.165,29.93 +2020-09-18 22:15:00,81.75,57.588,45.165,29.93 +2020-09-18 22:30:00,74.47,54.06,45.165,29.93 +2020-09-18 22:45:00,77.94,51.985,45.165,29.93 +2020-09-18 23:00:00,76.49,49.233000000000004,39.121,29.93 +2020-09-18 23:15:00,74.79,47.049,39.121,29.93 +2020-09-18 23:30:00,72.85,45.343999999999994,39.121,29.93 +2020-09-18 23:45:00,71.53,44.448,39.121,29.93 +2020-09-19 00:00:00,71.22,38.38,38.49,29.816 +2020-09-19 00:15:00,70.98,37.655,38.49,29.816 +2020-09-19 00:30:00,62.35,37.674,38.49,29.816 +2020-09-19 00:45:00,70.07,37.529,38.49,29.816 +2020-09-19 01:00:00,68.84,37.658,34.5,29.816 +2020-09-19 01:15:00,66.83,36.938,34.5,29.816 +2020-09-19 01:30:00,59.53,35.755,34.5,29.816 +2020-09-19 01:45:00,60.48,35.583,34.5,29.816 +2020-09-19 02:00:00,61.5,36.045,32.236,29.816 +2020-09-19 02:15:00,67.13,35.221,32.236,29.816 +2020-09-19 02:30:00,59.26,35.586999999999996,32.236,29.816 +2020-09-19 02:45:00,59.5,36.468,32.236,29.816 +2020-09-19 03:00:00,58.35,37.939,32.067,29.816 +2020-09-19 03:15:00,60.56,36.596,32.067,29.816 +2020-09-19 03:30:00,58.82,36.205,32.067,29.816 +2020-09-19 03:45:00,60.46,38.491,32.067,29.816 +2020-09-19 04:00:00,61.29,44.302,33.071,29.816 +2020-09-19 04:15:00,60.56,49.925,33.071,29.816 +2020-09-19 04:30:00,57.39,47.338,33.071,29.816 +2020-09-19 04:45:00,61.32,47.757,33.071,29.816 +2020-09-19 05:00:00,63.99,58.761,33.014,29.816 +2020-09-19 05:15:00,64.16,64.37100000000001,33.014,29.816 +2020-09-19 05:30:00,65.42,59.894,33.014,29.816 +2020-09-19 05:45:00,62.16,58.2,33.014,29.816 +2020-09-19 06:00:00,67.73,71.545,34.628,29.816 +2020-09-19 06:15:00,67.75,83.959,34.628,29.816 +2020-09-19 06:30:00,71.21,78.477,34.628,29.816 +2020-09-19 06:45:00,72.05,75.16,34.628,29.816 +2020-09-19 07:00:00,74.14,72.542,38.871,29.816 +2020-09-19 07:15:00,71.97,73.934,38.871,29.816 +2020-09-19 07:30:00,76.89,73.319,38.871,29.816 +2020-09-19 07:45:00,80.01,75.127,38.871,29.816 +2020-09-19 08:00:00,80.53,73.286,43.293,29.816 +2020-09-19 08:15:00,82.15,75.622,43.293,29.816 +2020-09-19 08:30:00,81.09,74.517,43.293,29.816 +2020-09-19 08:45:00,76.52,75.882,43.293,29.816 +2020-09-19 09:00:00,75.61,71.438,44.559,29.816 +2020-09-19 09:15:00,74.54,71.634,44.559,29.816 +2020-09-19 09:30:00,74.81,72.601,44.559,29.816 +2020-09-19 09:45:00,76.44,72.857,44.559,29.816 +2020-09-19 10:00:00,76.74,70.247,42.091,29.816 +2020-09-19 10:15:00,77.89,70.598,42.091,29.816 +2020-09-19 10:30:00,82.26,70.22800000000001,42.091,29.816 +2020-09-19 10:45:00,79.12,70.435,42.091,29.816 +2020-09-19 11:00:00,78.34,68.741,38.505,29.816 +2020-09-19 11:15:00,81.3,68.98899999999999,38.505,29.816 +2020-09-19 11:30:00,77.75,69.456,38.505,29.816 +2020-09-19 11:45:00,76.77,68.646,38.505,29.816 +2020-09-19 12:00:00,74.59,66.622,35.388000000000005,29.816 +2020-09-19 12:15:00,77.13,66.02600000000001,35.388000000000005,29.816 +2020-09-19 12:30:00,69.95,65.217,35.388000000000005,29.816 +2020-09-19 12:45:00,65.4,65.63600000000001,35.388000000000005,29.816 +2020-09-19 13:00:00,66.49,65.413,31.355999999999998,29.816 +2020-09-19 13:15:00,69.13,64.945,31.355999999999998,29.816 +2020-09-19 13:30:00,67.92,64.436,31.355999999999998,29.816 +2020-09-19 13:45:00,71.32,63.224,31.355999999999998,29.816 +2020-09-19 14:00:00,68.7,63.16,30.522,29.816 +2020-09-19 14:15:00,68.61,62.07899999999999,30.522,29.816 +2020-09-19 14:30:00,70.26,61.327,30.522,29.816 +2020-09-19 14:45:00,70.21,61.659,30.522,29.816 +2020-09-19 15:00:00,71.68,62.038999999999994,34.36,29.816 +2020-09-19 15:15:00,71.93,61.226000000000006,34.36,29.816 +2020-09-19 15:30:00,73.29,60.536,34.36,29.816 +2020-09-19 15:45:00,75.49,59.263999999999996,34.36,29.816 +2020-09-19 16:00:00,78.43,60.763999999999996,39.507,29.816 +2020-09-19 16:15:00,79.18,60.381,39.507,29.816 +2020-09-19 16:30:00,79.14,61.047,39.507,29.816 +2020-09-19 16:45:00,81.7,58.574,39.507,29.816 +2020-09-19 17:00:00,83.48,60.071999999999996,47.151,29.816 +2020-09-19 17:15:00,84.27,60.06100000000001,47.151,29.816 +2020-09-19 17:30:00,85.58,59.604,47.151,29.816 +2020-09-19 17:45:00,85.8,59.68600000000001,47.151,29.816 +2020-09-19 18:00:00,89.34,60.965,50.303999999999995,29.816 +2020-09-19 18:15:00,89.84,61.67100000000001,50.303999999999995,29.816 +2020-09-19 18:30:00,90.7,61.82,50.303999999999995,29.816 +2020-09-19 18:45:00,89.05,62.67,50.303999999999995,29.816 +2020-09-19 19:00:00,87.56,62.681000000000004,50.622,29.816 +2020-09-19 19:15:00,84.6,61.475,50.622,29.816 +2020-09-19 19:30:00,82.62,61.503,50.622,29.816 +2020-09-19 19:45:00,81.68,62.065,50.622,29.816 +2020-09-19 20:00:00,77.1,61.138999999999996,45.391000000000005,29.816 +2020-09-19 20:15:00,73.8,60.276,45.391000000000005,29.816 +2020-09-19 20:30:00,72.52,57.81,45.391000000000005,29.816 +2020-09-19 20:45:00,74.56,58.104,45.391000000000005,29.816 +2020-09-19 21:00:00,70.72,57.077,39.98,29.816 +2020-09-19 21:15:00,71.27,59.6,39.98,29.816 +2020-09-19 21:30:00,68.48,58.958,39.98,29.816 +2020-09-19 21:45:00,68.12,57.851000000000006,39.98,29.816 +2020-09-19 22:00:00,63.93,57.068999999999996,37.53,29.816 +2020-09-19 22:15:00,68.03,56.803000000000004,37.53,29.816 +2020-09-19 22:30:00,62.43,54.72,37.53,29.816 +2020-09-19 22:45:00,64.15,53.43600000000001,37.53,29.816 +2020-09-19 23:00:00,60.71,50.88399999999999,30.97,29.816 +2020-09-19 23:15:00,60.35,48.526,30.97,29.816 +2020-09-19 23:30:00,59.11,48.121,30.97,29.816 +2020-09-19 23:45:00,56.8,46.816,30.97,29.816 +2020-09-20 00:00:00,55.45,39.763000000000005,27.24,29.816 +2020-09-20 00:15:00,55.34,38.111999999999995,27.24,29.816 +2020-09-20 00:30:00,53.87,37.93,27.24,29.816 +2020-09-20 00:45:00,54.21,37.898,27.24,29.816 +2020-09-20 01:00:00,52.57,38.174,25.662,29.816 +2020-09-20 01:15:00,53.16,37.672,25.662,29.816 +2020-09-20 01:30:00,53.67,36.552,25.662,29.816 +2020-09-20 01:45:00,54.29,36.016999999999996,25.662,29.816 +2020-09-20 02:00:00,51.96,36.338,25.67,29.816 +2020-09-20 02:15:00,53.19,35.809,25.67,29.816 +2020-09-20 02:30:00,53.0,36.524,25.67,29.816 +2020-09-20 02:45:00,53.05,37.319,25.67,29.816 +2020-09-20 03:00:00,52.73,39.335,24.258000000000003,29.816 +2020-09-20 03:15:00,53.33,38.053000000000004,24.258000000000003,29.816 +2020-09-20 03:30:00,52.77,37.523,24.258000000000003,29.816 +2020-09-20 03:45:00,53.76,39.198,24.258000000000003,29.816 +2020-09-20 04:00:00,54.65,44.95399999999999,25.051,29.816 +2020-09-20 04:15:00,54.55,50.015,25.051,29.816 +2020-09-20 04:30:00,55.06,48.416000000000004,25.051,29.816 +2020-09-20 04:45:00,55.46,48.582,25.051,29.816 +2020-09-20 05:00:00,56.45,58.891999999999996,25.145,29.816 +2020-09-20 05:15:00,57.48,63.419,25.145,29.816 +2020-09-20 05:30:00,58.07,58.583,25.145,29.816 +2020-09-20 05:45:00,58.21,56.698,25.145,29.816 +2020-09-20 06:00:00,59.41,68.219,26.371,29.816 +2020-09-20 06:15:00,60.9,80.828,26.371,29.816 +2020-09-20 06:30:00,62.42,74.55,26.371,29.816 +2020-09-20 06:45:00,64.42,70.22399999999999,26.371,29.816 +2020-09-20 07:00:00,66.46,68.428,28.756999999999998,29.816 +2020-09-20 07:15:00,67.42,68.416,28.756999999999998,29.816 +2020-09-20 07:30:00,69.0,68.42699999999999,28.756999999999998,29.816 +2020-09-20 07:45:00,71.83,70.008,28.756999999999998,29.816 +2020-09-20 08:00:00,72.34,69.197,32.82,29.816 +2020-09-20 08:15:00,74.03,72.319,32.82,29.816 +2020-09-20 08:30:00,74.41,72.318,32.82,29.816 +2020-09-20 08:45:00,74.79,74.168,32.82,29.816 +2020-09-20 09:00:00,76.91,69.527,35.534,29.816 +2020-09-20 09:15:00,76.22,69.52,35.534,29.816 +2020-09-20 09:30:00,78.51,70.729,35.534,29.816 +2020-09-20 09:45:00,78.9,71.635,35.534,29.816 +2020-09-20 10:00:00,78.27,70.111,35.925,29.816 +2020-09-20 10:15:00,84.18,70.676,35.925,29.816 +2020-09-20 10:30:00,87.27,70.625,35.925,29.816 +2020-09-20 10:45:00,86.96,70.958,35.925,29.816 +2020-09-20 11:00:00,84.99,69.303,37.056,29.816 +2020-09-20 11:15:00,85.58,69.262,37.056,29.816 +2020-09-20 11:30:00,85.55,69.827,37.056,29.816 +2020-09-20 11:45:00,82.98,69.359,37.056,29.816 +2020-09-20 12:00:00,78.15,67.827,33.124,29.816 +2020-09-20 12:15:00,79.24,67.391,33.124,29.816 +2020-09-20 12:30:00,78.19,66.303,33.124,29.816 +2020-09-20 12:45:00,78.17,65.979,33.124,29.816 +2020-09-20 13:00:00,77.34,65.316,29.874000000000002,29.816 +2020-09-20 13:15:00,74.05,65.38,29.874000000000002,29.816 +2020-09-20 13:30:00,74.77,64.042,29.874000000000002,29.816 +2020-09-20 13:45:00,77.63,63.398,29.874000000000002,29.816 +2020-09-20 14:00:00,79.84,64.18,27.302,29.816 +2020-09-20 14:15:00,80.59,63.778999999999996,27.302,29.816 +2020-09-20 14:30:00,80.69,62.596000000000004,27.302,29.816 +2020-09-20 14:45:00,79.0,62.066,27.302,29.816 +2020-09-20 15:00:00,76.77,62.028,27.642,29.816 +2020-09-20 15:15:00,78.69,60.915,27.642,29.816 +2020-09-20 15:30:00,79.78,60.31399999999999,27.642,29.816 +2020-09-20 15:45:00,80.99,59.467,27.642,29.816 +2020-09-20 16:00:00,82.95,60.283,31.945999999999998,29.816 +2020-09-20 16:15:00,83.28,59.808,31.945999999999998,29.816 +2020-09-20 16:30:00,84.6,61.248999999999995,31.945999999999998,29.816 +2020-09-20 16:45:00,89.69,58.915,31.945999999999998,29.816 +2020-09-20 17:00:00,94.49,60.631,40.387,29.816 +2020-09-20 17:15:00,95.66,61.602,40.387,29.816 +2020-09-20 17:30:00,95.71,61.797,40.387,29.816 +2020-09-20 17:45:00,97.43,62.872,40.387,29.816 +2020-09-20 18:00:00,98.66,64.33,44.575,29.816 +2020-09-20 18:15:00,95.42,65.113,44.575,29.816 +2020-09-20 18:30:00,96.3,64.515,44.575,29.816 +2020-09-20 18:45:00,92.61,65.957,44.575,29.816 +2020-09-20 19:00:00,91.79,67.405,45.623999999999995,29.816 +2020-09-20 19:15:00,93.46,65.542,45.623999999999995,29.816 +2020-09-20 19:30:00,95.78,65.34,45.623999999999995,29.816 +2020-09-20 19:45:00,94.24,65.939,45.623999999999995,29.816 +2020-09-20 20:00:00,87.59,65.218,44.583999999999996,29.816 +2020-09-20 20:15:00,90.25,64.521,44.583999999999996,29.816 +2020-09-20 20:30:00,92.85,62.952,44.583999999999996,29.816 +2020-09-20 20:45:00,92.59,61.67100000000001,44.583999999999996,29.816 +2020-09-20 21:00:00,85.14,59.825,39.732,29.816 +2020-09-20 21:15:00,82.41,61.951,39.732,29.816 +2020-09-20 21:30:00,79.04,60.927,39.732,29.816 +2020-09-20 21:45:00,80.63,60.081,39.732,29.816 +2020-09-20 22:00:00,78.14,60.426,38.571,29.816 +2020-09-20 22:15:00,79.55,58.713,38.571,29.816 +2020-09-20 22:30:00,75.33,55.406000000000006,38.571,29.816 +2020-09-20 22:45:00,75.99,52.976000000000006,38.571,29.816 +2020-09-20 23:00:00,74.75,49.496,33.121,29.816 +2020-09-20 23:15:00,77.14,48.525,33.121,29.816 +2020-09-20 23:30:00,75.39,47.949,33.121,29.816 +2020-09-20 23:45:00,71.17,46.997,33.121,29.816 +2020-09-21 00:00:00,63.72,42.257,32.506,29.93 +2020-09-21 00:15:00,71.94,41.973,32.506,29.93 +2020-09-21 00:30:00,71.02,41.553999999999995,32.506,29.93 +2020-09-21 00:45:00,73.89,41.096000000000004,32.506,29.93 +2020-09-21 01:00:00,68.98,41.651,31.121,29.93 +2020-09-21 01:15:00,69.62,41.038000000000004,31.121,29.93 +2020-09-21 01:30:00,73.22,40.213,31.121,29.93 +2020-09-21 01:45:00,73.73,39.638000000000005,31.121,29.93 +2020-09-21 02:00:00,72.86,40.29,29.605999999999998,29.93 +2020-09-21 02:15:00,70.41,39.441,29.605999999999998,29.93 +2020-09-21 02:30:00,75.57,40.343,29.605999999999998,29.93 +2020-09-21 02:45:00,75.89,40.859,29.605999999999998,29.93 +2020-09-21 03:00:00,73.84,43.576,28.124000000000002,29.93 +2020-09-21 03:15:00,72.28,43.278,28.124000000000002,29.93 +2020-09-21 03:30:00,78.86,43.173,28.124000000000002,29.93 +2020-09-21 03:45:00,81.91,44.346000000000004,28.124000000000002,29.93 +2020-09-21 04:00:00,83.6,53.56399999999999,29.743000000000002,29.93 +2020-09-21 04:15:00,81.79,61.896,29.743000000000002,29.93 +2020-09-21 04:30:00,85.13,60.549,29.743000000000002,29.93 +2020-09-21 04:45:00,90.52,61.034,29.743000000000002,29.93 +2020-09-21 05:00:00,99.97,80.40899999999999,36.191,29.93 +2020-09-21 05:15:00,106.86,97.719,36.191,29.93 +2020-09-21 05:30:00,113.92,91.788,36.191,29.93 +2020-09-21 05:45:00,121.46,86.161,36.191,29.93 +2020-09-21 06:00:00,123.22,85.652,55.277,29.93 +2020-09-21 06:15:00,121.57,87.79899999999999,55.277,29.93 +2020-09-21 06:30:00,121.97,86.494,55.277,29.93 +2020-09-21 06:45:00,124.18,88.302,55.277,29.93 +2020-09-21 07:00:00,126.05,87.74700000000001,65.697,29.93 +2020-09-21 07:15:00,123.56,89.73700000000001,65.697,29.93 +2020-09-21 07:30:00,119.15,88.95200000000001,65.697,29.93 +2020-09-21 07:45:00,121.25,90.06200000000001,65.697,29.93 +2020-09-21 08:00:00,122.84,86.54299999999999,57.028,29.93 +2020-09-21 08:15:00,123.58,88.176,57.028,29.93 +2020-09-21 08:30:00,124.68,86.37299999999999,57.028,29.93 +2020-09-21 08:45:00,123.86,87.43,57.028,29.93 +2020-09-21 09:00:00,122.73,81.84100000000001,52.633,29.93 +2020-09-21 09:15:00,122.01,79.57300000000001,52.633,29.93 +2020-09-21 09:30:00,121.3,79.82,52.633,29.93 +2020-09-21 09:45:00,123.09,79.15100000000001,52.633,29.93 +2020-09-21 10:00:00,127.26,77.78,50.647,29.93 +2020-09-21 10:15:00,126.28,78.107,50.647,29.93 +2020-09-21 10:30:00,120.76,77.462,50.647,29.93 +2020-09-21 10:45:00,121.89,76.899,50.647,29.93 +2020-09-21 11:00:00,118.55,74.681,50.245,29.93 +2020-09-21 11:15:00,120.27,75.36399999999999,50.245,29.93 +2020-09-21 11:30:00,121.52,76.82600000000001,50.245,29.93 +2020-09-21 11:45:00,118.95,76.563,50.245,29.93 +2020-09-21 12:00:00,109.74,74.55,46.956,29.93 +2020-09-21 12:15:00,113.88,74.17699999999999,46.956,29.93 +2020-09-21 12:30:00,105.64,72.477,46.956,29.93 +2020-09-21 12:45:00,114.96,72.624,46.956,29.93 +2020-09-21 13:00:00,114.37,72.619,47.383,29.93 +2020-09-21 13:15:00,107.54,71.649,47.383,29.93 +2020-09-21 13:30:00,102.4,70.263,47.383,29.93 +2020-09-21 13:45:00,107.14,70.24600000000001,47.383,29.93 +2020-09-21 14:00:00,122.16,70.2,47.1,29.93 +2020-09-21 14:15:00,123.25,70.017,47.1,29.93 +2020-09-21 14:30:00,111.73,68.586,47.1,29.93 +2020-09-21 14:45:00,113.21,69.455,47.1,29.93 +2020-09-21 15:00:00,114.42,69.688,49.355,29.93 +2020-09-21 15:15:00,113.68,67.744,49.355,29.93 +2020-09-21 15:30:00,115.25,67.443,49.355,29.93 +2020-09-21 15:45:00,112.83,66.139,49.355,29.93 +2020-09-21 16:00:00,116.9,67.553,52.14,29.93 +2020-09-21 16:15:00,113.27,66.914,52.14,29.93 +2020-09-21 16:30:00,113.73,67.6,52.14,29.93 +2020-09-21 16:45:00,116.11,64.986,52.14,29.93 +2020-09-21 17:00:00,119.45,65.829,58.705,29.93 +2020-09-21 17:15:00,118.12,66.80199999999999,58.705,29.93 +2020-09-21 17:30:00,117.64,66.57,58.705,29.93 +2020-09-21 17:45:00,115.33,66.952,58.705,29.93 +2020-09-21 18:00:00,117.55,67.64,59.153,29.93 +2020-09-21 18:15:00,117.76,66.494,59.153,29.93 +2020-09-21 18:30:00,118.86,65.55,59.153,29.93 +2020-09-21 18:45:00,118.16,69.444,59.153,29.93 +2020-09-21 19:00:00,113.92,70.17,61.483000000000004,29.93 +2020-09-21 19:15:00,109.99,68.964,61.483000000000004,29.93 +2020-09-21 19:30:00,107.98,68.619,61.483000000000004,29.93 +2020-09-21 19:45:00,105.49,68.542,61.483000000000004,29.93 +2020-09-21 20:00:00,105.98,66.316,67.55,29.93 +2020-09-21 20:15:00,107.86,65.946,67.55,29.93 +2020-09-21 20:30:00,105.11,64.217,67.55,29.93 +2020-09-21 20:45:00,95.78,63.5,67.55,29.93 +2020-09-21 21:00:00,94.14,61.354,60.026,29.93 +2020-09-21 21:15:00,93.33,63.472,60.026,29.93 +2020-09-21 21:30:00,94.41,62.532,60.026,29.93 +2020-09-21 21:45:00,94.3,61.356,60.026,29.93 +2020-09-21 22:00:00,89.01,59.428000000000004,52.736999999999995,29.93 +2020-09-21 22:15:00,84.04,58.778999999999996,52.736999999999995,29.93 +2020-09-21 22:30:00,83.53,49.756,52.736999999999995,29.93 +2020-09-21 22:45:00,85.82,46.015,52.736999999999995,29.93 +2020-09-21 23:00:00,83.32,42.842,44.408,29.93 +2020-09-21 23:15:00,76.73,41.349,44.408,29.93 +2020-09-21 23:30:00,74.23,41.486000000000004,44.408,29.93 +2020-09-21 23:45:00,81.41,40.847,44.408,29.93 +2020-09-22 00:00:00,79.25,40.692,44.438,29.93 +2020-09-22 00:15:00,79.02,41.415,44.438,29.93 +2020-09-22 00:30:00,71.81,41.271,44.438,29.93 +2020-09-22 00:45:00,70.35,41.129,44.438,29.93 +2020-09-22 01:00:00,78.04,41.229,41.468999999999994,29.93 +2020-09-22 01:15:00,79.59,40.599000000000004,41.468999999999994,29.93 +2020-09-22 01:30:00,77.85,39.745,41.468999999999994,29.93 +2020-09-22 01:45:00,74.14,38.878,41.468999999999994,29.93 +2020-09-22 02:00:00,76.1,39.185,39.708,29.93 +2020-09-22 02:15:00,78.9,39.167,39.708,29.93 +2020-09-22 02:30:00,78.9,39.602,39.708,29.93 +2020-09-22 02:45:00,74.95,40.354,39.708,29.93 +2020-09-22 03:00:00,72.1,42.326,38.919000000000004,29.93 +2020-09-22 03:15:00,77.33,42.497,38.919000000000004,29.93 +2020-09-22 03:30:00,75.89,42.504,38.919000000000004,29.93 +2020-09-22 03:45:00,80.08,42.905,38.919000000000004,29.93 +2020-09-22 04:00:00,83.28,51.184,40.092,29.93 +2020-09-22 04:15:00,83.18,59.461999999999996,40.092,29.93 +2020-09-22 04:30:00,88.24,57.948,40.092,29.93 +2020-09-22 04:45:00,92.26,59.123999999999995,40.092,29.93 +2020-09-22 05:00:00,101.36,81.043,43.713,29.93 +2020-09-22 05:15:00,112.59,98.867,43.713,29.93 +2020-09-22 05:30:00,117.29,92.676,43.713,29.93 +2020-09-22 05:45:00,118.66,86.45299999999999,43.713,29.93 +2020-09-22 06:00:00,117.1,86.402,56.033,29.93 +2020-09-22 06:15:00,118.12,89.12799999999999,56.033,29.93 +2020-09-22 06:30:00,117.39,87.425,56.033,29.93 +2020-09-22 06:45:00,118.72,88.47200000000001,56.033,29.93 +2020-09-22 07:00:00,120.28,87.978,66.003,29.93 +2020-09-22 07:15:00,118.56,89.75299999999999,66.003,29.93 +2020-09-22 07:30:00,117.78,88.869,66.003,29.93 +2020-09-22 07:45:00,117.36,89.32600000000001,66.003,29.93 +2020-09-22 08:00:00,115.63,85.794,57.474,29.93 +2020-09-22 08:15:00,115.97,86.789,57.474,29.93 +2020-09-22 08:30:00,116.15,85.074,57.474,29.93 +2020-09-22 08:45:00,116.14,85.37,57.474,29.93 +2020-09-22 09:00:00,113.46,79.796,51.928000000000004,29.93 +2020-09-22 09:15:00,114.86,77.87100000000001,51.928000000000004,29.93 +2020-09-22 09:30:00,116.65,78.78399999999999,51.928000000000004,29.93 +2020-09-22 09:45:00,117.21,78.98100000000001,51.928000000000004,29.93 +2020-09-22 10:00:00,116.31,76.461,49.46,29.93 +2020-09-22 10:15:00,112.61,76.334,49.46,29.93 +2020-09-22 10:30:00,112.62,75.747,49.46,29.93 +2020-09-22 10:45:00,113.46,75.955,49.46,29.93 +2020-09-22 11:00:00,111.98,74.19800000000001,48.206,29.93 +2020-09-22 11:15:00,107.81,75.045,48.206,29.93 +2020-09-22 11:30:00,113.38,75.354,48.206,29.93 +2020-09-22 11:45:00,106.2,74.97399999999999,48.206,29.93 +2020-09-22 12:00:00,105.03,72.382,46.285,29.93 +2020-09-22 12:15:00,104.5,72.095,46.285,29.93 +2020-09-22 12:30:00,103.2,71.24,46.285,29.93 +2020-09-22 12:45:00,99.21,71.758,46.285,29.93 +2020-09-22 13:00:00,102.59,71.366,46.861999999999995,29.93 +2020-09-22 13:15:00,108.02,71.529,46.861999999999995,29.93 +2020-09-22 13:30:00,103.9,70.479,46.861999999999995,29.93 +2020-09-22 13:45:00,102.03,69.86,46.861999999999995,29.93 +2020-09-22 14:00:00,106.58,70.15899999999999,46.488,29.93 +2020-09-22 14:15:00,105.28,69.903,46.488,29.93 +2020-09-22 14:30:00,101.09,68.925,46.488,29.93 +2020-09-22 14:45:00,102.61,69.22399999999999,46.488,29.93 +2020-09-22 15:00:00,105.33,69.167,48.442,29.93 +2020-09-22 15:15:00,104.23,67.98,48.442,29.93 +2020-09-22 15:30:00,103.53,67.63600000000001,48.442,29.93 +2020-09-22 15:45:00,105.75,66.461,48.442,29.93 +2020-09-22 16:00:00,108.11,67.479,50.397,29.93 +2020-09-22 16:15:00,108.64,67.058,50.397,29.93 +2020-09-22 16:30:00,110.45,67.669,50.397,29.93 +2020-09-22 16:45:00,113.26,65.699,50.397,29.93 +2020-09-22 17:00:00,114.67,66.813,56.668,29.93 +2020-09-22 17:15:00,114.65,68.11,56.668,29.93 +2020-09-22 17:30:00,116.47,67.773,56.668,29.93 +2020-09-22 17:45:00,117.39,67.911,56.668,29.93 +2020-09-22 18:00:00,121.5,67.854,57.957,29.93 +2020-09-22 18:15:00,119.7,67.63600000000001,57.957,29.93 +2020-09-22 18:30:00,121.25,66.432,57.957,29.93 +2020-09-22 18:45:00,119.41,70.4,57.957,29.93 +2020-09-22 19:00:00,117.27,70.289,57.056000000000004,29.93 +2020-09-22 19:15:00,114.03,69.138,57.056000000000004,29.93 +2020-09-22 19:30:00,113.68,68.457,57.056000000000004,29.93 +2020-09-22 19:45:00,108.16,68.626,57.056000000000004,29.93 +2020-09-22 20:00:00,106.04,66.758,64.156,29.93 +2020-09-22 20:15:00,110.06,65.166,64.156,29.93 +2020-09-22 20:30:00,108.07,63.717,64.156,29.93 +2020-09-22 20:45:00,100.9,63.108999999999995,64.156,29.93 +2020-09-22 21:00:00,93.55,61.407,56.507,29.93 +2020-09-22 21:15:00,93.78,62.69,56.507,29.93 +2020-09-22 21:30:00,90.09,61.68899999999999,56.507,29.93 +2020-09-22 21:45:00,92.81,60.684,56.507,29.93 +2020-09-22 22:00:00,88.4,59.316,50.728,29.93 +2020-09-22 22:15:00,90.76,58.328,50.728,29.93 +2020-09-22 22:30:00,83.74,49.538000000000004,50.728,29.93 +2020-09-22 22:45:00,79.35,45.873000000000005,50.728,29.93 +2020-09-22 23:00:00,73.73,42.176,43.556999999999995,29.93 +2020-09-22 23:15:00,74.31,41.556999999999995,43.556999999999995,29.93 +2020-09-22 23:30:00,72.56,41.6,43.556999999999995,29.93 +2020-09-22 23:45:00,73.7,40.976000000000006,43.556999999999995,29.93 +2020-09-23 00:00:00,74.14,40.966,41.151,29.93 +2020-09-23 00:15:00,80.43,41.68600000000001,41.151,29.93 +2020-09-23 00:30:00,80.03,41.549,41.151,29.93 +2020-09-23 00:45:00,79.17,41.41,41.151,29.93 +2020-09-23 01:00:00,72.52,41.504,37.763000000000005,29.93 +2020-09-23 01:15:00,76.54,40.896,37.763000000000005,29.93 +2020-09-23 01:30:00,79.93,40.059,37.763000000000005,29.93 +2020-09-23 01:45:00,81.24,39.193000000000005,37.763000000000005,29.93 +2020-09-23 02:00:00,78.02,39.505,35.615,29.93 +2020-09-23 02:15:00,77.07,39.507,35.615,29.93 +2020-09-23 02:30:00,81.33,39.919000000000004,35.615,29.93 +2020-09-23 02:45:00,81.34,40.667,35.615,29.93 +2020-09-23 03:00:00,77.9,42.626000000000005,35.153,29.93 +2020-09-23 03:15:00,76.25,42.815,35.153,29.93 +2020-09-23 03:30:00,84.12,42.827,35.153,29.93 +2020-09-23 03:45:00,87.89,43.21,35.153,29.93 +2020-09-23 04:00:00,91.84,51.525,36.203,29.93 +2020-09-23 04:15:00,87.47,59.836999999999996,36.203,29.93 +2020-09-23 04:30:00,95.22,58.328,36.203,29.93 +2020-09-23 04:45:00,101.67,59.511,36.203,29.93 +2020-09-23 05:00:00,111.18,81.527,39.922,29.93 +2020-09-23 05:15:00,112.35,99.454,39.922,29.93 +2020-09-23 05:30:00,111.69,93.244,39.922,29.93 +2020-09-23 05:45:00,116.38,86.97,39.922,29.93 +2020-09-23 06:00:00,119.88,86.88600000000001,56.443999999999996,29.93 +2020-09-23 06:15:00,119.76,89.635,56.443999999999996,29.93 +2020-09-23 06:30:00,120.0,87.935,56.443999999999996,29.93 +2020-09-23 06:45:00,121.7,88.979,56.443999999999996,29.93 +2020-09-23 07:00:00,124.88,88.484,68.683,29.93 +2020-09-23 07:15:00,120.09,90.274,68.683,29.93 +2020-09-23 07:30:00,121.21,89.425,68.683,29.93 +2020-09-23 07:45:00,123.12,89.883,68.683,29.93 +2020-09-23 08:00:00,116.84,86.35799999999999,59.003,29.93 +2020-09-23 08:15:00,115.12,87.31700000000001,59.003,29.93 +2020-09-23 08:30:00,115.48,85.62,59.003,29.93 +2020-09-23 08:45:00,115.51,85.897,59.003,29.93 +2020-09-23 09:00:00,114.02,80.328,56.21,29.93 +2020-09-23 09:15:00,114.6,78.39399999999999,56.21,29.93 +2020-09-23 09:30:00,110.5,79.286,56.21,29.93 +2020-09-23 09:45:00,108.97,79.453,56.21,29.93 +2020-09-23 10:00:00,108.11,76.928,52.358999999999995,29.93 +2020-09-23 10:15:00,109.82,76.764,52.358999999999995,29.93 +2020-09-23 10:30:00,110.15,76.161,52.358999999999995,29.93 +2020-09-23 10:45:00,105.93,76.354,52.358999999999995,29.93 +2020-09-23 11:00:00,105.59,74.609,51.161,29.93 +2020-09-23 11:15:00,106.75,75.438,51.161,29.93 +2020-09-23 11:30:00,104.38,75.747,51.161,29.93 +2020-09-23 11:45:00,103.61,75.348,51.161,29.93 +2020-09-23 12:00:00,100.04,72.733,49.119,29.93 +2020-09-23 12:15:00,106.82,72.434,49.119,29.93 +2020-09-23 12:30:00,109.78,71.612,49.119,29.93 +2020-09-23 12:45:00,102.31,72.122,49.119,29.93 +2020-09-23 13:00:00,103.09,71.70100000000001,49.187,29.93 +2020-09-23 13:15:00,103.29,71.862,49.187,29.93 +2020-09-23 13:30:00,102.62,70.81,49.187,29.93 +2020-09-23 13:45:00,105.34,70.196,49.187,29.93 +2020-09-23 14:00:00,107.99,70.447,49.787,29.93 +2020-09-23 14:15:00,105.73,70.204,49.787,29.93 +2020-09-23 14:30:00,107.7,69.259,49.787,29.93 +2020-09-23 14:45:00,109.29,69.555,49.787,29.93 +2020-09-23 15:00:00,108.43,69.45100000000001,51.458999999999996,29.93 +2020-09-23 15:15:00,104.72,68.285,51.458999999999996,29.93 +2020-09-23 15:30:00,107.19,67.971,51.458999999999996,29.93 +2020-09-23 15:45:00,107.85,66.812,51.458999999999996,29.93 +2020-09-23 16:00:00,109.45,67.789,53.663000000000004,29.93 +2020-09-23 16:15:00,108.62,67.385,53.663000000000004,29.93 +2020-09-23 16:30:00,110.5,67.992,53.663000000000004,29.93 +2020-09-23 16:45:00,113.22,66.078,53.663000000000004,29.93 +2020-09-23 17:00:00,116.47,67.153,58.183,29.93 +2020-09-23 17:15:00,114.39,68.475,58.183,29.93 +2020-09-23 17:30:00,116.38,68.142,58.183,29.93 +2020-09-23 17:45:00,117.99,68.31,58.183,29.93 +2020-09-23 18:00:00,119.46,68.234,60.141000000000005,29.93 +2020-09-23 18:15:00,119.27,68.014,60.141000000000005,29.93 +2020-09-23 18:30:00,119.58,66.82,60.141000000000005,29.93 +2020-09-23 18:45:00,119.51,70.78399999999999,60.141000000000005,29.93 +2020-09-23 19:00:00,115.57,70.683,60.582,29.93 +2020-09-23 19:15:00,111.65,69.531,60.582,29.93 +2020-09-23 19:30:00,110.06,68.845,60.582,29.93 +2020-09-23 19:45:00,107.79,69.005,60.582,29.93 +2020-09-23 20:00:00,103.36,67.161,66.61,29.93 +2020-09-23 20:15:00,99.89,65.566,66.61,29.93 +2020-09-23 20:30:00,99.32,64.09,66.61,29.93 +2020-09-23 20:45:00,101.02,63.443000000000005,66.61,29.93 +2020-09-23 21:00:00,98.0,61.744,57.658,29.93 +2020-09-23 21:15:00,99.6,63.016999999999996,57.658,29.93 +2020-09-23 21:30:00,86.71,62.025,57.658,29.93 +2020-09-23 21:45:00,90.43,60.985,57.658,29.93 +2020-09-23 22:00:00,84.37,59.597,51.81,29.93 +2020-09-23 22:15:00,91.09,58.586000000000006,51.81,29.93 +2020-09-23 22:30:00,88.12,49.79,51.81,29.93 +2020-09-23 22:45:00,83.92,46.13,51.81,29.93 +2020-09-23 23:00:00,76.12,42.474,42.93600000000001,29.93 +2020-09-23 23:15:00,75.88,41.823,42.93600000000001,29.93 +2020-09-23 23:30:00,79.32,41.87,42.93600000000001,29.93 +2020-09-23 23:45:00,82.59,41.246,42.93600000000001,29.93 +2020-09-24 00:00:00,79.44,41.243,39.211,29.93 +2020-09-24 00:15:00,76.44,41.958999999999996,39.211,29.93 +2020-09-24 00:30:00,75.48,41.83,39.211,29.93 +2020-09-24 00:45:00,79.62,41.693999999999996,39.211,29.93 +2020-09-24 01:00:00,76.77,41.78,37.607,29.93 +2020-09-24 01:15:00,80.51,41.193000000000005,37.607,29.93 +2020-09-24 01:30:00,72.87,40.375,37.607,29.93 +2020-09-24 01:45:00,79.28,39.509,37.607,29.93 +2020-09-24 02:00:00,78.34,39.827,36.44,29.93 +2020-09-24 02:15:00,78.78,39.849000000000004,36.44,29.93 +2020-09-24 02:30:00,74.7,40.239000000000004,36.44,29.93 +2020-09-24 02:45:00,82.77,40.981,36.44,29.93 +2020-09-24 03:00:00,81.53,42.928000000000004,36.116,29.93 +2020-09-24 03:15:00,79.01,43.135,36.116,29.93 +2020-09-24 03:30:00,78.21,43.151,36.116,29.93 +2020-09-24 03:45:00,82.11,43.516999999999996,36.116,29.93 +2020-09-24 04:00:00,87.36,51.869,37.398,29.93 +2020-09-24 04:15:00,93.6,60.213,37.398,29.93 +2020-09-24 04:30:00,97.83,58.708999999999996,37.398,29.93 +2020-09-24 04:45:00,99.6,59.9,37.398,29.93 +2020-09-24 05:00:00,109.7,82.016,41.776,29.93 +2020-09-24 05:15:00,108.24,100.046,41.776,29.93 +2020-09-24 05:30:00,110.1,93.816,41.776,29.93 +2020-09-24 05:45:00,112.5,87.49,41.776,29.93 +2020-09-24 06:00:00,118.92,87.375,55.61,29.93 +2020-09-24 06:15:00,120.61,90.146,55.61,29.93 +2020-09-24 06:30:00,123.08,88.449,55.61,29.93 +2020-09-24 06:45:00,124.17,89.49,55.61,29.93 +2020-09-24 07:00:00,126.37,88.994,67.13600000000001,29.93 +2020-09-24 07:15:00,125.34,90.79899999999999,67.13600000000001,29.93 +2020-09-24 07:30:00,127.26,89.985,67.13600000000001,29.93 +2020-09-24 07:45:00,127.08,90.443,67.13600000000001,29.93 +2020-09-24 08:00:00,125.35,86.925,57.55,29.93 +2020-09-24 08:15:00,126.55,87.84899999999999,57.55,29.93 +2020-09-24 08:30:00,129.1,86.16799999999999,57.55,29.93 +2020-09-24 08:45:00,130.06,86.425,57.55,29.93 +2020-09-24 09:00:00,128.57,80.861,52.931999999999995,29.93 +2020-09-24 09:15:00,126.84,78.918,52.931999999999995,29.93 +2020-09-24 09:30:00,123.67,79.79,52.931999999999995,29.93 +2020-09-24 09:45:00,127.27,79.92699999999999,52.931999999999995,29.93 +2020-09-24 10:00:00,125.65,77.398,50.36600000000001,29.93 +2020-09-24 10:15:00,124.59,77.194,50.36600000000001,29.93 +2020-09-24 10:30:00,120.94,76.577,50.36600000000001,29.93 +2020-09-24 10:45:00,116.89,76.75399999999999,50.36600000000001,29.93 +2020-09-24 11:00:00,110.52,75.02,47.893,29.93 +2020-09-24 11:15:00,109.17,75.833,47.893,29.93 +2020-09-24 11:30:00,110.97,76.141,47.893,29.93 +2020-09-24 11:45:00,106.21,75.725,47.893,29.93 +2020-09-24 12:00:00,102.53,73.084,45.271,29.93 +2020-09-24 12:15:00,102.03,72.77199999999999,45.271,29.93 +2020-09-24 12:30:00,99.75,71.986,45.271,29.93 +2020-09-24 12:45:00,99.48,72.488,45.271,29.93 +2020-09-24 13:00:00,98.27,72.03699999999999,44.351000000000006,29.93 +2020-09-24 13:15:00,99.53,72.196,44.351000000000006,29.93 +2020-09-24 13:30:00,100.61,71.143,44.351000000000006,29.93 +2020-09-24 13:45:00,102.58,70.532,44.351000000000006,29.93 +2020-09-24 14:00:00,103.51,70.735,44.99,29.93 +2020-09-24 14:15:00,103.08,70.508,44.99,29.93 +2020-09-24 14:30:00,101.16,69.596,44.99,29.93 +2020-09-24 14:45:00,101.81,69.888,44.99,29.93 +2020-09-24 15:00:00,101.94,69.737,46.869,29.93 +2020-09-24 15:15:00,103.21,68.59,46.869,29.93 +2020-09-24 15:30:00,103.97,68.308,46.869,29.93 +2020-09-24 15:45:00,104.84,67.164,46.869,29.93 +2020-09-24 16:00:00,108.09,68.101,48.902,29.93 +2020-09-24 16:15:00,109.23,67.714,48.902,29.93 +2020-09-24 16:30:00,110.09,68.317,48.902,29.93 +2020-09-24 16:45:00,113.09,66.46,48.902,29.93 +2020-09-24 17:00:00,116.14,67.495,53.244,29.93 +2020-09-24 17:15:00,116.08,68.84100000000001,53.244,29.93 +2020-09-24 17:30:00,117.28,68.513,53.244,29.93 +2020-09-24 17:45:00,116.1,68.711,53.244,29.93 +2020-09-24 18:00:00,120.4,68.615,54.343999999999994,29.93 +2020-09-24 18:15:00,120.5,68.393,54.343999999999994,29.93 +2020-09-24 18:30:00,123.4,67.212,54.343999999999994,29.93 +2020-09-24 18:45:00,118.83,71.17,54.343999999999994,29.93 +2020-09-24 19:00:00,115.06,71.08,54.332,29.93 +2020-09-24 19:15:00,112.24,69.925,54.332,29.93 +2020-09-24 19:30:00,114.88,69.235,54.332,29.93 +2020-09-24 19:45:00,110.88,69.387,54.332,29.93 +2020-09-24 20:00:00,107.62,67.565,58.06,29.93 +2020-09-24 20:15:00,108.78,65.968,58.06,29.93 +2020-09-24 20:30:00,104.84,64.46600000000001,58.06,29.93 +2020-09-24 20:45:00,96.86,63.781000000000006,58.06,29.93 +2020-09-24 21:00:00,94.23,62.083999999999996,52.411,29.93 +2020-09-24 21:15:00,89.92,63.346000000000004,52.411,29.93 +2020-09-24 21:30:00,87.79,62.361000000000004,52.411,29.93 +2020-09-24 21:45:00,89.98,61.287,52.411,29.93 +2020-09-24 22:00:00,88.04,59.88,47.148999999999994,29.93 +2020-09-24 22:15:00,89.31,58.845,47.148999999999994,29.93 +2020-09-24 22:30:00,80.71,50.044,47.148999999999994,29.93 +2020-09-24 22:45:00,79.84,46.388000000000005,47.148999999999994,29.93 +2020-09-24 23:00:00,71.75,42.773,40.814,29.93 +2020-09-24 23:15:00,77.77,42.091,40.814,29.93 +2020-09-24 23:30:00,81.26,42.141000000000005,40.814,29.93 +2020-09-24 23:45:00,80.31,41.516999999999996,40.814,29.93 +2020-09-25 00:00:00,74.0,39.914,39.153,29.93 +2020-09-25 00:15:00,72.28,40.838,39.153,29.93 +2020-09-25 00:30:00,77.46,40.879,39.153,29.93 +2020-09-25 00:45:00,78.16,41.085,39.153,29.93 +2020-09-25 01:00:00,76.04,40.797,37.228,29.93 +2020-09-25 01:15:00,73.4,40.051,37.228,29.93 +2020-09-25 01:30:00,76.94,39.709,37.228,29.93 +2020-09-25 01:45:00,79.03,38.69,37.228,29.93 +2020-09-25 02:00:00,75.49,39.719,35.851,29.93 +2020-09-25 02:15:00,75.88,39.689,35.851,29.93 +2020-09-25 02:30:00,78.09,40.806,35.851,29.93 +2020-09-25 02:45:00,74.34,41.058,35.851,29.93 +2020-09-25 03:00:00,73.16,43.275,36.54,29.93 +2020-09-25 03:15:00,77.62,42.89,36.54,29.93 +2020-09-25 03:30:00,83.89,42.731,36.54,29.93 +2020-09-25 03:45:00,88.6,43.809,36.54,29.93 +2020-09-25 04:00:00,88.15,52.363,37.578,29.93 +2020-09-25 04:15:00,90.7,59.54,37.578,29.93 +2020-09-25 04:30:00,93.93,58.795,37.578,29.93 +2020-09-25 04:45:00,97.88,59.202,37.578,29.93 +2020-09-25 05:00:00,100.27,80.708,40.387,29.93 +2020-09-25 05:15:00,109.18,99.95299999999999,40.387,29.93 +2020-09-25 05:30:00,109.6,94.13,40.387,29.93 +2020-09-25 05:45:00,110.57,87.402,40.387,29.93 +2020-09-25 06:00:00,115.32,87.54799999999999,54.668,29.93 +2020-09-25 06:15:00,115.66,90.071,54.668,29.93 +2020-09-25 06:30:00,118.26,88.10700000000001,54.668,29.93 +2020-09-25 06:45:00,117.52,89.484,54.668,29.93 +2020-09-25 07:00:00,120.63,89.242,63.971000000000004,29.93 +2020-09-25 07:15:00,121.05,92.009,63.971000000000004,29.93 +2020-09-25 07:30:00,117.32,89.76299999999999,63.971000000000004,29.93 +2020-09-25 07:45:00,117.02,89.816,63.971000000000004,29.93 +2020-09-25 08:00:00,113.23,86.553,56.042,29.93 +2020-09-25 08:15:00,112.83,87.81,56.042,29.93 +2020-09-25 08:30:00,112.79,86.36,56.042,29.93 +2020-09-25 08:45:00,114.62,86.03399999999999,56.042,29.93 +2020-09-25 09:00:00,110.41,78.957,52.832,29.93 +2020-09-25 09:15:00,113.15,78.538,52.832,29.93 +2020-09-25 09:30:00,115.51,78.775,52.832,29.93 +2020-09-25 09:45:00,121.32,79.123,52.832,29.93 +2020-09-25 10:00:00,122.11,76.09899999999999,50.044,29.93 +2020-09-25 10:15:00,119.95,75.921,50.044,29.93 +2020-09-25 10:30:00,119.89,75.65,50.044,29.93 +2020-09-25 10:45:00,123.44,75.60600000000001,50.044,29.93 +2020-09-25 11:00:00,126.14,74.045,49.06100000000001,29.93 +2020-09-25 11:15:00,126.84,73.806,49.06100000000001,29.93 +2020-09-25 11:30:00,127.73,74.418,49.06100000000001,29.93 +2020-09-25 11:45:00,125.85,73.337,49.06100000000001,29.93 +2020-09-25 12:00:00,124.09,71.339,45.595,29.93 +2020-09-25 12:15:00,125.3,69.887,45.595,29.93 +2020-09-25 12:30:00,123.99,69.266,45.595,29.93 +2020-09-25 12:45:00,122.75,69.4,45.595,29.93 +2020-09-25 13:00:00,119.15,69.617,43.218,29.93 +2020-09-25 13:15:00,119.21,70.164,43.218,29.93 +2020-09-25 13:30:00,117.01,69.656,43.218,29.93 +2020-09-25 13:45:00,117.78,69.24,43.218,29.93 +2020-09-25 14:00:00,116.77,68.476,41.926,29.93 +2020-09-25 14:15:00,115.25,68.501,41.926,29.93 +2020-09-25 14:30:00,113.74,68.818,41.926,29.93 +2020-09-25 14:45:00,110.79,68.735,41.926,29.93 +2020-09-25 15:00:00,110.83,68.325,43.79,29.93 +2020-09-25 15:15:00,108.35,66.892,43.79,29.93 +2020-09-25 15:30:00,109.43,65.762,43.79,29.93 +2020-09-25 15:45:00,113.35,65.20100000000001,43.79,29.93 +2020-09-25 16:00:00,112.61,65.172,45.895,29.93 +2020-09-25 16:15:00,111.78,65.234,45.895,29.93 +2020-09-25 16:30:00,114.66,65.737,45.895,29.93 +2020-09-25 16:45:00,114.16,63.353,45.895,29.93 +2020-09-25 17:00:00,116.51,65.618,51.36,29.93 +2020-09-25 17:15:00,117.46,66.74,51.36,29.93 +2020-09-25 17:30:00,116.53,66.434,51.36,29.93 +2020-09-25 17:45:00,119.69,66.47399999999999,51.36,29.93 +2020-09-25 18:00:00,124.62,66.657,52.985,29.93 +2020-09-25 18:15:00,119.12,65.642,52.985,29.93 +2020-09-25 18:30:00,119.12,64.514,52.985,29.93 +2020-09-25 18:45:00,114.05,68.764,52.985,29.93 +2020-09-25 19:00:00,110.75,69.617,52.602,29.93 +2020-09-25 19:15:00,109.43,69.267,52.602,29.93 +2020-09-25 19:30:00,107.76,68.505,52.602,29.93 +2020-09-25 19:45:00,104.87,67.777,52.602,29.93 +2020-09-25 20:00:00,100.97,65.862,58.063,29.93 +2020-09-25 20:15:00,102.74,64.84,58.063,29.93 +2020-09-25 20:30:00,100.5,62.946999999999996,58.063,29.93 +2020-09-25 20:45:00,93.97,61.794,58.063,29.93 +2020-09-25 21:00:00,88.38,61.198,50.135,29.93 +2020-09-25 21:15:00,87.81,63.786,50.135,29.93 +2020-09-25 21:30:00,85.24,62.715,50.135,29.93 +2020-09-25 21:45:00,86.18,61.906000000000006,50.135,29.93 +2020-09-25 22:00:00,83.96,60.652,45.165,29.93 +2020-09-25 22:15:00,79.17,59.376000000000005,45.165,29.93 +2020-09-25 22:30:00,75.1,55.809,45.165,29.93 +2020-09-25 22:45:00,77.09,53.768,45.165,29.93 +2020-09-25 23:00:00,79.16,51.29600000000001,39.121,29.93 +2020-09-25 23:15:00,75.29,48.898,39.121,29.93 +2020-09-25 23:30:00,70.25,47.217,39.121,29.93 +2020-09-25 23:45:00,67.44,46.32,39.121,29.93 +2020-09-26 00:00:00,64.11,40.303000000000004,38.49,29.816 +2020-09-26 00:15:00,64.22,39.549,38.49,29.816 +2020-09-26 00:30:00,71.12,39.624,38.49,29.816 +2020-09-26 00:45:00,71.01,39.498000000000005,38.49,29.816 +2020-09-26 01:00:00,69.95,39.577,34.5,29.816 +2020-09-26 01:15:00,63.99,39.008,34.5,29.816 +2020-09-26 01:30:00,69.4,37.955,34.5,29.816 +2020-09-26 01:45:00,69.93,37.782,34.5,29.816 +2020-09-26 02:00:00,68.96,38.286,32.236,29.816 +2020-09-26 02:15:00,64.21,37.6,32.236,29.816 +2020-09-26 02:30:00,68.74,37.809,32.236,29.816 +2020-09-26 02:45:00,67.86,38.655,32.236,29.816 +2020-09-26 03:00:00,62.1,40.036,32.067,29.816 +2020-09-26 03:15:00,61.48,38.824,32.067,29.816 +2020-09-26 03:30:00,62.24,38.464,32.067,29.816 +2020-09-26 03:45:00,62.77,40.629,32.067,29.816 +2020-09-26 04:00:00,63.44,46.69,33.071,29.816 +2020-09-26 04:15:00,63.08,52.544,33.071,29.816 +2020-09-26 04:30:00,63.34,49.988,33.071,29.816 +2020-09-26 04:45:00,65.2,50.463,33.071,29.816 +2020-09-26 05:00:00,67.69,62.15,33.014,29.816 +2020-09-26 05:15:00,69.25,68.483,33.014,29.816 +2020-09-26 05:30:00,71.19,63.869,33.014,29.816 +2020-09-26 05:45:00,70.98,61.817,33.014,29.816 +2020-09-26 06:00:00,72.82,74.937,34.628,29.816 +2020-09-26 06:15:00,73.93,87.507,34.628,29.816 +2020-09-26 06:30:00,75.0,82.045,34.628,29.816 +2020-09-26 06:45:00,77.15,78.709,34.628,29.816 +2020-09-26 07:00:00,78.72,76.085,38.871,29.816 +2020-09-26 07:15:00,78.7,77.581,38.871,29.816 +2020-09-26 07:30:00,79.43,77.208,38.871,29.816 +2020-09-26 07:45:00,81.14,79.021,38.871,29.816 +2020-09-26 08:00:00,82.04,77.229,43.293,29.816 +2020-09-26 08:15:00,81.29,79.325,43.293,29.816 +2020-09-26 08:30:00,79.31,78.339,43.293,29.816 +2020-09-26 08:45:00,78.64,79.563,43.293,29.816 +2020-09-26 09:00:00,77.73,75.152,44.559,29.816 +2020-09-26 09:15:00,77.3,75.286,44.559,29.816 +2020-09-26 09:30:00,76.01,76.116,44.559,29.816 +2020-09-26 09:45:00,76.69,76.161,44.559,29.816 +2020-09-26 10:00:00,77.09,73.52,42.091,29.816 +2020-09-26 10:15:00,78.02,73.59899999999999,42.091,29.816 +2020-09-26 10:30:00,79.27,73.126,42.091,29.816 +2020-09-26 10:45:00,75.73,73.221,42.091,29.816 +2020-09-26 11:00:00,74.16,71.61399999999999,38.505,29.816 +2020-09-26 11:15:00,73.22,71.741,38.505,29.816 +2020-09-26 11:30:00,74.17,72.20100000000001,38.505,29.816 +2020-09-26 11:45:00,72.12,71.267,38.505,29.816 +2020-09-26 12:00:00,70.46,69.072,35.388000000000005,29.816 +2020-09-26 12:15:00,67.44,68.39,35.388000000000005,29.816 +2020-09-26 12:30:00,71.03,67.819,35.388000000000005,29.816 +2020-09-26 12:45:00,69.48,68.183,35.388000000000005,29.816 +2020-09-26 13:00:00,63.24,67.752,31.355999999999998,29.816 +2020-09-26 13:15:00,64.45,67.275,31.355999999999998,29.816 +2020-09-26 13:30:00,64.55,66.75,31.355999999999998,29.816 +2020-09-26 13:45:00,63.96,65.571,31.355999999999998,29.816 +2020-09-26 14:00:00,64.09,65.168,30.522,29.816 +2020-09-26 14:15:00,67.34,64.189,30.522,29.816 +2020-09-26 14:30:00,65.35,63.668,30.522,29.816 +2020-09-26 14:45:00,66.06,63.978,30.522,29.816 +2020-09-26 15:00:00,67.79,64.027,34.36,29.816 +2020-09-26 15:15:00,66.51,63.351000000000006,34.36,29.816 +2020-09-26 15:30:00,68.16,62.885,34.36,29.816 +2020-09-26 15:45:00,72.59,61.718999999999994,34.36,29.816 +2020-09-26 16:00:00,75.45,62.938,39.507,29.816 +2020-09-26 16:15:00,77.55,62.669,39.507,29.816 +2020-09-26 16:30:00,79.63,63.308,39.507,29.816 +2020-09-26 16:45:00,83.05,61.232,39.507,29.816 +2020-09-26 17:00:00,85.81,62.456,47.151,29.816 +2020-09-26 17:15:00,86.82,62.61,47.151,29.816 +2020-09-26 17:30:00,88.1,62.18899999999999,47.151,29.816 +2020-09-26 17:45:00,90.04,62.478,47.151,29.816 +2020-09-26 18:00:00,95.33,63.623000000000005,50.303999999999995,29.816 +2020-09-26 18:15:00,94.65,64.312,50.303999999999995,29.816 +2020-09-26 18:30:00,97.77,64.545,50.303999999999995,29.816 +2020-09-26 18:45:00,95.72,65.35300000000001,50.303999999999995,29.816 +2020-09-26 19:00:00,90.07,65.441,50.622,29.816 +2020-09-26 19:15:00,86.83,64.219,50.622,29.816 +2020-09-26 19:30:00,85.33,64.218,50.622,29.816 +2020-09-26 19:45:00,84.12,64.718,50.622,29.816 +2020-09-26 20:00:00,79.3,63.951,45.391000000000005,29.816 +2020-09-26 20:15:00,80.25,63.068000000000005,45.391000000000005,29.816 +2020-09-26 20:30:00,78.57,60.422,45.391000000000005,29.816 +2020-09-26 20:45:00,77.53,60.446000000000005,45.391000000000005,29.816 +2020-09-26 21:00:00,73.77,59.438,39.98,29.816 +2020-09-26 21:15:00,75.16,61.888000000000005,39.98,29.816 +2020-09-26 21:30:00,69.54,61.303999999999995,39.98,29.816 +2020-09-26 21:45:00,69.49,59.95399999999999,39.98,29.816 +2020-09-26 22:00:00,66.64,59.036,37.53,29.816 +2020-09-26 22:15:00,67.12,58.604,37.53,29.816 +2020-09-26 22:30:00,63.4,56.483000000000004,37.53,29.816 +2020-09-26 22:45:00,63.85,55.235,37.53,29.816 +2020-09-26 23:00:00,58.44,52.963,30.97,29.816 +2020-09-26 23:15:00,57.4,50.388000000000005,30.97,29.816 +2020-09-26 23:30:00,57.88,50.007,30.97,29.816 +2020-09-26 23:45:00,57.35,48.701,30.97,29.816 +2020-09-27 00:00:00,58.71,41.699,27.24,29.816 +2020-09-27 00:15:00,55.74,40.016999999999996,27.24,29.816 +2020-09-27 00:30:00,54.56,39.891,27.24,29.816 +2020-09-27 00:45:00,54.16,39.876999999999995,27.24,29.816 +2020-09-27 01:00:00,52.05,40.103,25.662,29.816 +2020-09-27 01:15:00,53.71,39.753,25.662,29.816 +2020-09-27 01:30:00,52.61,38.762,25.662,29.816 +2020-09-27 01:45:00,52.16,38.228,25.662,29.816 +2020-09-27 02:00:00,52.19,38.591,25.67,29.816 +2020-09-27 02:15:00,52.28,38.199,25.67,29.816 +2020-09-27 02:30:00,52.04,38.759,25.67,29.816 +2020-09-27 02:45:00,51.99,39.516,25.67,29.816 +2020-09-27 03:00:00,52.66,41.443000000000005,24.258000000000003,29.816 +2020-09-27 03:15:00,52.96,40.293,24.258000000000003,29.816 +2020-09-27 03:30:00,54.26,39.794000000000004,24.258000000000003,29.816 +2020-09-27 03:45:00,54.89,41.345,24.258000000000003,29.816 +2020-09-27 04:00:00,55.49,47.354,25.051,29.816 +2020-09-27 04:15:00,55.87,52.648999999999994,25.051,29.816 +2020-09-27 04:30:00,55.54,51.083999999999996,25.051,29.816 +2020-09-27 04:45:00,56.78,51.303999999999995,25.051,29.816 +2020-09-27 05:00:00,58.11,62.303999999999995,25.145,29.816 +2020-09-27 05:15:00,58.1,67.563,25.145,29.816 +2020-09-27 05:30:00,58.65,62.586000000000006,25.145,29.816 +2020-09-27 05:45:00,58.18,60.34,25.145,29.816 +2020-09-27 06:00:00,59.68,71.635,26.371,29.816 +2020-09-27 06:15:00,60.43,84.40100000000001,26.371,29.816 +2020-09-27 06:30:00,61.96,78.143,26.371,29.816 +2020-09-27 06:45:00,63.71,73.797,26.371,29.816 +2020-09-27 07:00:00,64.31,71.99600000000001,28.756999999999998,29.816 +2020-09-27 07:15:00,65.55,72.084,28.756999999999998,29.816 +2020-09-27 07:30:00,66.0,72.34,28.756999999999998,29.816 +2020-09-27 07:45:00,66.81,73.921,28.756999999999998,29.816 +2020-09-27 08:00:00,66.27,73.15899999999999,32.82,29.816 +2020-09-27 08:15:00,65.26,76.039,32.82,29.816 +2020-09-27 08:30:00,64.16,76.156,32.82,29.816 +2020-09-27 08:45:00,63.97,77.865,32.82,29.816 +2020-09-27 09:00:00,62.41,73.256,35.534,29.816 +2020-09-27 09:15:00,62.07,73.188,35.534,29.816 +2020-09-27 09:30:00,61.44,74.26,35.534,29.816 +2020-09-27 09:45:00,62.57,74.954,35.534,29.816 +2020-09-27 10:00:00,63.81,73.396,35.925,29.816 +2020-09-27 10:15:00,64.87,73.69,35.925,29.816 +2020-09-27 10:30:00,65.54,73.535,35.925,29.816 +2020-09-27 10:45:00,66.79,73.756,35.925,29.816 +2020-09-27 11:00:00,64.87,72.186,37.056,29.816 +2020-09-27 11:15:00,63.25,72.028,37.056,29.816 +2020-09-27 11:30:00,60.2,72.585,37.056,29.816 +2020-09-27 11:45:00,59.31,71.992,37.056,29.816 +2020-09-27 12:00:00,54.52,70.286,33.124,29.816 +2020-09-27 12:15:00,56.11,69.765,33.124,29.816 +2020-09-27 12:30:00,55.91,68.917,33.124,29.816 +2020-09-27 12:45:00,58.63,68.538,33.124,29.816 +2020-09-27 13:00:00,58.5,67.667,29.874000000000002,29.816 +2020-09-27 13:15:00,55.01,67.723,29.874000000000002,29.816 +2020-09-27 13:30:00,55.43,66.368,29.874000000000002,29.816 +2020-09-27 13:45:00,55.98,65.756,29.874000000000002,29.816 +2020-09-27 14:00:00,57.32,66.19800000000001,27.302,29.816 +2020-09-27 14:15:00,57.14,65.899,27.302,29.816 +2020-09-27 14:30:00,57.98,64.95,27.302,29.816 +2020-09-27 14:45:00,60.21,64.396,27.302,29.816 +2020-09-27 15:00:00,61.57,64.02600000000001,27.642,29.816 +2020-09-27 15:15:00,62.18,63.051,27.642,29.816 +2020-09-27 15:30:00,64.05,62.675,27.642,29.816 +2020-09-27 15:45:00,67.12,61.934,27.642,29.816 +2020-09-27 16:00:00,70.42,62.468,31.945999999999998,29.816 +2020-09-27 16:15:00,73.68,62.107,31.945999999999998,29.816 +2020-09-27 16:30:00,76.58,63.519,31.945999999999998,29.816 +2020-09-27 16:45:00,79.25,61.583999999999996,31.945999999999998,29.816 +2020-09-27 17:00:00,82.98,63.023,40.387,29.816 +2020-09-27 17:15:00,82.75,64.161,40.387,29.816 +2020-09-27 17:30:00,87.05,64.39399999999999,40.387,29.816 +2020-09-27 17:45:00,86.61,65.675,40.387,29.816 +2020-09-27 18:00:00,90.18,67.0,44.575,29.816 +2020-09-27 18:15:00,89.39,67.767,44.575,29.816 +2020-09-27 18:30:00,89.29,67.25399999999999,44.575,29.816 +2020-09-27 18:45:00,87.47,68.656,44.575,29.816 +2020-09-27 19:00:00,91.61,70.178,45.623999999999995,29.816 +2020-09-27 19:15:00,93.17,68.29899999999999,45.623999999999995,29.816 +2020-09-27 19:30:00,92.79,68.07,45.623999999999995,29.816 +2020-09-27 19:45:00,86.34,68.607,45.623999999999995,29.816 +2020-09-27 20:00:00,78.55,68.045,44.583999999999996,29.816 +2020-09-27 20:15:00,81.4,67.33,44.583999999999996,29.816 +2020-09-27 20:30:00,83.71,65.579,44.583999999999996,29.816 +2020-09-27 20:45:00,80.58,64.027,44.583999999999996,29.816 +2020-09-27 21:00:00,84.36,62.199,39.732,29.816 +2020-09-27 21:15:00,89.77,64.25,39.732,29.816 +2020-09-27 21:30:00,86.37,63.286,39.732,29.816 +2020-09-27 21:45:00,80.3,62.198,39.732,29.816 +2020-09-27 22:00:00,75.34,62.406000000000006,38.571,29.816 +2020-09-27 22:15:00,77.9,60.526,38.571,29.816 +2020-09-27 22:30:00,78.22,57.183,38.571,29.816 +2020-09-27 22:45:00,80.64,54.788999999999994,38.571,29.816 +2020-09-27 23:00:00,76.15,51.59,33.121,29.816 +2020-09-27 23:15:00,70.67,50.4,33.121,29.816 +2020-09-27 23:30:00,68.9,49.846000000000004,33.121,29.816 +2020-09-27 23:45:00,69.98,48.894,33.121,29.816 +2020-09-28 00:00:00,72.56,44.205,32.506,29.93 +2020-09-28 00:15:00,73.53,43.888999999999996,32.506,29.93 +2020-09-28 00:30:00,72.62,43.526,32.506,29.93 +2020-09-28 00:45:00,68.54,43.083999999999996,32.506,29.93 +2020-09-28 01:00:00,65.59,43.588,31.121,29.93 +2020-09-28 01:15:00,65.31,43.13,31.121,29.93 +2020-09-28 01:30:00,66.33,42.431999999999995,31.121,29.93 +2020-09-28 01:45:00,66.87,41.858999999999995,31.121,29.93 +2020-09-28 02:00:00,64.01,42.553999999999995,29.605999999999998,29.93 +2020-09-28 02:15:00,69.97,41.842,29.605999999999998,29.93 +2020-09-28 02:30:00,74.06,42.589,29.605999999999998,29.93 +2020-09-28 02:45:00,75.8,43.067,29.605999999999998,29.93 +2020-09-28 03:00:00,72.48,45.693999999999996,28.124000000000002,29.93 +2020-09-28 03:15:00,69.89,45.528999999999996,28.124000000000002,29.93 +2020-09-28 03:30:00,74.11,45.453,28.124000000000002,29.93 +2020-09-28 03:45:00,79.39,46.504,28.124000000000002,29.93 +2020-09-28 04:00:00,83.04,55.977,29.743000000000002,29.93 +2020-09-28 04:15:00,81.76,64.546,29.743000000000002,29.93 +2020-09-28 04:30:00,89.53,63.233000000000004,29.743000000000002,29.93 +2020-09-28 04:45:00,96.21,63.772,29.743000000000002,29.93 +2020-09-28 05:00:00,105.6,83.844,36.191,29.93 +2020-09-28 05:15:00,107.36,101.895,36.191,29.93 +2020-09-28 05:30:00,107.75,95.816,36.191,29.93 +2020-09-28 05:45:00,111.23,89.825,36.191,29.93 +2020-09-28 06:00:00,115.68,89.09299999999999,55.277,29.93 +2020-09-28 06:15:00,115.66,91.397,55.277,29.93 +2020-09-28 06:30:00,117.81,90.111,55.277,29.93 +2020-09-28 06:45:00,119.85,91.897,55.277,29.93 +2020-09-28 07:00:00,122.15,91.338,65.697,29.93 +2020-09-28 07:15:00,122.14,93.427,65.697,29.93 +2020-09-28 07:30:00,121.34,92.88600000000001,65.697,29.93 +2020-09-28 07:45:00,116.32,93.995,65.697,29.93 +2020-09-28 08:00:00,117.15,90.522,57.028,29.93 +2020-09-28 08:15:00,115.61,91.912,57.028,29.93 +2020-09-28 08:30:00,119.14,90.226,57.028,29.93 +2020-09-28 08:45:00,116.91,91.141,57.028,29.93 +2020-09-28 09:00:00,116.01,85.585,52.633,29.93 +2020-09-28 09:15:00,119.67,83.25399999999999,52.633,29.93 +2020-09-28 09:30:00,118.68,83.365,52.633,29.93 +2020-09-28 09:45:00,122.1,82.484,52.633,29.93 +2020-09-28 10:00:00,117.58,81.078,50.647,29.93 +2020-09-28 10:15:00,119.15,81.132,50.647,29.93 +2020-09-28 10:30:00,116.21,80.383,50.647,29.93 +2020-09-28 10:45:00,116.73,79.708,50.647,29.93 +2020-09-28 11:00:00,119.42,77.577,50.245,29.93 +2020-09-28 11:15:00,116.62,78.139,50.245,29.93 +2020-09-28 11:30:00,121.85,79.594,50.245,29.93 +2020-09-28 11:45:00,117.42,79.208,50.245,29.93 +2020-09-28 12:00:00,113.11,77.02,46.956,29.93 +2020-09-28 12:15:00,116.0,76.562,46.956,29.93 +2020-09-28 12:30:00,113.7,75.102,46.956,29.93 +2020-09-28 12:45:00,110.78,75.194,46.956,29.93 +2020-09-28 13:00:00,109.56,74.983,47.383,29.93 +2020-09-28 13:15:00,112.19,74.00399999999999,47.383,29.93 +2020-09-28 13:30:00,112.32,72.598,47.383,29.93 +2020-09-28 13:45:00,113.59,72.613,47.383,29.93 +2020-09-28 14:00:00,115.79,72.227,47.1,29.93 +2020-09-28 14:15:00,115.49,72.146,47.1,29.93 +2020-09-28 14:30:00,114.12,70.952,47.1,29.93 +2020-09-28 14:45:00,114.56,71.797,47.1,29.93 +2020-09-28 15:00:00,117.46,71.695,49.355,29.93 +2020-09-28 15:15:00,116.47,69.889,49.355,29.93 +2020-09-28 15:30:00,114.37,69.814,49.355,29.93 +2020-09-28 15:45:00,114.24,68.618,49.355,29.93 +2020-09-28 16:00:00,115.27,69.747,52.14,29.93 +2020-09-28 16:15:00,117.99,69.223,52.14,29.93 +2020-09-28 16:30:00,117.86,69.88,52.14,29.93 +2020-09-28 16:45:00,120.05,67.665,52.14,29.93 +2020-09-28 17:00:00,122.78,68.229,58.705,29.93 +2020-09-28 17:15:00,121.13,69.37100000000001,58.705,29.93 +2020-09-28 17:30:00,122.96,69.17699999999999,58.705,29.93 +2020-09-28 17:45:00,120.89,69.768,58.705,29.93 +2020-09-28 18:00:00,124.3,70.32,59.153,29.93 +2020-09-28 18:15:00,121.62,69.16,59.153,29.93 +2020-09-28 18:30:00,123.48,68.303,59.153,29.93 +2020-09-28 18:45:00,119.09,72.157,59.153,29.93 +2020-09-28 19:00:00,115.15,72.955,61.483000000000004,29.93 +2020-09-28 19:15:00,109.93,71.735,61.483000000000004,29.93 +2020-09-28 19:30:00,107.66,71.362,61.483000000000004,29.93 +2020-09-28 19:45:00,110.06,71.22399999999999,61.483000000000004,29.93 +2020-09-28 20:00:00,99.87,69.15899999999999,67.55,29.93 +2020-09-28 20:15:00,98.53,68.77,67.55,29.93 +2020-09-28 20:30:00,102.19,66.858,67.55,29.93 +2020-09-28 20:45:00,103.9,65.867,67.55,29.93 +2020-09-28 21:00:00,96.81,63.74,60.026,29.93 +2020-09-28 21:15:00,91.9,65.782,60.026,29.93 +2020-09-28 21:30:00,86.66,64.904,60.026,29.93 +2020-09-28 21:45:00,91.92,63.486999999999995,60.026,29.93 +2020-09-28 22:00:00,89.43,61.42,52.736999999999995,29.93 +2020-09-28 22:15:00,89.06,60.604,52.736999999999995,29.93 +2020-09-28 22:30:00,81.86,51.547,52.736999999999995,29.93 +2020-09-28 22:45:00,86.05,47.843999999999994,52.736999999999995,29.93 +2020-09-28 23:00:00,82.52,44.95,44.408,29.93 +2020-09-28 23:15:00,80.8,43.236000000000004,44.408,29.93 +2020-09-28 23:30:00,76.73,43.395,44.408,29.93 +2020-09-28 23:45:00,79.66,42.756,44.408,29.93 +2020-09-29 00:00:00,77.73,42.65,44.438,29.93 +2020-09-29 00:15:00,76.61,43.342,44.438,29.93 +2020-09-29 00:30:00,76.57,43.253,44.438,29.93 +2020-09-29 00:45:00,78.99,43.126999999999995,44.438,29.93 +2020-09-29 01:00:00,77.92,43.176,41.468999999999994,29.93 +2020-09-29 01:15:00,78.18,42.699,41.468999999999994,29.93 +2020-09-29 01:30:00,74.21,41.974,41.468999999999994,29.93 +2020-09-29 01:45:00,77.34,41.108999999999995,41.468999999999994,29.93 +2020-09-29 02:00:00,78.32,41.458,39.708,29.93 +2020-09-29 02:15:00,77.97,41.577,39.708,29.93 +2020-09-29 02:30:00,78.04,41.858000000000004,39.708,29.93 +2020-09-29 02:45:00,80.85,42.573,39.708,29.93 +2020-09-29 03:00:00,79.93,44.456,38.919000000000004,29.93 +2020-09-29 03:15:00,76.51,44.75899999999999,38.919000000000004,29.93 +2020-09-29 03:30:00,79.49,44.794,38.919000000000004,29.93 +2020-09-29 03:45:00,80.02,45.07,38.919000000000004,29.93 +2020-09-29 04:00:00,89.45,53.608999999999995,40.092,29.93 +2020-09-29 04:15:00,93.05,62.126999999999995,40.092,29.93 +2020-09-29 04:30:00,95.48,60.648,40.092,29.93 +2020-09-29 04:45:00,95.39,61.878,40.092,29.93 +2020-09-29 05:00:00,103.88,84.5,43.713,29.93 +2020-09-29 05:15:00,108.14,103.072,43.713,29.93 +2020-09-29 05:30:00,110.41,96.729,43.713,29.93 +2020-09-29 05:45:00,114.39,90.139,43.713,29.93 +2020-09-29 06:00:00,118.77,89.865,56.033,29.93 +2020-09-29 06:15:00,120.88,92.749,56.033,29.93 +2020-09-29 06:30:00,121.79,91.06299999999999,56.033,29.93 +2020-09-29 06:45:00,123.75,92.088,56.033,29.93 +2020-09-29 07:00:00,126.35,91.59100000000001,66.003,29.93 +2020-09-29 07:15:00,126.55,93.463,66.003,29.93 +2020-09-29 07:30:00,125.28,92.823,66.003,29.93 +2020-09-29 07:45:00,123.7,93.275,66.003,29.93 +2020-09-29 08:00:00,120.71,89.791,57.474,29.93 +2020-09-29 08:15:00,117.0,90.537,57.474,29.93 +2020-09-29 08:30:00,116.25,88.941,57.474,29.93 +2020-09-29 08:45:00,114.5,89.095,57.474,29.93 +2020-09-29 09:00:00,115.58,83.553,51.928000000000004,29.93 +2020-09-29 09:15:00,120.06,81.567,51.928000000000004,29.93 +2020-09-29 09:30:00,116.6,82.34299999999999,51.928000000000004,29.93 +2020-09-29 09:45:00,112.53,82.32700000000001,51.928000000000004,29.93 +2020-09-29 10:00:00,117.82,79.771,49.46,29.93 +2020-09-29 10:15:00,117.6,79.37100000000001,49.46,29.93 +2020-09-29 10:30:00,115.38,78.68,49.46,29.93 +2020-09-29 10:45:00,115.03,78.775,49.46,29.93 +2020-09-29 11:00:00,112.32,77.102,48.206,29.93 +2020-09-29 11:15:00,114.51,77.828,48.206,29.93 +2020-09-29 11:30:00,115.85,78.133,48.206,29.93 +2020-09-29 11:45:00,122.55,77.62899999999999,48.206,29.93 +2020-09-29 12:00:00,121.21,74.861,46.285,29.93 +2020-09-29 12:15:00,123.65,74.48899999999999,46.285,29.93 +2020-09-29 12:30:00,119.98,73.876,46.285,29.93 +2020-09-29 12:45:00,119.13,74.34,46.285,29.93 +2020-09-29 13:00:00,117.91,73.741,46.861999999999995,29.93 +2020-09-29 13:15:00,116.67,73.89399999999999,46.861999999999995,29.93 +2020-09-29 13:30:00,117.65,72.825,46.861999999999995,29.93 +2020-09-29 13:45:00,118.25,72.237,46.861999999999995,29.93 +2020-09-29 14:00:00,115.97,72.195,46.488,29.93 +2020-09-29 14:15:00,115.47,72.04,46.488,29.93 +2020-09-29 14:30:00,114.39,71.3,46.488,29.93 +2020-09-29 14:45:00,114.29,71.57600000000001,46.488,29.93 +2020-09-29 15:00:00,110.86,71.184,48.442,29.93 +2020-09-29 15:15:00,112.31,70.13600000000001,48.442,29.93 +2020-09-29 15:30:00,111.91,70.016,48.442,29.93 +2020-09-29 15:45:00,114.05,68.95,48.442,29.93 +2020-09-29 16:00:00,116.1,69.681,50.397,29.93 +2020-09-29 16:15:00,115.08,69.375,50.397,29.93 +2020-09-29 16:30:00,117.86,69.956,50.397,29.93 +2020-09-29 16:45:00,119.27,68.388,50.397,29.93 +2020-09-29 17:00:00,122.25,69.221,56.668,29.93 +2020-09-29 17:15:00,118.99,70.688,56.668,29.93 +2020-09-29 17:30:00,119.0,70.389,56.668,29.93 +2020-09-29 17:45:00,121.21,70.738,56.668,29.93 +2020-09-29 18:00:00,124.72,70.545,57.957,29.93 +2020-09-29 18:15:00,120.93,70.315,57.957,29.93 +2020-09-29 18:30:00,122.34,69.196,57.957,29.93 +2020-09-29 18:45:00,118.19,73.126,57.957,29.93 +2020-09-29 19:00:00,111.46,73.086,57.056000000000004,29.93 +2020-09-29 19:15:00,107.02,71.922,57.056000000000004,29.93 +2020-09-29 19:30:00,105.82,71.21300000000001,57.056000000000004,29.93 +2020-09-29 19:45:00,104.05,71.321,57.056000000000004,29.93 +2020-09-29 20:00:00,99.51,69.616,64.156,29.93 +2020-09-29 20:15:00,95.64,68.005,64.156,29.93 +2020-09-29 20:30:00,91.07,66.372,64.156,29.93 +2020-09-29 20:45:00,88.43,65.49,64.156,29.93 +2020-09-29 21:00:00,82.9,63.803999999999995,56.507,29.93 +2020-09-29 21:15:00,78.99,65.01100000000001,56.507,29.93 +2020-09-29 21:30:00,76.78,64.074,56.507,29.93 +2020-09-29 21:45:00,75.2,62.826,56.507,29.93 +2020-09-29 22:00:00,71.1,61.318999999999996,50.728,29.93 +2020-09-29 22:15:00,70.7,60.165,50.728,29.93 +2020-09-29 22:30:00,67.54,51.342,50.728,29.93 +2020-09-29 22:45:00,65.87,47.714,50.728,29.93 +2020-09-29 23:00:00,77.01,44.299,43.556999999999995,29.93 +2020-09-29 23:15:00,80.01,43.45399999999999,43.556999999999995,29.93 +2020-09-29 23:30:00,80.68,43.519,43.556999999999995,29.93 +2020-09-29 23:45:00,80.11,42.897,43.556999999999995,29.93 +2020-09-30 00:00:00,75.35,42.93600000000001,41.151,29.93 +2020-09-30 00:15:00,78.11,43.623000000000005,41.151,29.93 +2020-09-30 00:30:00,79.8,43.541000000000004,41.151,29.93 +2020-09-30 00:45:00,81.56,43.416000000000004,41.151,29.93 +2020-09-30 01:00:00,76.14,43.457,37.763000000000005,29.93 +2020-09-30 01:15:00,75.09,43.004,37.763000000000005,29.93 +2020-09-30 01:30:00,75.45,42.298,37.763000000000005,29.93 +2020-09-30 01:45:00,80.02,41.433,37.763000000000005,29.93 +2020-09-30 02:00:00,79.63,41.788000000000004,35.615,29.93 +2020-09-30 02:15:00,78.9,41.925,35.615,29.93 +2020-09-30 02:30:00,76.86,42.18600000000001,35.615,29.93 +2020-09-30 02:45:00,78.72,42.895,35.615,29.93 +2020-09-30 03:00:00,81.85,44.765,35.153,29.93 +2020-09-30 03:15:00,81.74,45.086999999999996,35.153,29.93 +2020-09-30 03:30:00,82.99,45.126000000000005,35.153,29.93 +2020-09-30 03:45:00,83.55,45.383,35.153,29.93 +2020-09-30 04:00:00,88.62,53.961000000000006,36.203,29.93 +2020-09-30 04:15:00,91.38,62.516000000000005,36.203,29.93 +2020-09-30 04:30:00,94.94,61.042,36.203,29.93 +2020-09-30 04:45:00,97.42,62.28,36.203,29.93 +2020-09-30 05:00:00,103.8,85.005,39.922,29.93 +2020-09-30 05:15:00,107.95,103.689,39.922,29.93 +2020-09-30 05:30:00,110.12,97.321,39.922,29.93 +2020-09-30 05:45:00,113.38,90.678,39.922,29.93 +2020-09-30 06:00:00,119.1,90.37100000000001,56.443999999999996,29.93 +2020-09-30 06:15:00,117.38,93.279,56.443999999999996,29.93 +2020-09-30 06:30:00,119.12,91.594,56.443999999999996,29.93 +2020-09-30 06:45:00,120.5,92.615,56.443999999999996,29.93 +2020-09-30 07:00:00,123.29,92.118,68.683,29.93 +2020-09-30 07:15:00,121.03,94.00399999999999,68.683,29.93 +2020-09-30 07:30:00,122.13,93.398,68.683,29.93 +2020-09-30 07:45:00,120.93,93.84700000000001,68.683,29.93 +2020-09-30 08:00:00,117.66,90.37,59.003,29.93 +2020-09-30 08:15:00,117.3,91.08,59.003,29.93 +2020-09-30 08:30:00,116.66,89.5,59.003,29.93 +2020-09-30 08:45:00,116.56,89.633,59.003,29.93 +2020-09-30 09:00:00,115.38,84.096,56.21,29.93 +2020-09-30 09:15:00,115.67,82.101,56.21,29.93 +2020-09-30 09:30:00,115.08,82.85799999999999,56.21,29.93 +2020-09-30 09:45:00,114.96,82.81,56.21,29.93 +2020-09-30 10:00:00,115.1,80.25,52.358999999999995,29.93 +2020-09-30 10:15:00,116.5,79.81,52.358999999999995,29.93 +2020-09-30 10:30:00,113.95,79.10300000000001,52.358999999999995,29.93 +2020-09-30 10:45:00,112.23,79.183,52.358999999999995,29.93 +2020-09-30 11:00:00,108.77,77.523,51.161,29.93 +2020-09-30 11:15:00,108.69,78.23100000000001,51.161,29.93 +2020-09-30 11:30:00,109.39,78.536,51.161,29.93 +2020-09-30 11:45:00,108.48,78.013,51.161,29.93 +2020-09-30 12:00:00,104.28,75.219,49.119,29.93 +2020-09-30 12:15:00,106.05,74.836,49.119,29.93 +2020-09-30 12:30:00,104.62,74.258,49.119,29.93 +2020-09-30 12:45:00,103.69,74.71300000000001,49.119,29.93 +2020-09-30 13:00:00,102.95,74.086,49.187,29.93 +2020-09-30 13:15:00,102.97,74.237,49.187,29.93 +2020-09-30 13:30:00,103.28,73.165,49.187,29.93 +2020-09-30 13:45:00,103.85,72.581,49.187,29.93 +2020-09-30 14:00:00,104.84,72.49,49.787,29.93 +2020-09-30 14:15:00,104.58,72.35,49.787,29.93 +2020-09-30 14:30:00,105.13,71.645,49.787,29.93 +2020-09-30 14:45:00,105.67,71.917,49.787,29.93 +2020-09-30 15:00:00,105.44,71.477,51.458999999999996,29.93 +2020-09-30 15:15:00,106.06,70.44800000000001,51.458999999999996,29.93 +2020-09-30 15:30:00,106.29,70.362,51.458999999999996,29.93 +2020-09-30 15:45:00,107.58,69.31,51.458999999999996,29.93 +2020-09-30 16:00:00,110.43,70.0,53.663000000000004,29.93 +2020-09-30 16:15:00,109.91,69.711,53.663000000000004,29.93 +2020-09-30 16:30:00,111.8,70.28699999999999,53.663000000000004,29.93 +2020-09-30 16:45:00,113.95,68.777,53.663000000000004,29.93 +2020-09-30 17:00:00,117.7,69.568,58.183,29.93 +2020-09-30 17:15:00,116.19,71.06,58.183,29.93 +2020-09-30 17:30:00,119.57,70.767,58.183,29.93 +2020-09-30 17:45:00,120.66,71.14699999999999,58.183,29.93 +2020-09-30 18:00:00,122.31,70.935,60.141000000000005,29.93 +2020-09-30 18:15:00,120.45,70.704,60.141000000000005,29.93 +2020-09-30 18:30:00,121.29,69.597,60.141000000000005,29.93 +2020-09-30 18:45:00,119.4,73.52199999999999,60.141000000000005,29.93 +2020-09-30 19:00:00,116.88,73.491,60.582,29.93 +2020-09-30 19:15:00,115.1,72.325,60.582,29.93 +2020-09-30 19:30:00,111.94,71.613,60.582,29.93 +2020-09-30 19:45:00,110.07,71.71300000000001,60.582,29.93 +2020-09-30 20:00:00,104.26,70.03,66.61,29.93 +2020-09-30 20:15:00,105.15,68.417,66.61,29.93 +2020-09-30 20:30:00,101.51,66.757,66.61,29.93 +2020-09-30 20:45:00,102.47,65.836,66.61,29.93 +2020-09-30 21:00:00,96.11,64.15100000000001,57.658,29.93 +2020-09-30 21:15:00,94.9,65.348,57.658,29.93 +2020-09-30 21:30:00,93.22,64.42,57.658,29.93 +2020-09-30 21:45:00,94.05,63.138000000000005,57.658,29.93 +2020-09-30 22:00:00,89.15,61.61,51.81,29.93 +2020-09-30 22:15:00,86.92,60.433,51.81,29.93 +2020-09-30 22:30:00,83.39,51.606,51.81,29.93 +2020-09-30 22:45:00,81.96,47.983999999999995,51.81,29.93 +2020-09-30 23:00:00,72.81,44.608999999999995,42.93600000000001,29.93 +2020-09-30 23:15:00,74.96,43.731,42.93600000000001,29.93 +2020-09-30 23:30:00,74.25,43.799,42.93600000000001,29.93 +2020-09-30 23:45:00,77.92,43.177,42.93600000000001,29.93 +2020-10-01 00:00:00,74.36,48.013000000000005,42.746,31.349 +2020-10-01 00:15:00,73.35,49.218999999999994,42.746,31.349 +2020-10-01 00:30:00,71.89,48.891000000000005,42.746,31.349 +2020-10-01 00:45:00,74.82,48.773,42.746,31.349 +2020-10-01 01:00:00,74.95,49.074,40.025999999999996,31.349 +2020-10-01 01:15:00,73.4,48.443000000000005,40.025999999999996,31.349 +2020-10-01 01:30:00,70.75,47.641000000000005,40.025999999999996,31.349 +2020-10-01 01:45:00,74.21,47.16,40.025999999999996,31.349 +2020-10-01 02:00:00,74.54,47.581,38.154,31.349 +2020-10-01 02:15:00,75.11,47.598,38.154,31.349 +2020-10-01 02:30:00,70.83,48.211000000000006,38.154,31.349 +2020-10-01 02:45:00,74.98,48.898999999999994,38.154,31.349 +2020-10-01 03:00:00,76.99,51.108999999999995,37.575,31.349 +2020-10-01 03:15:00,78.83,51.843,37.575,31.349 +2020-10-01 03:30:00,75.95,51.895,37.575,31.349 +2020-10-01 03:45:00,81.07,52.646,37.575,31.349 +2020-10-01 04:00:00,86.24,61.338,39.154,31.349 +2020-10-01 04:15:00,88.04,69.964,39.154,31.349 +2020-10-01 04:30:00,85.63,69.25,39.154,31.349 +2020-10-01 04:45:00,92.33,70.865,39.154,31.349 +2020-10-01 05:00:00,98.87,96.057,44.085,31.349 +2020-10-01 05:15:00,108.96,118.06,44.085,31.349 +2020-10-01 05:30:00,116.11,112.14200000000001,44.085,31.349 +2020-10-01 05:45:00,112.83,104.488,44.085,31.349 +2020-10-01 06:00:00,118.1,104.522,57.49,31.349 +2020-10-01 06:15:00,117.62,108.09299999999999,57.49,31.349 +2020-10-01 06:30:00,119.15,106.42200000000001,57.49,31.349 +2020-10-01 06:45:00,121.05,107.11200000000001,57.49,31.349 +2020-10-01 07:00:00,122.34,107.375,73.617,31.349 +2020-10-01 07:15:00,120.54,109.579,73.617,31.349 +2020-10-01 07:30:00,118.69,109.189,73.617,31.349 +2020-10-01 07:45:00,117.8,109.36,73.617,31.349 +2020-10-01 08:00:00,115.95,107.73899999999999,69.281,31.349 +2020-10-01 08:15:00,115.95,108.02799999999999,69.281,31.349 +2020-10-01 08:30:00,122.25,105.654,69.281,31.349 +2020-10-01 08:45:00,124.29,104.889,69.281,31.349 +2020-10-01 09:00:00,122.13,100.23700000000001,63.926,31.349 +2020-10-01 09:15:00,121.05,98.084,63.926,31.349 +2020-10-01 09:30:00,116.55,98.544,63.926,31.349 +2020-10-01 09:45:00,115.32,98.477,63.926,31.349 +2020-10-01 10:00:00,116.93,95.274,59.442,31.349 +2020-10-01 10:15:00,119.48,94.848,59.442,31.349 +2020-10-01 10:30:00,116.82,93.89399999999999,59.442,31.349 +2020-10-01 10:45:00,115.21,93.82700000000001,59.442,31.349 +2020-10-01 11:00:00,110.64,90.531,56.771,31.349 +2020-10-01 11:15:00,110.57,91.235,56.771,31.349 +2020-10-01 11:30:00,114.18,91.44200000000001,56.771,31.349 +2020-10-01 11:45:00,110.72,91.22200000000001,56.771,31.349 +2020-10-01 12:00:00,110.27,87.663,53.701,31.349 +2020-10-01 12:15:00,110.53,87.29899999999999,53.701,31.349 +2020-10-01 12:30:00,110.68,86.45700000000001,53.701,31.349 +2020-10-01 12:45:00,110.33,86.771,53.701,31.349 +2020-10-01 13:00:00,109.27,86.516,52.364,31.349 +2020-10-01 13:15:00,108.61,86.375,52.364,31.349 +2020-10-01 13:30:00,106.82,85.52,52.364,31.349 +2020-10-01 13:45:00,108.3,85.07799999999999,52.364,31.349 +2020-10-01 14:00:00,106.34,84.788,53.419,31.349 +2020-10-01 14:15:00,103.21,85.045,53.419,31.349 +2020-10-01 14:30:00,104.24,84.14200000000001,53.419,31.349 +2020-10-01 14:45:00,101.78,84.021,53.419,31.349 +2020-10-01 15:00:00,102.18,84.177,56.744,31.349 +2020-10-01 15:15:00,105.7,83.725,56.744,31.349 +2020-10-01 15:30:00,109.3,83.95100000000001,56.744,31.349 +2020-10-01 15:45:00,112.38,83.89299999999999,56.744,31.349 +2020-10-01 16:00:00,110.92,83.941,60.458,31.349 +2020-10-01 16:15:00,110.81,83.338,60.458,31.349 +2020-10-01 16:30:00,115.07,84.031,60.458,31.349 +2020-10-01 16:45:00,114.72,82.064,60.458,31.349 +2020-10-01 17:00:00,119.03,82.493,66.295,31.349 +2020-10-01 17:15:00,116.61,83.67299999999999,66.295,31.349 +2020-10-01 17:30:00,118.16,83.4,66.295,31.349 +2020-10-01 17:45:00,121.44,84.021,66.295,31.349 +2020-10-01 18:00:00,125.72,83.735,68.468,31.349 +2020-10-01 18:15:00,122.88,83.59700000000001,68.468,31.349 +2020-10-01 18:30:00,124.48,82.62799999999999,68.468,31.349 +2020-10-01 18:45:00,119.47,86.285,68.468,31.349 +2020-10-01 19:00:00,116.32,86.665,66.39399999999999,31.349 +2020-10-01 19:15:00,113.44,85.539,66.39399999999999,31.349 +2020-10-01 19:30:00,108.44,85.01100000000001,66.39399999999999,31.349 +2020-10-01 19:45:00,109.78,85.054,66.39399999999999,31.349 +2020-10-01 20:00:00,106.35,84.295,63.183,31.349 +2020-10-01 20:15:00,108.46,82.07799999999999,63.183,31.349 +2020-10-01 20:30:00,106.1,81.286,63.183,31.349 +2020-10-01 20:45:00,101.39,80.277,63.183,31.349 +2020-10-01 21:00:00,93.83,78.27,55.133,31.349 +2020-10-01 21:15:00,92.88,78.835,55.133,31.349 +2020-10-01 21:30:00,91.65,77.945,55.133,31.349 +2020-10-01 21:45:00,92.82,76.352,55.133,31.349 +2020-10-01 22:00:00,85.0,73.477,50.111999999999995,31.349 +2020-10-01 22:15:00,85.89,71.318,50.111999999999995,31.349 +2020-10-01 22:30:00,82.6,60.872,50.111999999999995,31.349 +2020-10-01 22:45:00,86.0,55.68899999999999,50.111999999999995,31.349 +2020-10-01 23:00:00,78.43,50.915,44.536,31.349 +2020-10-01 23:15:00,77.59,50.651,44.536,31.349 +2020-10-01 23:30:00,77.83,50.118,44.536,31.349 +2020-10-01 23:45:00,79.72,50.093999999999994,44.536,31.349 +2020-10-02 00:00:00,74.84,46.853,42.291000000000004,31.349 +2020-10-02 00:15:00,76.22,48.257,42.291000000000004,31.349 +2020-10-02 00:30:00,70.57,48.04,42.291000000000004,31.349 +2020-10-02 00:45:00,72.88,48.211000000000006,42.291000000000004,31.349 +2020-10-02 01:00:00,69.56,48.17,41.008,31.349 +2020-10-02 01:15:00,70.58,47.573,41.008,31.349 +2020-10-02 01:30:00,77.02,47.115,41.008,31.349 +2020-10-02 01:45:00,78.7,46.528999999999996,41.008,31.349 +2020-10-02 02:00:00,78.05,47.533,39.521,31.349 +2020-10-02 02:15:00,73.35,47.49,39.521,31.349 +2020-10-02 02:30:00,78.63,48.773,39.521,31.349 +2020-10-02 02:45:00,79.6,49.075,39.521,31.349 +2020-10-02 03:00:00,74.8,51.332,39.812,31.349 +2020-10-02 03:15:00,75.56,51.751000000000005,39.812,31.349 +2020-10-02 03:30:00,79.48,51.661,39.812,31.349 +2020-10-02 03:45:00,85.82,53.034,39.812,31.349 +2020-10-02 04:00:00,90.92,61.923,41.22,31.349 +2020-10-02 04:15:00,87.14,69.571,41.22,31.349 +2020-10-02 04:30:00,91.86,69.50399999999999,41.22,31.349 +2020-10-02 04:45:00,98.29,70.303,41.22,31.349 +2020-10-02 05:00:00,108.33,94.79799999999999,45.115,31.349 +2020-10-02 05:15:00,107.36,118.024,45.115,31.349 +2020-10-02 05:30:00,109.91,112.62,45.115,31.349 +2020-10-02 05:45:00,112.72,104.635,45.115,31.349 +2020-10-02 06:00:00,119.78,104.962,59.06100000000001,31.349 +2020-10-02 06:15:00,119.47,108.075,59.06100000000001,31.349 +2020-10-02 06:30:00,123.95,106.051,59.06100000000001,31.349 +2020-10-02 06:45:00,121.8,107.29899999999999,59.06100000000001,31.349 +2020-10-02 07:00:00,122.66,107.613,71.874,31.349 +2020-10-02 07:15:00,121.44,110.766,71.874,31.349 +2020-10-02 07:30:00,120.73,109.22200000000001,71.874,31.349 +2020-10-02 07:45:00,118.08,108.91799999999999,71.874,31.349 +2020-10-02 08:00:00,116.05,107.29700000000001,68.439,31.349 +2020-10-02 08:15:00,115.68,107.771,68.439,31.349 +2020-10-02 08:30:00,115.03,105.775,68.439,31.349 +2020-10-02 08:45:00,114.17,104.251,68.439,31.349 +2020-10-02 09:00:00,111.59,98.507,65.523,31.349 +2020-10-02 09:15:00,110.49,97.64299999999999,65.523,31.349 +2020-10-02 09:30:00,110.15,97.525,65.523,31.349 +2020-10-02 09:45:00,108.89,97.596,65.523,31.349 +2020-10-02 10:00:00,107.9,93.774,62.005,31.349 +2020-10-02 10:15:00,108.37,93.501,62.005,31.349 +2020-10-02 10:30:00,108.22,92.79,62.005,31.349 +2020-10-02 10:45:00,109.74,92.461,62.005,31.349 +2020-10-02 11:00:00,105.01,89.28200000000001,60.351000000000006,31.349 +2020-10-02 11:15:00,103.83,88.994,60.351000000000006,31.349 +2020-10-02 11:30:00,107.03,89.801,60.351000000000006,31.349 +2020-10-02 11:45:00,102.87,89.085,60.351000000000006,31.349 +2020-10-02 12:00:00,100.19,86.24,55.331,31.349 +2020-10-02 12:15:00,99.6,84.566,55.331,31.349 +2020-10-02 12:30:00,97.98,83.89200000000001,55.331,31.349 +2020-10-02 12:45:00,96.69,84.041,55.331,31.349 +2020-10-02 13:00:00,95.31,84.48899999999999,53.361999999999995,31.349 +2020-10-02 13:15:00,96.32,84.825,53.361999999999995,31.349 +2020-10-02 13:30:00,95.77,84.383,53.361999999999995,31.349 +2020-10-02 13:45:00,96.42,84.069,53.361999999999995,31.349 +2020-10-02 14:00:00,97.78,82.788,51.708,31.349 +2020-10-02 14:15:00,97.81,83.20100000000001,51.708,31.349 +2020-10-02 14:30:00,97.63,83.339,51.708,31.349 +2020-10-02 14:45:00,99.23,83.00299999999999,51.708,31.349 +2020-10-02 15:00:00,99.88,82.859,54.571000000000005,31.349 +2020-10-02 15:15:00,98.98,82.102,54.571000000000005,31.349 +2020-10-02 15:30:00,100.68,81.37100000000001,54.571000000000005,31.349 +2020-10-02 15:45:00,102.59,81.78399999999999,54.571000000000005,31.349 +2020-10-02 16:00:00,107.25,80.862,58.662,31.349 +2020-10-02 16:15:00,107.43,80.667,58.662,31.349 +2020-10-02 16:30:00,108.62,81.304,58.662,31.349 +2020-10-02 16:45:00,109.89,78.92399999999999,58.662,31.349 +2020-10-02 17:00:00,114.07,80.343,65.941,31.349 +2020-10-02 17:15:00,111.49,81.27,65.941,31.349 +2020-10-02 17:30:00,115.35,80.94800000000001,65.941,31.349 +2020-10-02 17:45:00,115.86,81.39699999999999,65.941,31.349 +2020-10-02 18:00:00,122.12,81.46300000000001,65.628,31.349 +2020-10-02 18:15:00,118.93,80.623,65.628,31.349 +2020-10-02 18:30:00,117.78,79.774,65.628,31.349 +2020-10-02 18:45:00,115.24,83.65299999999999,65.628,31.349 +2020-10-02 19:00:00,111.24,84.94,63.662,31.349 +2020-10-02 19:15:00,106.67,84.70299999999999,63.662,31.349 +2020-10-02 19:30:00,105.91,84.031,63.662,31.349 +2020-10-02 19:45:00,102.96,83.294,63.662,31.349 +2020-10-02 20:00:00,92.61,82.48899999999999,61.945,31.349 +2020-10-02 20:15:00,99.16,80.71600000000001,61.945,31.349 +2020-10-02 20:30:00,98.55,79.605,61.945,31.349 +2020-10-02 20:45:00,96.27,78.343,61.945,31.349 +2020-10-02 21:00:00,88.3,77.288,53.903,31.349 +2020-10-02 21:15:00,85.55,78.967,53.903,31.349 +2020-10-02 21:30:00,80.59,78.02,53.903,31.349 +2020-10-02 21:45:00,84.21,76.735,53.903,31.349 +2020-10-02 22:00:00,80.6,74.171,48.403999999999996,31.349 +2020-10-02 22:15:00,79.32,71.794,48.403999999999996,31.349 +2020-10-02 22:30:00,71.59,66.65899999999999,48.403999999999996,31.349 +2020-10-02 22:45:00,70.6,63.413000000000004,48.403999999999996,31.349 +2020-10-02 23:00:00,62.12,59.43899999999999,41.07,31.349 +2020-10-02 23:15:00,61.6,57.461999999999996,41.07,31.349 +2020-10-02 23:30:00,61.56,55.302,41.07,31.349 +2020-10-02 23:45:00,59.87,54.931000000000004,41.07,31.349 +2020-10-03 00:00:00,57.65,47.711000000000006,11.117,31.177 +2020-10-03 00:15:00,57.59,46.246,11.117,31.177 +2020-10-03 00:30:00,56.67,46.058,11.117,31.177 +2020-10-03 00:45:00,56.96,46.294,11.117,31.177 +2020-10-03 01:00:00,56.34,46.769,10.685,31.177 +2020-10-03 01:15:00,56.06,46.481,10.685,31.177 +2020-10-03 01:30:00,55.87,45.493,10.685,31.177 +2020-10-03 01:45:00,55.71,45.202,10.685,31.177 +2020-10-03 02:00:00,55.96,45.67,7.925,31.177 +2020-10-03 02:15:00,56.34,45.118,7.925,31.177 +2020-10-03 02:30:00,56.14,45.926,7.925,31.177 +2020-10-03 02:45:00,56.27,46.731,7.925,31.177 +2020-10-03 03:00:00,55.64,48.928000000000004,7.627999999999999,31.177 +2020-10-03 03:15:00,57.67,48.434,7.627999999999999,31.177 +2020-10-03 03:30:00,57.91,48.034,7.627999999999999,31.177 +2020-10-03 03:45:00,58.8,49.805,7.627999999999999,31.177 +2020-10-03 04:00:00,60.47,55.907,7.986000000000001,31.177 +2020-10-03 04:15:00,59.42,61.435,7.986000000000001,31.177 +2020-10-03 04:30:00,59.0,60.357,7.986000000000001,31.177 +2020-10-03 04:45:00,58.38,60.965,7.986000000000001,31.177 +2020-10-03 05:00:00,59.9,73.63,9.039,31.177 +2020-10-03 05:15:00,59.86,81.95299999999999,9.039,31.177 +2020-10-03 05:30:00,60.28,77.28,9.039,31.177 +2020-10-03 05:45:00,61.97,73.884,9.039,31.177 +2020-10-03 06:00:00,62.0,86.214,10.683,31.177 +2020-10-03 06:15:00,62.94,100.009,10.683,31.177 +2020-10-03 06:30:00,64.88,93.45,10.683,31.177 +2020-10-03 06:45:00,68.16,88.402,10.683,31.177 +2020-10-03 07:00:00,74.5,87.154,14.055,31.177 +2020-10-03 07:15:00,72.95,87.805,14.055,31.177 +2020-10-03 07:30:00,76.05,88.484,14.055,31.177 +2020-10-03 07:45:00,75.81,89.97200000000001,14.055,31.177 +2020-10-03 08:00:00,77.66,91.262,17.652,31.177 +2020-10-03 08:15:00,78.36,93.70299999999999,17.652,31.177 +2020-10-03 08:30:00,76.08,93.544,17.652,31.177 +2020-10-03 08:45:00,77.02,94.595,17.652,31.177 +2020-10-03 09:00:00,76.35,90.97,21.353,31.177 +2020-10-03 09:15:00,79.16,90.64,21.353,31.177 +2020-10-03 09:30:00,78.99,91.32799999999999,21.353,31.177 +2020-10-03 09:45:00,81.23,91.696,21.353,31.177 +2020-10-03 10:00:00,83.68,89.508,23.467,31.177 +2020-10-03 10:15:00,86.8,89.756,23.467,31.177 +2020-10-03 10:30:00,87.14,89.29700000000001,23.467,31.177 +2020-10-03 10:45:00,87.03,89.06,23.467,31.177 +2020-10-03 11:00:00,82.41,86.065,24.539,31.177 +2020-10-03 11:15:00,82.83,85.729,24.539,31.177 +2020-10-03 11:30:00,76.93,86.07799999999999,24.539,31.177 +2020-10-03 11:45:00,78.79,85.66799999999999,24.539,31.177 +2020-10-03 12:00:00,74.79,82.721,21.488000000000003,31.177 +2020-10-03 12:15:00,72.19,82.29799999999999,21.488000000000003,31.177 +2020-10-03 12:30:00,59.91,81.195,21.488000000000003,31.177 +2020-10-03 12:45:00,62.91,80.593,21.488000000000003,31.177 +2020-10-03 13:00:00,61.74,79.922,18.776,31.177 +2020-10-03 13:15:00,59.78,80.102,18.776,31.177 +2020-10-03 13:30:00,66.56,78.882,18.776,31.177 +2020-10-03 13:45:00,70.81,78.37899999999999,18.776,31.177 +2020-10-03 14:00:00,68.47,78.347,17.301,31.177 +2020-10-03 14:15:00,68.4,78.655,17.301,31.177 +2020-10-03 14:30:00,67.72,77.69800000000001,17.301,31.177 +2020-10-03 14:45:00,64.03,76.969,17.301,31.177 +2020-10-03 15:00:00,66.02,76.7,21.236,31.177 +2020-10-03 15:15:00,68.96,76.618,21.236,31.177 +2020-10-03 15:30:00,70.76,76.907,21.236,31.177 +2020-10-03 15:45:00,72.25,77.335,21.236,31.177 +2020-10-03 16:00:00,74.29,77.045,28.31,31.177 +2020-10-03 16:15:00,75.52,76.52199999999999,28.31,31.177 +2020-10-03 16:30:00,78.34,77.89699999999999,28.31,31.177 +2020-10-03 16:45:00,82.8,76.039,28.31,31.177 +2020-10-03 17:00:00,87.35,76.783,41.687,31.177 +2020-10-03 17:15:00,87.12,77.97,41.687,31.177 +2020-10-03 17:30:00,88.97,78.093,41.687,31.177 +2020-10-03 17:45:00,90.28,79.843,41.687,31.177 +2020-10-03 18:00:00,94.76,80.597,49.201,31.177 +2020-10-03 18:15:00,92.25,81.734,49.201,31.177 +2020-10-03 18:30:00,94.32,81.203,49.201,31.177 +2020-10-03 18:45:00,91.48,82.59100000000001,49.201,31.177 +2020-10-03 19:00:00,92.73,84.505,51.937,31.177 +2020-10-03 19:15:00,98.28,83.11,51.937,31.177 +2020-10-03 19:30:00,96.89,82.96,51.937,31.177 +2020-10-03 19:45:00,88.92,83.443,51.937,31.177 +2020-10-03 20:00:00,85.03,84.101,52.617,31.177 +2020-10-03 20:15:00,85.02,83.164,52.617,31.177 +2020-10-03 20:30:00,90.9,82.34700000000001,52.617,31.177 +2020-10-03 20:45:00,91.31,80.497,52.617,31.177 +2020-10-03 21:00:00,85.41,78.417,46.238,31.177 +2020-10-03 21:15:00,84.03,79.649,46.238,31.177 +2020-10-03 21:30:00,86.88,79.069,46.238,31.177 +2020-10-03 21:45:00,86.4,77.541,46.238,31.177 +2020-10-03 22:00:00,81.78,76.17,48.275,31.177 +2020-10-03 22:15:00,78.99,73.67,48.275,31.177 +2020-10-03 22:30:00,80.17,69.319,48.275,31.177 +2020-10-03 22:45:00,79.39,65.98899999999999,48.275,31.177 +2020-10-03 23:00:00,55.75,61.363,38.071999999999996,31.177 +2020-10-03 23:15:00,57.09,60.38399999999999,38.071999999999996,31.177 +2020-10-03 23:30:00,53.87,58.913999999999994,38.071999999999996,31.177 +2020-10-03 23:45:00,56.4,58.196000000000005,38.071999999999996,31.177 +2020-10-04 00:00:00,53.94,48.018,28.229,31.177 +2020-10-04 00:15:00,54.47,46.545,28.229,31.177 +2020-10-04 00:30:00,52.62,46.365,28.229,31.177 +2020-10-04 00:45:00,53.51,46.6,28.229,31.177 +2020-10-04 01:00:00,50.25,47.07899999999999,25.669,31.177 +2020-10-04 01:15:00,52.39,46.812,25.669,31.177 +2020-10-04 01:30:00,51.82,45.841,25.669,31.177 +2020-10-04 01:45:00,51.72,45.548,25.669,31.177 +2020-10-04 02:00:00,51.1,46.023999999999994,24.948,31.177 +2020-10-04 02:15:00,52.15,45.488,24.948,31.177 +2020-10-04 02:30:00,51.41,46.276,24.948,31.177 +2020-10-04 02:45:00,50.99,47.077,24.948,31.177 +2020-10-04 03:00:00,52.2,49.261,24.445,31.177 +2020-10-04 03:15:00,51.81,48.788000000000004,24.445,31.177 +2020-10-04 03:30:00,52.33,48.39,24.445,31.177 +2020-10-04 03:45:00,53.15,50.145,24.445,31.177 +2020-10-04 04:00:00,53.33,56.275,25.839000000000002,31.177 +2020-10-04 04:15:00,53.92,61.833,25.839000000000002,31.177 +2020-10-04 04:30:00,54.43,60.755,25.839000000000002,31.177 +2020-10-04 04:45:00,54.79,61.372,25.839000000000002,31.177 +2020-10-04 05:00:00,57.04,74.122,26.803,31.177 +2020-10-04 05:15:00,56.99,82.527,26.803,31.177 +2020-10-04 05:30:00,58.51,77.84100000000001,26.803,31.177 +2020-10-04 05:45:00,59.57,74.402,26.803,31.177 +2020-10-04 06:00:00,60.65,86.709,28.147,31.177 +2020-10-04 06:15:00,59.54,100.521,28.147,31.177 +2020-10-04 06:30:00,61.12,93.97399999999999,28.147,31.177 +2020-10-04 06:45:00,61.43,88.93,28.147,31.177 +2020-10-04 07:00:00,66.21,87.679,31.116,31.177 +2020-10-04 07:15:00,65.25,88.34700000000001,31.116,31.177 +2020-10-04 07:30:00,64.85,89.059,31.116,31.177 +2020-10-04 07:45:00,65.24,90.551,31.116,31.177 +2020-10-04 08:00:00,63.88,91.851,35.739000000000004,31.177 +2020-10-04 08:15:00,66.44,94.265,35.739000000000004,31.177 +2020-10-04 08:30:00,70.93,94.132,35.739000000000004,31.177 +2020-10-04 08:45:00,67.85,95.15899999999999,35.739000000000004,31.177 +2020-10-04 09:00:00,66.06,91.535,39.455999999999996,31.177 +2020-10-04 09:15:00,67.28,91.199,39.455999999999996,31.177 +2020-10-04 09:30:00,70.83,91.869,39.455999999999996,31.177 +2020-10-04 09:45:00,77.44,92.211,39.455999999999996,31.177 +2020-10-04 10:00:00,78.19,90.01700000000001,41.343999999999994,31.177 +2020-10-04 10:15:00,81.74,90.225,41.343999999999994,31.177 +2020-10-04 10:30:00,83.73,89.74799999999999,41.343999999999994,31.177 +2020-10-04 10:45:00,84.45,89.494,41.343999999999994,31.177 +2020-10-04 11:00:00,83.31,86.509,43.645,31.177 +2020-10-04 11:15:00,81.0,86.15299999999999,43.645,31.177 +2020-10-04 11:30:00,79.77,86.50200000000001,43.645,31.177 +2020-10-04 11:45:00,79.1,86.075,43.645,31.177 +2020-10-04 12:00:00,75.39,83.105,39.796,31.177 +2020-10-04 12:15:00,75.42,82.67200000000001,39.796,31.177 +2020-10-04 12:30:00,74.51,81.604,39.796,31.177 +2020-10-04 12:45:00,74.31,80.998,39.796,31.177 +2020-10-04 13:00:00,73.03,80.295,36.343,31.177 +2020-10-04 13:15:00,72.61,80.479,36.343,31.177 +2020-10-04 13:30:00,73.42,79.259,36.343,31.177 +2020-10-04 13:45:00,73.96,78.757,36.343,31.177 +2020-10-04 14:00:00,72.48,78.671,33.162,31.177 +2020-10-04 14:15:00,72.19,78.99600000000001,33.162,31.177 +2020-10-04 14:30:00,73.52,78.075,33.162,31.177 +2020-10-04 14:45:00,73.96,77.342,33.162,31.177 +2020-10-04 15:00:00,74.74,77.039,33.215,31.177 +2020-10-04 15:15:00,74.08,76.976,33.215,31.177 +2020-10-04 15:30:00,74.78,77.30199999999999,33.215,31.177 +2020-10-04 15:45:00,76.21,77.745,33.215,31.177 +2020-10-04 16:00:00,78.09,77.42,37.385999999999996,31.177 +2020-10-04 16:15:00,79.1,76.916,37.385999999999996,31.177 +2020-10-04 16:30:00,80.57,78.28699999999999,37.385999999999996,31.177 +2020-10-04 16:45:00,82.81,76.484,37.385999999999996,31.177 +2020-10-04 17:00:00,87.51,77.188,46.618,31.177 +2020-10-04 17:15:00,86.86,78.395,46.618,31.177 +2020-10-04 17:30:00,92.1,78.51899999999999,46.618,31.177 +2020-10-04 17:45:00,89.55,80.29,46.618,31.177 +2020-10-04 18:00:00,95.29,81.03,50.111000000000004,31.177 +2020-10-04 18:15:00,88.95,82.15100000000001,50.111000000000004,31.177 +2020-10-04 18:30:00,91.01,81.631,50.111000000000004,31.177 +2020-10-04 18:45:00,87.31,83.012,50.111000000000004,31.177 +2020-10-04 19:00:00,86.3,84.939,50.25,31.177 +2020-10-04 19:15:00,84.42,83.538,50.25,31.177 +2020-10-04 19:30:00,83.14,83.38,50.25,31.177 +2020-10-04 19:45:00,81.46,83.845,50.25,31.177 +2020-10-04 20:00:00,77.95,84.52600000000001,44.265,31.177 +2020-10-04 20:15:00,79.6,83.583,44.265,31.177 +2020-10-04 20:30:00,77.92,82.73899999999999,44.265,31.177 +2020-10-04 20:45:00,78.85,80.859,44.265,31.177 +2020-10-04 21:00:00,77.61,78.781,39.717,31.177 +2020-10-04 21:15:00,77.95,80.00399999999999,39.717,31.177 +2020-10-04 21:30:00,75.4,79.433,39.717,31.177 +2020-10-04 21:45:00,75.62,77.875,39.717,31.177 +2020-10-04 22:00:00,72.93,76.492,39.224000000000004,31.177 +2020-10-04 22:15:00,72.3,73.969,39.224000000000004,31.177 +2020-10-04 22:30:00,69.94,69.63,39.224000000000004,31.177 +2020-10-04 22:45:00,69.71,66.307,39.224000000000004,31.177 +2020-10-04 23:00:00,65.21,61.708999999999996,33.518,31.177 +2020-10-04 23:15:00,66.43,60.7,33.518,31.177 +2020-10-04 23:30:00,65.32,59.232,33.518,31.177 +2020-10-04 23:45:00,65.1,58.507,33.518,31.177 +2020-10-05 00:00:00,66.33,50.821999999999996,34.301,31.349 +2020-10-05 00:15:00,64.68,50.945,34.301,31.349 +2020-10-05 00:30:00,63.44,50.6,34.301,31.349 +2020-10-05 00:45:00,65.02,50.398,34.301,31.349 +2020-10-05 01:00:00,64.78,51.103,34.143,31.349 +2020-10-05 01:15:00,64.46,50.661,34.143,31.349 +2020-10-05 01:30:00,64.07,49.93899999999999,34.143,31.349 +2020-10-05 01:45:00,64.5,49.631,34.143,31.349 +2020-10-05 02:00:00,63.51,50.364,33.650999999999996,31.349 +2020-10-05 02:15:00,65.03,49.815,33.650999999999996,31.349 +2020-10-05 02:30:00,65.36,50.809,33.650999999999996,31.349 +2020-10-05 02:45:00,66.21,51.282,33.650999999999996,31.349 +2020-10-05 03:00:00,69.64,54.224,32.599000000000004,31.349 +2020-10-05 03:15:00,74.23,54.818999999999996,32.599000000000004,31.349 +2020-10-05 03:30:00,75.31,54.718,32.599000000000004,31.349 +2020-10-05 03:45:00,73.63,55.974,32.599000000000004,31.349 +2020-10-05 04:00:00,76.43,65.589,33.785,31.349 +2020-10-05 04:15:00,77.8,74.464,33.785,31.349 +2020-10-05 04:30:00,89.37,73.941,33.785,31.349 +2020-10-05 04:45:00,95.43,74.844,33.785,31.349 +2020-10-05 05:00:00,102.6,97.568,41.285,31.349 +2020-10-05 05:15:00,101.26,119.60700000000001,41.285,31.349 +2020-10-05 05:30:00,105.1,114.088,41.285,31.349 +2020-10-05 05:45:00,112.77,106.70100000000001,41.285,31.349 +2020-10-05 06:00:00,116.26,106.459,60.486000000000004,31.349 +2020-10-05 06:15:00,116.2,109.37,60.486000000000004,31.349 +2020-10-05 06:30:00,117.92,108.182,60.486000000000004,31.349 +2020-10-05 06:45:00,120.49,109.552,60.486000000000004,31.349 +2020-10-05 07:00:00,124.95,109.79,74.012,31.349 +2020-10-05 07:15:00,123.49,112.266,74.012,31.349 +2020-10-05 07:30:00,122.31,112.215,74.012,31.349 +2020-10-05 07:45:00,122.8,112.87799999999999,74.012,31.349 +2020-10-05 08:00:00,122.15,111.291,69.569,31.349 +2020-10-05 08:15:00,123.81,112.141,69.569,31.349 +2020-10-05 08:30:00,125.38,109.84100000000001,69.569,31.349 +2020-10-05 08:45:00,126.19,109.616,69.569,31.349 +2020-10-05 09:00:00,123.98,105.119,66.152,31.349 +2020-10-05 09:15:00,123.63,102.352,66.152,31.349 +2020-10-05 09:30:00,123.4,102.083,66.152,31.349 +2020-10-05 09:45:00,125.12,101.262,66.152,31.349 +2020-10-05 10:00:00,124.46,99.031,62.923,31.349 +2020-10-05 10:15:00,127.71,98.98,62.923,31.349 +2020-10-05 10:30:00,127.77,97.86399999999999,62.923,31.349 +2020-10-05 10:45:00,125.66,97.073,62.923,31.349 +2020-10-05 11:00:00,120.69,93.165,61.522,31.349 +2020-10-05 11:15:00,117.37,93.73,61.522,31.349 +2020-10-05 11:30:00,112.63,95.04799999999999,61.522,31.349 +2020-10-05 11:45:00,108.66,94.689,61.522,31.349 +2020-10-05 12:00:00,102.98,91.74600000000001,58.632,31.349 +2020-10-05 12:15:00,103.41,91.36,58.632,31.349 +2020-10-05 12:30:00,100.84,89.89299999999999,58.632,31.349 +2020-10-05 12:45:00,101.8,89.97,58.632,31.349 +2020-10-05 13:00:00,100.62,89.954,59.06,31.349 +2020-10-05 13:15:00,100.28,89.065,59.06,31.349 +2020-10-05 13:30:00,100.19,87.693,59.06,31.349 +2020-10-05 13:45:00,100.01,87.664,59.06,31.349 +2020-10-05 14:00:00,100.8,86.809,59.791000000000004,31.349 +2020-10-05 14:15:00,101.96,87.161,59.791000000000004,31.349 +2020-10-05 14:30:00,103.14,85.947,59.791000000000004,31.349 +2020-10-05 14:45:00,104.3,86.251,59.791000000000004,31.349 +2020-10-05 15:00:00,107.02,86.544,61.148,31.349 +2020-10-05 15:15:00,104.62,85.551,61.148,31.349 +2020-10-05 15:30:00,105.02,85.93,61.148,31.349 +2020-10-05 15:45:00,104.28,85.939,61.148,31.349 +2020-10-05 16:00:00,105.06,86.02600000000001,66.009,31.349 +2020-10-05 16:15:00,106.92,85.244,66.009,31.349 +2020-10-05 16:30:00,109.95,85.84,66.009,31.349 +2020-10-05 16:45:00,111.32,83.586,66.009,31.349 +2020-10-05 17:00:00,116.22,83.50299999999999,73.683,31.349 +2020-10-05 17:15:00,114.24,84.52600000000001,73.683,31.349 +2020-10-05 17:30:00,117.49,84.211,73.683,31.349 +2020-10-05 17:45:00,122.2,85.152,73.683,31.349 +2020-10-05 18:00:00,125.09,85.385,72.848,31.349 +2020-10-05 18:15:00,121.39,84.57,72.848,31.349 +2020-10-05 18:30:00,120.7,83.915,72.848,31.349 +2020-10-05 18:45:00,118.89,87.323,72.848,31.349 +2020-10-05 19:00:00,119.68,88.385,71.139,31.349 +2020-10-05 19:15:00,117.11,87.249,71.139,31.349 +2020-10-05 19:30:00,112.56,87.075,71.139,31.349 +2020-10-05 19:45:00,108.13,86.844,71.139,31.349 +2020-10-05 20:00:00,101.98,85.89200000000001,69.667,31.349 +2020-10-05 20:15:00,101.73,84.719,69.667,31.349 +2020-10-05 20:30:00,97.75,83.385,69.667,31.349 +2020-10-05 20:45:00,97.25,82.257,69.667,31.349 +2020-10-05 21:00:00,93.19,80.05,61.166000000000004,31.349 +2020-10-05 21:15:00,97.6,81.041,61.166000000000004,31.349 +2020-10-05 21:30:00,94.2,80.37899999999999,61.166000000000004,31.349 +2020-10-05 21:45:00,93.44,78.468,61.166000000000004,31.349 +2020-10-05 22:00:00,85.58,74.764,52.772,31.349 +2020-10-05 22:15:00,82.71,72.807,52.772,31.349 +2020-10-05 22:30:00,81.05,62.21,52.772,31.349 +2020-10-05 22:45:00,84.7,56.94,52.772,31.349 +2020-10-05 23:00:00,82.54,52.706,45.136,31.349 +2020-10-05 23:15:00,82.36,51.802,45.136,31.349 +2020-10-05 23:30:00,74.67,51.42,45.136,31.349 +2020-10-05 23:45:00,79.64,51.446999999999996,45.136,31.349 +2020-10-06 00:00:00,78.11,49.55,47.35,31.349 +2020-10-06 00:15:00,81.09,50.716,47.35,31.349 +2020-10-06 00:30:00,75.23,50.428000000000004,47.35,31.349 +2020-10-06 00:45:00,74.03,50.303999999999995,47.35,31.349 +2020-10-06 01:00:00,70.95,50.623999999999995,43.424,31.349 +2020-10-06 01:15:00,79.65,50.096000000000004,43.424,31.349 +2020-10-06 01:30:00,79.59,49.382,43.424,31.349 +2020-10-06 01:45:00,78.21,48.891999999999996,43.424,31.349 +2020-10-06 02:00:00,72.09,49.352,41.778999999999996,31.349 +2020-10-06 02:15:00,78.09,49.449,41.778999999999996,31.349 +2020-10-06 02:30:00,79.6,49.968999999999994,41.778999999999996,31.349 +2020-10-06 02:45:00,79.98,50.633,41.778999999999996,31.349 +2020-10-06 03:00:00,79.25,52.776,40.771,31.349 +2020-10-06 03:15:00,82.62,53.61,40.771,31.349 +2020-10-06 03:30:00,83.6,53.68,40.771,31.349 +2020-10-06 03:45:00,83.88,54.345,40.771,31.349 +2020-10-06 04:00:00,83.35,63.176,41.816,31.349 +2020-10-06 04:15:00,89.08,71.957,41.816,31.349 +2020-10-06 04:30:00,94.83,71.24600000000001,41.816,31.349 +2020-10-06 04:45:00,100.98,72.905,41.816,31.349 +2020-10-06 05:00:00,99.83,98.514,45.842,31.349 +2020-10-06 05:15:00,101.8,120.931,45.842,31.349 +2020-10-06 05:30:00,108.7,114.943,45.842,31.349 +2020-10-06 05:45:00,110.57,107.079,45.842,31.349 +2020-10-06 06:00:00,117.91,106.995,59.12,31.349 +2020-10-06 06:15:00,116.24,110.652,59.12,31.349 +2020-10-06 06:30:00,118.48,109.045,59.12,31.349 +2020-10-06 06:45:00,119.02,109.751,59.12,31.349 +2020-10-06 07:00:00,123.59,110.00200000000001,70.33,31.349 +2020-10-06 07:15:00,121.19,112.28299999999999,70.33,31.349 +2020-10-06 07:30:00,117.92,112.06200000000001,70.33,31.349 +2020-10-06 07:45:00,117.6,112.25200000000001,70.33,31.349 +2020-10-06 08:00:00,114.16,110.685,67.788,31.349 +2020-10-06 08:15:00,114.34,110.84,67.788,31.349 +2020-10-06 08:30:00,118.79,108.587,67.788,31.349 +2020-10-06 08:45:00,121.22,107.713,67.788,31.349 +2020-10-06 09:00:00,113.61,103.059,62.622,31.349 +2020-10-06 09:15:00,114.17,100.876,62.622,31.349 +2020-10-06 09:30:00,117.63,101.25,62.622,31.349 +2020-10-06 09:45:00,116.96,101.04799999999999,62.622,31.349 +2020-10-06 10:00:00,111.74,97.815,60.887,31.349 +2020-10-06 10:15:00,115.0,97.189,60.887,31.349 +2020-10-06 10:30:00,113.09,96.147,60.887,31.349 +2020-10-06 10:45:00,113.9,95.99799999999999,60.887,31.349 +2020-10-06 11:00:00,115.53,92.74600000000001,59.812,31.349 +2020-10-06 11:15:00,116.95,93.359,59.812,31.349 +2020-10-06 11:30:00,115.24,93.559,59.812,31.349 +2020-10-06 11:45:00,109.97,93.256,59.812,31.349 +2020-10-06 12:00:00,105.42,89.58,56.614,31.349 +2020-10-06 12:15:00,102.97,89.16799999999999,56.614,31.349 +2020-10-06 12:30:00,103.1,88.50299999999999,56.614,31.349 +2020-10-06 12:45:00,106.67,88.796,56.614,31.349 +2020-10-06 13:00:00,103.21,88.37899999999999,56.824,31.349 +2020-10-06 13:15:00,101.73,88.26,56.824,31.349 +2020-10-06 13:30:00,102.74,87.40100000000001,56.824,31.349 +2020-10-06 13:45:00,104.36,86.963,56.824,31.349 +2020-10-06 14:00:00,104.47,86.411,57.623999999999995,31.349 +2020-10-06 14:15:00,105.34,86.749,57.623999999999995,31.349 +2020-10-06 14:30:00,104.12,86.022,57.623999999999995,31.349 +2020-10-06 14:45:00,104.25,85.884,57.623999999999995,31.349 +2020-10-06 15:00:00,104.24,85.866,59.724,31.349 +2020-10-06 15:15:00,105.47,85.515,59.724,31.349 +2020-10-06 15:30:00,105.21,85.925,59.724,31.349 +2020-10-06 15:45:00,107.87,85.944,59.724,31.349 +2020-10-06 16:00:00,110.19,85.816,61.64,31.349 +2020-10-06 16:15:00,113.18,85.307,61.64,31.349 +2020-10-06 16:30:00,112.84,85.98299999999999,61.64,31.349 +2020-10-06 16:45:00,115.02,84.288,61.64,31.349 +2020-10-06 17:00:00,119.5,84.516,68.962,31.349 +2020-10-06 17:15:00,116.33,85.795,68.962,31.349 +2020-10-06 17:30:00,121.99,85.531,68.962,31.349 +2020-10-06 17:45:00,123.39,86.258,68.962,31.349 +2020-10-06 18:00:00,127.05,85.90100000000001,69.149,31.349 +2020-10-06 18:15:00,122.1,85.682,69.149,31.349 +2020-10-06 18:30:00,121.85,84.766,69.149,31.349 +2020-10-06 18:45:00,120.37,88.39,69.149,31.349 +2020-10-06 19:00:00,118.45,88.83200000000001,68.832,31.349 +2020-10-06 19:15:00,118.71,87.679,68.832,31.349 +2020-10-06 19:30:00,115.61,87.10600000000001,68.832,31.349 +2020-10-06 19:45:00,112.12,87.06,68.832,31.349 +2020-10-06 20:00:00,100.95,86.41799999999999,66.403,31.349 +2020-10-06 20:15:00,100.94,84.17299999999999,66.403,31.349 +2020-10-06 20:30:00,98.55,83.243,66.403,31.349 +2020-10-06 20:45:00,98.12,82.089,66.403,31.349 +2020-10-06 21:00:00,91.59,80.08800000000001,57.352,31.349 +2020-10-06 21:15:00,90.82,80.605,57.352,31.349 +2020-10-06 21:30:00,87.87,79.758,57.352,31.349 +2020-10-06 21:45:00,90.21,78.021,57.352,31.349 +2020-10-06 22:00:00,90.01,75.087,51.148999999999994,31.349 +2020-10-06 22:15:00,89.32,72.814,51.148999999999994,31.349 +2020-10-06 22:30:00,85.07,62.431999999999995,51.148999999999994,31.349 +2020-10-06 22:45:00,83.99,57.276,51.148999999999994,31.349 +2020-10-06 23:00:00,78.1,52.643,41.8,31.349 +2020-10-06 23:15:00,82.01,52.229,41.8,31.349 +2020-10-06 23:30:00,84.4,51.70399999999999,41.8,31.349 +2020-10-06 23:45:00,87.64,51.651,41.8,31.349 +2020-10-07 00:00:00,80.25,49.86,42.269,31.349 +2020-10-07 00:15:00,80.19,51.019,42.269,31.349 +2020-10-07 00:30:00,81.39,50.736999999999995,42.269,31.349 +2020-10-07 00:45:00,86.62,50.611999999999995,42.269,31.349 +2020-10-07 01:00:00,82.23,50.937,38.527,31.349 +2020-10-07 01:15:00,79.29,50.428999999999995,38.527,31.349 +2020-10-07 01:30:00,81.05,49.733000000000004,38.527,31.349 +2020-10-07 01:45:00,83.23,49.24100000000001,38.527,31.349 +2020-10-07 02:00:00,83.52,49.708,36.393,31.349 +2020-10-07 02:15:00,82.09,49.821000000000005,36.393,31.349 +2020-10-07 02:30:00,79.19,50.321999999999996,36.393,31.349 +2020-10-07 02:45:00,79.81,50.982,36.393,31.349 +2020-10-07 03:00:00,86.92,53.113,36.167,31.349 +2020-10-07 03:15:00,87.2,53.966,36.167,31.349 +2020-10-07 03:30:00,88.26,54.038999999999994,36.167,31.349 +2020-10-07 03:45:00,86.31,54.68600000000001,36.167,31.349 +2020-10-07 04:00:00,94.04,63.54600000000001,38.092,31.349 +2020-10-07 04:15:00,97.41,72.358,38.092,31.349 +2020-10-07 04:30:00,98.81,71.648,38.092,31.349 +2020-10-07 04:45:00,98.37,73.316,38.092,31.349 +2020-10-07 05:00:00,104.2,99.01,42.268,31.349 +2020-10-07 05:15:00,110.49,121.51100000000001,42.268,31.349 +2020-10-07 05:30:00,112.99,115.508,42.268,31.349 +2020-10-07 05:45:00,120.34,107.602,42.268,31.349 +2020-10-07 06:00:00,123.12,107.494,60.158,31.349 +2020-10-07 06:15:00,115.11,111.17,60.158,31.349 +2020-10-07 06:30:00,121.4,109.574,60.158,31.349 +2020-10-07 06:45:00,121.67,110.28299999999999,60.158,31.349 +2020-10-07 07:00:00,123.41,110.53299999999999,74.792,31.349 +2020-10-07 07:15:00,121.71,112.82700000000001,74.792,31.349 +2020-10-07 07:30:00,121.33,112.64,74.792,31.349 +2020-10-07 07:45:00,119.53,112.833,74.792,31.349 +2020-10-07 08:00:00,115.82,111.277,70.499,31.349 +2020-10-07 08:15:00,116.66,111.404,70.499,31.349 +2020-10-07 08:30:00,116.5,109.17399999999999,70.499,31.349 +2020-10-07 08:45:00,116.09,108.279,70.499,31.349 +2020-10-07 09:00:00,113.61,103.625,68.892,31.349 +2020-10-07 09:15:00,113.03,101.435,68.892,31.349 +2020-10-07 09:30:00,112.32,101.794,68.892,31.349 +2020-10-07 09:45:00,113.24,101.564,68.892,31.349 +2020-10-07 10:00:00,113.52,98.324,66.88600000000001,31.349 +2020-10-07 10:15:00,116.01,97.65799999999999,66.88600000000001,31.349 +2020-10-07 10:30:00,112.9,96.59899999999999,66.88600000000001,31.349 +2020-10-07 10:45:00,113.01,96.432,66.88600000000001,31.349 +2020-10-07 11:00:00,110.74,93.19,66.187,31.349 +2020-10-07 11:15:00,107.17,93.78399999999999,66.187,31.349 +2020-10-07 11:30:00,106.12,93.98299999999999,66.187,31.349 +2020-10-07 11:45:00,106.28,93.664,66.187,31.349 +2020-10-07 12:00:00,103.85,89.963,62.18,31.349 +2020-10-07 12:15:00,104.47,89.54299999999999,62.18,31.349 +2020-10-07 12:30:00,103.29,88.913,62.18,31.349 +2020-10-07 12:45:00,104.27,89.20200000000001,62.18,31.349 +2020-10-07 13:00:00,103.01,88.75399999999999,62.23,31.349 +2020-10-07 13:15:00,102.19,88.63799999999999,62.23,31.349 +2020-10-07 13:30:00,102.91,87.77799999999999,62.23,31.349 +2020-10-07 13:45:00,103.83,87.34,62.23,31.349 +2020-10-07 14:00:00,104.04,86.73700000000001,63.721000000000004,31.349 +2020-10-07 14:15:00,103.67,87.09,63.721000000000004,31.349 +2020-10-07 14:30:00,102.78,86.399,63.721000000000004,31.349 +2020-10-07 14:45:00,102.93,86.258,63.721000000000004,31.349 +2020-10-07 15:00:00,104.88,86.205,66.523,31.349 +2020-10-07 15:15:00,108.17,85.874,66.523,31.349 +2020-10-07 15:30:00,106.16,86.321,66.523,31.349 +2020-10-07 15:45:00,106.9,86.355,66.523,31.349 +2020-10-07 16:00:00,110.44,86.19200000000001,69.679,31.349 +2020-10-07 16:15:00,110.4,85.70200000000001,69.679,31.349 +2020-10-07 16:30:00,112.36,86.374,69.679,31.349 +2020-10-07 16:45:00,114.77,84.734,69.679,31.349 +2020-10-07 17:00:00,117.12,84.92200000000001,75.04,31.349 +2020-10-07 17:15:00,116.68,86.22,75.04,31.349 +2020-10-07 17:30:00,121.96,85.959,75.04,31.349 +2020-10-07 17:45:00,123.54,86.70700000000001,75.04,31.349 +2020-10-07 18:00:00,125.9,86.336,75.915,31.349 +2020-10-07 18:15:00,122.45,86.101,75.915,31.349 +2020-10-07 18:30:00,122.89,85.197,75.915,31.349 +2020-10-07 18:45:00,119.42,88.81299999999999,75.915,31.349 +2020-10-07 19:00:00,114.78,89.26799999999999,74.66,31.349 +2020-10-07 19:15:00,112.06,88.11,74.66,31.349 +2020-10-07 19:30:00,107.91,87.527,74.66,31.349 +2020-10-07 19:45:00,108.91,87.464,74.66,31.349 +2020-10-07 20:00:00,101.7,86.845,71.204,31.349 +2020-10-07 20:15:00,104.07,84.595,71.204,31.349 +2020-10-07 20:30:00,101.57,83.637,71.204,31.349 +2020-10-07 20:45:00,99.17,82.454,71.204,31.349 +2020-10-07 21:00:00,94.53,80.454,61.052,31.349 +2020-10-07 21:15:00,94.88,80.96,61.052,31.349 +2020-10-07 21:30:00,96.49,80.123,61.052,31.349 +2020-10-07 21:45:00,95.82,78.357,61.052,31.349 +2020-10-07 22:00:00,91.81,75.411,54.691,31.349 +2020-10-07 22:15:00,85.9,73.115,54.691,31.349 +2020-10-07 22:30:00,83.52,62.748000000000005,54.691,31.349 +2020-10-07 22:45:00,79.93,57.597,54.691,31.349 +2020-10-07 23:00:00,76.11,52.993,45.18,31.349 +2020-10-07 23:15:00,76.08,52.548,45.18,31.349 +2020-10-07 23:30:00,75.38,52.023999999999994,45.18,31.349 +2020-10-07 23:45:00,79.99,51.966,45.18,31.349 +2020-10-08 00:00:00,79.69,50.172,42.746,31.349 +2020-10-08 00:15:00,79.41,51.321000000000005,42.746,31.349 +2020-10-08 00:30:00,77.72,51.048,42.746,31.349 +2020-10-08 00:45:00,77.1,50.92,42.746,31.349 +2020-10-08 01:00:00,77.16,51.248999999999995,40.025999999999996,31.349 +2020-10-08 01:15:00,79.65,50.761,40.025999999999996,31.349 +2020-10-08 01:30:00,78.99,50.083999999999996,40.025999999999996,31.349 +2020-10-08 01:45:00,73.79,49.59,40.025999999999996,31.349 +2020-10-08 02:00:00,79.29,50.065,38.154,31.349 +2020-10-08 02:15:00,80.26,50.193999999999996,38.154,31.349 +2020-10-08 02:30:00,78.95,50.676,38.154,31.349 +2020-10-08 02:45:00,76.08,51.33,38.154,31.349 +2020-10-08 03:00:00,75.33,53.449,37.575,31.349 +2020-10-08 03:15:00,82.42,54.321999999999996,37.575,31.349 +2020-10-08 03:30:00,84.01,54.4,37.575,31.349 +2020-10-08 03:45:00,86.07,55.028,37.575,31.349 +2020-10-08 04:00:00,84.0,63.916000000000004,39.154,31.349 +2020-10-08 04:15:00,90.23,72.76100000000001,39.154,31.349 +2020-10-08 04:30:00,94.69,72.051,39.154,31.349 +2020-10-08 04:45:00,97.5,73.727,39.154,31.349 +2020-10-08 05:00:00,97.92,99.507,44.085,31.349 +2020-10-08 05:15:00,101.71,122.09299999999999,44.085,31.349 +2020-10-08 05:30:00,107.92,116.074,44.085,31.349 +2020-10-08 05:45:00,111.57,108.126,44.085,31.349 +2020-10-08 06:00:00,116.78,107.995,57.49,31.349 +2020-10-08 06:15:00,114.86,111.68700000000001,57.49,31.349 +2020-10-08 06:30:00,114.18,110.105,57.49,31.349 +2020-10-08 06:45:00,118.63,110.81700000000001,57.49,31.349 +2020-10-08 07:00:00,120.92,111.065,73.617,31.349 +2020-10-08 07:15:00,119.12,113.374,73.617,31.349 +2020-10-08 07:30:00,117.33,113.219,73.617,31.349 +2020-10-08 07:45:00,116.58,113.415,73.617,31.349 +2020-10-08 08:00:00,115.2,111.869,69.281,31.349 +2020-10-08 08:15:00,114.49,111.969,69.281,31.349 +2020-10-08 08:30:00,113.74,109.76299999999999,69.281,31.349 +2020-10-08 08:45:00,113.82,108.845,69.281,31.349 +2020-10-08 09:00:00,111.25,104.189,63.926,31.349 +2020-10-08 09:15:00,110.51,101.995,63.926,31.349 +2020-10-08 09:30:00,110.18,102.336,63.926,31.349 +2020-10-08 09:45:00,110.35,102.079,63.926,31.349 +2020-10-08 10:00:00,108.45,98.833,59.442,31.349 +2020-10-08 10:15:00,109.67,98.12799999999999,59.442,31.349 +2020-10-08 10:30:00,108.62,97.05,59.442,31.349 +2020-10-08 10:45:00,107.19,96.867,59.442,31.349 +2020-10-08 11:00:00,105.31,93.634,56.771,31.349 +2020-10-08 11:15:00,106.63,94.209,56.771,31.349 +2020-10-08 11:30:00,106.21,94.40700000000001,56.771,31.349 +2020-10-08 11:45:00,108.79,94.072,56.771,31.349 +2020-10-08 12:00:00,101.41,90.34700000000001,53.701,31.349 +2020-10-08 12:15:00,101.81,89.917,53.701,31.349 +2020-10-08 12:30:00,99.91,89.323,53.701,31.349 +2020-10-08 12:45:00,100.67,89.609,53.701,31.349 +2020-10-08 13:00:00,99.91,89.12799999999999,52.364,31.349 +2020-10-08 13:15:00,100.85,89.016,52.364,31.349 +2020-10-08 13:30:00,99.77,88.156,52.364,31.349 +2020-10-08 13:45:00,102.06,87.71799999999999,52.364,31.349 +2020-10-08 14:00:00,101.66,87.06200000000001,53.419,31.349 +2020-10-08 14:15:00,102.04,87.431,53.419,31.349 +2020-10-08 14:30:00,101.37,86.77600000000001,53.419,31.349 +2020-10-08 14:45:00,102.03,86.632,53.419,31.349 +2020-10-08 15:00:00,102.38,86.545,56.744,31.349 +2020-10-08 15:15:00,106.15,86.235,56.744,31.349 +2020-10-08 15:30:00,105.14,86.71700000000001,56.744,31.349 +2020-10-08 15:45:00,106.58,86.766,56.744,31.349 +2020-10-08 16:00:00,108.41,86.568,60.458,31.349 +2020-10-08 16:15:00,109.23,86.09700000000001,60.458,31.349 +2020-10-08 16:30:00,111.33,86.765,60.458,31.349 +2020-10-08 16:45:00,112.69,85.179,60.458,31.349 +2020-10-08 17:00:00,117.22,85.32600000000001,66.295,31.349 +2020-10-08 17:15:00,114.98,86.646,66.295,31.349 +2020-10-08 17:30:00,121.72,86.387,66.295,31.349 +2020-10-08 17:45:00,122.79,87.156,66.295,31.349 +2020-10-08 18:00:00,124.63,86.772,68.468,31.349 +2020-10-08 18:15:00,124.71,86.521,68.468,31.349 +2020-10-08 18:30:00,121.3,85.62799999999999,68.468,31.349 +2020-10-08 18:45:00,120.86,89.238,68.468,31.349 +2020-10-08 19:00:00,121.84,89.704,66.39399999999999,31.349 +2020-10-08 19:15:00,118.99,88.54,66.39399999999999,31.349 +2020-10-08 19:30:00,110.25,87.949,66.39399999999999,31.349 +2020-10-08 19:45:00,112.82,87.868,66.39399999999999,31.349 +2020-10-08 20:00:00,104.39,87.273,63.183,31.349 +2020-10-08 20:15:00,104.11,85.01700000000001,63.183,31.349 +2020-10-08 20:30:00,96.44,84.031,63.183,31.349 +2020-10-08 20:45:00,97.74,82.82,63.183,31.349 +2020-10-08 21:00:00,95.47,80.82,55.133,31.349 +2020-10-08 21:15:00,94.29,81.316,55.133,31.349 +2020-10-08 21:30:00,88.11,80.48899999999999,55.133,31.349 +2020-10-08 21:45:00,94.31,78.695,55.133,31.349 +2020-10-08 22:00:00,86.54,75.735,50.111999999999995,31.349 +2020-10-08 22:15:00,84.64,73.417,50.111999999999995,31.349 +2020-10-08 22:30:00,84.73,63.065,50.111999999999995,31.349 +2020-10-08 22:45:00,80.12,57.919,50.111999999999995,31.349 +2020-10-08 23:00:00,82.03,53.343,44.536,31.349 +2020-10-08 23:15:00,83.35,52.867,44.536,31.349 +2020-10-08 23:30:00,82.16,52.345,44.536,31.349 +2020-10-08 23:45:00,78.69,52.281000000000006,44.536,31.349 +2020-10-09 00:00:00,78.1,49.02,42.291000000000004,31.349 +2020-10-09 00:15:00,79.85,50.364,42.291000000000004,31.349 +2020-10-09 00:30:00,79.77,50.202,42.291000000000004,31.349 +2020-10-09 00:45:00,76.69,50.361999999999995,42.291000000000004,31.349 +2020-10-09 01:00:00,73.43,50.349,41.008,31.349 +2020-10-09 01:15:00,80.07,49.895,41.008,31.349 +2020-10-09 01:30:00,78.35,49.56100000000001,41.008,31.349 +2020-10-09 01:45:00,75.18,48.961999999999996,41.008,31.349 +2020-10-09 02:00:00,76.68,50.022,39.521,31.349 +2020-10-09 02:15:00,74.18,50.09,39.521,31.349 +2020-10-09 02:30:00,79.65,51.242,39.521,31.349 +2020-10-09 02:45:00,79.83,51.511,39.521,31.349 +2020-10-09 03:00:00,80.63,53.677,39.812,31.349 +2020-10-09 03:15:00,76.92,54.235,39.812,31.349 +2020-10-09 03:30:00,78.86,54.17,39.812,31.349 +2020-10-09 03:45:00,81.09,55.42,39.812,31.349 +2020-10-09 04:00:00,87.69,64.506,41.22,31.349 +2020-10-09 04:15:00,90.33,72.376,41.22,31.349 +2020-10-09 04:30:00,89.24,72.312,41.22,31.349 +2020-10-09 04:45:00,95.09,73.171,41.22,31.349 +2020-10-09 05:00:00,107.98,98.257,45.115,31.349 +2020-10-09 05:15:00,110.72,122.072,45.115,31.349 +2020-10-09 05:30:00,111.56,116.56299999999999,45.115,31.349 +2020-10-09 05:45:00,111.91,108.28200000000001,45.115,31.349 +2020-10-09 06:00:00,119.82,108.445,59.06100000000001,31.349 +2020-10-09 06:15:00,118.17,111.68,59.06100000000001,31.349 +2020-10-09 06:30:00,118.58,109.745,59.06100000000001,31.349 +2020-10-09 06:45:00,120.01,111.014,59.06100000000001,31.349 +2020-10-09 07:00:00,122.14,111.315,71.874,31.349 +2020-10-09 07:15:00,120.54,114.571,71.874,31.349 +2020-10-09 07:30:00,118.81,113.26100000000001,71.874,31.349 +2020-10-09 07:45:00,118.12,112.98,71.874,31.349 +2020-10-09 08:00:00,116.07,111.43299999999999,68.439,31.349 +2020-10-09 08:15:00,113.32,111.71600000000001,68.439,31.349 +2020-10-09 08:30:00,113.35,109.887,68.439,31.349 +2020-10-09 08:45:00,116.44,108.208,68.439,31.349 +2020-10-09 09:00:00,120.09,102.461,65.523,31.349 +2020-10-09 09:15:00,119.56,101.556,65.523,31.349 +2020-10-09 09:30:00,119.2,101.32,65.523,31.349 +2020-10-09 09:45:00,120.05,101.2,65.523,31.349 +2020-10-09 10:00:00,118.05,97.335,62.005,31.349 +2020-10-09 10:15:00,120.4,96.78399999999999,62.005,31.349 +2020-10-09 10:30:00,117.17,95.949,62.005,31.349 +2020-10-09 10:45:00,113.01,95.501,62.005,31.349 +2020-10-09 11:00:00,109.41,92.38600000000001,60.351000000000006,31.349 +2020-10-09 11:15:00,112.78,91.969,60.351000000000006,31.349 +2020-10-09 11:30:00,116.56,92.76799999999999,60.351000000000006,31.349 +2020-10-09 11:45:00,115.51,91.935,60.351000000000006,31.349 +2020-10-09 12:00:00,112.96,88.92399999999999,55.331,31.349 +2020-10-09 12:15:00,112.0,87.18700000000001,55.331,31.349 +2020-10-09 12:30:00,109.13,86.762,55.331,31.349 +2020-10-09 12:45:00,106.59,86.881,55.331,31.349 +2020-10-09 13:00:00,98.34,87.105,53.361999999999995,31.349 +2020-10-09 13:15:00,98.84,87.469,53.361999999999995,31.349 +2020-10-09 13:30:00,99.07,87.02,53.361999999999995,31.349 +2020-10-09 13:45:00,99.93,86.71,53.361999999999995,31.349 +2020-10-09 14:00:00,99.24,85.06299999999999,51.708,31.349 +2020-10-09 14:15:00,99.81,85.588,51.708,31.349 +2020-10-09 14:30:00,100.03,85.976,51.708,31.349 +2020-10-09 14:45:00,101.58,85.617,51.708,31.349 +2020-10-09 15:00:00,101.74,85.23100000000001,54.571000000000005,31.349 +2020-10-09 15:15:00,104.75,84.615,54.571000000000005,31.349 +2020-10-09 15:30:00,101.76,84.139,54.571000000000005,31.349 +2020-10-09 15:45:00,104.43,84.66,54.571000000000005,31.349 +2020-10-09 16:00:00,106.95,83.49,58.662,31.349 +2020-10-09 16:15:00,108.48,83.429,58.662,31.349 +2020-10-09 16:30:00,109.76,84.04,58.662,31.349 +2020-10-09 16:45:00,111.36,82.041,58.662,31.349 +2020-10-09 17:00:00,113.98,83.177,65.941,31.349 +2020-10-09 17:15:00,116.0,84.243,65.941,31.349 +2020-10-09 17:30:00,116.08,83.93700000000001,65.941,31.349 +2020-10-09 17:45:00,120.05,84.536,65.941,31.349 +2020-10-09 18:00:00,122.43,84.505,65.628,31.349 +2020-10-09 18:15:00,118.95,83.552,65.628,31.349 +2020-10-09 18:30:00,120.14,82.779,65.628,31.349 +2020-10-09 18:45:00,116.46,86.61200000000001,65.628,31.349 +2020-10-09 19:00:00,110.08,87.984,63.662,31.349 +2020-10-09 19:15:00,107.3,87.711,63.662,31.349 +2020-10-09 19:30:00,102.67,86.975,63.662,31.349 +2020-10-09 19:45:00,102.52,86.115,63.662,31.349 +2020-10-09 20:00:00,94.57,85.47399999999999,61.945,31.349 +2020-10-09 20:15:00,92.41,83.66,61.945,31.349 +2020-10-09 20:30:00,93.56,82.355,61.945,31.349 +2020-10-09 20:45:00,88.21,80.892,61.945,31.349 +2020-10-09 21:00:00,83.31,79.843,53.903,31.349 +2020-10-09 21:15:00,83.13,81.453,53.903,31.349 +2020-10-09 21:30:00,83.95,80.569,53.903,31.349 +2020-10-09 21:45:00,86.18,79.085,53.903,31.349 +2020-10-09 22:00:00,80.94,76.436,48.403999999999996,31.349 +2020-10-09 22:15:00,79.92,73.9,48.403999999999996,31.349 +2020-10-09 22:30:00,73.84,68.861,48.403999999999996,31.349 +2020-10-09 22:45:00,69.3,65.65100000000001,48.403999999999996,31.349 +2020-10-09 23:00:00,66.03,61.873999999999995,41.07,31.349 +2020-10-09 23:15:00,66.63,59.684,41.07,31.349 +2020-10-09 23:30:00,71.33,57.535,41.07,31.349 +2020-10-09 23:45:00,72.04,57.123999999999995,41.07,31.349 +2020-10-10 00:00:00,68.05,48.924,38.989000000000004,31.177 +2020-10-10 00:15:00,66.03,48.18899999999999,38.989000000000004,31.177 +2020-10-10 00:30:00,63.47,48.287,38.989000000000004,31.177 +2020-10-10 00:45:00,70.67,48.302,38.989000000000004,31.177 +2020-10-10 01:00:00,68.97,48.708,35.275,31.177 +2020-10-10 01:15:00,68.98,48.229,35.275,31.177 +2020-10-10 01:30:00,62.64,47.24100000000001,35.275,31.177 +2020-10-10 01:45:00,63.19,47.28,35.275,31.177 +2020-10-10 02:00:00,60.49,48.051,32.838,31.177 +2020-10-10 02:15:00,67.91,47.53,32.838,31.177 +2020-10-10 02:30:00,68.09,47.768,32.838,31.177 +2020-10-10 02:45:00,61.18,48.528999999999996,32.838,31.177 +2020-10-10 03:00:00,60.57,50.135,32.418,31.177 +2020-10-10 03:15:00,60.96,49.832,32.418,31.177 +2020-10-10 03:30:00,60.75,49.342,32.418,31.177 +2020-10-10 03:45:00,61.33,51.481,32.418,31.177 +2020-10-10 04:00:00,63.26,57.9,32.099000000000004,31.177 +2020-10-10 04:15:00,62.9,64.293,32.099000000000004,31.177 +2020-10-10 04:30:00,62.36,62.413000000000004,32.099000000000004,31.177 +2020-10-10 04:45:00,64.21,63.248000000000005,32.099000000000004,31.177 +2020-10-10 05:00:00,67.64,77.72,32.926,31.177 +2020-10-10 05:15:00,68.34,87.98899999999999,32.926,31.177 +2020-10-10 05:30:00,67.96,83.508,32.926,31.177 +2020-10-10 05:45:00,69.98,79.889,32.926,31.177 +2020-10-10 06:00:00,70.7,93.516,35.069,31.177 +2020-10-10 06:15:00,73.22,107.594,35.069,31.177 +2020-10-10 06:30:00,74.31,101.949,35.069,31.177 +2020-10-10 06:45:00,77.52,97.90799999999999,35.069,31.177 +2020-10-10 07:00:00,79.57,95.572,40.906,31.177 +2020-10-10 07:15:00,80.64,97.601,40.906,31.177 +2020-10-10 07:30:00,80.5,98.264,40.906,31.177 +2020-10-10 07:45:00,81.73,100.09,40.906,31.177 +2020-10-10 08:00:00,80.79,100.335,46.603,31.177 +2020-10-10 08:15:00,80.92,101.959,46.603,31.177 +2020-10-10 08:30:00,80.69,100.809,46.603,31.177 +2020-10-10 08:45:00,79.3,100.93,46.603,31.177 +2020-10-10 09:00:00,80.93,97.544,49.935,31.177 +2020-10-10 09:15:00,78.7,97.215,49.935,31.177 +2020-10-10 09:30:00,77.39,97.61399999999999,49.935,31.177 +2020-10-10 09:45:00,77.01,97.289,49.935,31.177 +2020-10-10 10:00:00,75.49,93.726,47.585,31.177 +2020-10-10 10:15:00,76.27,93.39399999999999,47.585,31.177 +2020-10-10 10:30:00,75.48,92.43299999999999,47.585,31.177 +2020-10-10 10:45:00,75.32,92.353,47.585,31.177 +2020-10-10 11:00:00,73.13,89.22200000000001,43.376999999999995,31.177 +2020-10-10 11:15:00,72.55,88.932,43.376999999999995,31.177 +2020-10-10 11:30:00,69.49,89.382,43.376999999999995,31.177 +2020-10-10 11:45:00,71.73,88.456,43.376999999999995,31.177 +2020-10-10 12:00:00,67.21,85.07799999999999,40.855,31.177 +2020-10-10 12:15:00,65.23,84.057,40.855,31.177 +2020-10-10 12:30:00,64.52,83.74700000000001,40.855,31.177 +2020-10-10 12:45:00,64.43,83.87899999999999,40.855,31.177 +2020-10-10 13:00:00,62.06,83.459,37.251,31.177 +2020-10-10 13:15:00,63.54,82.611,37.251,31.177 +2020-10-10 13:30:00,64.24,82.051,37.251,31.177 +2020-10-10 13:45:00,65.9,81.27199999999999,37.251,31.177 +2020-10-10 14:00:00,65.04,80.149,38.548,31.177 +2020-10-10 14:15:00,65.98,79.81,38.548,31.177 +2020-10-10 14:30:00,67.11,79.18,38.548,31.177 +2020-10-10 14:45:00,68.17,79.163,38.548,31.177 +2020-10-10 15:00:00,68.93,79.266,42.883,31.177 +2020-10-10 15:15:00,68.68,79.398,42.883,31.177 +2020-10-10 15:30:00,70.26,79.771,42.883,31.177 +2020-10-10 15:45:00,73.92,79.848,42.883,31.177 +2020-10-10 16:00:00,76.34,79.433,48.143,31.177 +2020-10-10 16:15:00,80.43,79.315,48.143,31.177 +2020-10-10 16:30:00,82.57,80.018,48.143,31.177 +2020-10-10 16:45:00,81.96,78.45,48.143,31.177 +2020-10-10 17:00:00,87.07,78.757,55.25,31.177 +2020-10-10 17:15:00,85.61,79.41,55.25,31.177 +2020-10-10 17:30:00,93.1,78.993,55.25,31.177 +2020-10-10 17:45:00,93.78,79.703,55.25,31.177 +2020-10-10 18:00:00,94.81,80.311,57.506,31.177 +2020-10-10 18:15:00,93.4,81.016,57.506,31.177 +2020-10-10 18:30:00,95.37,81.557,57.506,31.177 +2020-10-10 18:45:00,92.73,82.074,57.506,31.177 +2020-10-10 19:00:00,86.72,83.03299999999999,55.528999999999996,31.177 +2020-10-10 19:15:00,83.84,81.98700000000001,55.528999999999996,31.177 +2020-10-10 19:30:00,81.98,81.98100000000001,55.528999999999996,31.177 +2020-10-10 19:45:00,80.54,82.01799999999999,55.528999999999996,31.177 +2020-10-10 20:00:00,77.04,82.738,46.166000000000004,31.177 +2020-10-10 20:15:00,76.13,81.439,46.166000000000004,31.177 +2020-10-10 20:30:00,75.51,79.47399999999999,46.166000000000004,31.177 +2020-10-10 20:45:00,73.93,78.848,46.166000000000004,31.177 +2020-10-10 21:00:00,71.35,77.916,40.406,31.177 +2020-10-10 21:15:00,70.87,79.498,40.406,31.177 +2020-10-10 21:30:00,66.41,79.23100000000001,40.406,31.177 +2020-10-10 21:45:00,65.71,77.248,40.406,31.177 +2020-10-10 22:00:00,61.42,75.122,39.616,31.177 +2020-10-10 22:15:00,62.07,73.71600000000001,39.616,31.177 +2020-10-10 22:30:00,59.56,71.013,39.616,31.177 +2020-10-10 22:45:00,58.47,68.78,39.616,31.177 +2020-10-10 23:00:00,54.74,65.638,32.205,31.177 +2020-10-10 23:15:00,54.08,62.986999999999995,32.205,31.177 +2020-10-10 23:30:00,55.19,61.508,32.205,31.177 +2020-10-10 23:45:00,53.38,60.303999999999995,32.205,31.177 +2020-10-11 00:00:00,50.93,50.198,28.229,31.177 +2020-10-11 00:15:00,51.94,48.662,28.229,31.177 +2020-10-11 00:30:00,50.97,48.536,28.229,31.177 +2020-10-11 00:45:00,51.87,48.757,28.229,31.177 +2020-10-11 01:00:00,49.67,49.263999999999996,25.669,31.177 +2020-10-11 01:15:00,51.23,49.141000000000005,25.669,31.177 +2020-10-11 01:30:00,50.66,48.294,25.669,31.177 +2020-10-11 01:45:00,51.05,47.988,25.669,31.177 +2020-10-11 02:00:00,51.26,48.52,24.948,31.177 +2020-10-11 02:15:00,50.24,48.092,24.948,31.177 +2020-10-11 02:30:00,49.75,48.754,24.948,31.177 +2020-10-11 02:45:00,49.74,49.521,24.948,31.177 +2020-10-11 03:00:00,49.25,51.614,24.445,31.177 +2020-10-11 03:15:00,50.7,51.281000000000006,24.445,31.177 +2020-10-11 03:30:00,51.34,50.907,24.445,31.177 +2020-10-11 03:45:00,51.61,52.536,24.445,31.177 +2020-10-11 04:00:00,52.83,58.86600000000001,25.839000000000002,31.177 +2020-10-11 04:15:00,53.93,64.65,25.839000000000002,31.177 +2020-10-11 04:30:00,53.84,63.577,25.839000000000002,31.177 +2020-10-11 04:45:00,54.0,64.253,25.839000000000002,31.177 +2020-10-11 05:00:00,56.26,77.597,26.803,31.177 +2020-10-11 05:15:00,56.25,86.6,26.803,31.177 +2020-10-11 05:30:00,56.59,81.8,26.803,31.177 +2020-10-11 05:45:00,54.83,78.065,26.803,31.177 +2020-10-11 06:00:00,60.32,90.211,28.147,31.177 +2020-10-11 06:15:00,59.82,104.145,28.147,31.177 +2020-10-11 06:30:00,62.29,97.684,28.147,31.177 +2020-10-11 06:45:00,63.34,92.662,28.147,31.177 +2020-10-11 07:00:00,65.28,91.40100000000001,31.116,31.177 +2020-10-11 07:15:00,66.84,92.166,31.116,31.177 +2020-10-11 07:30:00,67.06,93.11200000000001,31.116,31.177 +2020-10-11 07:45:00,67.82,94.62100000000001,31.116,31.177 +2020-10-11 08:00:00,66.65,95.993,35.739000000000004,31.177 +2020-10-11 08:15:00,66.05,98.213,35.739000000000004,31.177 +2020-10-11 08:30:00,64.97,98.24600000000001,35.739000000000004,31.177 +2020-10-11 08:45:00,64.71,99.119,35.739000000000004,31.177 +2020-10-11 09:00:00,62.3,95.48899999999999,39.455999999999996,31.177 +2020-10-11 09:15:00,61.87,95.111,39.455999999999996,31.177 +2020-10-11 09:30:00,61.94,95.667,39.455999999999996,31.177 +2020-10-11 09:45:00,63.26,95.81700000000001,39.455999999999996,31.177 +2020-10-11 10:00:00,63.21,93.579,41.343999999999994,31.177 +2020-10-11 10:15:00,63.71,93.508,41.343999999999994,31.177 +2020-10-11 10:30:00,64.26,92.906,41.343999999999994,31.177 +2020-10-11 10:45:00,63.77,92.535,41.343999999999994,31.177 +2020-10-11 11:00:00,60.7,89.61200000000001,43.645,31.177 +2020-10-11 11:15:00,59.98,89.12700000000001,43.645,31.177 +2020-10-11 11:30:00,57.95,89.469,43.645,31.177 +2020-10-11 11:45:00,60.21,88.926,43.645,31.177 +2020-10-11 12:00:00,56.47,85.79,39.796,31.177 +2020-10-11 12:15:00,53.94,85.294,39.796,31.177 +2020-10-11 12:30:00,52.59,84.475,39.796,31.177 +2020-10-11 12:45:00,54.32,83.84200000000001,39.796,31.177 +2020-10-11 13:00:00,49.62,82.914,36.343,31.177 +2020-10-11 13:15:00,51.08,83.12700000000001,36.343,31.177 +2020-10-11 13:30:00,51.24,81.898,36.343,31.177 +2020-10-11 13:45:00,52.47,81.398,36.343,31.177 +2020-10-11 14:00:00,52.86,80.949,33.162,31.177 +2020-10-11 14:15:00,54.18,81.385,33.162,31.177 +2020-10-11 14:30:00,54.72,80.714,33.162,31.177 +2020-10-11 14:45:00,57.08,79.959,33.162,31.177 +2020-10-11 15:00:00,61.5,79.414,33.215,31.177 +2020-10-11 15:15:00,61.01,79.492,33.215,31.177 +2020-10-11 15:30:00,61.52,80.07300000000001,33.215,31.177 +2020-10-11 15:45:00,64.89,80.623,33.215,31.177 +2020-10-11 16:00:00,69.86,80.05,37.385999999999996,31.177 +2020-10-11 16:15:00,73.5,79.68,37.385999999999996,31.177 +2020-10-11 16:30:00,72.54,81.023,37.385999999999996,31.177 +2020-10-11 16:45:00,77.12,79.60300000000001,37.385999999999996,31.177 +2020-10-11 17:00:00,81.3,80.021,46.618,31.177 +2020-10-11 17:15:00,82.78,81.369,46.618,31.177 +2020-10-11 17:30:00,89.15,81.513,46.618,31.177 +2020-10-11 17:45:00,88.39,83.434,46.618,31.177 +2020-10-11 18:00:00,91.86,84.07700000000001,50.111000000000004,31.177 +2020-10-11 18:15:00,89.54,85.089,50.111000000000004,31.177 +2020-10-11 18:30:00,91.48,84.645,50.111000000000004,31.177 +2020-10-11 18:45:00,86.46,85.98299999999999,50.111000000000004,31.177 +2020-10-11 19:00:00,81.58,87.99,50.25,31.177 +2020-10-11 19:15:00,87.47,86.554,50.25,31.177 +2020-10-11 19:30:00,87.65,86.333,50.25,31.177 +2020-10-11 19:45:00,84.98,86.675,50.25,31.177 +2020-10-11 20:00:00,82.24,87.521,44.265,31.177 +2020-10-11 20:15:00,88.55,86.537,44.265,31.177 +2020-10-11 20:30:00,87.01,85.499,44.265,31.177 +2020-10-11 20:45:00,88.49,83.41799999999999,44.265,31.177 +2020-10-11 21:00:00,79.77,81.342,39.717,31.177 +2020-10-11 21:15:00,77.18,82.494,39.717,31.177 +2020-10-11 21:30:00,75.52,81.98899999999999,39.717,31.177 +2020-10-11 21:45:00,74.79,80.236,39.717,31.177 +2020-10-11 22:00:00,70.87,78.767,39.224000000000004,31.177 +2020-10-11 22:15:00,77.05,76.087,39.224000000000004,31.177 +2020-10-11 22:30:00,76.79,71.846,39.224000000000004,31.177 +2020-10-11 22:45:00,77.47,68.562,39.224000000000004,31.177 +2020-10-11 23:00:00,67.29,64.157,33.518,31.177 +2020-10-11 23:15:00,67.19,62.933,33.518,31.177 +2020-10-11 23:30:00,72.95,61.476000000000006,33.518,31.177 +2020-10-11 23:45:00,73.21,60.711999999999996,33.518,31.177 +2020-10-12 00:00:00,70.94,53.006,34.301,31.349 +2020-10-12 00:15:00,68.42,53.067,34.301,31.349 +2020-10-12 00:30:00,68.11,52.773999999999994,34.301,31.349 +2020-10-12 00:45:00,72.11,52.558,34.301,31.349 +2020-10-12 01:00:00,70.36,53.288999999999994,34.143,31.349 +2020-10-12 01:15:00,68.12,52.99100000000001,34.143,31.349 +2020-10-12 01:30:00,65.37,52.393,34.143,31.349 +2020-10-12 01:45:00,69.82,52.073,34.143,31.349 +2020-10-12 02:00:00,69.15,52.861999999999995,33.650999999999996,31.349 +2020-10-12 02:15:00,70.12,52.422,33.650999999999996,31.349 +2020-10-12 02:30:00,66.23,53.29,33.650999999999996,31.349 +2020-10-12 02:45:00,70.36,53.729,33.650999999999996,31.349 +2020-10-12 03:00:00,72.15,56.57899999999999,32.599000000000004,31.349 +2020-10-12 03:15:00,72.16,57.313,32.599000000000004,31.349 +2020-10-12 03:30:00,69.17,57.236000000000004,32.599000000000004,31.349 +2020-10-12 03:45:00,76.19,58.367,32.599000000000004,31.349 +2020-10-12 04:00:00,82.36,68.183,33.785,31.349 +2020-10-12 04:15:00,85.74,77.285,33.785,31.349 +2020-10-12 04:30:00,84.49,76.767,33.785,31.349 +2020-10-12 04:45:00,89.3,77.73,33.785,31.349 +2020-10-12 05:00:00,101.52,101.051,41.285,31.349 +2020-10-12 05:15:00,106.64,123.691,41.285,31.349 +2020-10-12 05:30:00,106.95,118.056,41.285,31.349 +2020-10-12 05:45:00,106.85,110.37,41.285,31.349 +2020-10-12 06:00:00,115.15,109.96799999999999,60.486000000000004,31.349 +2020-10-12 06:15:00,115.95,113.00200000000001,60.486000000000004,31.349 +2020-10-12 06:30:00,115.89,111.90100000000001,60.486000000000004,31.349 +2020-10-12 06:45:00,118.2,113.29,60.486000000000004,31.349 +2020-10-12 07:00:00,121.53,113.51899999999999,74.012,31.349 +2020-10-12 07:15:00,122.2,116.09200000000001,74.012,31.349 +2020-10-12 07:30:00,118.03,116.274,74.012,31.349 +2020-10-12 07:45:00,116.52,116.95,74.012,31.349 +2020-10-12 08:00:00,114.57,115.436,69.569,31.349 +2020-10-12 08:15:00,114.1,116.088,69.569,31.349 +2020-10-12 08:30:00,114.31,113.955,69.569,31.349 +2020-10-12 08:45:00,116.52,113.573,69.569,31.349 +2020-10-12 09:00:00,111.0,109.07,66.152,31.349 +2020-10-12 09:15:00,110.6,106.264,66.152,31.349 +2020-10-12 09:30:00,110.4,105.881,66.152,31.349 +2020-10-12 09:45:00,110.21,104.867,66.152,31.349 +2020-10-12 10:00:00,109.5,102.59200000000001,62.923,31.349 +2020-10-12 10:15:00,116.29,102.26299999999999,62.923,31.349 +2020-10-12 10:30:00,112.61,101.021,62.923,31.349 +2020-10-12 10:45:00,114.5,100.115,62.923,31.349 +2020-10-12 11:00:00,111.52,96.26700000000001,61.522,31.349 +2020-10-12 11:15:00,115.75,96.70100000000001,61.522,31.349 +2020-10-12 11:30:00,110.56,98.014,61.522,31.349 +2020-10-12 11:45:00,105.29,97.54,61.522,31.349 +2020-10-12 12:00:00,103.06,94.429,58.632,31.349 +2020-10-12 12:15:00,103.27,93.98200000000001,58.632,31.349 +2020-10-12 12:30:00,99.5,92.764,58.632,31.349 +2020-10-12 12:45:00,102.59,92.81299999999999,58.632,31.349 +2020-10-12 13:00:00,103.02,92.575,59.06,31.349 +2020-10-12 13:15:00,104.16,91.713,59.06,31.349 +2020-10-12 13:30:00,101.81,90.331,59.06,31.349 +2020-10-12 13:45:00,102.12,90.304,59.06,31.349 +2020-10-12 14:00:00,104.85,89.085,59.791000000000004,31.349 +2020-10-12 14:15:00,111.72,89.54899999999999,59.791000000000004,31.349 +2020-10-12 14:30:00,108.5,88.586,59.791000000000004,31.349 +2020-10-12 14:45:00,107.1,88.87,59.791000000000004,31.349 +2020-10-12 15:00:00,107.33,88.921,61.148,31.349 +2020-10-12 15:15:00,107.86,88.06700000000001,61.148,31.349 +2020-10-12 15:30:00,108.23,88.7,61.148,31.349 +2020-10-12 15:45:00,110.3,88.81700000000001,61.148,31.349 +2020-10-12 16:00:00,111.96,88.654,66.009,31.349 +2020-10-12 16:15:00,114.55,88.006,66.009,31.349 +2020-10-12 16:30:00,114.39,88.575,66.009,31.349 +2020-10-12 16:45:00,116.45,86.70299999999999,66.009,31.349 +2020-10-12 17:00:00,117.5,86.335,73.683,31.349 +2020-10-12 17:15:00,116.77,87.499,73.683,31.349 +2020-10-12 17:30:00,118.94,87.204,73.683,31.349 +2020-10-12 17:45:00,119.82,88.29700000000001,73.683,31.349 +2020-10-12 18:00:00,121.77,88.435,72.848,31.349 +2020-10-12 18:15:00,121.95,87.51100000000001,72.848,31.349 +2020-10-12 18:30:00,119.5,86.93299999999999,72.848,31.349 +2020-10-12 18:45:00,116.12,90.29799999999999,72.848,31.349 +2020-10-12 19:00:00,110.92,91.43799999999999,71.139,31.349 +2020-10-12 19:15:00,106.85,90.26799999999999,71.139,31.349 +2020-10-12 19:30:00,103.85,90.03200000000001,71.139,31.349 +2020-10-12 19:45:00,107.99,89.679,71.139,31.349 +2020-10-12 20:00:00,105.84,88.89,69.667,31.349 +2020-10-12 20:15:00,105.89,87.679,69.667,31.349 +2020-10-12 20:30:00,98.58,86.147,69.667,31.349 +2020-10-12 20:45:00,96.33,84.819,69.667,31.349 +2020-10-12 21:00:00,91.25,82.61399999999999,61.166000000000004,31.349 +2020-10-12 21:15:00,90.87,83.53299999999999,61.166000000000004,31.349 +2020-10-12 21:30:00,91.81,82.939,61.166000000000004,31.349 +2020-10-12 21:45:00,92.26,80.832,61.166000000000004,31.349 +2020-10-12 22:00:00,88.19,77.041,52.772,31.349 +2020-10-12 22:15:00,87.73,74.929,52.772,31.349 +2020-10-12 22:30:00,87.11,64.431,52.772,31.349 +2020-10-12 22:45:00,87.25,59.202,52.772,31.349 +2020-10-12 23:00:00,79.67,55.16,45.136,31.349 +2020-10-12 23:15:00,78.21,54.04,45.136,31.349 +2020-10-12 23:30:00,80.76,53.67,45.136,31.349 +2020-10-12 23:45:00,81.41,53.657,45.136,31.349 +2020-10-13 00:00:00,73.81,51.739,47.35,31.349 +2020-10-13 00:15:00,72.3,52.842,47.35,31.349 +2020-10-13 00:30:00,74.12,52.604,47.35,31.349 +2020-10-13 00:45:00,77.97,52.465,47.35,31.349 +2020-10-13 01:00:00,75.1,52.812,43.424,31.349 +2020-10-13 01:15:00,74.15,52.428000000000004,43.424,31.349 +2020-10-13 01:30:00,75.85,51.838,43.424,31.349 +2020-10-13 01:45:00,77.72,51.335,43.424,31.349 +2020-10-13 02:00:00,77.58,51.851000000000006,41.778999999999996,31.349 +2020-10-13 02:15:00,75.84,52.056000000000004,41.778999999999996,31.349 +2020-10-13 02:30:00,75.85,52.452,41.778999999999996,31.349 +2020-10-13 02:45:00,75.3,53.08,41.778999999999996,31.349 +2020-10-13 03:00:00,79.74,55.13399999999999,40.771,31.349 +2020-10-13 03:15:00,78.15,56.108000000000004,40.771,31.349 +2020-10-13 03:30:00,75.52,56.201,40.771,31.349 +2020-10-13 03:45:00,75.48,56.739,40.771,31.349 +2020-10-13 04:00:00,77.47,65.77199999999999,41.816,31.349 +2020-10-13 04:15:00,82.06,74.781,41.816,31.349 +2020-10-13 04:30:00,90.51,74.07600000000001,41.816,31.349 +2020-10-13 04:45:00,97.73,75.794,41.816,31.349 +2020-10-13 05:00:00,105.85,102.00200000000001,45.842,31.349 +2020-10-13 05:15:00,102.75,125.023,45.842,31.349 +2020-10-13 05:30:00,106.37,118.916,45.842,31.349 +2020-10-13 05:45:00,110.98,110.75399999999999,45.842,31.349 +2020-10-13 06:00:00,119.85,110.51,59.12,31.349 +2020-10-13 06:15:00,119.21,114.292,59.12,31.349 +2020-10-13 06:30:00,118.8,112.76899999999999,59.12,31.349 +2020-10-13 06:45:00,121.82,113.495,59.12,31.349 +2020-10-13 07:00:00,123.79,113.73899999999999,70.33,31.349 +2020-10-13 07:15:00,123.79,116.11399999999999,70.33,31.349 +2020-10-13 07:30:00,123.33,116.124,70.33,31.349 +2020-10-13 07:45:00,123.65,116.325,70.33,31.349 +2020-10-13 08:00:00,123.2,114.829,67.788,31.349 +2020-10-13 08:15:00,123.26,114.786,67.788,31.349 +2020-10-13 08:30:00,124.04,112.699,67.788,31.349 +2020-10-13 08:45:00,124.33,111.66799999999999,67.788,31.349 +2020-10-13 09:00:00,122.91,107.008,62.622,31.349 +2020-10-13 09:15:00,121.43,104.785,62.622,31.349 +2020-10-13 09:30:00,119.64,105.04799999999999,62.622,31.349 +2020-10-13 09:45:00,119.0,104.652,62.622,31.349 +2020-10-13 10:00:00,118.75,101.374,60.887,31.349 +2020-10-13 10:15:00,118.55,100.47,60.887,31.349 +2020-10-13 10:30:00,116.94,99.303,60.887,31.349 +2020-10-13 10:45:00,119.41,99.036,60.887,31.349 +2020-10-13 11:00:00,113.99,95.845,59.812,31.349 +2020-10-13 11:15:00,112.18,96.32600000000001,59.812,31.349 +2020-10-13 11:30:00,114.67,96.523,59.812,31.349 +2020-10-13 11:45:00,109.79,96.10600000000001,59.812,31.349 +2020-10-13 12:00:00,105.65,92.26,56.614,31.349 +2020-10-13 12:15:00,104.47,91.789,56.614,31.349 +2020-10-13 12:30:00,103.49,91.37299999999999,56.614,31.349 +2020-10-13 12:45:00,102.45,91.63799999999999,56.614,31.349 +2020-10-13 13:00:00,102.46,91.0,56.824,31.349 +2020-10-13 13:15:00,105.71,90.90700000000001,56.824,31.349 +2020-10-13 13:30:00,106.9,90.038,56.824,31.349 +2020-10-13 13:45:00,109.76,89.601,56.824,31.349 +2020-10-13 14:00:00,110.65,88.68700000000001,57.623999999999995,31.349 +2020-10-13 14:15:00,111.37,89.135,57.623999999999995,31.349 +2020-10-13 14:30:00,111.33,88.661,57.623999999999995,31.349 +2020-10-13 14:45:00,111.96,88.50200000000001,57.623999999999995,31.349 +2020-10-13 15:00:00,111.31,88.243,59.724,31.349 +2020-10-13 15:15:00,107.85,88.03,59.724,31.349 +2020-10-13 15:30:00,108.03,88.694,59.724,31.349 +2020-10-13 15:45:00,111.58,88.821,59.724,31.349 +2020-10-13 16:00:00,111.54,88.443,61.64,31.349 +2020-10-13 16:15:00,111.16,88.069,61.64,31.349 +2020-10-13 16:30:00,113.67,88.71700000000001,61.64,31.349 +2020-10-13 16:45:00,116.95,87.404,61.64,31.349 +2020-10-13 17:00:00,118.92,87.344,68.962,31.349 +2020-10-13 17:15:00,119.99,88.766,68.962,31.349 +2020-10-13 17:30:00,122.98,88.524,68.962,31.349 +2020-10-13 17:45:00,123.02,89.40299999999999,68.962,31.349 +2020-10-13 18:00:00,123.95,88.95,69.149,31.349 +2020-10-13 18:15:00,123.41,88.626,69.149,31.349 +2020-10-13 18:30:00,120.16,87.787,69.149,31.349 +2020-10-13 18:45:00,119.43,91.367,69.149,31.349 +2020-10-13 19:00:00,114.42,91.887,68.832,31.349 +2020-10-13 19:15:00,112.4,90.699,68.832,31.349 +2020-10-13 19:30:00,115.3,90.065,68.832,31.349 +2020-10-13 19:45:00,110.17,89.897,68.832,31.349 +2020-10-13 20:00:00,100.53,89.419,66.403,31.349 +2020-10-13 20:15:00,101.98,87.134,66.403,31.349 +2020-10-13 20:30:00,100.14,86.008,66.403,31.349 +2020-10-13 20:45:00,100.17,84.654,66.403,31.349 +2020-10-13 21:00:00,98.31,82.654,57.352,31.349 +2020-10-13 21:15:00,99.67,83.098,57.352,31.349 +2020-10-13 21:30:00,94.62,82.321,57.352,31.349 +2020-10-13 21:45:00,94.12,80.389,57.352,31.349 +2020-10-13 22:00:00,86.01,77.366,51.148999999999994,31.349 +2020-10-13 22:15:00,88.4,74.939,51.148999999999994,31.349 +2020-10-13 22:30:00,85.44,64.658,51.148999999999994,31.349 +2020-10-13 22:45:00,86.13,59.544,51.148999999999994,31.349 +2020-10-13 23:00:00,77.28,55.103,41.8,31.349 +2020-10-13 23:15:00,80.43,54.472,41.8,31.349 +2020-10-13 23:30:00,81.88,53.957,41.8,31.349 +2020-10-13 23:45:00,80.49,53.865,41.8,31.349 +2020-10-14 00:00:00,73.56,52.053000000000004,42.269,31.349 +2020-10-14 00:15:00,77.84,53.146,42.269,31.349 +2020-10-14 00:30:00,78.77,52.915,42.269,31.349 +2020-10-14 00:45:00,79.64,52.773999999999994,42.269,31.349 +2020-10-14 01:00:00,73.44,53.123999999999995,38.527,31.349 +2020-10-14 01:15:00,70.94,52.76,38.527,31.349 +2020-10-14 01:30:00,68.57,52.188,38.527,31.349 +2020-10-14 01:45:00,76.68,51.684,38.527,31.349 +2020-10-14 02:00:00,78.54,52.208,36.393,31.349 +2020-10-14 02:15:00,78.33,52.428000000000004,36.393,31.349 +2020-10-14 02:30:00,74.42,52.806000000000004,36.393,31.349 +2020-10-14 02:45:00,75.3,53.431000000000004,36.393,31.349 +2020-10-14 03:00:00,80.29,55.471000000000004,36.167,31.349 +2020-10-14 03:15:00,82.02,56.465,36.167,31.349 +2020-10-14 03:30:00,80.68,56.562,36.167,31.349 +2020-10-14 03:45:00,79.09,57.08,36.167,31.349 +2020-10-14 04:00:00,83.44,66.143,38.092,31.349 +2020-10-14 04:15:00,90.73,75.185,38.092,31.349 +2020-10-14 04:30:00,92.79,74.482,38.092,31.349 +2020-10-14 04:45:00,97.44,76.208,38.092,31.349 +2020-10-14 05:00:00,100.81,102.50299999999999,42.268,31.349 +2020-10-14 05:15:00,104.2,125.611,42.268,31.349 +2020-10-14 05:30:00,107.8,119.486,42.268,31.349 +2020-10-14 05:45:00,107.29,111.281,42.268,31.349 +2020-10-14 06:00:00,119.76,111.015,60.158,31.349 +2020-10-14 06:15:00,120.14,114.814,60.158,31.349 +2020-10-14 06:30:00,119.69,113.304,60.158,31.349 +2020-10-14 06:45:00,121.56,114.03200000000001,60.158,31.349 +2020-10-14 07:00:00,125.08,114.275,74.792,31.349 +2020-10-14 07:15:00,123.17,116.663,74.792,31.349 +2020-10-14 07:30:00,121.58,116.706,74.792,31.349 +2020-10-14 07:45:00,120.8,116.906,74.792,31.349 +2020-10-14 08:00:00,118.68,115.421,70.499,31.349 +2020-10-14 08:15:00,118.45,115.34899999999999,70.499,31.349 +2020-10-14 08:30:00,117.08,113.28399999999999,70.499,31.349 +2020-10-14 08:45:00,117.25,112.23,70.499,31.349 +2020-10-14 09:00:00,115.59,107.568,68.892,31.349 +2020-10-14 09:15:00,114.95,105.34,68.892,31.349 +2020-10-14 09:30:00,114.97,105.587,68.892,31.349 +2020-10-14 09:45:00,113.19,105.165,68.892,31.349 +2020-10-14 10:00:00,112.69,101.88,66.88600000000001,31.349 +2020-10-14 10:15:00,113.46,100.93700000000001,66.88600000000001,31.349 +2020-10-14 10:30:00,111.52,99.751,66.88600000000001,31.349 +2020-10-14 10:45:00,111.35,99.469,66.88600000000001,31.349 +2020-10-14 11:00:00,109.19,96.285,66.187,31.349 +2020-10-14 11:15:00,108.97,96.74799999999999,66.187,31.349 +2020-10-14 11:30:00,109.66,96.945,66.187,31.349 +2020-10-14 11:45:00,107.61,96.51100000000001,66.187,31.349 +2020-10-14 12:00:00,105.62,92.641,62.18,31.349 +2020-10-14 12:15:00,107.74,92.162,62.18,31.349 +2020-10-14 12:30:00,102.93,91.781,62.18,31.349 +2020-10-14 12:45:00,105.05,92.044,62.18,31.349 +2020-10-14 13:00:00,103.94,91.37299999999999,62.23,31.349 +2020-10-14 13:15:00,104.45,91.285,62.23,31.349 +2020-10-14 13:30:00,103.05,90.414,62.23,31.349 +2020-10-14 13:45:00,104.96,89.976,62.23,31.349 +2020-10-14 14:00:00,105.25,89.01100000000001,63.721000000000004,31.349 +2020-10-14 14:15:00,105.9,89.475,63.721000000000004,31.349 +2020-10-14 14:30:00,106.58,89.037,63.721000000000004,31.349 +2020-10-14 14:45:00,106.09,88.876,63.721000000000004,31.349 +2020-10-14 15:00:00,106.28,88.58200000000001,66.523,31.349 +2020-10-14 15:15:00,106.7,88.389,66.523,31.349 +2020-10-14 15:30:00,107.34,89.088,66.523,31.349 +2020-10-14 15:45:00,109.23,89.23,66.523,31.349 +2020-10-14 16:00:00,110.31,88.816,69.679,31.349 +2020-10-14 16:15:00,111.71,88.462,69.679,31.349 +2020-10-14 16:30:00,113.73,89.105,69.679,31.349 +2020-10-14 16:45:00,116.27,87.84700000000001,69.679,31.349 +2020-10-14 17:00:00,121.1,87.74600000000001,75.04,31.349 +2020-10-14 17:15:00,119.15,89.189,75.04,31.349 +2020-10-14 17:30:00,125.29,88.95,75.04,31.349 +2020-10-14 17:45:00,126.05,89.851,75.04,31.349 +2020-10-14 18:00:00,126.68,89.385,75.915,31.349 +2020-10-14 18:15:00,123.93,89.046,75.915,31.349 +2020-10-14 18:30:00,123.69,88.21799999999999,75.915,31.349 +2020-10-14 18:45:00,120.41,91.794,75.915,31.349 +2020-10-14 19:00:00,120.5,92.323,74.66,31.349 +2020-10-14 19:15:00,119.72,91.131,74.66,31.349 +2020-10-14 19:30:00,116.45,90.488,74.66,31.349 +2020-10-14 19:45:00,107.96,90.303,74.66,31.349 +2020-10-14 20:00:00,107.34,89.848,71.204,31.349 +2020-10-14 20:15:00,109.49,87.55799999999999,71.204,31.349 +2020-10-14 20:30:00,100.41,86.404,71.204,31.349 +2020-10-14 20:45:00,104.83,85.022,71.204,31.349 +2020-10-14 21:00:00,93.46,83.021,61.052,31.349 +2020-10-14 21:15:00,92.85,83.454,61.052,31.349 +2020-10-14 21:30:00,93.83,82.68700000000001,61.052,31.349 +2020-10-14 21:45:00,95.35,80.72800000000001,61.052,31.349 +2020-10-14 22:00:00,90.21,77.693,54.691,31.349 +2020-10-14 22:15:00,86.19,75.244,54.691,31.349 +2020-10-14 22:30:00,79.18,64.979,54.691,31.349 +2020-10-14 22:45:00,83.68,59.871,54.691,31.349 +2020-10-14 23:00:00,81.26,55.456,45.18,31.349 +2020-10-14 23:15:00,82.45,54.794,45.18,31.349 +2020-10-14 23:30:00,76.29,54.28,45.18,31.349 +2020-10-14 23:45:00,79.49,54.183,45.18,31.349 +2020-10-15 00:00:00,78.17,52.367,42.746,31.349 +2020-10-15 00:15:00,79.69,53.452,42.746,31.349 +2020-10-15 00:30:00,76.7,53.227,42.746,31.349 +2020-10-15 00:45:00,80.0,53.082,42.746,31.349 +2020-10-15 01:00:00,77.04,53.437,40.025999999999996,31.349 +2020-10-15 01:15:00,76.62,53.093999999999994,40.025999999999996,31.349 +2020-10-15 01:30:00,72.38,52.538999999999994,40.025999999999996,31.349 +2020-10-15 01:45:00,72.39,52.033,40.025999999999996,31.349 +2020-10-15 02:00:00,75.25,52.565,38.154,31.349 +2020-10-15 02:15:00,79.25,52.799,38.154,31.349 +2020-10-15 02:30:00,80.42,53.161,38.154,31.349 +2020-10-15 02:45:00,76.0,53.781000000000006,38.154,31.349 +2020-10-15 03:00:00,78.42,55.809,37.575,31.349 +2020-10-15 03:15:00,81.16,56.821000000000005,37.575,31.349 +2020-10-15 03:30:00,82.38,56.92100000000001,37.575,31.349 +2020-10-15 03:45:00,80.84,57.42100000000001,37.575,31.349 +2020-10-15 04:00:00,86.29,66.514,39.154,31.349 +2020-10-15 04:15:00,91.49,75.59,39.154,31.349 +2020-10-15 04:30:00,94.3,74.887,39.154,31.349 +2020-10-15 04:45:00,97.08,76.622,39.154,31.349 +2020-10-15 05:00:00,98.74,103.00299999999999,44.085,31.349 +2020-10-15 05:15:00,105.55,126.2,44.085,31.349 +2020-10-15 05:30:00,107.13,120.055,44.085,31.349 +2020-10-15 05:45:00,111.73,111.80799999999999,44.085,31.349 +2020-10-15 06:00:00,119.93,111.52,57.49,31.349 +2020-10-15 06:15:00,118.79,115.337,57.49,31.349 +2020-10-15 06:30:00,120.03,113.837,57.49,31.349 +2020-10-15 06:45:00,122.37,114.57,57.49,31.349 +2020-10-15 07:00:00,124.75,114.81200000000001,73.617,31.349 +2020-10-15 07:15:00,124.91,117.212,73.617,31.349 +2020-10-15 07:30:00,125.12,117.286,73.617,31.349 +2020-10-15 07:45:00,121.11,117.48700000000001,73.617,31.349 +2020-10-15 08:00:00,118.19,116.01,69.281,31.349 +2020-10-15 08:15:00,117.96,115.90899999999999,69.281,31.349 +2020-10-15 08:30:00,122.14,113.867,69.281,31.349 +2020-10-15 08:45:00,122.14,112.792,69.281,31.349 +2020-10-15 09:00:00,120.75,108.12799999999999,63.926,31.349 +2020-10-15 09:15:00,121.83,105.895,63.926,31.349 +2020-10-15 09:30:00,123.0,106.12700000000001,63.926,31.349 +2020-10-15 09:45:00,123.77,105.677,63.926,31.349 +2020-10-15 10:00:00,117.9,102.385,59.442,31.349 +2020-10-15 10:15:00,119.2,101.40299999999999,59.442,31.349 +2020-10-15 10:30:00,115.6,100.199,59.442,31.349 +2020-10-15 10:45:00,114.68,99.9,59.442,31.349 +2020-10-15 11:00:00,111.42,96.72399999999999,56.771,31.349 +2020-10-15 11:15:00,110.71,97.169,56.771,31.349 +2020-10-15 11:30:00,111.45,97.36399999999999,56.771,31.349 +2020-10-15 11:45:00,110.73,96.916,56.771,31.349 +2020-10-15 12:00:00,108.08,93.021,53.701,31.349 +2020-10-15 12:15:00,108.8,92.53399999999999,53.701,31.349 +2020-10-15 12:30:00,106.76,92.189,53.701,31.349 +2020-10-15 12:45:00,106.52,92.447,53.701,31.349 +2020-10-15 13:00:00,106.01,91.74600000000001,52.364,31.349 +2020-10-15 13:15:00,105.75,91.661,52.364,31.349 +2020-10-15 13:30:00,107.45,90.788,52.364,31.349 +2020-10-15 13:45:00,110.96,90.34899999999999,52.364,31.349 +2020-10-15 14:00:00,110.78,89.334,53.419,31.349 +2020-10-15 14:15:00,109.56,89.81299999999999,53.419,31.349 +2020-10-15 14:30:00,111.28,89.411,53.419,31.349 +2020-10-15 14:45:00,109.87,89.24700000000001,53.419,31.349 +2020-10-15 15:00:00,111.22,88.921,56.744,31.349 +2020-10-15 15:15:00,111.54,88.74600000000001,56.744,31.349 +2020-10-15 15:30:00,111.77,89.48200000000001,56.744,31.349 +2020-10-15 15:45:00,113.05,89.63799999999999,56.744,31.349 +2020-10-15 16:00:00,116.56,89.189,60.458,31.349 +2020-10-15 16:15:00,120.2,88.854,60.458,31.349 +2020-10-15 16:30:00,122.91,89.492,60.458,31.349 +2020-10-15 16:45:00,122.73,88.289,60.458,31.349 +2020-10-15 17:00:00,126.75,88.146,66.295,31.349 +2020-10-15 17:15:00,126.46,89.611,66.295,31.349 +2020-10-15 17:30:00,128.48,89.376,66.295,31.349 +2020-10-15 17:45:00,124.9,90.3,66.295,31.349 +2020-10-15 18:00:00,127.14,89.82,68.468,31.349 +2020-10-15 18:15:00,123.0,89.46700000000001,68.468,31.349 +2020-10-15 18:30:00,124.51,88.649,68.468,31.349 +2020-10-15 18:45:00,120.83,92.22,68.468,31.349 +2020-10-15 19:00:00,114.09,92.759,66.39399999999999,31.349 +2020-10-15 19:15:00,112.02,91.56299999999999,66.39399999999999,31.349 +2020-10-15 19:30:00,107.14,90.911,66.39399999999999,31.349 +2020-10-15 19:45:00,113.12,90.709,66.39399999999999,31.349 +2020-10-15 20:00:00,108.37,90.277,63.183,31.349 +2020-10-15 20:15:00,108.7,87.98200000000001,63.183,31.349 +2020-10-15 20:30:00,99.66,86.8,63.183,31.349 +2020-10-15 20:45:00,101.56,85.389,63.183,31.349 +2020-10-15 21:00:00,93.27,83.387,55.133,31.349 +2020-10-15 21:15:00,93.41,83.809,55.133,31.349 +2020-10-15 21:30:00,93.15,83.053,55.133,31.349 +2020-10-15 21:45:00,94.83,81.067,55.133,31.349 +2020-10-15 22:00:00,90.93,78.02,50.111999999999995,31.349 +2020-10-15 22:15:00,84.87,75.54899999999999,50.111999999999995,31.349 +2020-10-15 22:30:00,80.83,65.3,50.111999999999995,31.349 +2020-10-15 22:45:00,85.9,60.198,50.111999999999995,31.349 +2020-10-15 23:00:00,83.4,55.809,44.536,31.349 +2020-10-15 23:15:00,83.04,55.11600000000001,44.536,31.349 +2020-10-15 23:30:00,78.5,54.603,44.536,31.349 +2020-10-15 23:45:00,78.94,54.5,44.536,31.349 +2020-10-16 00:00:00,78.53,56.543,42.291000000000004,31.349 +2020-10-16 00:15:00,80.34,58.228,42.291000000000004,31.349 +2020-10-16 00:30:00,75.11,57.871,42.291000000000004,31.349 +2020-10-16 00:45:00,78.3,57.95399999999999,42.291000000000004,31.349 +2020-10-16 01:00:00,77.43,58.34,41.008,31.349 +2020-10-16 01:15:00,79.28,57.986000000000004,41.008,31.349 +2020-10-16 01:30:00,77.29,57.566,41.008,31.349 +2020-10-16 01:45:00,76.13,57.258,41.008,31.349 +2020-10-16 02:00:00,78.43,58.57,39.521,31.349 +2020-10-16 02:15:00,78.48,58.61600000000001,39.521,31.349 +2020-10-16 02:30:00,76.3,59.912,39.521,31.349 +2020-10-16 02:45:00,72.93,60.25899999999999,39.521,31.349 +2020-10-16 03:00:00,75.99,62.902,39.812,31.349 +2020-10-16 03:15:00,78.35,63.992,39.812,31.349 +2020-10-16 03:30:00,83.32,64.1,39.812,31.349 +2020-10-16 03:45:00,87.67,65.527,39.812,31.349 +2020-10-16 04:00:00,86.71,75.56,41.22,31.349 +2020-10-16 04:15:00,87.83,84.14,41.22,31.349 +2020-10-16 04:30:00,93.37,84.585,41.22,31.349 +2020-10-16 04:45:00,97.81,85.63799999999999,41.22,31.349 +2020-10-16 05:00:00,105.43,113.375,45.115,31.349 +2020-10-16 05:15:00,108.97,139.797,45.115,31.349 +2020-10-16 05:30:00,110.44,134.631,45.115,31.349 +2020-10-16 05:45:00,116.33,125.77,45.115,31.349 +2020-10-16 06:00:00,121.67,125.60799999999999,59.06100000000001,31.349 +2020-10-16 06:15:00,121.67,129.158,59.06100000000001,31.349 +2020-10-16 06:30:00,123.2,127.425,59.06100000000001,31.349 +2020-10-16 06:45:00,125.24,128.667,59.06100000000001,31.349 +2020-10-16 07:00:00,129.04,129.357,71.874,31.349 +2020-10-16 07:15:00,129.43,132.971,71.874,31.349 +2020-10-16 07:30:00,129.86,132.234,71.874,31.349 +2020-10-16 07:45:00,129.09,131.789,71.874,31.349 +2020-10-16 08:00:00,129.38,131.43,68.439,31.349 +2020-10-16 08:15:00,130.59,131.24200000000002,68.439,31.349 +2020-10-16 08:30:00,135.1,129.144,68.439,31.349 +2020-10-16 08:45:00,135.29,126.51,68.439,31.349 +2020-10-16 09:00:00,133.1,121.61399999999999,65.523,31.349 +2020-10-16 09:15:00,134.37,120.37200000000001,65.523,31.349 +2020-10-16 09:30:00,133.1,119.85799999999999,65.523,31.349 +2020-10-16 09:45:00,136.75,119.541,65.523,31.349 +2020-10-16 10:00:00,133.64,115.68799999999999,62.005,31.349 +2020-10-16 10:15:00,134.38,115.005,62.005,31.349 +2020-10-16 10:30:00,133.95,113.708,62.005,31.349 +2020-10-16 10:45:00,134.02,113.04,62.005,31.349 +2020-10-16 11:00:00,132.18,109.76799999999999,60.351000000000006,31.349 +2020-10-16 11:15:00,131.6,109.25299999999999,60.351000000000006,31.349 +2020-10-16 11:30:00,132.82,110.32799999999999,60.351000000000006,31.349 +2020-10-16 11:45:00,132.96,110.382,60.351000000000006,31.349 +2020-10-16 12:00:00,127.23,107.589,55.331,31.349 +2020-10-16 12:15:00,125.71,105.316,55.331,31.349 +2020-10-16 12:30:00,122.7,105.105,55.331,31.349 +2020-10-16 12:45:00,120.88,105.475,55.331,31.349 +2020-10-16 13:00:00,119.13,106.29700000000001,53.361999999999995,31.349 +2020-10-16 13:15:00,118.48,106.384,53.361999999999995,31.349 +2020-10-16 13:30:00,117.76,105.999,53.361999999999995,31.349 +2020-10-16 13:45:00,117.81,105.741,53.361999999999995,31.349 +2020-10-16 14:00:00,116.71,104.645,51.708,31.349 +2020-10-16 14:15:00,115.79,104.56,51.708,31.349 +2020-10-16 14:30:00,116.75,104.74799999999999,51.708,31.349 +2020-10-16 14:45:00,114.09,104.55,51.708,31.349 +2020-10-16 15:00:00,111.63,103.62700000000001,54.571000000000005,31.349 +2020-10-16 15:15:00,111.74,103.186,54.571000000000005,31.349 +2020-10-16 15:30:00,111.8,102.095,54.571000000000005,31.349 +2020-10-16 15:45:00,112.85,102.43700000000001,54.571000000000005,31.349 +2020-10-16 16:00:00,116.73,102.41799999999999,58.662,31.349 +2020-10-16 16:15:00,115.35,103.176,58.662,31.349 +2020-10-16 16:30:00,120.57,103.56,58.662,31.349 +2020-10-16 16:45:00,121.41,101.693,58.662,31.349 +2020-10-16 17:00:00,125.43,103.272,65.941,31.349 +2020-10-16 17:15:00,124.15,103.835,65.941,31.349 +2020-10-16 17:30:00,126.8,103.678,65.941,31.349 +2020-10-16 17:45:00,124.08,103.572,65.941,31.349 +2020-10-16 18:00:00,125.15,105.102,65.628,31.349 +2020-10-16 18:15:00,120.47,104.225,65.628,31.349 +2020-10-16 18:30:00,119.06,103.037,65.628,31.349 +2020-10-16 18:45:00,118.12,106.52799999999999,65.628,31.349 +2020-10-16 19:00:00,111.76,108.321,63.662,31.349 +2020-10-16 19:15:00,108.6,107.913,63.662,31.349 +2020-10-16 19:30:00,107.6,106.984,63.662,31.349 +2020-10-16 19:45:00,105.8,105.885,63.662,31.349 +2020-10-16 20:00:00,105.65,102.874,61.945,31.349 +2020-10-16 20:15:00,104.08,100.425,61.945,31.349 +2020-10-16 20:30:00,97.94,99.93700000000001,61.945,31.349 +2020-10-16 20:45:00,91.95,98.415,61.945,31.349 +2020-10-16 21:00:00,89.11,95.71700000000001,53.903,31.349 +2020-10-16 21:15:00,85.85,96.61399999999999,53.903,31.349 +2020-10-16 21:30:00,82.94,95.831,53.903,31.349 +2020-10-16 21:45:00,80.74,93.97200000000001,53.903,31.349 +2020-10-16 22:00:00,77.43,89.986,48.403999999999996,31.349 +2020-10-16 22:15:00,76.58,86.50399999999999,48.403999999999996,31.349 +2020-10-16 22:30:00,73.82,80.395,48.403999999999996,31.349 +2020-10-16 22:45:00,73.45,76.143,48.403999999999996,31.349 +2020-10-16 23:00:00,75.53,70.91199999999999,41.07,31.349 +2020-10-16 23:15:00,75.35,69.029,41.07,31.349 +2020-10-16 23:30:00,75.46,66.437,41.07,31.349 +2020-10-16 23:45:00,67.11,66.324,41.07,31.349 +2020-10-17 00:00:00,64.47,56.047,38.989000000000004,31.177 +2020-10-17 00:15:00,67.85,55.284,38.989000000000004,31.177 +2020-10-17 00:30:00,69.98,55.373000000000005,38.989000000000004,31.177 +2020-10-17 00:45:00,71.35,55.461000000000006,38.989000000000004,31.177 +2020-10-17 01:00:00,65.24,56.316,35.275,31.177 +2020-10-17 01:15:00,64.34,55.773999999999994,35.275,31.177 +2020-10-17 01:30:00,64.87,54.731,35.275,31.177 +2020-10-17 01:45:00,69.92,54.907,35.275,31.177 +2020-10-17 02:00:00,67.08,56.114,32.838,31.177 +2020-10-17 02:15:00,64.62,55.611999999999995,32.838,31.177 +2020-10-17 02:30:00,62.67,55.968,32.838,31.177 +2020-10-17 02:45:00,62.07,56.736999999999995,32.838,31.177 +2020-10-17 03:00:00,61.8,59.028999999999996,32.418,31.177 +2020-10-17 03:15:00,61.68,59.211999999999996,32.418,31.177 +2020-10-17 03:30:00,62.25,58.705,32.418,31.177 +2020-10-17 03:45:00,62.17,60.88399999999999,32.418,31.177 +2020-10-17 04:00:00,63.27,68.046,32.099000000000004,31.177 +2020-10-17 04:15:00,63.3,74.992,32.099000000000004,31.177 +2020-10-17 04:30:00,63.92,73.575,32.099000000000004,31.177 +2020-10-17 04:45:00,65.08,74.53,32.099000000000004,31.177 +2020-10-17 05:00:00,68.03,90.771,32.926,31.177 +2020-10-17 05:15:00,68.43,102.831,32.926,31.177 +2020-10-17 05:30:00,69.72,98.57600000000001,32.926,31.177 +2020-10-17 05:45:00,72.43,94.47,32.926,31.177 +2020-10-17 06:00:00,75.18,108.464,35.069,31.177 +2020-10-17 06:15:00,75.93,123.736,35.069,31.177 +2020-10-17 06:30:00,77.23,118.042,35.069,31.177 +2020-10-17 06:45:00,80.22,113.37700000000001,35.069,31.177 +2020-10-17 07:00:00,83.39,111.169,40.906,31.177 +2020-10-17 07:15:00,84.09,113.566,40.906,31.177 +2020-10-17 07:30:00,85.78,114.92299999999999,40.906,31.177 +2020-10-17 07:45:00,87.06,116.913,40.906,31.177 +2020-10-17 08:00:00,87.85,118.645,46.603,31.177 +2020-10-17 08:15:00,88.48,120.234,46.603,31.177 +2020-10-17 08:30:00,88.17,118.999,46.603,31.177 +2020-10-17 08:45:00,89.51,118.406,46.603,31.177 +2020-10-17 09:00:00,88.81,115.679,49.935,31.177 +2020-10-17 09:15:00,91.01,115.045,49.935,31.177 +2020-10-17 09:30:00,92.06,115.214,49.935,31.177 +2020-10-17 09:45:00,91.72,114.764,49.935,31.177 +2020-10-17 10:00:00,86.24,111.156,47.585,31.177 +2020-10-17 10:15:00,83.34,110.669,47.585,31.177 +2020-10-17 10:30:00,82.45,109.304,47.585,31.177 +2020-10-17 10:45:00,81.32,109.196,47.585,31.177 +2020-10-17 11:00:00,79.02,105.931,43.376999999999995,31.177 +2020-10-17 11:15:00,79.49,105.35600000000001,43.376999999999995,31.177 +2020-10-17 11:30:00,76.81,105.913,43.376999999999995,31.177 +2020-10-17 11:45:00,81.02,105.679,43.376999999999995,31.177 +2020-10-17 12:00:00,75.69,102.375,40.855,31.177 +2020-10-17 12:15:00,78.67,100.795,40.855,31.177 +2020-10-17 12:30:00,77.84,100.75200000000001,40.855,31.177 +2020-10-17 12:45:00,75.43,100.959,40.855,31.177 +2020-10-17 13:00:00,74.06,101.12700000000001,37.251,31.177 +2020-10-17 13:15:00,75.51,99.822,37.251,31.177 +2020-10-17 13:30:00,76.5,99.24700000000001,37.251,31.177 +2020-10-17 13:45:00,76.49,98.74799999999999,37.251,31.177 +2020-10-17 14:00:00,73.63,98.32,38.548,31.177 +2020-10-17 14:15:00,76.38,97.464,38.548,31.177 +2020-10-17 14:30:00,77.84,96.46700000000001,38.548,31.177 +2020-10-17 14:45:00,80.65,96.57700000000001,38.548,31.177 +2020-10-17 15:00:00,81.45,96.179,42.883,31.177 +2020-10-17 15:15:00,81.41,96.49600000000001,42.883,31.177 +2020-10-17 15:30:00,83.3,96.42,42.883,31.177 +2020-10-17 15:45:00,84.57,96.435,42.883,31.177 +2020-10-17 16:00:00,85.25,96.794,48.143,31.177 +2020-10-17 16:15:00,84.79,97.71600000000001,48.143,31.177 +2020-10-17 16:30:00,86.19,98.15799999999999,48.143,31.177 +2020-10-17 16:45:00,89.26,96.83,48.143,31.177 +2020-10-17 17:00:00,95.62,97.73299999999999,55.25,31.177 +2020-10-17 17:15:00,96.78,98.31700000000001,55.25,31.177 +2020-10-17 17:30:00,101.35,98.053,55.25,31.177 +2020-10-17 17:45:00,99.12,97.947,55.25,31.177 +2020-10-17 18:00:00,101.19,99.876,57.506,31.177 +2020-10-17 18:15:00,98.31,100.652,57.506,31.177 +2020-10-17 18:30:00,99.49,100.771,57.506,31.177 +2020-10-17 18:45:00,95.84,100.971,57.506,31.177 +2020-10-17 19:00:00,89.41,102.62799999999999,55.528999999999996,31.177 +2020-10-17 19:15:00,87.46,101.51,55.528999999999996,31.177 +2020-10-17 19:30:00,85.9,101.3,55.528999999999996,31.177 +2020-10-17 19:45:00,84.72,100.851,55.528999999999996,31.177 +2020-10-17 20:00:00,80.41,99.40299999999999,46.166000000000004,31.177 +2020-10-17 20:15:00,80.37,97.781,46.166000000000004,31.177 +2020-10-17 20:30:00,79.26,96.693,46.166000000000004,31.177 +2020-10-17 20:45:00,77.14,95.757,46.166000000000004,31.177 +2020-10-17 21:00:00,73.18,93.601,40.406,31.177 +2020-10-17 21:15:00,72.29,94.561,40.406,31.177 +2020-10-17 21:30:00,70.67,94.51,40.406,31.177 +2020-10-17 21:45:00,69.52,92.175,40.406,31.177 +2020-10-17 22:00:00,66.37,88.87700000000001,39.616,31.177 +2020-10-17 22:15:00,65.35,86.779,39.616,31.177 +2020-10-17 22:30:00,63.44,83.772,39.616,31.177 +2020-10-17 22:45:00,62.35,80.665,39.616,31.177 +2020-10-17 23:00:00,58.53,76.434,32.205,31.177 +2020-10-17 23:15:00,56.67,73.855,32.205,31.177 +2020-10-17 23:30:00,56.68,71.44,32.205,31.177 +2020-10-17 23:45:00,56.38,70.208,32.205,31.177 +2020-10-18 00:00:00,52.9,57.243,28.229,31.177 +2020-10-18 00:15:00,53.22,55.765,28.229,31.177 +2020-10-18 00:30:00,52.88,55.606,28.229,31.177 +2020-10-18 00:45:00,53.61,55.98,28.229,31.177 +2020-10-18 01:00:00,51.32,56.903999999999996,25.669,31.177 +2020-10-18 01:15:00,52.64,56.831,25.669,31.177 +2020-10-18 01:30:00,51.62,55.996,25.669,31.177 +2020-10-18 01:45:00,51.85,55.832,25.669,31.177 +2020-10-18 02:00:00,51.91,56.717,24.948,31.177 +2020-10-18 02:15:00,52.54,56.151,24.948,31.177 +2020-10-18 02:30:00,52.65,56.998000000000005,24.948,31.177 +2020-10-18 02:45:00,52.06,57.849,24.948,31.177 +2020-10-18 03:00:00,51.53,60.59,24.445,31.177 +2020-10-18 03:15:00,52.25,60.667,24.445,31.177 +2020-10-18 03:30:00,52.4,60.486999999999995,24.445,31.177 +2020-10-18 03:45:00,52.91,62.228,24.445,31.177 +2020-10-18 04:00:00,54.69,69.267,25.839000000000002,31.177 +2020-10-18 04:15:00,55.62,75.545,25.839000000000002,31.177 +2020-10-18 04:30:00,56.31,74.807,25.839000000000002,31.177 +2020-10-18 04:45:00,56.9,75.675,25.839000000000002,31.177 +2020-10-18 05:00:00,58.65,90.402,26.803,31.177 +2020-10-18 05:15:00,58.63,101.00399999999999,26.803,31.177 +2020-10-18 05:30:00,59.14,96.463,26.803,31.177 +2020-10-18 05:45:00,57.88,92.30799999999999,26.803,31.177 +2020-10-18 06:00:00,62.76,105.06200000000001,28.147,31.177 +2020-10-18 06:15:00,63.62,119.914,28.147,31.177 +2020-10-18 06:30:00,63.69,113.374,28.147,31.177 +2020-10-18 06:45:00,65.7,107.73,28.147,31.177 +2020-10-18 07:00:00,70.03,106.82,31.116,31.177 +2020-10-18 07:15:00,71.46,108.045,31.116,31.177 +2020-10-18 07:30:00,71.58,109.411,31.116,31.177 +2020-10-18 07:45:00,72.74,111.01,31.116,31.177 +2020-10-18 08:00:00,71.27,113.97399999999999,35.739000000000004,31.177 +2020-10-18 08:15:00,71.83,116.021,35.739000000000004,31.177 +2020-10-18 08:30:00,71.64,116.059,35.739000000000004,31.177 +2020-10-18 08:45:00,73.24,116.448,35.739000000000004,31.177 +2020-10-18 09:00:00,73.13,113.434,39.455999999999996,31.177 +2020-10-18 09:15:00,75.2,112.875,39.455999999999996,31.177 +2020-10-18 09:30:00,75.81,113.135,39.455999999999996,31.177 +2020-10-18 09:45:00,78.43,113.03299999999999,39.455999999999996,31.177 +2020-10-18 10:00:00,74.81,110.97200000000001,41.343999999999994,31.177 +2020-10-18 10:15:00,75.06,110.79299999999999,41.343999999999994,31.177 +2020-10-18 10:30:00,75.48,109.82700000000001,41.343999999999994,31.177 +2020-10-18 10:45:00,76.1,109.087,41.343999999999994,31.177 +2020-10-18 11:00:00,75.4,106.171,43.645,31.177 +2020-10-18 11:15:00,70.54,105.47,43.645,31.177 +2020-10-18 11:30:00,68.91,105.75200000000001,43.645,31.177 +2020-10-18 11:45:00,69.28,105.943,43.645,31.177 +2020-10-18 12:00:00,69.75,102.689,39.796,31.177 +2020-10-18 12:15:00,67.11,101.93700000000001,39.796,31.177 +2020-10-18 12:30:00,67.36,101.18799999999999,39.796,31.177 +2020-10-18 12:45:00,69.13,100.594,39.796,31.177 +2020-10-18 13:00:00,64.24,100.186,36.343,31.177 +2020-10-18 13:15:00,62.42,100.39,36.343,31.177 +2020-10-18 13:30:00,63.26,99.26299999999999,36.343,31.177 +2020-10-18 13:45:00,63.85,98.81700000000001,36.343,31.177 +2020-10-18 14:00:00,61.41,98.94,33.162,31.177 +2020-10-18 14:15:00,61.03,98.955,33.162,31.177 +2020-10-18 14:30:00,62.69,98.22399999999999,33.162,31.177 +2020-10-18 14:45:00,64.11,97.681,33.162,31.177 +2020-10-18 15:00:00,66.4,96.44,33.215,31.177 +2020-10-18 15:15:00,66.52,96.898,33.215,31.177 +2020-10-18 15:30:00,68.51,97.12799999999999,33.215,31.177 +2020-10-18 15:45:00,71.34,97.664,33.215,31.177 +2020-10-18 16:00:00,75.08,98.287,37.385999999999996,31.177 +2020-10-18 16:15:00,76.81,98.823,37.385999999999996,31.177 +2020-10-18 16:30:00,79.34,99.815,37.385999999999996,31.177 +2020-10-18 16:45:00,83.18,98.639,37.385999999999996,31.177 +2020-10-18 17:00:00,89.98,99.574,46.618,31.177 +2020-10-18 17:15:00,92.24,100.639,46.618,31.177 +2020-10-18 17:30:00,94.57,100.874,46.618,31.177 +2020-10-18 17:45:00,94.81,102.181,46.618,31.177 +2020-10-18 18:00:00,98.0,104.02799999999999,50.111000000000004,31.177 +2020-10-18 18:15:00,94.97,105.304,50.111000000000004,31.177 +2020-10-18 18:30:00,93.21,104.22200000000001,50.111000000000004,31.177 +2020-10-18 18:45:00,91.12,105.446,50.111000000000004,31.177 +2020-10-18 19:00:00,88.83,107.86,50.25,31.177 +2020-10-18 19:15:00,92.4,106.557,50.25,31.177 +2020-10-18 19:30:00,93.45,106.14,50.25,31.177 +2020-10-18 19:45:00,92.2,106.221,50.25,31.177 +2020-10-18 20:00:00,82.79,104.83200000000001,44.265,31.177 +2020-10-18 20:15:00,85.44,103.654,44.265,31.177 +2020-10-18 20:30:00,87.59,103.538,44.265,31.177 +2020-10-18 20:45:00,91.02,101.215,44.265,31.177 +2020-10-18 21:00:00,88.07,97.631,39.717,31.177 +2020-10-18 21:15:00,83.42,98.124,39.717,31.177 +2020-10-18 21:30:00,80.07,97.94200000000001,39.717,31.177 +2020-10-18 21:45:00,83.72,95.816,39.717,31.177 +2020-10-18 22:00:00,81.94,92.81299999999999,39.224000000000004,31.177 +2020-10-18 22:15:00,82.38,89.552,39.224000000000004,31.177 +2020-10-18 22:30:00,77.0,84.719,39.224000000000004,31.177 +2020-10-18 22:45:00,77.52,80.611,39.224000000000004,31.177 +2020-10-18 23:00:00,75.69,74.82,33.518,31.177 +2020-10-18 23:15:00,75.04,73.737,33.518,31.177 +2020-10-18 23:30:00,72.22,71.502,33.518,31.177 +2020-10-18 23:45:00,70.95,70.789,33.518,31.177 +2020-10-19 00:00:00,72.57,60.35,34.301,31.349 +2020-10-19 00:15:00,72.05,60.687,34.301,31.349 +2020-10-19 00:30:00,71.7,60.415,34.301,31.349 +2020-10-19 00:45:00,68.05,60.333999999999996,34.301,31.349 +2020-10-19 01:00:00,63.81,61.445,34.143,31.349 +2020-10-19 01:15:00,70.57,61.14,34.143,31.349 +2020-10-19 01:30:00,73.46,60.521,34.143,31.349 +2020-10-19 01:45:00,73.96,60.363,34.143,31.349 +2020-10-19 02:00:00,69.68,61.452,33.650999999999996,31.349 +2020-10-19 02:15:00,71.16,61.118,33.650999999999996,31.349 +2020-10-19 02:30:00,74.31,62.191,33.650999999999996,31.349 +2020-10-19 02:45:00,75.73,62.668,33.650999999999996,31.349 +2020-10-19 03:00:00,74.19,66.23100000000001,32.599000000000004,31.349 +2020-10-19 03:15:00,76.4,67.46600000000001,32.599000000000004,31.349 +2020-10-19 03:30:00,80.44,67.487,32.599000000000004,31.349 +2020-10-19 03:45:00,82.75,68.72399999999999,32.599000000000004,31.349 +2020-10-19 04:00:00,83.01,79.33800000000001,33.785,31.349 +2020-10-19 04:15:00,86.46,89.04,33.785,31.349 +2020-10-19 04:30:00,93.16,89.11200000000001,33.785,31.349 +2020-10-19 04:45:00,99.61,90.245,33.785,31.349 +2020-10-19 05:00:00,103.97,115.891,41.285,31.349 +2020-10-19 05:15:00,106.86,141.102,41.285,31.349 +2020-10-19 05:30:00,111.68,135.92700000000002,41.285,31.349 +2020-10-19 05:45:00,123.4,127.571,41.285,31.349 +2020-10-19 06:00:00,130.7,127.085,60.486000000000004,31.349 +2020-10-19 06:15:00,123.9,130.441,60.486000000000004,31.349 +2020-10-19 06:30:00,124.3,129.692,60.486000000000004,31.349 +2020-10-19 06:45:00,125.37,130.83,60.486000000000004,31.349 +2020-10-19 07:00:00,128.39,131.643,74.012,31.349 +2020-10-19 07:15:00,126.92,134.558,74.012,31.349 +2020-10-19 07:30:00,124.7,135.17,74.012,31.349 +2020-10-19 07:45:00,123.89,135.63299999999998,74.012,31.349 +2020-10-19 08:00:00,122.1,135.503,69.569,31.349 +2020-10-19 08:15:00,120.91,135.888,69.569,31.349 +2020-10-19 08:30:00,123.72,133.418,69.569,31.349 +2020-10-19 08:45:00,126.6,132.155,69.569,31.349 +2020-10-19 09:00:00,123.28,128.308,66.152,31.349 +2020-10-19 09:15:00,122.26,125.132,66.152,31.349 +2020-10-19 09:30:00,120.9,124.45,66.152,31.349 +2020-10-19 09:45:00,122.7,123.491,66.152,31.349 +2020-10-19 10:00:00,123.26,121.242,62.923,31.349 +2020-10-19 10:15:00,123.92,120.78200000000001,62.923,31.349 +2020-10-19 10:30:00,124.65,119.12799999999999,62.923,31.349 +2020-10-19 10:45:00,121.74,118.13,62.923,31.349 +2020-10-19 11:00:00,111.45,113.977,61.522,31.349 +2020-10-19 11:15:00,111.72,114.376,61.522,31.349 +2020-10-19 11:30:00,111.64,115.708,61.522,31.349 +2020-10-19 11:45:00,114.51,115.85799999999999,61.522,31.349 +2020-10-19 12:00:00,109.18,113.042,58.632,31.349 +2020-10-19 12:15:00,109.27,112.32799999999999,58.632,31.349 +2020-10-19 12:30:00,119.09,111.34,58.632,31.349 +2020-10-19 12:45:00,120.49,111.61399999999999,58.632,31.349 +2020-10-19 13:00:00,121.81,111.93299999999999,59.06,31.349 +2020-10-19 13:15:00,123.36,111.008,59.06,31.349 +2020-10-19 13:30:00,124.18,109.641,59.06,31.349 +2020-10-19 13:45:00,123.73,109.555,59.06,31.349 +2020-10-19 14:00:00,128.78,108.941,59.791000000000004,31.349 +2020-10-19 14:15:00,127.05,108.83,59.791000000000004,31.349 +2020-10-19 14:30:00,122.62,107.762,59.791000000000004,31.349 +2020-10-19 14:45:00,122.32,107.991,59.791000000000004,31.349 +2020-10-19 15:00:00,121.8,107.624,61.148,31.349 +2020-10-19 15:15:00,124.23,107.045,61.148,31.349 +2020-10-19 15:30:00,120.17,107.131,61.148,31.349 +2020-10-19 15:45:00,122.07,107.24,61.148,31.349 +2020-10-19 16:00:00,125.4,108.13600000000001,66.009,31.349 +2020-10-19 16:15:00,122.23,108.296,66.009,31.349 +2020-10-19 16:30:00,123.96,108.48,66.009,31.349 +2020-10-19 16:45:00,124.66,106.705,66.009,31.349 +2020-10-19 17:00:00,132.19,106.90799999999999,73.683,31.349 +2020-10-19 17:15:00,128.75,107.635,73.683,31.349 +2020-10-19 17:30:00,131.94,107.412,73.683,31.349 +2020-10-19 17:45:00,128.27,107.76,73.683,31.349 +2020-10-19 18:00:00,132.72,109.30799999999999,72.848,31.349 +2020-10-19 18:15:00,126.09,108.596,72.848,31.349 +2020-10-19 18:30:00,127.58,107.54700000000001,72.848,31.349 +2020-10-19 18:45:00,123.85,110.50200000000001,72.848,31.349 +2020-10-19 19:00:00,120.43,111.916,71.139,31.349 +2020-10-19 19:15:00,112.13,110.569,71.139,31.349 +2020-10-19 19:30:00,108.57,110.238,71.139,31.349 +2020-10-19 19:45:00,108.74,109.59,71.139,31.349 +2020-10-19 20:00:00,106.01,106.426,69.667,31.349 +2020-10-19 20:15:00,109.28,104.56700000000001,69.667,31.349 +2020-10-19 20:30:00,106.18,103.68299999999999,69.667,31.349 +2020-10-19 20:45:00,100.16,102.28200000000001,69.667,31.349 +2020-10-19 21:00:00,94.21,98.705,61.166000000000004,31.349 +2020-10-19 21:15:00,95.43,98.78,61.166000000000004,31.349 +2020-10-19 21:30:00,96.14,98.366,61.166000000000004,31.349 +2020-10-19 21:45:00,94.59,95.859,61.166000000000004,31.349 +2020-10-19 22:00:00,89.3,90.444,52.772,31.349 +2020-10-19 22:15:00,84.72,87.365,52.772,31.349 +2020-10-19 22:30:00,86.82,75.70100000000001,52.772,31.349 +2020-10-19 22:45:00,86.53,69.094,52.772,31.349 +2020-10-19 23:00:00,81.21,63.718,45.136,31.349 +2020-10-19 23:15:00,79.44,63.248999999999995,45.136,31.349 +2020-10-19 23:30:00,81.27,62.427,45.136,31.349 +2020-10-19 23:45:00,78.67,62.832,45.136,31.349 +2020-10-20 00:00:00,75.94,59.277,47.35,31.349 +2020-10-20 00:15:00,72.26,60.708999999999996,47.35,31.349 +2020-10-20 00:30:00,77.13,60.317,47.35,31.349 +2020-10-20 00:45:00,79.34,60.123999999999995,47.35,31.349 +2020-10-20 01:00:00,76.83,60.9,43.424,31.349 +2020-10-20 01:15:00,74.74,60.449,43.424,31.349 +2020-10-20 01:30:00,77.46,59.869,43.424,31.349 +2020-10-20 01:45:00,79.29,59.611999999999995,43.424,31.349 +2020-10-20 02:00:00,74.21,60.479,41.778999999999996,31.349 +2020-10-20 02:15:00,72.87,60.657,41.778999999999996,31.349 +2020-10-20 02:30:00,80.67,61.238,41.778999999999996,31.349 +2020-10-20 02:45:00,80.69,61.875,41.778999999999996,31.349 +2020-10-20 03:00:00,84.16,64.58,40.771,31.349 +2020-10-20 03:15:00,76.84,65.876,40.771,31.349 +2020-10-20 03:30:00,81.98,66.119,40.771,31.349 +2020-10-20 03:45:00,85.52,66.896,40.771,31.349 +2020-10-20 04:00:00,88.83,76.83,41.816,31.349 +2020-10-20 04:15:00,86.61,86.4,41.816,31.349 +2020-10-20 04:30:00,89.0,86.262,41.816,31.349 +2020-10-20 04:45:00,93.57,88.22,41.816,31.349 +2020-10-20 05:00:00,103.8,117.08200000000001,45.842,31.349 +2020-10-20 05:15:00,114.96,142.561,45.842,31.349 +2020-10-20 05:30:00,119.33,136.741,45.842,31.349 +2020-10-20 05:45:00,118.64,127.999,45.842,31.349 +2020-10-20 06:00:00,120.99,127.436,59.12,31.349 +2020-10-20 06:15:00,123.0,131.688,59.12,31.349 +2020-10-20 06:30:00,124.14,130.493,59.12,31.349 +2020-10-20 06:45:00,126.4,131.034,59.12,31.349 +2020-10-20 07:00:00,128.62,131.822,70.33,31.349 +2020-10-20 07:15:00,126.71,134.554,70.33,31.349 +2020-10-20 07:30:00,126.14,134.931,70.33,31.349 +2020-10-20 07:45:00,125.14,135.055,70.33,31.349 +2020-10-20 08:00:00,124.17,134.97,67.788,31.349 +2020-10-20 08:15:00,128.23,134.60299999999998,67.788,31.349 +2020-10-20 08:30:00,129.26,132.143,67.788,31.349 +2020-10-20 08:45:00,129.38,130.308,67.788,31.349 +2020-10-20 09:00:00,127.86,126.161,62.622,31.349 +2020-10-20 09:15:00,129.76,123.774,62.622,31.349 +2020-10-20 09:30:00,128.95,123.736,62.622,31.349 +2020-10-20 09:45:00,129.62,123.212,62.622,31.349 +2020-10-20 10:00:00,130.0,120.056,60.887,31.349 +2020-10-20 10:15:00,130.9,118.914,60.887,31.349 +2020-10-20 10:30:00,130.71,117.34899999999999,60.887,31.349 +2020-10-20 10:45:00,129.56,116.9,60.887,31.349 +2020-10-20 11:00:00,126.57,113.57799999999999,59.812,31.349 +2020-10-20 11:15:00,127.65,113.931,59.812,31.349 +2020-10-20 11:30:00,129.33,114.149,59.812,31.349 +2020-10-20 11:45:00,128.71,114.49700000000001,59.812,31.349 +2020-10-20 12:00:00,126.55,110.807,56.614,31.349 +2020-10-20 12:15:00,128.41,109.98,56.614,31.349 +2020-10-20 12:30:00,126.5,109.773,56.614,31.349 +2020-10-20 12:45:00,126.79,110.145,56.614,31.349 +2020-10-20 13:00:00,125.27,110.044,56.824,31.349 +2020-10-20 13:15:00,127.5,109.61200000000001,56.824,31.349 +2020-10-20 13:30:00,127.16,108.911,56.824,31.349 +2020-10-20 13:45:00,125.66,108.56200000000001,56.824,31.349 +2020-10-20 14:00:00,126.58,108.228,57.623999999999995,31.349 +2020-10-20 14:15:00,125.58,108.148,57.623999999999995,31.349 +2020-10-20 14:30:00,125.54,107.60600000000001,57.623999999999995,31.349 +2020-10-20 14:45:00,124.61,107.48700000000001,57.623999999999995,31.349 +2020-10-20 15:00:00,124.48,106.787,59.724,31.349 +2020-10-20 15:15:00,125.53,106.771,59.724,31.349 +2020-10-20 15:30:00,122.51,106.945,59.724,31.349 +2020-10-20 15:45:00,123.47,106.96700000000001,59.724,31.349 +2020-10-20 16:00:00,124.86,107.792,61.64,31.349 +2020-10-20 16:15:00,126.88,108.274,61.64,31.349 +2020-10-20 16:30:00,127.79,108.666,61.64,31.349 +2020-10-20 16:45:00,130.62,107.39299999999999,61.64,31.349 +2020-10-20 17:00:00,132.63,107.946,68.962,31.349 +2020-10-20 17:15:00,134.24,108.882,68.962,31.349 +2020-10-20 17:30:00,132.03,108.838,68.962,31.349 +2020-10-20 17:45:00,129.1,108.991,68.962,31.349 +2020-10-20 18:00:00,128.24,110.06,69.149,31.349 +2020-10-20 18:15:00,127.87,109.694,69.149,31.349 +2020-10-20 18:30:00,123.22,108.37899999999999,69.149,31.349 +2020-10-20 18:45:00,124.82,111.66799999999999,69.149,31.349 +2020-10-20 19:00:00,115.2,112.62100000000001,68.832,31.349 +2020-10-20 19:15:00,110.22,111.197,68.832,31.349 +2020-10-20 19:30:00,108.41,110.40899999999999,68.832,31.349 +2020-10-20 19:45:00,104.68,109.90299999999999,68.832,31.349 +2020-10-20 20:00:00,101.56,107.01299999999999,66.403,31.349 +2020-10-20 20:15:00,101.65,104.178,66.403,31.349 +2020-10-20 20:30:00,100.36,103.81,66.403,31.349 +2020-10-20 20:45:00,100.6,102.27600000000001,66.403,31.349 +2020-10-20 21:00:00,91.23,98.71700000000001,57.352,31.349 +2020-10-20 21:15:00,90.92,98.59700000000001,57.352,31.349 +2020-10-20 21:30:00,88.27,97.89299999999999,57.352,31.349 +2020-10-20 21:45:00,86.17,95.569,57.352,31.349 +2020-10-20 22:00:00,81.6,91.111,51.148999999999994,31.349 +2020-10-20 22:15:00,80.06,87.729,51.148999999999994,31.349 +2020-10-20 22:30:00,78.33,76.27,51.148999999999994,31.349 +2020-10-20 22:45:00,77.72,69.81,51.148999999999994,31.349 +2020-10-20 23:00:00,75.14,64.126,41.8,31.349 +2020-10-20 23:15:00,72.31,63.852,41.8,31.349 +2020-10-20 23:30:00,72.2,62.843,41.8,31.349 +2020-10-20 23:45:00,70.69,63.091,41.8,31.349 +2020-10-21 00:00:00,69.76,59.614,42.269,31.349 +2020-10-21 00:15:00,70.51,61.033,42.269,31.349 +2020-10-21 00:30:00,68.91,60.648,42.269,31.349 +2020-10-21 00:45:00,70.39,60.449,42.269,31.349 +2020-10-21 01:00:00,68.96,61.236999999999995,38.527,31.349 +2020-10-21 01:15:00,69.7,60.805,38.527,31.349 +2020-10-21 01:30:00,66.77,60.242,38.527,31.349 +2020-10-21 01:45:00,69.61,59.981,38.527,31.349 +2020-10-21 02:00:00,69.44,60.858000000000004,36.393,31.349 +2020-10-21 02:15:00,70.57,61.048,36.393,31.349 +2020-10-21 02:30:00,70.25,61.61600000000001,36.393,31.349 +2020-10-21 02:45:00,70.99,62.246,36.393,31.349 +2020-10-21 03:00:00,72.78,64.939,36.167,31.349 +2020-10-21 03:15:00,73.7,66.257,36.167,31.349 +2020-10-21 03:30:00,74.4,66.502,36.167,31.349 +2020-10-21 03:45:00,74.06,67.263,36.167,31.349 +2020-10-21 04:00:00,80.73,77.21600000000001,38.092,31.349 +2020-10-21 04:15:00,83.23,86.816,38.092,31.349 +2020-10-21 04:30:00,94.46,86.675,38.092,31.349 +2020-10-21 04:45:00,98.59,88.64200000000001,38.092,31.349 +2020-10-21 05:00:00,105.32,117.571,42.268,31.349 +2020-10-21 05:15:00,106.7,143.113,42.268,31.349 +2020-10-21 05:30:00,113.14,137.283,42.268,31.349 +2020-10-21 05:45:00,116.18,128.509,42.268,31.349 +2020-10-21 06:00:00,122.3,127.931,60.158,31.349 +2020-10-21 06:15:00,125.24,132.197,60.158,31.349 +2020-10-21 06:30:00,128.18,131.024,60.158,31.349 +2020-10-21 06:45:00,129.46,131.575,60.158,31.349 +2020-10-21 07:00:00,132.09,132.361,74.792,31.349 +2020-10-21 07:15:00,133.19,135.106,74.792,31.349 +2020-10-21 07:30:00,135.44,135.514,74.792,31.349 +2020-10-21 07:45:00,133.65,135.642,74.792,31.349 +2020-10-21 08:00:00,132.41,135.57,70.499,31.349 +2020-10-21 08:15:00,134.22,135.18200000000002,70.499,31.349 +2020-10-21 08:30:00,134.75,132.749,70.499,31.349 +2020-10-21 08:45:00,134.77,130.888,70.499,31.349 +2020-10-21 09:00:00,135.22,126.735,68.892,31.349 +2020-10-21 09:15:00,136.3,124.346,68.892,31.349 +2020-10-21 09:30:00,138.43,124.294,68.892,31.349 +2020-10-21 09:45:00,138.85,123.74600000000001,68.892,31.349 +2020-10-21 10:00:00,138.15,120.583,66.88600000000001,31.349 +2020-10-21 10:15:00,138.15,119.40100000000001,66.88600000000001,31.349 +2020-10-21 10:30:00,137.96,117.815,66.88600000000001,31.349 +2020-10-21 10:45:00,135.02,117.351,66.88600000000001,31.349 +2020-10-21 11:00:00,131.3,114.03200000000001,66.187,31.349 +2020-10-21 11:15:00,131.26,114.366,66.187,31.349 +2020-10-21 11:30:00,131.11,114.583,66.187,31.349 +2020-10-21 11:45:00,132.59,114.916,66.187,31.349 +2020-10-21 12:00:00,129.61,111.206,62.18,31.349 +2020-10-21 12:15:00,129.87,110.37299999999999,62.18,31.349 +2020-10-21 12:30:00,128.91,110.20200000000001,62.18,31.349 +2020-10-21 12:45:00,128.14,110.572,62.18,31.349 +2020-10-21 13:00:00,124.86,110.43700000000001,62.23,31.349 +2020-10-21 13:15:00,121.86,110.012,62.23,31.349 +2020-10-21 13:30:00,123.13,109.31,62.23,31.349 +2020-10-21 13:45:00,120.42,108.958,62.23,31.349 +2020-10-21 14:00:00,118.39,108.573,63.721000000000004,31.349 +2020-10-21 14:15:00,120.77,108.508,63.721000000000004,31.349 +2020-10-21 14:30:00,121.85,108.00299999999999,63.721000000000004,31.349 +2020-10-21 14:45:00,121.34,107.882,63.721000000000004,31.349 +2020-10-21 15:00:00,118.71,107.161,66.523,31.349 +2020-10-21 15:15:00,118.61,107.163,66.523,31.349 +2020-10-21 15:30:00,118.37,107.37700000000001,66.523,31.349 +2020-10-21 15:45:00,117.66,107.412,66.523,31.349 +2020-10-21 16:00:00,117.97,108.20700000000001,69.679,31.349 +2020-10-21 16:15:00,119.99,108.711,69.679,31.349 +2020-10-21 16:30:00,122.09,109.101,69.679,31.349 +2020-10-21 16:45:00,123.57,107.87899999999999,69.679,31.349 +2020-10-21 17:00:00,130.15,108.39,75.04,31.349 +2020-10-21 17:15:00,128.12,109.345,75.04,31.349 +2020-10-21 17:30:00,132.36,109.304,75.04,31.349 +2020-10-21 17:45:00,129.08,109.47200000000001,75.04,31.349 +2020-10-21 18:00:00,130.24,110.53299999999999,75.915,31.349 +2020-10-21 18:15:00,127.7,110.141,75.915,31.349 +2020-10-21 18:30:00,127.89,108.836,75.915,31.349 +2020-10-21 18:45:00,123.32,112.119,75.915,31.349 +2020-10-21 19:00:00,118.23,113.083,74.66,31.349 +2020-10-21 19:15:00,113.94,111.652,74.66,31.349 +2020-10-21 19:30:00,110.24,110.85,74.66,31.349 +2020-10-21 19:45:00,109.15,110.32,74.66,31.349 +2020-10-21 20:00:00,103.44,107.45299999999999,71.204,31.349 +2020-10-21 20:15:00,104.11,104.609,71.204,31.349 +2020-10-21 20:30:00,104.78,104.212,71.204,31.349 +2020-10-21 20:45:00,105.75,102.661,71.204,31.349 +2020-10-21 21:00:00,100.72,99.09899999999999,61.052,31.349 +2020-10-21 21:15:00,95.64,98.96700000000001,61.052,31.349 +2020-10-21 21:30:00,91.76,98.273,61.052,31.349 +2020-10-21 21:45:00,95.11,95.929,61.052,31.349 +2020-10-21 22:00:00,91.39,91.464,54.691,31.349 +2020-10-21 22:15:00,87.48,88.06200000000001,54.691,31.349 +2020-10-21 22:30:00,84.33,76.632,54.691,31.349 +2020-10-21 22:45:00,85.24,70.179,54.691,31.349 +2020-10-21 23:00:00,81.86,64.51,45.18,31.349 +2020-10-21 23:15:00,83.34,64.208,45.18,31.349 +2020-10-21 23:30:00,79.08,63.201,45.18,31.349 +2020-10-21 23:45:00,78.07,63.438,45.18,31.349 +2020-10-22 00:00:00,78.96,59.951,42.746,31.349 +2020-10-22 00:15:00,80.34,61.357,42.746,31.349 +2020-10-22 00:30:00,76.75,60.979,42.746,31.349 +2020-10-22 00:45:00,74.47,60.773,42.746,31.349 +2020-10-22 01:00:00,74.08,61.575,40.025999999999996,31.349 +2020-10-22 01:15:00,78.96,61.161,40.025999999999996,31.349 +2020-10-22 01:30:00,78.92,60.614,40.025999999999996,31.349 +2020-10-22 01:45:00,72.66,60.349,40.025999999999996,31.349 +2020-10-22 02:00:00,72.85,61.236000000000004,38.154,31.349 +2020-10-22 02:15:00,72.18,61.438,38.154,31.349 +2020-10-22 02:30:00,80.12,61.99100000000001,38.154,31.349 +2020-10-22 02:45:00,80.24,62.618,38.154,31.349 +2020-10-22 03:00:00,77.98,65.297,37.575,31.349 +2020-10-22 03:15:00,77.21,66.63600000000001,37.575,31.349 +2020-10-22 03:30:00,76.58,66.88600000000001,37.575,31.349 +2020-10-22 03:45:00,81.48,67.62899999999999,37.575,31.349 +2020-10-22 04:00:00,90.01,77.601,39.154,31.349 +2020-10-22 04:15:00,92.44,87.23,39.154,31.349 +2020-10-22 04:30:00,87.34,87.086,39.154,31.349 +2020-10-22 04:45:00,93.79,89.06200000000001,39.154,31.349 +2020-10-22 05:00:00,105.64,118.059,44.085,31.349 +2020-10-22 05:15:00,114.87,143.665,44.085,31.349 +2020-10-22 05:30:00,114.7,137.82399999999998,44.085,31.349 +2020-10-22 05:45:00,113.54,129.018,44.085,31.349 +2020-10-22 06:00:00,122.12,128.42600000000002,57.49,31.349 +2020-10-22 06:15:00,124.14,132.705,57.49,31.349 +2020-10-22 06:30:00,127.55,131.553,57.49,31.349 +2020-10-22 06:45:00,128.59,132.114,57.49,31.349 +2020-10-22 07:00:00,132.08,132.9,73.617,31.349 +2020-10-22 07:15:00,132.26,135.658,73.617,31.349 +2020-10-22 07:30:00,133.23,136.096,73.617,31.349 +2020-10-22 07:45:00,133.61,136.227,73.617,31.349 +2020-10-22 08:00:00,133.59,136.168,69.281,31.349 +2020-10-22 08:15:00,133.94,135.75799999999998,69.281,31.349 +2020-10-22 08:30:00,133.56,133.351,69.281,31.349 +2020-10-22 08:45:00,134.29,131.466,69.281,31.349 +2020-10-22 09:00:00,132.11,127.307,63.926,31.349 +2020-10-22 09:15:00,134.27,124.915,63.926,31.349 +2020-10-22 09:30:00,133.85,124.851,63.926,31.349 +2020-10-22 09:45:00,133.96,124.27799999999999,63.926,31.349 +2020-10-22 10:00:00,131.38,121.10799999999999,59.442,31.349 +2020-10-22 10:15:00,133.21,119.88600000000001,59.442,31.349 +2020-10-22 10:30:00,132.9,118.28,59.442,31.349 +2020-10-22 10:45:00,132.11,117.79899999999999,59.442,31.349 +2020-10-22 11:00:00,127.4,114.485,56.771,31.349 +2020-10-22 11:15:00,129.2,114.801,56.771,31.349 +2020-10-22 11:30:00,127.65,115.016,56.771,31.349 +2020-10-22 11:45:00,125.62,115.334,56.771,31.349 +2020-10-22 12:00:00,123.46,111.602,53.701,31.349 +2020-10-22 12:15:00,124.88,110.765,53.701,31.349 +2020-10-22 12:30:00,119.09,110.62799999999999,53.701,31.349 +2020-10-22 12:45:00,120.14,110.99799999999999,53.701,31.349 +2020-10-22 13:00:00,122.1,110.82799999999999,52.364,31.349 +2020-10-22 13:15:00,123.26,110.411,52.364,31.349 +2020-10-22 13:30:00,124.34,109.709,52.364,31.349 +2020-10-22 13:45:00,124.93,109.353,52.364,31.349 +2020-10-22 14:00:00,128.1,108.916,53.419,31.349 +2020-10-22 14:15:00,124.78,108.868,53.419,31.349 +2020-10-22 14:30:00,123.07,108.398,53.419,31.349 +2020-10-22 14:45:00,119.7,108.27600000000001,53.419,31.349 +2020-10-22 15:00:00,121.74,107.53399999999999,56.744,31.349 +2020-10-22 15:15:00,119.68,107.554,56.744,31.349 +2020-10-22 15:30:00,119.77,107.806,56.744,31.349 +2020-10-22 15:45:00,118.01,107.85600000000001,56.744,31.349 +2020-10-22 16:00:00,123.58,108.62,60.458,31.349 +2020-10-22 16:15:00,123.95,109.146,60.458,31.349 +2020-10-22 16:30:00,124.24,109.53299999999999,60.458,31.349 +2020-10-22 16:45:00,126.61,108.363,60.458,31.349 +2020-10-22 17:00:00,132.29,108.83200000000001,66.295,31.349 +2020-10-22 17:15:00,131.4,109.807,66.295,31.349 +2020-10-22 17:30:00,130.67,109.76799999999999,66.295,31.349 +2020-10-22 17:45:00,130.55,109.95200000000001,66.295,31.349 +2020-10-22 18:00:00,130.07,111.006,68.468,31.349 +2020-10-22 18:15:00,127.24,110.588,68.468,31.349 +2020-10-22 18:30:00,126.24,109.292,68.468,31.349 +2020-10-22 18:45:00,124.94,112.571,68.468,31.349 +2020-10-22 19:00:00,116.69,113.544,66.39399999999999,31.349 +2020-10-22 19:15:00,113.13,112.10600000000001,66.39399999999999,31.349 +2020-10-22 19:30:00,109.94,111.291,66.39399999999999,31.349 +2020-10-22 19:45:00,109.01,110.73700000000001,66.39399999999999,31.349 +2020-10-22 20:00:00,106.55,107.891,63.183,31.349 +2020-10-22 20:15:00,110.78,105.039,63.183,31.349 +2020-10-22 20:30:00,109.45,104.61399999999999,63.183,31.349 +2020-10-22 20:45:00,102.93,103.04299999999999,63.183,31.349 +2020-10-22 21:00:00,95.75,99.479,55.133,31.349 +2020-10-22 21:15:00,94.53,99.337,55.133,31.349 +2020-10-22 21:30:00,91.45,98.652,55.133,31.349 +2020-10-22 21:45:00,96.05,96.286,55.133,31.349 +2020-10-22 22:00:00,89.93,91.816,50.111999999999995,31.349 +2020-10-22 22:15:00,88.13,88.395,50.111999999999995,31.349 +2020-10-22 22:30:00,89.13,76.995,50.111999999999995,31.349 +2020-10-22 22:45:00,89.9,70.547,50.111999999999995,31.349 +2020-10-22 23:00:00,83.75,64.893,44.536,31.349 +2020-10-22 23:15:00,78.07,64.564,44.536,31.349 +2020-10-22 23:30:00,80.56,63.558,44.536,31.349 +2020-10-22 23:45:00,82.63,63.785,44.536,31.349 +2020-10-23 00:00:00,78.83,58.907,42.291000000000004,31.349 +2020-10-23 00:15:00,76.14,60.5,42.291000000000004,31.349 +2020-10-23 00:30:00,70.38,60.188,42.291000000000004,31.349 +2020-10-23 00:45:00,78.33,60.231,42.291000000000004,31.349 +2020-10-23 01:00:00,77.49,60.708999999999996,41.008,31.349 +2020-10-23 01:15:00,78.38,60.483000000000004,41.008,31.349 +2020-10-23 01:30:00,72.93,60.181999999999995,41.008,31.349 +2020-10-23 01:45:00,73.02,59.847,41.008,31.349 +2020-10-23 02:00:00,71.03,61.23,39.521,31.349 +2020-10-23 02:15:00,73.65,61.361000000000004,39.521,31.349 +2020-10-23 02:30:00,79.52,62.555,39.521,31.349 +2020-10-23 02:45:00,80.33,62.872,39.521,31.349 +2020-10-23 03:00:00,79.49,65.42,39.812,31.349 +2020-10-23 03:15:00,77.2,66.66199999999999,39.812,31.349 +2020-10-23 03:30:00,77.66,66.79,39.812,31.349 +2020-10-23 03:45:00,80.08,68.09899999999999,39.812,31.349 +2020-10-23 04:00:00,93.02,78.267,41.22,31.349 +2020-10-23 04:15:00,92.04,87.04899999999999,41.22,31.349 +2020-10-23 04:30:00,95.88,87.476,41.22,31.349 +2020-10-23 04:45:00,103.64,88.589,41.22,31.349 +2020-10-23 05:00:00,110.56,116.802,45.115,31.349 +2020-10-23 05:15:00,109.64,143.666,45.115,31.349 +2020-10-23 05:30:00,112.58,138.431,45.115,31.349 +2020-10-23 05:45:00,115.81,129.345,45.115,31.349 +2020-10-23 06:00:00,127.62,129.082,59.06100000000001,31.349 +2020-10-23 06:15:00,125.7,132.726,59.06100000000001,31.349 +2020-10-23 06:30:00,129.36,131.142,59.06100000000001,31.349 +2020-10-23 06:45:00,130.72,132.45600000000002,59.06100000000001,31.349 +2020-10-23 07:00:00,132.87,133.134,71.874,31.349 +2020-10-23 07:15:00,132.91,136.845,71.874,31.349 +2020-10-23 07:30:00,132.75,136.32399999999998,71.874,31.349 +2020-10-23 07:45:00,132.39,135.908,71.874,31.349 +2020-10-23 08:00:00,135.33,135.643,68.439,31.349 +2020-10-23 08:15:00,133.18,135.3,68.439,31.349 +2020-10-23 08:30:00,131.71,133.393,68.439,31.349 +2020-10-23 08:45:00,132.74,130.588,68.439,31.349 +2020-10-23 09:00:00,130.86,125.649,65.523,31.349 +2020-10-23 09:15:00,129.44,124.387,65.523,31.349 +2020-10-23 09:30:00,130.21,123.78299999999999,65.523,31.349 +2020-10-23 09:45:00,126.99,123.292,65.523,31.349 +2020-10-23 10:00:00,126.29,119.389,62.005,31.349 +2020-10-23 10:15:00,126.97,118.429,62.005,31.349 +2020-10-23 10:30:00,127.62,116.988,62.005,31.349 +2020-10-23 10:45:00,128.81,116.204,62.005,31.349 +2020-10-23 11:00:00,125.12,112.964,60.351000000000006,31.349 +2020-10-23 11:15:00,124.04,112.315,60.351000000000006,31.349 +2020-10-23 11:30:00,124.64,113.381,60.351000000000006,31.349 +2020-10-23 11:45:00,128.31,113.32799999999999,60.351000000000006,31.349 +2020-10-23 12:00:00,121.36,110.387,55.331,31.349 +2020-10-23 12:15:00,123.71,108.07700000000001,55.331,31.349 +2020-10-23 12:30:00,121.4,108.113,55.331,31.349 +2020-10-23 12:45:00,120.71,108.477,55.331,31.349 +2020-10-23 13:00:00,114.05,109.057,53.361999999999995,31.349 +2020-10-23 13:15:00,112.96,109.195,53.361999999999995,31.349 +2020-10-23 13:30:00,114.06,108.807,53.361999999999995,31.349 +2020-10-23 13:45:00,109.64,108.527,53.361999999999995,31.349 +2020-10-23 14:00:00,109.29,107.06299999999999,51.708,31.349 +2020-10-23 14:15:00,107.91,107.09200000000001,51.708,31.349 +2020-10-23 14:30:00,109.64,107.539,51.708,31.349 +2020-10-23 14:45:00,108.55,107.329,51.708,31.349 +2020-10-23 15:00:00,108.97,106.251,54.571000000000005,31.349 +2020-10-23 15:15:00,110.24,105.94,54.571000000000005,31.349 +2020-10-23 15:30:00,110.25,105.12200000000001,54.571000000000005,31.349 +2020-10-23 15:45:00,111.4,105.56299999999999,54.571000000000005,31.349 +2020-10-23 16:00:00,111.51,105.333,58.662,31.349 +2020-10-23 16:15:00,113.23,106.244,58.662,31.349 +2020-10-23 16:30:00,114.42,106.60799999999999,58.662,31.349 +2020-10-23 16:45:00,118.53,105.10600000000001,58.662,31.349 +2020-10-23 17:00:00,122.49,106.39399999999999,65.941,31.349 +2020-10-23 17:15:00,122.56,107.088,65.941,31.349 +2020-10-23 17:30:00,123.72,106.946,65.941,31.349 +2020-10-23 17:45:00,123.59,106.948,65.941,31.349 +2020-10-23 18:00:00,121.84,108.42299999999999,65.628,31.349 +2020-10-23 18:15:00,121.3,107.361,65.628,31.349 +2020-10-23 18:30:00,117.05,106.244,65.628,31.349 +2020-10-23 18:45:00,113.81,109.695,65.628,31.349 +2020-10-23 19:00:00,107.92,111.56299999999999,63.662,31.349 +2020-10-23 19:15:00,105.21,111.102,63.662,31.349 +2020-10-23 19:30:00,103.36,110.083,63.662,31.349 +2020-10-23 19:45:00,100.53,108.814,63.662,31.349 +2020-10-23 20:00:00,96.19,105.954,61.945,31.349 +2020-10-23 20:15:00,96.91,103.45,61.945,31.349 +2020-10-23 20:30:00,98.96,102.758,61.945,31.349 +2020-10-23 20:45:00,98.21,101.104,61.945,31.349 +2020-10-23 21:00:00,88.95,98.39200000000001,53.903,31.349 +2020-10-23 21:15:00,85.03,99.21700000000001,53.903,31.349 +2020-10-23 21:30:00,86.27,98.49700000000001,53.903,31.349 +2020-10-23 21:45:00,86.36,96.485,53.903,31.349 +2020-10-23 22:00:00,82.11,92.458,48.403999999999996,31.349 +2020-10-23 22:15:00,79.76,88.836,48.403999999999996,31.349 +2020-10-23 22:30:00,78.87,82.929,48.403999999999996,31.349 +2020-10-23 22:45:00,77.66,78.721,48.403999999999996,31.349 +2020-10-23 23:00:00,72.45,73.60300000000001,41.07,31.349 +2020-10-23 23:15:00,66.27,71.525,41.07,31.349 +2020-10-23 23:30:00,71.85,68.943,41.07,31.349 +2020-10-23 23:45:00,72.89,68.757,41.07,31.349 +2020-10-24 00:00:00,69.15,58.409,38.989000000000004,31.177 +2020-10-24 00:15:00,66.21,57.553999999999995,38.989000000000004,31.177 +2020-10-24 00:30:00,66.17,57.687,38.989000000000004,31.177 +2020-10-24 00:45:00,68.56,57.732,38.989000000000004,31.177 +2020-10-24 01:00:00,67.79,58.681000000000004,35.275,31.177 +2020-10-24 01:15:00,63.68,58.266000000000005,35.275,31.177 +2020-10-24 01:30:00,65.18,57.342,35.275,31.177 +2020-10-24 01:45:00,67.86,57.486999999999995,35.275,31.177 +2020-10-24 02:00:00,66.58,58.766999999999996,32.838,31.177 +2020-10-24 02:15:00,64.36,58.35,32.838,31.177 +2020-10-24 02:30:00,61.86,58.606,32.838,31.177 +2020-10-24 02:45:00,66.88,59.343999999999994,32.838,31.177 +2020-10-24 03:00:00,65.58,61.542,32.418,31.177 +2020-10-24 03:15:00,60.76,61.876000000000005,32.418,31.177 +2020-10-24 03:30:00,61.45,61.39,32.418,31.177 +2020-10-24 03:45:00,60.33,63.449,32.418,31.177 +2020-10-24 04:00:00,61.63,70.747,32.099000000000004,31.177 +2020-10-24 04:15:00,61.62,77.895,32.099000000000004,31.177 +2020-10-24 04:30:00,61.95,76.46,32.099000000000004,31.177 +2020-10-24 04:45:00,64.96,77.475,32.099000000000004,31.177 +2020-10-24 05:00:00,66.95,94.19200000000001,32.926,31.177 +2020-10-24 05:15:00,68.94,106.695,32.926,31.177 +2020-10-24 05:30:00,70.32,102.368,32.926,31.177 +2020-10-24 05:45:00,72.01,98.037,32.926,31.177 +2020-10-24 06:00:00,75.35,111.932,35.069,31.177 +2020-10-24 06:15:00,77.38,127.29899999999999,35.069,31.177 +2020-10-24 06:30:00,80.27,121.75399999999999,35.069,31.177 +2020-10-24 06:45:00,82.52,117.161,35.069,31.177 +2020-10-24 07:00:00,84.52,114.94,40.906,31.177 +2020-10-24 07:15:00,86.47,117.434,40.906,31.177 +2020-10-24 07:30:00,88.55,119.00399999999999,40.906,31.177 +2020-10-24 07:45:00,90.46,121.02,40.906,31.177 +2020-10-24 08:00:00,93.09,122.844,46.603,31.177 +2020-10-24 08:15:00,92.39,124.279,46.603,31.177 +2020-10-24 08:30:00,92.69,123.23299999999999,46.603,31.177 +2020-10-24 08:45:00,92.18,122.46799999999999,46.603,31.177 +2020-10-24 09:00:00,92.61,119.697,49.935,31.177 +2020-10-24 09:15:00,95.38,119.044,49.935,31.177 +2020-10-24 09:30:00,92.79,119.12299999999999,49.935,31.177 +2020-10-24 09:45:00,98.37,118.501,49.935,31.177 +2020-10-24 10:00:00,95.5,114.84299999999999,47.585,31.177 +2020-10-24 10:15:00,96.58,114.081,47.585,31.177 +2020-10-24 10:30:00,97.94,112.571,47.585,31.177 +2020-10-24 10:45:00,98.26,112.348,47.585,31.177 +2020-10-24 11:00:00,98.27,109.11399999999999,43.376999999999995,31.177 +2020-10-24 11:15:00,98.66,108.404,43.376999999999995,31.177 +2020-10-24 11:30:00,99.06,108.95299999999999,43.376999999999995,31.177 +2020-10-24 11:45:00,96.15,108.61399999999999,43.376999999999995,31.177 +2020-10-24 12:00:00,91.6,105.161,40.855,31.177 +2020-10-24 12:15:00,91.87,103.545,40.855,31.177 +2020-10-24 12:30:00,93.38,103.74799999999999,40.855,31.177 +2020-10-24 12:45:00,90.21,103.949,40.855,31.177 +2020-10-24 13:00:00,86.6,103.87899999999999,37.251,31.177 +2020-10-24 13:15:00,86.51,102.62299999999999,37.251,31.177 +2020-10-24 13:30:00,85.95,102.045,37.251,31.177 +2020-10-24 13:45:00,85.82,101.522,37.251,31.177 +2020-10-24 14:00:00,86.06,100.728,38.548,31.177 +2020-10-24 14:15:00,86.43,99.986,38.548,31.177 +2020-10-24 14:30:00,86.03,99.24700000000001,38.548,31.177 +2020-10-24 14:45:00,85.75,99.345,38.548,31.177 +2020-10-24 15:00:00,84.72,98.795,42.883,31.177 +2020-10-24 15:15:00,85.56,99.241,42.883,31.177 +2020-10-24 15:30:00,85.6,99.435,42.883,31.177 +2020-10-24 15:45:00,86.3,99.54899999999999,42.883,31.177 +2020-10-24 16:00:00,89.01,99.697,48.143,31.177 +2020-10-24 16:15:00,89.51,100.771,48.143,31.177 +2020-10-24 16:30:00,92.55,101.194,48.143,31.177 +2020-10-24 16:45:00,96.25,100.23100000000001,48.143,31.177 +2020-10-24 17:00:00,102.45,100.84299999999999,55.25,31.177 +2020-10-24 17:15:00,102.68,101.559,55.25,31.177 +2020-10-24 17:30:00,105.3,101.311,55.25,31.177 +2020-10-24 17:45:00,102.74,101.31299999999999,55.25,31.177 +2020-10-24 18:00:00,102.08,103.189,57.506,31.177 +2020-10-24 18:15:00,99.7,103.78200000000001,57.506,31.177 +2020-10-24 18:30:00,98.42,103.971,57.506,31.177 +2020-10-24 18:45:00,96.26,104.133,57.506,31.177 +2020-10-24 19:00:00,91.99,105.861,55.528999999999996,31.177 +2020-10-24 19:15:00,88.78,104.691,55.528999999999996,31.177 +2020-10-24 19:30:00,86.53,104.39200000000001,55.528999999999996,31.177 +2020-10-24 19:45:00,85.01,103.775,55.528999999999996,31.177 +2020-10-24 20:00:00,81.07,102.477,46.166000000000004,31.177 +2020-10-24 20:15:00,81.19,100.8,46.166000000000004,31.177 +2020-10-24 20:30:00,79.06,99.508,46.166000000000004,31.177 +2020-10-24 20:45:00,78.71,98.44,46.166000000000004,31.177 +2020-10-24 21:00:00,74.27,96.27,40.406,31.177 +2020-10-24 21:15:00,73.82,97.156,40.406,31.177 +2020-10-24 21:30:00,69.74,97.17,40.406,31.177 +2020-10-24 21:45:00,70.03,94.685,40.406,31.177 +2020-10-24 22:00:00,66.57,91.34200000000001,39.616,31.177 +2020-10-24 22:15:00,66.52,89.10799999999999,39.616,31.177 +2020-10-24 22:30:00,63.76,86.304,39.616,31.177 +2020-10-24 22:45:00,63.35,83.242,39.616,31.177 +2020-10-24 23:00:00,59.13,79.12100000000001,32.205,31.177 +2020-10-24 23:15:00,57.91,76.347,32.205,31.177 +2020-10-24 23:30:00,58.49,73.942,32.205,31.177 +2020-10-24 23:45:00,57.43,72.638,32.205,31.177 +2020-10-25 00:00:00,54.37,59.602,28.229,31.177 +2020-10-25 00:15:00,54.85,58.032,28.229,31.177 +2020-10-25 00:30:00,53.15,57.915,28.229,31.177 +2020-10-25 00:45:00,53.46,58.245,28.229,31.177 +2020-10-25 01:00:00,52.45,59.261,25.669,31.177 +2020-10-25 01:15:00,53.12,59.316,25.669,31.177 +2020-10-25 01:30:00,52.62,58.598,25.669,31.177 +2020-10-25 01:45:00,53.42,58.406000000000006,25.669,31.177 +2020-10-25 02:00:00,53.47,59.361999999999995,24.948,31.177 +2020-10-25 02:15:00,52.07,58.88,24.948,31.177 +2020-10-25 02:30:00,53.29,59.628,24.948,31.177 +2020-10-25 02:45:00,50.45,60.449,24.948,31.177 +2020-10-25 02:00:00,53.47,59.361999999999995,24.948,31.177 +2020-10-25 02:15:00,52.07,58.88,24.948,31.177 +2020-10-25 02:30:00,53.29,59.628,24.948,31.177 +2020-10-25 02:45:00,50.45,60.449,24.948,31.177 +2020-10-25 03:00:00,51.47,63.097,25.839000000000002,31.177 +2020-10-25 03:15:00,53.26,63.324,25.839000000000002,31.177 +2020-10-25 03:30:00,53.34,63.165,25.839000000000002,31.177 +2020-10-25 03:45:00,54.51,64.78699999999999,25.839000000000002,31.177 +2020-10-25 04:00:00,53.29,71.962,26.803,31.177 +2020-10-25 04:15:00,53.34,78.443,26.803,31.177 +2020-10-25 04:30:00,53.8,77.687,26.803,31.177 +2020-10-25 04:45:00,54.16,78.613,26.803,31.177 +2020-10-25 05:00:00,55.1,93.816,28.147,31.177 +2020-10-25 05:15:00,57.45,104.86200000000001,28.147,31.177 +2020-10-25 05:30:00,57.71,100.24700000000001,28.147,31.177 +2020-10-25 05:45:00,58.69,95.866,28.147,31.177 +2020-10-25 06:00:00,62.27,108.524,31.116,31.177 +2020-10-25 06:15:00,63.1,123.47,31.116,31.177 +2020-10-25 06:30:00,62.46,117.07799999999999,31.116,31.177 +2020-10-25 06:45:00,62.7,111.507,31.116,31.177 +2020-10-25 07:00:00,63.83,110.587,35.739000000000004,31.177 +2020-10-25 07:15:00,68.03,111.905,35.739000000000004,31.177 +2020-10-25 07:30:00,70.45,113.48200000000001,35.739000000000004,31.177 +2020-10-25 07:45:00,73.32,115.104,35.739000000000004,31.177 +2020-10-25 08:00:00,74.3,118.15899999999999,39.455999999999996,31.177 +2020-10-25 08:15:00,75.67,120.051,39.455999999999996,31.177 +2020-10-25 08:30:00,77.85,120.27600000000001,39.455999999999996,31.177 +2020-10-25 08:45:00,79.31,120.493,39.455999999999996,31.177 +2020-10-25 09:00:00,79.61,117.434,41.343999999999994,31.177 +2020-10-25 09:15:00,84.58,116.85700000000001,41.343999999999994,31.177 +2020-10-25 09:30:00,85.64,117.029,41.343999999999994,31.177 +2020-10-25 09:45:00,82.94,116.75399999999999,41.343999999999994,31.177 +2020-10-25 10:00:00,85.77,114.64299999999999,43.645,31.177 +2020-10-25 10:15:00,85.99,114.189,43.645,31.177 +2020-10-25 10:30:00,87.54,113.08,43.645,31.177 +2020-10-25 10:45:00,90.19,112.226,43.645,31.177 +2020-10-25 11:00:00,91.19,109.338,39.796,31.177 +2020-10-25 11:15:00,94.15,108.50299999999999,39.796,31.177 +2020-10-25 11:30:00,95.37,108.779,39.796,31.177 +2020-10-25 11:45:00,98.86,108.865,39.796,31.177 +2020-10-25 12:00:00,91.98,105.462,36.343,31.177 +2020-10-25 12:15:00,91.68,104.677,36.343,31.177 +2020-10-25 12:30:00,88.9,104.17200000000001,36.343,31.177 +2020-10-25 12:45:00,90.13,103.572,36.343,31.177 +2020-10-25 13:00:00,82.09,102.927,33.162,31.177 +2020-10-25 13:15:00,81.4,103.18,33.162,31.177 +2020-10-25 13:30:00,81.38,102.04700000000001,33.162,31.177 +2020-10-25 13:45:00,81.84,101.579,33.162,31.177 +2020-10-25 14:00:00,81.77,101.337,33.215,31.177 +2020-10-25 14:15:00,79.73,101.465,33.215,31.177 +2020-10-25 14:30:00,79.1,100.992,33.215,31.177 +2020-10-25 14:45:00,79.01,100.43799999999999,33.215,31.177 +2020-10-25 15:00:00,79.17,99.04700000000001,37.385999999999996,31.177 +2020-10-25 15:15:00,80.91,99.631,37.385999999999996,31.177 +2020-10-25 15:30:00,81.52,100.13,37.385999999999996,31.177 +2020-10-25 15:45:00,82.75,100.765,37.385999999999996,31.177 +2020-10-25 16:00:00,82.73,101.178,46.618,31.177 +2020-10-25 16:15:00,84.13,101.86399999999999,46.618,31.177 +2020-10-25 16:30:00,88.98,102.837,46.618,31.177 +2020-10-25 16:45:00,90.57,102.025,46.618,31.177 +2020-10-25 17:00:00,95.12,102.66799999999999,50.111000000000004,31.177 +2020-10-25 17:15:00,97.69,103.867,50.111000000000004,31.177 +2020-10-25 17:30:00,96.42,104.118,50.111000000000004,31.177 +2020-10-25 17:45:00,98.46,105.537,50.111000000000004,31.177 +2020-10-25 18:00:00,103.58,107.33200000000001,50.25,31.177 +2020-10-25 18:15:00,101.93,108.426,50.25,31.177 +2020-10-25 18:30:00,102.54,107.414,50.25,31.177 +2020-10-25 18:45:00,100.8,108.6,50.25,31.177 +2020-10-25 19:00:00,96.36,111.086,44.265,31.177 +2020-10-25 19:15:00,94.85,109.73100000000001,44.265,31.177 +2020-10-25 19:30:00,95.47,109.225,44.265,31.177 +2020-10-25 19:45:00,92.72,109.13799999999999,44.265,31.177 +2020-10-25 20:00:00,95.91,107.9,39.717,31.177 +2020-10-25 20:15:00,98.51,106.665,39.717,31.177 +2020-10-25 20:30:00,93.22,106.344,39.717,31.177 +2020-10-25 20:45:00,87.29,103.89200000000001,39.717,31.177 +2020-10-25 21:00:00,89.17,100.29299999999999,39.224000000000004,31.177 +2020-10-25 21:15:00,87.17,100.712,39.224000000000004,31.177 +2020-10-25 21:30:00,90.1,100.595,39.224000000000004,31.177 +2020-10-25 21:45:00,88.38,98.32,39.224000000000004,31.177 +2020-10-25 22:00:00,85.43,95.274,33.518,31.177 +2020-10-25 22:15:00,83.95,91.87799999999999,33.518,31.177 +2020-10-25 22:30:00,78.09,87.24700000000001,33.518,31.177 +2020-10-25 22:45:00,78.18,83.186,33.518,31.177 +2020-10-25 23:00:00,78.0,77.503,33.518,31.177 +2020-10-25 23:15:00,83.24,76.22399999999999,33.518,31.177 +2020-10-25 23:30:00,81.26,73.999,33.518,31.177 +2020-10-25 23:45:00,79.19,73.21600000000001,33.518,31.177 +2020-10-26 00:00:00,70.49,62.706,34.301,31.349 +2020-10-26 00:15:00,74.87,62.949,34.301,31.349 +2020-10-26 00:30:00,78.69,62.718999999999994,34.301,31.349 +2020-10-26 00:45:00,81.84,62.592,34.301,31.349 +2020-10-26 01:00:00,75.6,63.79600000000001,34.143,31.349 +2020-10-26 01:15:00,71.72,63.619,34.143,31.349 +2020-10-26 01:30:00,75.5,63.11600000000001,34.143,31.349 +2020-10-26 01:45:00,77.08,62.928999999999995,34.143,31.349 +2020-10-26 02:00:00,76.26,64.08800000000001,33.650999999999996,31.349 +2020-10-26 02:15:00,72.33,63.838,33.650999999999996,31.349 +2020-10-26 02:30:00,72.88,64.815,33.650999999999996,31.349 +2020-10-26 02:45:00,75.86,65.26100000000001,33.650999999999996,31.349 +2020-10-26 03:00:00,73.71,68.73,32.599000000000004,31.349 +2020-10-26 03:15:00,76.24,70.116,32.599000000000004,31.349 +2020-10-26 03:30:00,71.02,70.158,32.599000000000004,31.349 +2020-10-26 03:45:00,78.2,71.275,32.599000000000004,31.349 +2020-10-26 04:00:00,79.63,82.025,33.785,31.349 +2020-10-26 04:15:00,78.32,91.931,33.785,31.349 +2020-10-26 04:30:00,77.2,91.984,33.785,31.349 +2020-10-26 04:45:00,76.44,93.176,33.785,31.349 +2020-10-26 05:00:00,81.4,119.296,41.285,31.349 +2020-10-26 05:15:00,86.72,144.952,41.285,31.349 +2020-10-26 05:30:00,94.41,139.7,41.285,31.349 +2020-10-26 05:45:00,100.77,131.119,41.285,31.349 +2020-10-26 06:00:00,111.1,130.539,60.486000000000004,31.349 +2020-10-26 06:15:00,109.54,133.99,60.486000000000004,31.349 +2020-10-26 06:30:00,116.42,133.387,60.486000000000004,31.349 +2020-10-26 06:45:00,125.54,134.597,60.486000000000004,31.349 +2020-10-26 07:00:00,128.36,135.40200000000002,74.012,31.349 +2020-10-26 07:15:00,127.02,138.408,74.012,31.349 +2020-10-26 07:30:00,129.02,139.22899999999998,74.012,31.349 +2020-10-26 07:45:00,130.69,139.71200000000002,74.012,31.349 +2020-10-26 08:00:00,133.0,139.673,69.569,31.349 +2020-10-26 08:15:00,130.26,139.901,69.569,31.349 +2020-10-26 08:30:00,131.6,137.61700000000002,69.569,31.349 +2020-10-26 08:45:00,131.65,136.181,69.569,31.349 +2020-10-26 09:00:00,132.1,132.289,66.152,31.349 +2020-10-26 09:15:00,133.34,129.095,66.152,31.349 +2020-10-26 09:30:00,133.68,128.327,66.152,31.349 +2020-10-26 09:45:00,134.8,127.196,66.152,31.349 +2020-10-26 10:00:00,135.34,124.896,62.923,31.349 +2020-10-26 10:15:00,134.92,124.164,62.923,31.349 +2020-10-26 10:30:00,135.66,122.366,62.923,31.349 +2020-10-26 10:45:00,136.84,121.25299999999999,62.923,31.349 +2020-10-26 11:00:00,136.57,117.12899999999999,61.522,31.349 +2020-10-26 11:15:00,138.31,117.39399999999999,61.522,31.349 +2020-10-26 11:30:00,137.5,118.719,61.522,31.349 +2020-10-26 11:45:00,136.55,118.765,61.522,31.349 +2020-10-26 12:00:00,132.39,115.801,58.632,31.349 +2020-10-26 12:15:00,133.44,115.056,58.632,31.349 +2020-10-26 12:30:00,136.05,114.31200000000001,58.632,31.349 +2020-10-26 12:45:00,136.12,114.579,58.632,31.349 +2020-10-26 13:00:00,131.73,114.663,59.06,31.349 +2020-10-26 13:15:00,133.41,113.785,59.06,31.349 +2020-10-26 13:30:00,132.28,112.412,59.06,31.349 +2020-10-26 13:45:00,129.34,112.303,59.06,31.349 +2020-10-26 14:00:00,128.88,111.329,59.791000000000004,31.349 +2020-10-26 14:15:00,130.35,111.32799999999999,59.791000000000004,31.349 +2020-10-26 14:30:00,130.63,110.51799999999999,59.791000000000004,31.349 +2020-10-26 14:45:00,129.71,110.73700000000001,59.791000000000004,31.349 +2020-10-26 15:00:00,133.57,110.221,61.148,31.349 +2020-10-26 15:15:00,130.84,109.76799999999999,61.148,31.349 +2020-10-26 15:30:00,129.48,110.12,61.148,31.349 +2020-10-26 15:45:00,127.89,110.32600000000001,61.148,31.349 +2020-10-26 16:00:00,129.63,111.01299999999999,66.009,31.349 +2020-10-26 16:15:00,130.73,111.325,66.009,31.349 +2020-10-26 16:30:00,128.87,111.488,66.009,31.349 +2020-10-26 16:45:00,131.75,110.07600000000001,66.009,31.349 +2020-10-26 17:00:00,135.6,109.988,73.683,31.349 +2020-10-26 17:15:00,135.29,110.84899999999999,73.683,31.349 +2020-10-26 17:30:00,138.5,110.64399999999999,73.683,31.349 +2020-10-26 17:45:00,138.2,111.103,73.683,31.349 +2020-10-26 18:00:00,139.43,112.59899999999999,72.848,31.349 +2020-10-26 18:15:00,137.29,111.71,72.848,31.349 +2020-10-26 18:30:00,135.93,110.73100000000001,72.848,31.349 +2020-10-26 18:45:00,133.94,113.649,72.848,31.349 +2020-10-26 19:00:00,131.63,115.131,71.139,31.349 +2020-10-26 19:15:00,128.86,113.73299999999999,71.139,31.349 +2020-10-26 19:30:00,126.98,113.314,71.139,31.349 +2020-10-26 19:45:00,125.67,112.5,71.139,31.349 +2020-10-26 20:00:00,117.03,109.485,69.667,31.349 +2020-10-26 20:15:00,114.91,107.57,69.667,31.349 +2020-10-26 20:30:00,114.37,106.48299999999999,69.667,31.349 +2020-10-26 20:45:00,109.01,104.954,69.667,31.349 +2020-10-26 21:00:00,112.26,101.359,61.166000000000004,31.349 +2020-10-26 21:15:00,112.02,101.35799999999999,61.166000000000004,31.349 +2020-10-26 21:30:00,107.75,101.01100000000001,61.166000000000004,31.349 +2020-10-26 21:45:00,102.71,98.35700000000001,61.166000000000004,31.349 +2020-10-26 22:00:00,96.59,92.899,52.772,31.349 +2020-10-26 22:15:00,95.82,89.686,52.772,31.349 +2020-10-26 22:30:00,95.29,78.227,52.772,31.349 +2020-10-26 22:45:00,98.09,71.666,52.772,31.349 +2020-10-26 23:00:00,93.54,66.396,45.136,31.349 +2020-10-26 23:15:00,86.6,65.73100000000001,45.136,31.349 +2020-10-26 23:30:00,84.41,64.919,45.136,31.349 +2020-10-26 23:45:00,88.99,65.25399999999999,45.136,31.349 +2020-10-27 00:00:00,81.74,61.629,47.35,31.349 +2020-10-27 00:15:00,82.4,62.966,47.35,31.349 +2020-10-27 00:30:00,77.99,62.614,47.35,31.349 +2020-10-27 00:45:00,77.23,62.376000000000005,47.35,31.349 +2020-10-27 01:00:00,80.95,63.243,43.424,31.349 +2020-10-27 01:15:00,82.6,62.918,43.424,31.349 +2020-10-27 01:30:00,79.87,62.45399999999999,43.424,31.349 +2020-10-27 01:45:00,72.99,62.169,43.424,31.349 +2020-10-27 02:00:00,72.38,63.107,41.778999999999996,31.349 +2020-10-27 02:15:00,71.66,63.367,41.778999999999996,31.349 +2020-10-27 02:30:00,79.74,63.854,41.778999999999996,31.349 +2020-10-27 02:45:00,81.55,64.459,41.778999999999996,31.349 +2020-10-27 03:00:00,82.6,67.071,40.771,31.349 +2020-10-27 03:15:00,75.83,68.518,40.771,31.349 +2020-10-27 03:30:00,74.84,68.78,40.771,31.349 +2020-10-27 03:45:00,75.64,69.439,40.771,31.349 +2020-10-27 04:00:00,78.97,79.508,41.816,31.349 +2020-10-27 04:15:00,82.85,89.28299999999999,41.816,31.349 +2020-10-27 04:30:00,84.37,89.12799999999999,41.816,31.349 +2020-10-27 04:45:00,85.9,91.14399999999999,41.816,31.349 +2020-10-27 05:00:00,85.7,120.477,45.842,31.349 +2020-10-27 05:15:00,86.54,146.40200000000002,45.842,31.349 +2020-10-27 05:30:00,89.88,140.503,45.842,31.349 +2020-10-27 05:45:00,92.99,131.537,45.842,31.349 +2020-10-27 06:00:00,102.93,130.881,59.12,31.349 +2020-10-27 06:15:00,108.45,135.22899999999998,59.12,31.349 +2020-10-27 06:30:00,113.1,134.179,59.12,31.349 +2020-10-27 06:45:00,115.04,134.792,59.12,31.349 +2020-10-27 07:00:00,122.18,135.57399999999998,70.33,31.349 +2020-10-27 07:15:00,119.35,138.393,70.33,31.349 +2020-10-27 07:30:00,121.17,138.977,70.33,31.349 +2020-10-27 07:45:00,122.32,139.118,70.33,31.349 +2020-10-27 08:00:00,123.86,139.123,67.788,31.349 +2020-10-27 08:15:00,121.9,138.599,67.788,31.349 +2020-10-27 08:30:00,120.05,136.322,67.788,31.349 +2020-10-27 08:45:00,119.09,134.313,67.788,31.349 +2020-10-27 09:00:00,117.83,130.122,62.622,31.349 +2020-10-27 09:15:00,117.56,127.71700000000001,62.622,31.349 +2020-10-27 09:30:00,117.58,127.595,62.622,31.349 +2020-10-27 09:45:00,116.75,126.899,62.622,31.349 +2020-10-27 10:00:00,115.99,123.69200000000001,60.887,31.349 +2020-10-27 10:15:00,115.96,122.279,60.887,31.349 +2020-10-27 10:30:00,114.65,120.57,60.887,31.349 +2020-10-27 10:45:00,115.67,120.009,60.887,31.349 +2020-10-27 11:00:00,114.34,116.712,59.812,31.349 +2020-10-27 11:15:00,114.91,116.93299999999999,59.812,31.349 +2020-10-27 11:30:00,116.28,117.145,59.812,31.349 +2020-10-27 11:45:00,115.75,117.391,59.812,31.349 +2020-10-27 12:00:00,113.14,113.553,56.614,31.349 +2020-10-27 12:15:00,114.82,112.695,56.614,31.349 +2020-10-27 12:30:00,122.4,112.73200000000001,56.614,31.349 +2020-10-27 12:45:00,118.13,113.09700000000001,56.614,31.349 +2020-10-27 13:00:00,120.98,112.76,56.824,31.349 +2020-10-27 13:15:00,123.1,112.37700000000001,56.824,31.349 +2020-10-27 13:30:00,120.06,111.669,56.824,31.349 +2020-10-27 13:45:00,116.95,111.295,56.824,31.349 +2020-10-27 14:00:00,111.93,110.604,57.623999999999995,31.349 +2020-10-27 14:15:00,110.15,110.633,57.623999999999995,31.349 +2020-10-27 14:30:00,114.67,110.348,57.623999999999995,31.349 +2020-10-27 14:45:00,116.3,110.22,57.623999999999995,31.349 +2020-10-27 15:00:00,118.01,109.37299999999999,59.724,31.349 +2020-10-27 15:15:00,119.23,109.48100000000001,59.724,31.349 +2020-10-27 15:30:00,118.98,109.921,59.724,31.349 +2020-10-27 15:45:00,123.51,110.039,59.724,31.349 +2020-10-27 16:00:00,124.34,110.655,61.64,31.349 +2020-10-27 16:15:00,123.37,111.288,61.64,31.349 +2020-10-27 16:30:00,126.21,111.66,61.64,31.349 +2020-10-27 16:45:00,127.32,110.74799999999999,61.64,31.349 +2020-10-27 17:00:00,132.95,111.009,68.962,31.349 +2020-10-27 17:15:00,130.54,112.081,68.962,31.349 +2020-10-27 17:30:00,133.6,112.05799999999999,68.962,31.349 +2020-10-27 17:45:00,132.78,112.322,68.962,31.349 +2020-10-27 18:00:00,133.9,113.34,69.149,31.349 +2020-10-27 18:15:00,131.39,112.79799999999999,69.149,31.349 +2020-10-27 18:30:00,130.49,111.552,69.149,31.349 +2020-10-27 18:45:00,129.9,114.807,69.149,31.349 +2020-10-27 19:00:00,126.61,115.82600000000001,68.832,31.349 +2020-10-27 19:15:00,124.87,114.351,68.832,31.349 +2020-10-27 19:30:00,122.2,113.476,68.832,31.349 +2020-10-27 19:45:00,121.07,112.803,68.832,31.349 +2020-10-27 20:00:00,114.52,110.06200000000001,66.403,31.349 +2020-10-27 20:15:00,112.08,107.171,66.403,31.349 +2020-10-27 20:30:00,108.46,106.601,66.403,31.349 +2020-10-27 20:45:00,105.79,104.941,66.403,31.349 +2020-10-27 21:00:00,98.15,101.363,57.352,31.349 +2020-10-27 21:15:00,94.41,101.166,57.352,31.349 +2020-10-27 21:30:00,92.13,100.529,57.352,31.349 +2020-10-27 21:45:00,91.04,98.061,57.352,31.349 +2020-10-27 22:00:00,84.86,93.559,51.148999999999994,31.349 +2020-10-27 22:15:00,81.56,90.044,51.148999999999994,31.349 +2020-10-27 22:30:00,78.97,78.791,51.148999999999994,31.349 +2020-10-27 22:45:00,75.04,72.378,51.148999999999994,31.349 +2020-10-27 23:00:00,69.33,66.797,41.8,31.349 +2020-10-27 23:15:00,72.04,66.328,41.8,31.349 +2020-10-27 23:30:00,67.62,65.33,41.8,31.349 +2020-10-27 23:45:00,70.34,65.508,41.8,31.349 +2020-10-28 00:00:00,66.64,61.961999999999996,42.269,31.349 +2020-10-28 00:15:00,63.47,63.285,42.269,31.349 +2020-10-28 00:30:00,62.17,62.938,42.269,31.349 +2020-10-28 00:45:00,59.22,62.693000000000005,42.269,31.349 +2020-10-28 01:00:00,59.46,63.573,38.527,31.349 +2020-10-28 01:15:00,59.73,63.266000000000005,38.527,31.349 +2020-10-28 01:30:00,57.69,62.818000000000005,38.527,31.349 +2020-10-28 01:45:00,59.75,62.528,38.527,31.349 +2020-10-28 02:00:00,58.9,63.477,36.393,31.349 +2020-10-28 02:15:00,57.99,63.747,36.393,31.349 +2020-10-28 02:30:00,58.66,64.222,36.393,31.349 +2020-10-28 02:45:00,59.76,64.822,36.393,31.349 +2020-10-28 03:00:00,58.64,67.422,36.167,31.349 +2020-10-28 03:15:00,59.18,68.89,36.167,31.349 +2020-10-28 03:30:00,59.1,69.155,36.167,31.349 +2020-10-28 03:45:00,59.8,69.797,36.167,31.349 +2020-10-28 04:00:00,57.35,79.88600000000001,38.092,31.349 +2020-10-28 04:15:00,60.36,89.689,38.092,31.349 +2020-10-28 04:30:00,61.45,89.531,38.092,31.349 +2020-10-28 04:45:00,62.49,91.555,38.092,31.349 +2020-10-28 05:00:00,64.56,120.95700000000001,42.268,31.349 +2020-10-28 05:15:00,64.41,146.945,42.268,31.349 +2020-10-28 05:30:00,63.43,141.032,42.268,31.349 +2020-10-28 05:45:00,60.47,132.036,42.268,31.349 +2020-10-28 06:00:00,64.37,131.36700000000002,60.158,31.349 +2020-10-28 06:15:00,65.72,135.72799999999998,60.158,31.349 +2020-10-28 06:30:00,65.45,134.7,60.158,31.349 +2020-10-28 06:45:00,65.86,135.322,60.158,31.349 +2020-10-28 07:00:00,68.96,136.10399999999998,74.792,31.349 +2020-10-28 07:15:00,70.01,138.934,74.792,31.349 +2020-10-28 07:30:00,71.67,139.547,74.792,31.349 +2020-10-28 07:45:00,73.31,139.689,74.792,31.349 +2020-10-28 08:00:00,76.29,139.70600000000002,70.499,31.349 +2020-10-28 08:15:00,75.92,139.159,70.499,31.349 +2020-10-28 08:30:00,76.39,136.907,70.499,31.349 +2020-10-28 08:45:00,76.44,134.873,70.499,31.349 +2020-10-28 09:00:00,75.16,130.675,68.892,31.349 +2020-10-28 09:15:00,75.44,128.268,68.892,31.349 +2020-10-28 09:30:00,72.53,128.134,68.892,31.349 +2020-10-28 09:45:00,73.66,127.415,68.892,31.349 +2020-10-28 10:00:00,72.88,124.2,66.88600000000001,31.349 +2020-10-28 10:15:00,69.24,122.749,66.88600000000001,31.349 +2020-10-28 10:30:00,71.4,121.02,66.88600000000001,31.349 +2020-10-28 10:45:00,72.71,120.443,66.88600000000001,31.349 +2020-10-28 11:00:00,72.82,117.149,66.187,31.349 +2020-10-28 11:15:00,74.56,117.351,66.187,31.349 +2020-10-28 11:30:00,75.37,117.56200000000001,66.187,31.349 +2020-10-28 11:45:00,76.16,117.794,66.187,31.349 +2020-10-28 12:00:00,73.91,113.936,62.18,31.349 +2020-10-28 12:15:00,72.6,113.075,62.18,31.349 +2020-10-28 12:30:00,71.55,113.145,62.18,31.349 +2020-10-28 12:45:00,70.09,113.51,62.18,31.349 +2020-10-28 13:00:00,70.36,113.141,62.23,31.349 +2020-10-28 13:15:00,67.51,112.76299999999999,62.23,31.349 +2020-10-28 13:30:00,65.99,112.054,62.23,31.349 +2020-10-28 13:45:00,66.82,111.676,62.23,31.349 +2020-10-28 14:00:00,67.12,110.936,63.721000000000004,31.349 +2020-10-28 14:15:00,67.37,110.98,63.721000000000004,31.349 +2020-10-28 14:30:00,69.07,110.73200000000001,63.721000000000004,31.349 +2020-10-28 14:45:00,72.24,110.602,63.721000000000004,31.349 +2020-10-28 15:00:00,74.87,109.736,66.523,31.349 +2020-10-28 15:15:00,75.32,109.86,66.523,31.349 +2020-10-28 15:30:00,79.14,110.337,66.523,31.349 +2020-10-28 15:45:00,80.15,110.46799999999999,66.523,31.349 +2020-10-28 16:00:00,83.7,111.055,69.679,31.349 +2020-10-28 16:15:00,83.65,111.71,69.679,31.349 +2020-10-28 16:30:00,85.79,112.07799999999999,69.679,31.349 +2020-10-28 16:45:00,87.82,111.21600000000001,69.679,31.349 +2020-10-28 17:00:00,95.0,111.43700000000001,75.04,31.349 +2020-10-28 17:15:00,96.91,112.52799999999999,75.04,31.349 +2020-10-28 17:30:00,100.33,112.508,75.04,31.349 +2020-10-28 17:45:00,101.95,112.79,75.04,31.349 +2020-10-28 18:00:00,102.58,113.801,75.915,31.349 +2020-10-28 18:15:00,101.4,113.235,75.915,31.349 +2020-10-28 18:30:00,100.78,111.999,75.915,31.349 +2020-10-28 18:45:00,99.5,115.25,75.915,31.349 +2020-10-28 19:00:00,96.55,116.27600000000001,74.66,31.349 +2020-10-28 19:15:00,96.95,114.795,74.66,31.349 +2020-10-28 19:30:00,94.44,113.90700000000001,74.66,31.349 +2020-10-28 19:45:00,93.81,113.212,74.66,31.349 +2020-10-28 20:00:00,97.85,110.491,71.204,31.349 +2020-10-28 20:15:00,98.96,107.59299999999999,71.204,31.349 +2020-10-28 20:30:00,93.72,106.994,71.204,31.349 +2020-10-28 20:45:00,96.99,105.316,71.204,31.349 +2020-10-28 21:00:00,88.01,101.735,61.052,31.349 +2020-10-28 21:15:00,91.89,101.527,61.052,31.349 +2020-10-28 21:30:00,86.26,100.899,61.052,31.349 +2020-10-28 21:45:00,85.22,98.413,61.052,31.349 +2020-10-28 22:00:00,82.78,93.904,54.691,31.349 +2020-10-28 22:15:00,84.41,90.37200000000001,54.691,31.349 +2020-10-28 22:30:00,80.28,79.148,54.691,31.349 +2020-10-28 22:45:00,82.44,72.742,54.691,31.349 +2020-10-28 23:00:00,82.46,67.175,45.18,31.349 +2020-10-28 23:15:00,86.27,66.678,45.18,31.349 +2020-10-28 23:30:00,83.78,65.682,45.18,31.349 +2020-10-28 23:45:00,82.95,65.851,45.18,31.349 +2020-10-29 00:00:00,77.65,62.294,42.746,31.349 +2020-10-29 00:15:00,79.71,63.602,42.746,31.349 +2020-10-29 00:30:00,79.87,63.261,42.746,31.349 +2020-10-29 00:45:00,70.04,63.008,42.746,31.349 +2020-10-29 01:00:00,74.52,63.9,40.025999999999996,31.349 +2020-10-29 01:15:00,77.14,63.611000000000004,40.025999999999996,31.349 +2020-10-29 01:30:00,78.1,63.178999999999995,40.025999999999996,31.349 +2020-10-29 01:45:00,74.52,62.885,40.025999999999996,31.349 +2020-10-29 02:00:00,74.6,63.843999999999994,38.154,31.349 +2020-10-29 02:15:00,78.3,64.126,38.154,31.349 +2020-10-29 02:30:00,77.12,64.58800000000001,38.154,31.349 +2020-10-29 02:45:00,73.83,65.185,38.154,31.349 +2020-10-29 03:00:00,77.13,67.771,37.575,31.349 +2020-10-29 03:15:00,78.81,69.26100000000001,37.575,31.349 +2020-10-29 03:30:00,79.06,69.528,37.575,31.349 +2020-10-29 03:45:00,73.2,70.152,37.575,31.349 +2020-10-29 04:00:00,76.0,80.26,39.154,31.349 +2020-10-29 04:15:00,81.59,90.094,39.154,31.349 +2020-10-29 04:30:00,84.79,89.934,39.154,31.349 +2020-10-29 04:45:00,83.0,91.965,39.154,31.349 +2020-10-29 05:00:00,83.0,121.43299999999999,44.085,31.349 +2020-10-29 05:15:00,86.43,147.484,44.085,31.349 +2020-10-29 05:30:00,90.66,141.559,44.085,31.349 +2020-10-29 05:45:00,100.19,132.532,44.085,31.349 +2020-10-29 06:00:00,111.83,131.852,57.49,31.349 +2020-10-29 06:15:00,117.74,136.226,57.49,31.349 +2020-10-29 06:30:00,119.59,135.217,57.49,31.349 +2020-10-29 06:45:00,115.34,135.85,57.49,31.349 +2020-10-29 07:00:00,122.6,136.632,73.617,31.349 +2020-10-29 07:15:00,123.1,139.474,73.617,31.349 +2020-10-29 07:30:00,124.09,140.114,73.617,31.349 +2020-10-29 07:45:00,126.48,140.257,73.617,31.349 +2020-10-29 08:00:00,126.46,140.285,69.281,31.349 +2020-10-29 08:15:00,123.29,139.715,69.281,31.349 +2020-10-29 08:30:00,119.21,137.487,69.281,31.349 +2020-10-29 08:45:00,118.47,135.429,69.281,31.349 +2020-10-29 09:00:00,118.0,131.224,63.926,31.349 +2020-10-29 09:15:00,122.76,128.815,63.926,31.349 +2020-10-29 09:30:00,119.63,128.67,63.926,31.349 +2020-10-29 09:45:00,122.87,127.926,63.926,31.349 +2020-10-29 10:00:00,122.23,124.705,59.442,31.349 +2020-10-29 10:15:00,119.89,123.21600000000001,59.442,31.349 +2020-10-29 10:30:00,124.6,121.46700000000001,59.442,31.349 +2020-10-29 10:45:00,131.0,120.874,59.442,31.349 +2020-10-29 11:00:00,127.83,117.583,56.771,31.349 +2020-10-29 11:15:00,128.86,117.766,56.771,31.349 +2020-10-29 11:30:00,130.16,117.978,56.771,31.349 +2020-10-29 11:45:00,131.01,118.196,56.771,31.349 +2020-10-29 12:00:00,130.57,114.31700000000001,53.701,31.349 +2020-10-29 12:15:00,130.91,113.45200000000001,53.701,31.349 +2020-10-29 12:30:00,133.3,113.556,53.701,31.349 +2020-10-29 12:45:00,130.48,113.921,53.701,31.349 +2020-10-29 13:00:00,133.45,113.51899999999999,52.364,31.349 +2020-10-29 13:15:00,130.71,113.147,52.364,31.349 +2020-10-29 13:30:00,128.95,112.436,52.364,31.349 +2020-10-29 13:45:00,129.8,112.055,52.364,31.349 +2020-10-29 14:00:00,130.29,111.265,53.419,31.349 +2020-10-29 14:15:00,129.67,111.324,53.419,31.349 +2020-10-29 14:30:00,128.02,111.11200000000001,53.419,31.349 +2020-10-29 14:45:00,130.31,110.98299999999999,53.419,31.349 +2020-10-29 15:00:00,127.71,110.096,56.744,31.349 +2020-10-29 15:15:00,126.9,110.236,56.744,31.349 +2020-10-29 15:30:00,126.36,110.75,56.744,31.349 +2020-10-29 15:45:00,125.0,110.89399999999999,56.744,31.349 +2020-10-29 16:00:00,125.03,111.45200000000001,60.458,31.349 +2020-10-29 16:15:00,124.56,112.12799999999999,60.458,31.349 +2020-10-29 16:30:00,124.0,112.494,60.458,31.349 +2020-10-29 16:45:00,128.25,111.682,60.458,31.349 +2020-10-29 17:00:00,134.32,111.861,66.295,31.349 +2020-10-29 17:15:00,137.45,112.973,66.295,31.349 +2020-10-29 17:30:00,135.07,112.95700000000001,66.295,31.349 +2020-10-29 17:45:00,135.2,113.255,66.295,31.349 +2020-10-29 18:00:00,134.97,114.259,68.468,31.349 +2020-10-29 18:15:00,133.97,113.67,68.468,31.349 +2020-10-29 18:30:00,134.41,112.444,68.468,31.349 +2020-10-29 18:45:00,130.82,115.691,68.468,31.349 +2020-10-29 19:00:00,127.46,116.72399999999999,66.39399999999999,31.349 +2020-10-29 19:15:00,126.41,115.23700000000001,66.39399999999999,31.349 +2020-10-29 19:30:00,124.92,114.338,66.39399999999999,31.349 +2020-10-29 19:45:00,123.49,113.619,66.39399999999999,31.349 +2020-10-29 20:00:00,123.79,110.91799999999999,63.183,31.349 +2020-10-29 20:15:00,121.13,108.01299999999999,63.183,31.349 +2020-10-29 20:30:00,115.55,107.385,63.183,31.349 +2020-10-29 20:45:00,110.22,105.69,63.183,31.349 +2020-10-29 21:00:00,106.12,102.105,55.133,31.349 +2020-10-29 21:15:00,108.93,101.885,55.133,31.349 +2020-10-29 21:30:00,108.46,101.26799999999999,55.133,31.349 +2020-10-29 21:45:00,104.99,98.762,55.133,31.349 +2020-10-29 22:00:00,95.45,94.24700000000001,50.111999999999995,31.349 +2020-10-29 22:15:00,94.84,90.696,50.111999999999995,31.349 +2020-10-29 22:30:00,95.34,79.503,50.111999999999995,31.349 +2020-10-29 22:45:00,96.16,73.104,50.111999999999995,31.349 +2020-10-29 23:00:00,90.51,67.551,44.536,31.349 +2020-10-29 23:15:00,87.23,67.025,44.536,31.349 +2020-10-29 23:30:00,86.68,66.032,44.536,31.349 +2020-10-29 23:45:00,87.36,66.191,44.536,31.349 +2020-10-30 00:00:00,85.77,61.242,42.291000000000004,31.349 +2020-10-30 00:15:00,82.09,62.739,42.291000000000004,31.349 +2020-10-30 00:30:00,77.43,62.463,42.291000000000004,31.349 +2020-10-30 00:45:00,82.66,62.458,42.291000000000004,31.349 +2020-10-30 01:00:00,80.41,63.026,41.008,31.349 +2020-10-30 01:15:00,82.12,62.924,41.008,31.349 +2020-10-30 01:30:00,75.47,62.736000000000004,41.008,31.349 +2020-10-30 01:45:00,77.24,62.371,41.008,31.349 +2020-10-30 02:00:00,74.28,63.827,39.521,31.349 +2020-10-30 02:15:00,74.78,64.03699999999999,39.521,31.349 +2020-10-30 02:30:00,79.52,65.142,39.521,31.349 +2020-10-30 02:45:00,81.93,65.428,39.521,31.349 +2020-10-30 03:00:00,80.63,67.885,39.812,31.349 +2020-10-30 03:15:00,75.97,69.27600000000001,39.812,31.349 +2020-10-30 03:30:00,78.26,69.422,39.812,31.349 +2020-10-30 03:45:00,82.39,70.611,39.812,31.349 +2020-10-30 04:00:00,82.18,80.916,41.22,31.349 +2020-10-30 04:15:00,78.3,89.902,41.22,31.349 +2020-10-30 04:30:00,84.06,90.31200000000001,41.22,31.349 +2020-10-30 04:45:00,88.56,91.48200000000001,41.22,31.349 +2020-10-30 05:00:00,92.62,120.163,45.115,31.349 +2020-10-30 05:15:00,89.69,147.474,45.115,31.349 +2020-10-30 05:30:00,95.75,142.15200000000002,45.115,31.349 +2020-10-30 05:45:00,102.23,132.844,45.115,31.349 +2020-10-30 06:00:00,113.59,132.496,59.06100000000001,31.349 +2020-10-30 06:15:00,111.04,136.235,59.06100000000001,31.349 +2020-10-30 06:30:00,113.71,134.79399999999998,59.06100000000001,31.349 +2020-10-30 06:45:00,116.04,136.179,59.06100000000001,31.349 +2020-10-30 07:00:00,119.06,136.855,71.874,31.349 +2020-10-30 07:15:00,120.84,140.64700000000002,71.874,31.349 +2020-10-30 07:30:00,122.72,140.326,71.874,31.349 +2020-10-30 07:45:00,122.52,139.918,71.874,31.349 +2020-10-30 08:00:00,125.5,139.739,68.439,31.349 +2020-10-30 08:15:00,124.47,139.237,68.439,31.349 +2020-10-30 08:30:00,127.07,137.506,68.439,31.349 +2020-10-30 08:45:00,127.1,134.52700000000002,68.439,31.349 +2020-10-30 09:00:00,128.83,129.542,65.523,31.349 +2020-10-30 09:15:00,129.4,128.264,65.523,31.349 +2020-10-30 09:30:00,128.5,127.581,65.523,31.349 +2020-10-30 09:45:00,127.31,126.919,65.523,31.349 +2020-10-30 10:00:00,126.26,122.965,62.005,31.349 +2020-10-30 10:15:00,124.62,121.74,62.005,31.349 +2020-10-30 10:30:00,123.49,120.156,62.005,31.349 +2020-10-30 10:45:00,124.03,119.26,62.005,31.349 +2020-10-30 11:00:00,124.17,116.04299999999999,60.351000000000006,31.349 +2020-10-30 11:15:00,126.28,115.26100000000001,60.351000000000006,31.349 +2020-10-30 11:30:00,123.52,116.325,60.351000000000006,31.349 +2020-10-30 11:45:00,123.86,116.17299999999999,60.351000000000006,31.349 +2020-10-30 12:00:00,121.54,113.084,55.331,31.349 +2020-10-30 12:15:00,125.28,110.75,55.331,31.349 +2020-10-30 12:30:00,124.06,111.024,55.331,31.349 +2020-10-30 12:45:00,121.96,111.383,55.331,31.349 +2020-10-30 13:00:00,118.71,111.73299999999999,53.361999999999995,31.349 +2020-10-30 13:15:00,120.56,111.917,53.361999999999995,31.349 +2020-10-30 13:30:00,118.3,111.51799999999999,53.361999999999995,31.349 +2020-10-30 13:45:00,120.01,111.213,53.361999999999995,31.349 +2020-10-30 14:00:00,117.73,109.399,51.708,31.349 +2020-10-30 14:15:00,118.43,109.535,51.708,31.349 +2020-10-30 14:30:00,118.52,110.236,51.708,31.349 +2020-10-30 14:45:00,119.61,110.02,51.708,31.349 +2020-10-30 15:00:00,120.23,108.8,54.571000000000005,31.349 +2020-10-30 15:15:00,118.83,108.60799999999999,54.571000000000005,31.349 +2020-10-30 15:30:00,118.31,108.04899999999999,54.571000000000005,31.349 +2020-10-30 15:45:00,119.66,108.585,54.571000000000005,31.349 +2020-10-30 16:00:00,120.97,108.147,58.662,31.349 +2020-10-30 16:15:00,120.45,109.209,58.662,31.349 +2020-10-30 16:30:00,122.73,109.552,58.662,31.349 +2020-10-30 16:45:00,126.37,108.40799999999999,58.662,31.349 +2020-10-30 17:00:00,132.24,109.404,65.941,31.349 +2020-10-30 17:15:00,131.66,110.236,65.941,31.349 +2020-10-30 17:30:00,135.32,110.119,65.941,31.349 +2020-10-30 17:45:00,131.92,110.234,65.941,31.349 +2020-10-30 18:00:00,131.8,111.662,65.628,31.349 +2020-10-30 18:15:00,129.88,110.432,65.628,31.349 +2020-10-30 18:30:00,128.86,109.383,65.628,31.349 +2020-10-30 18:45:00,128.89,112.803,65.628,31.349 +2020-10-30 19:00:00,127.22,114.73,63.662,31.349 +2020-10-30 19:15:00,123.33,114.22,63.662,31.349 +2020-10-30 19:30:00,121.1,113.117,63.662,31.349 +2020-10-30 19:45:00,121.23,111.685,63.662,31.349 +2020-10-30 20:00:00,115.04,108.969,61.945,31.349 +2020-10-30 20:15:00,110.74,106.413,61.945,31.349 +2020-10-30 20:30:00,109.01,105.51799999999999,61.945,31.349 +2020-10-30 20:45:00,106.5,103.742,61.945,31.349 +2020-10-30 21:00:00,100.01,101.007,53.903,31.349 +2020-10-30 21:15:00,102.44,101.75200000000001,53.903,31.349 +2020-10-30 21:30:00,100.98,101.102,53.903,31.349 +2020-10-30 21:45:00,94.15,98.95299999999999,53.903,31.349 +2020-10-30 22:00:00,87.77,94.88,48.403999999999996,31.349 +2020-10-30 22:15:00,85.26,91.131,48.403999999999996,31.349 +2020-10-30 22:30:00,84.58,85.432,48.403999999999996,31.349 +2020-10-30 22:45:00,83.7,81.27199999999999,48.403999999999996,31.349 +2020-10-30 23:00:00,82.16,76.253,41.07,31.349 +2020-10-30 23:15:00,81.37,73.979,41.07,31.349 +2020-10-30 23:30:00,73.71,71.40899999999999,41.07,31.349 +2020-10-30 23:45:00,71.13,71.156,41.07,31.349 +2020-10-31 00:00:00,72.45,61.597,38.989000000000004,31.177 +2020-10-31 00:15:00,73.37,59.941,38.989000000000004,31.177 +2020-10-31 00:30:00,73.61,59.854,38.989000000000004,31.177 +2020-10-31 00:45:00,71.83,60.141000000000005,38.989000000000004,31.177 +2020-10-31 01:00:00,68.78,61.233000000000004,35.275,31.177 +2020-10-31 01:15:00,71.23,61.393,35.275,31.177 +2020-10-31 01:30:00,69.97,60.773,35.275,31.177 +2020-10-31 01:45:00,65.93,60.555,35.275,31.177 +2020-10-31 02:00:00,64.99,61.573,32.838,31.177 +2020-10-31 02:15:00,69.96,61.156000000000006,32.838,31.177 +2020-10-31 02:30:00,68.68,61.831,32.838,31.177 +2020-10-31 02:45:00,66.64,62.625,32.838,31.177 +2020-10-31 03:00:00,65.79,65.196,32.418,31.177 +2020-10-31 03:15:00,67.38,65.55199999999999,32.418,31.177 +2020-10-31 03:30:00,67.91,65.406,32.418,31.177 +2020-10-31 03:45:00,63.45,66.92699999999999,32.418,31.177 +2020-10-31 04:00:00,60.12,74.21600000000001,32.099000000000004,31.177 +2020-10-31 04:15:00,61.24,80.874,32.099000000000004,31.177 +2020-10-31 04:30:00,62.55,80.10300000000001,32.099000000000004,31.177 +2020-10-31 04:45:00,62.68,81.078,32.099000000000004,31.177 +2020-10-31 05:00:00,63.12,96.679,32.926,31.177 +2020-10-31 05:15:00,62.57,108.109,32.926,31.177 +2020-10-31 05:30:00,63.2,103.416,32.926,31.177 +2020-10-31 05:45:00,65.37,98.848,32.926,31.177 +2020-10-31 06:00:00,68.19,111.434,35.069,31.177 +2020-10-31 06:15:00,69.79,126.461,35.069,31.177 +2020-10-31 06:30:00,71.11,120.191,35.069,31.177 +2020-10-31 06:45:00,73.82,114.679,35.069,31.177 +2020-10-31 07:00:00,75.79,113.76,40.906,31.177 +2020-10-31 07:15:00,77.22,115.145,40.906,31.177 +2020-10-31 07:30:00,78.86,116.89,40.906,31.177 +2020-10-31 07:45:00,81.1,118.516,40.906,31.177 +2020-10-31 08:00:00,83.23,121.64399999999999,46.603,31.177 +2020-10-31 08:15:00,84.49,123.396,46.603,31.177 +2020-10-31 08:30:00,86.25,123.77,46.603,31.177 +2020-10-31 08:45:00,88.76,123.838,46.603,31.177 +2020-10-31 09:00:00,92.45,120.73899999999999,49.935,31.177 +2020-10-31 09:15:00,93.68,120.148,49.935,31.177 +2020-10-31 09:30:00,94.02,120.255,49.935,31.177 +2020-10-31 09:45:00,94.02,119.835,49.935,31.177 +2020-10-31 10:00:00,92.45,117.68,47.585,31.177 +2020-10-31 10:15:00,93.24,117.001,47.585,31.177 +2020-10-31 10:30:00,92.64,115.77,47.585,31.177 +2020-10-31 10:45:00,92.15,114.821,47.585,31.177 +2020-10-31 11:00:00,93.34,111.95100000000001,43.376999999999995,31.177 +2020-10-31 11:15:00,96.08,111.00299999999999,43.376999999999995,31.177 +2020-10-31 11:30:00,95.83,111.277,43.376999999999995,31.177 +2020-10-31 11:45:00,96.13,111.28,43.376999999999995,31.177 +2020-10-31 12:00:00,95.48,107.751,40.855,31.177 +2020-10-31 12:15:00,95.57,106.947,40.855,31.177 +2020-10-31 12:30:00,92.12,106.645,40.855,31.177 +2020-10-31 12:45:00,91.78,106.042,40.855,31.177 +2020-10-31 13:00:00,88.92,105.20100000000001,37.251,31.177 +2020-10-31 13:15:00,89.25,105.492,37.251,31.177 +2020-10-31 13:30:00,88.99,104.34899999999999,37.251,31.177 +2020-10-31 13:45:00,89.08,103.85700000000001,37.251,31.177 +2020-10-31 14:00:00,86.2,103.321,38.548,31.177 +2020-10-31 14:15:00,87.18,103.538,38.548,31.177 +2020-10-31 14:30:00,86.99,103.285,38.548,31.177 +2020-10-31 14:45:00,87.31,102.726,38.548,31.177 +2020-10-31 15:00:00,87.48,101.215,42.883,31.177 +2020-10-31 15:15:00,87.93,101.897,42.883,31.177 +2020-10-31 15:30:00,87.72,102.617,42.883,31.177 +2020-10-31 15:45:00,88.54,103.33200000000001,42.883,31.177 +2020-10-31 16:00:00,90.83,103.56700000000001,48.143,31.177 +2020-10-31 16:15:00,90.48,104.383,48.143,31.177 +2020-10-31 16:30:00,92.56,105.337,48.143,31.177 +2020-10-31 16:45:00,97.53,104.829,48.143,31.177 +2020-10-31 17:00:00,102.04,105.22399999999999,55.25,31.177 +2020-10-31 17:15:00,100.68,106.541,55.25,31.177 +2020-10-31 17:30:00,102.51,106.81700000000001,55.25,31.177 +2020-10-31 17:45:00,102.66,108.331,55.25,31.177 +2020-10-31 18:00:00,104.1,110.088,57.506,31.177 +2020-10-31 18:15:00,107.0,111.04,57.506,31.177 +2020-10-31 18:30:00,103.91,110.087,57.506,31.177 +2020-10-31 18:45:00,104.55,111.25,57.506,31.177 +2020-10-31 19:00:00,100.55,113.781,55.528999999999996,31.177 +2020-10-31 19:15:00,99.24,112.38600000000001,55.528999999999996,31.177 +2020-10-31 19:30:00,97.68,111.81,55.528999999999996,31.177 +2020-10-31 19:45:00,96.75,111.583,55.528999999999996,31.177 +2020-10-31 20:00:00,91.74,110.46700000000001,46.166000000000004,31.177 +2020-10-31 20:15:00,88.35,109.18799999999999,46.166000000000004,31.177 +2020-10-31 20:30:00,86.31,108.696,46.166000000000004,31.177 +2020-10-31 20:45:00,83.91,106.139,46.166000000000004,31.177 +2020-10-31 21:00:00,80.56,102.51899999999999,40.406,31.177 +2020-10-31 21:15:00,80.4,102.87,40.406,31.177 +2020-10-31 21:30:00,79.05,102.81200000000001,40.406,31.177 +2020-10-31 21:45:00,77.96,100.42200000000001,40.406,31.177 +2020-10-31 22:00:00,75.29,97.337,39.616,31.177 +2020-10-31 22:15:00,74.6,93.835,39.616,31.177 +2020-10-31 22:30:00,71.03,89.384,39.616,31.177 +2020-10-31 22:45:00,70.49,85.363,39.616,31.177 +2020-10-31 23:00:00,67.0,79.763,32.205,31.177 +2020-10-31 23:15:00,66.8,78.317,32.205,31.177 +2020-10-31 23:30:00,64.91,76.102,32.205,31.177 +2020-10-31 23:45:00,63.18,75.262,32.205,31.177 +2020-11-01 00:00:00,58.91,73.844,36.376,32.047 +2020-11-01 00:15:00,57.64,71.598,36.376,32.047 +2020-11-01 00:30:00,58.09,71.771,36.376,32.047 +2020-11-01 00:45:00,56.8,72.655,36.376,32.047 +2020-11-01 01:00:00,53.61,74.10300000000001,32.992,32.047 +2020-11-01 01:15:00,55.48,74.691,32.992,32.047 +2020-11-01 01:30:00,54.86,74.157,32.992,32.047 +2020-11-01 01:45:00,55.56,74.169,32.992,32.047 +2020-11-01 02:00:00,53.33,75.35600000000001,32.327,32.047 +2020-11-01 02:15:00,53.19,74.95,32.327,32.047 +2020-11-01 02:30:00,53.38,75.499,32.327,32.047 +2020-11-01 02:45:00,53.54,76.763,32.327,32.047 +2020-11-01 03:00:00,52.29,79.542,31.169,32.047 +2020-11-01 03:15:00,53.27,79.608,31.169,32.047 +2020-11-01 03:30:00,53.38,80.009,31.169,32.047 +2020-11-01 03:45:00,53.67,81.561,31.169,32.047 +2020-11-01 04:00:00,53.04,90.10600000000001,30.796,32.047 +2020-11-01 04:15:00,53.69,97.82600000000001,30.796,32.047 +2020-11-01 04:30:00,54.71,97.304,30.796,32.047 +2020-11-01 04:45:00,54.7,98.21600000000001,30.796,32.047 +2020-11-01 05:00:00,56.28,114.69200000000001,30.848000000000003,32.047 +2020-11-01 05:15:00,56.84,126.919,30.848000000000003,32.047 +2020-11-01 05:30:00,56.12,122.334,30.848000000000003,32.047 +2020-11-01 05:45:00,57.2,117.79299999999999,30.848000000000003,32.047 +2020-11-01 06:00:00,58.93,131.375,31.166,32.047 +2020-11-01 06:15:00,60.16,148.164,31.166,32.047 +2020-11-01 06:30:00,60.09,141.499,31.166,32.047 +2020-11-01 06:45:00,61.33,135.033,31.166,32.047 +2020-11-01 07:00:00,62.62,134.209,33.527,32.047 +2020-11-01 07:15:00,64.53,135.998,33.527,32.047 +2020-11-01 07:30:00,66.19,138.07299999999998,33.527,32.047 +2020-11-01 07:45:00,69.32,140.029,33.527,32.047 +2020-11-01 08:00:00,72.18,143.476,36.616,32.047 +2020-11-01 08:15:00,73.05,145.618,36.616,32.047 +2020-11-01 08:30:00,75.6,146.314,36.616,32.047 +2020-11-01 08:45:00,76.81,146.583,36.616,32.047 +2020-11-01 09:00:00,78.39,143.313,37.857,32.047 +2020-11-01 09:15:00,79.58,142.33100000000002,37.857,32.047 +2020-11-01 09:30:00,79.36,141.849,37.857,32.047 +2020-11-01 09:45:00,80.15,141.034,37.857,32.047 +2020-11-01 10:00:00,78.79,139.36,36.319,32.047 +2020-11-01 10:15:00,81.18,138.553,36.319,32.047 +2020-11-01 10:30:00,82.06,137.161,36.319,32.047 +2020-11-01 10:45:00,84.37,135.914,36.319,32.047 +2020-11-01 11:00:00,87.44,132.654,37.236999999999995,32.047 +2020-11-01 11:15:00,90.16,131.516,37.236999999999995,32.047 +2020-11-01 11:30:00,92.79,131.694,37.236999999999995,32.047 +2020-11-01 11:45:00,94.47,132.332,37.236999999999995,32.047 +2020-11-01 12:00:00,93.72,128.334,34.871,32.047 +2020-11-01 12:15:00,91.05,127.323,34.871,32.047 +2020-11-01 12:30:00,88.73,126.853,34.871,32.047 +2020-11-01 12:45:00,87.98,126.25399999999999,34.871,32.047 +2020-11-01 13:00:00,81.86,125.57600000000001,29.738000000000003,32.047 +2020-11-01 13:15:00,79.48,125.912,29.738000000000003,32.047 +2020-11-01 13:30:00,78.24,124.734,29.738000000000003,32.047 +2020-11-01 13:45:00,77.77,124.292,29.738000000000003,32.047 +2020-11-01 14:00:00,78.82,124.43,27.333000000000002,32.047 +2020-11-01 14:15:00,77.97,124.331,27.333000000000002,32.047 +2020-11-01 14:30:00,78.18,123.743,27.333000000000002,32.047 +2020-11-01 14:45:00,79.25,123.29,27.333000000000002,32.047 +2020-11-01 15:00:00,79.18,121.579,28.232,32.047 +2020-11-01 15:15:00,79.37,122.45100000000001,28.232,32.047 +2020-11-01 15:30:00,79.55,122.805,28.232,32.047 +2020-11-01 15:45:00,81.75,123.443,28.232,32.047 +2020-11-01 16:00:00,83.82,125.545,32.815,32.047 +2020-11-01 16:15:00,83.89,127.041,32.815,32.047 +2020-11-01 16:30:00,84.8,127.854,32.815,32.047 +2020-11-01 16:45:00,89.87,127.169,32.815,32.047 +2020-11-01 17:00:00,96.6,129.106,43.068999999999996,32.047 +2020-11-01 17:15:00,96.91,130.164,43.068999999999996,32.047 +2020-11-01 17:30:00,99.73,130.485,43.068999999999996,32.047 +2020-11-01 17:45:00,101.95,131.416,43.068999999999996,32.047 +2020-11-01 18:00:00,100.66,134.56799999999998,50.498999999999995,32.047 +2020-11-01 18:15:00,100.99,135.72299999999998,50.498999999999995,32.047 +2020-11-01 18:30:00,97.5,134.084,50.498999999999995,32.047 +2020-11-01 18:45:00,96.13,135.263,50.498999999999995,32.047 +2020-11-01 19:00:00,94.03,137.83700000000002,53.481,32.047 +2020-11-01 19:15:00,94.0,136.382,53.481,32.047 +2020-11-01 19:30:00,92.13,135.606,53.481,32.047 +2020-11-01 19:45:00,92.89,135.071,53.481,32.047 +2020-11-01 20:00:00,90.69,132.186,51.687,32.047 +2020-11-01 20:15:00,94.58,130.61700000000002,51.687,32.047 +2020-11-01 20:30:00,92.24,130.408,51.687,32.047 +2020-11-01 20:45:00,86.66,127.39,51.687,32.047 +2020-11-01 21:00:00,83.37,123.02,47.674,32.047 +2020-11-01 21:15:00,84.21,122.93,47.674,32.047 +2020-11-01 21:30:00,88.7,122.751,47.674,32.047 +2020-11-01 21:45:00,90.47,120.36399999999999,47.674,32.047 +2020-11-01 22:00:00,90.97,116.14,48.178000000000004,32.047 +2020-11-01 22:15:00,86.98,112.27799999999999,48.178000000000004,32.047 +2020-11-01 22:30:00,83.62,106.661,48.178000000000004,32.047 +2020-11-01 22:45:00,82.66,102.23,48.178000000000004,32.047 +2020-11-01 23:00:00,81.93,96.52,42.553999999999995,32.047 +2020-11-01 23:15:00,85.7,94.42200000000001,42.553999999999995,32.047 +2020-11-01 23:30:00,82.06,92.15700000000001,42.553999999999995,32.047 +2020-11-01 23:45:00,78.61,90.99799999999999,42.553999999999995,32.047 +2020-11-02 00:00:00,77.71,77.483,37.177,32.225 +2020-11-02 00:15:00,78.04,77.413,37.177,32.225 +2020-11-02 00:30:00,75.0,77.514,37.177,32.225 +2020-11-02 00:45:00,71.86,77.888,37.177,32.225 +2020-11-02 01:00:00,73.26,79.508,35.358000000000004,32.225 +2020-11-02 01:15:00,75.19,79.788,35.358000000000004,32.225 +2020-11-02 01:30:00,74.29,79.453,35.358000000000004,32.225 +2020-11-02 01:45:00,71.36,79.48899999999999,35.358000000000004,32.225 +2020-11-02 02:00:00,74.46,80.84899999999999,35.03,32.225 +2020-11-02 02:15:00,76.19,80.941,35.03,32.225 +2020-11-02 02:30:00,73.58,81.756,35.03,32.225 +2020-11-02 02:45:00,70.49,82.568,35.03,32.225 +2020-11-02 03:00:00,71.48,86.30799999999999,34.394,32.225 +2020-11-02 03:15:00,76.54,87.72,34.394,32.225 +2020-11-02 03:30:00,78.06,88.242,34.394,32.225 +2020-11-02 03:45:00,72.8,89.243,34.394,32.225 +2020-11-02 04:00:00,77.28,101.759,34.421,32.225 +2020-11-02 04:15:00,80.21,113.29299999999999,34.421,32.225 +2020-11-02 04:30:00,81.33,113.90799999999999,34.421,32.225 +2020-11-02 04:45:00,78.06,115.085,34.421,32.225 +2020-11-02 05:00:00,85.36,144.252,39.435,32.225 +2020-11-02 05:15:00,91.98,173.24200000000002,39.435,32.225 +2020-11-02 05:30:00,96.04,168.166,39.435,32.225 +2020-11-02 05:45:00,95.92,158.836,39.435,32.225 +2020-11-02 06:00:00,104.79,157.326,55.685,32.225 +2020-11-02 06:15:00,114.4,161.115,55.685,32.225 +2020-11-02 06:30:00,121.39,161.122,55.685,32.225 +2020-11-02 06:45:00,125.87,162.344,55.685,32.225 +2020-11-02 07:00:00,126.76,163.525,66.837,32.225 +2020-11-02 07:15:00,126.88,167.03400000000002,66.837,32.225 +2020-11-02 07:30:00,127.09,168.299,66.837,32.225 +2020-11-02 07:45:00,125.37,168.74099999999999,66.837,32.225 +2020-11-02 08:00:00,127.99,168.55700000000002,72.217,32.225 +2020-11-02 08:15:00,126.32,168.81599999999997,72.217,32.225 +2020-11-02 08:30:00,126.89,166.49400000000003,72.217,32.225 +2020-11-02 08:45:00,124.03,164.62900000000002,72.217,32.225 +2020-11-02 09:00:00,123.27,160.44799999999998,66.117,32.225 +2020-11-02 09:15:00,123.92,156.477,66.117,32.225 +2020-11-02 09:30:00,125.15,154.977,66.117,32.225 +2020-11-02 09:45:00,128.93,153.493,66.117,32.225 +2020-11-02 10:00:00,124.84,151.434,62.1,32.225 +2020-11-02 10:15:00,123.92,150.312,62.1,32.225 +2020-11-02 10:30:00,124.83,148.13299999999998,62.1,32.225 +2020-11-02 10:45:00,125.47,146.829,62.1,32.225 +2020-11-02 11:00:00,125.02,141.958,60.021,32.225 +2020-11-02 11:15:00,116.55,142.15,60.021,32.225 +2020-11-02 11:30:00,119.3,143.525,60.021,32.225 +2020-11-02 11:45:00,122.68,144.034,60.021,32.225 +2020-11-02 12:00:00,116.26,140.8,56.75899999999999,32.225 +2020-11-02 12:15:00,116.59,139.82399999999998,56.75899999999999,32.225 +2020-11-02 12:30:00,124.27,139.215,56.75899999999999,32.225 +2020-11-02 12:45:00,119.06,139.694,56.75899999999999,32.225 +2020-11-02 13:00:00,118.02,139.783,56.04600000000001,32.225 +2020-11-02 13:15:00,119.4,138.85399999999998,56.04600000000001,32.225 +2020-11-02 13:30:00,122.02,137.35399999999998,56.04600000000001,32.225 +2020-11-02 13:45:00,119.1,137.218,56.04600000000001,32.225 +2020-11-02 14:00:00,122.77,136.59,55.475,32.225 +2020-11-02 14:15:00,120.69,136.239,55.475,32.225 +2020-11-02 14:30:00,120.41,135.251,55.475,32.225 +2020-11-02 14:45:00,124.04,135.44,55.475,32.225 +2020-11-02 15:00:00,120.94,134.887,57.048,32.225 +2020-11-02 15:15:00,120.7,134.558,57.048,32.225 +2020-11-02 15:30:00,120.66,134.607,57.048,32.225 +2020-11-02 15:45:00,121.41,134.784,57.048,32.225 +2020-11-02 16:00:00,122.05,137.142,59.06,32.225 +2020-11-02 16:15:00,124.08,138.155,59.06,32.225 +2020-11-02 16:30:00,124.36,138.067,59.06,32.225 +2020-11-02 16:45:00,129.23,136.614,59.06,32.225 +2020-11-02 17:00:00,134.05,137.868,65.419,32.225 +2020-11-02 17:15:00,132.55,138.431,65.419,32.225 +2020-11-02 17:30:00,137.22,138.244,65.419,32.225 +2020-11-02 17:45:00,135.22,138.03,65.419,32.225 +2020-11-02 18:00:00,135.07,141.039,69.345,32.225 +2020-11-02 18:15:00,132.86,140.007,69.345,32.225 +2020-11-02 18:30:00,131.02,138.54399999999998,69.345,32.225 +2020-11-02 18:45:00,131.86,141.345,69.345,32.225 +2020-11-02 19:00:00,128.07,142.715,73.825,32.225 +2020-11-02 19:15:00,126.32,140.951,73.825,32.225 +2020-11-02 19:30:00,127.01,140.357,73.825,32.225 +2020-11-02 19:45:00,122.74,139.009,73.825,32.225 +2020-11-02 20:00:00,119.02,134.085,64.027,32.225 +2020-11-02 20:15:00,113.3,131.38299999999998,64.027,32.225 +2020-11-02 20:30:00,108.72,130.102,64.027,32.225 +2020-11-02 20:45:00,106.21,128.234,64.027,32.225 +2020-11-02 21:00:00,107.92,123.986,57.952,32.225 +2020-11-02 21:15:00,109.71,123.275,57.952,32.225 +2020-11-02 21:30:00,108.09,122.71700000000001,57.952,32.225 +2020-11-02 21:45:00,100.41,119.898,57.952,32.225 +2020-11-02 22:00:00,93.26,112.988,53.031000000000006,32.225 +2020-11-02 22:15:00,93.34,108.98299999999999,53.031000000000006,32.225 +2020-11-02 22:30:00,98.42,95.46700000000001,53.031000000000006,32.225 +2020-11-02 22:45:00,95.12,87.84,53.031000000000006,32.225 +2020-11-02 23:00:00,89.39,82.635,45.085,32.225 +2020-11-02 23:15:00,85.21,81.652,45.085,32.225 +2020-11-02 23:30:00,86.99,81.206,45.085,32.225 +2020-11-02 23:45:00,87.54,81.581,45.085,32.225 +2020-11-03 00:00:00,84.2,76.499,42.843,32.225 +2020-11-03 00:15:00,78.55,77.664,42.843,32.225 +2020-11-03 00:30:00,76.57,77.46300000000001,42.843,32.225 +2020-11-03 00:45:00,82.07,77.531,42.843,32.225 +2020-11-03 01:00:00,80.46,78.831,41.542,32.225 +2020-11-03 01:15:00,79.44,78.893,41.542,32.225 +2020-11-03 01:30:00,74.87,78.63,41.542,32.225 +2020-11-03 01:45:00,72.5,78.641,41.542,32.225 +2020-11-03 02:00:00,78.57,79.813,40.19,32.225 +2020-11-03 02:15:00,80.48,80.32300000000001,40.19,32.225 +2020-11-03 02:30:00,80.8,80.593,40.19,32.225 +2020-11-03 02:45:00,74.67,81.544,40.19,32.225 +2020-11-03 03:00:00,71.93,84.294,39.626,32.225 +2020-11-03 03:15:00,79.33,85.59299999999999,39.626,32.225 +2020-11-03 03:30:00,82.08,86.404,39.626,32.225 +2020-11-03 03:45:00,82.13,87.045,39.626,32.225 +2020-11-03 04:00:00,77.08,98.926,40.196999999999996,32.225 +2020-11-03 04:15:00,77.12,110.274,40.196999999999996,32.225 +2020-11-03 04:30:00,82.35,110.641,40.196999999999996,32.225 +2020-11-03 04:45:00,87.06,112.781,40.196999999999996,32.225 +2020-11-03 05:00:00,91.11,145.749,43.378,32.225 +2020-11-03 05:15:00,88.11,174.916,43.378,32.225 +2020-11-03 05:30:00,88.09,168.97,43.378,32.225 +2020-11-03 05:45:00,96.86,159.315,43.378,32.225 +2020-11-03 06:00:00,106.9,157.497,55.691,32.225 +2020-11-03 06:15:00,109.83,162.39700000000002,55.691,32.225 +2020-11-03 06:30:00,115.64,161.89700000000002,55.691,32.225 +2020-11-03 06:45:00,121.32,162.537,55.691,32.225 +2020-11-03 07:00:00,121.08,163.66,65.567,32.225 +2020-11-03 07:15:00,122.41,166.97799999999998,65.567,32.225 +2020-11-03 07:30:00,122.07,167.926,65.567,32.225 +2020-11-03 07:45:00,122.18,168.12400000000002,65.567,32.225 +2020-11-03 08:00:00,123.78,168.005,73.001,32.225 +2020-11-03 08:15:00,123.3,167.40099999999998,73.001,32.225 +2020-11-03 08:30:00,121.01,165.054,73.001,32.225 +2020-11-03 08:45:00,120.18,162.636,73.001,32.225 +2020-11-03 09:00:00,119.27,158.009,67.08800000000001,32.225 +2020-11-03 09:15:00,120.13,155.065,67.08800000000001,32.225 +2020-11-03 09:30:00,119.45,154.263,67.08800000000001,32.225 +2020-11-03 09:45:00,119.38,153.096,67.08800000000001,32.225 +2020-11-03 10:00:00,117.17,150.135,62.803000000000004,32.225 +2020-11-03 10:15:00,116.37,148.19,62.803000000000004,32.225 +2020-11-03 10:30:00,114.86,146.122,62.803000000000004,32.225 +2020-11-03 10:45:00,114.46,145.343,62.803000000000004,32.225 +2020-11-03 11:00:00,115.53,141.503,60.155,32.225 +2020-11-03 11:15:00,116.0,141.577,60.155,32.225 +2020-11-03 11:30:00,114.96,141.749,60.155,32.225 +2020-11-03 11:45:00,115.15,142.586,60.155,32.225 +2020-11-03 12:00:00,114.54,138.3,56.845,32.225 +2020-11-03 12:15:00,113.41,137.136,56.845,32.225 +2020-11-03 12:30:00,116.09,137.35399999999998,56.845,32.225 +2020-11-03 12:45:00,113.87,137.849,56.845,32.225 +2020-11-03 13:00:00,112.05,137.47799999999998,56.163000000000004,32.225 +2020-11-03 13:15:00,113.4,136.88,56.163000000000004,32.225 +2020-11-03 13:30:00,111.98,136.209,56.163000000000004,32.225 +2020-11-03 13:45:00,112.78,135.895,56.163000000000004,32.225 +2020-11-03 14:00:00,114.01,135.56,55.934,32.225 +2020-11-03 14:15:00,114.62,135.273,55.934,32.225 +2020-11-03 14:30:00,114.22,134.879,55.934,32.225 +2020-11-03 14:45:00,116.32,134.763,55.934,32.225 +2020-11-03 15:00:00,118.75,133.835,57.43899999999999,32.225 +2020-11-03 15:15:00,121.12,134.054,57.43899999999999,32.225 +2020-11-03 15:30:00,118.99,134.238,57.43899999999999,32.225 +2020-11-03 15:45:00,120.3,134.248,57.43899999999999,32.225 +2020-11-03 16:00:00,121.23,136.631,59.968999999999994,32.225 +2020-11-03 16:15:00,121.12,138.031,59.968999999999994,32.225 +2020-11-03 16:30:00,124.29,138.269,59.968999999999994,32.225 +2020-11-03 16:45:00,128.38,137.31,59.968999999999994,32.225 +2020-11-03 17:00:00,134.36,138.976,67.428,32.225 +2020-11-03 17:15:00,135.38,139.727,67.428,32.225 +2020-11-03 17:30:00,136.98,139.846,67.428,32.225 +2020-11-03 17:45:00,136.7,139.438,67.428,32.225 +2020-11-03 18:00:00,135.03,142.02700000000002,71.533,32.225 +2020-11-03 18:15:00,133.99,141.156,71.533,32.225 +2020-11-03 18:30:00,132.72,139.4,71.533,32.225 +2020-11-03 18:45:00,132.44,142.667,71.533,32.225 +2020-11-03 19:00:00,129.7,143.67,73.32300000000001,32.225 +2020-11-03 19:15:00,127.73,141.774,73.32300000000001,32.225 +2020-11-03 19:30:00,125.23,140.636,73.32300000000001,32.225 +2020-11-03 19:45:00,124.06,139.406,73.32300000000001,32.225 +2020-11-03 20:00:00,116.4,134.749,64.166,32.225 +2020-11-03 20:15:00,113.06,131.075,64.166,32.225 +2020-11-03 20:30:00,111.89,130.452,64.166,32.225 +2020-11-03 20:45:00,108.23,128.349,64.166,32.225 +2020-11-03 21:00:00,111.71,123.95299999999999,57.891999999999996,32.225 +2020-11-03 21:15:00,112.07,123.279,57.891999999999996,32.225 +2020-11-03 21:30:00,109.48,122.31299999999999,57.891999999999996,32.225 +2020-11-03 21:45:00,100.8,119.70200000000001,57.891999999999996,32.225 +2020-11-03 22:00:00,96.06,113.992,53.242,32.225 +2020-11-03 22:15:00,98.84,109.67299999999999,53.242,32.225 +2020-11-03 22:30:00,98.89,96.368,53.242,32.225 +2020-11-03 22:45:00,97.23,88.929,53.242,32.225 +2020-11-03 23:00:00,87.29,83.465,46.665,32.225 +2020-11-03 23:15:00,90.27,82.43700000000001,46.665,32.225 +2020-11-03 23:30:00,89.3,81.75399999999999,46.665,32.225 +2020-11-03 23:45:00,90.61,81.89,46.665,32.225 +2020-11-04 00:00:00,81.4,76.873,43.16,32.225 +2020-11-04 00:15:00,84.07,78.01899999999999,43.16,32.225 +2020-11-04 00:30:00,85.61,77.822,43.16,32.225 +2020-11-04 00:45:00,83.69,77.87899999999999,43.16,32.225 +2020-11-04 01:00:00,75.16,79.202,40.972,32.225 +2020-11-04 01:15:00,82.28,79.28,40.972,32.225 +2020-11-04 01:30:00,82.05,79.033,40.972,32.225 +2020-11-04 01:45:00,82.13,79.03699999999999,40.972,32.225 +2020-11-04 02:00:00,77.14,80.223,39.749,32.225 +2020-11-04 02:15:00,83.18,80.742,39.749,32.225 +2020-11-04 02:30:00,81.85,81.001,39.749,32.225 +2020-11-04 02:45:00,79.64,81.949,39.749,32.225 +2020-11-04 03:00:00,77.05,84.684,39.422,32.225 +2020-11-04 03:15:00,82.52,86.007,39.422,32.225 +2020-11-04 03:30:00,83.16,86.821,39.422,32.225 +2020-11-04 03:45:00,81.55,87.447,39.422,32.225 +2020-11-04 04:00:00,77.98,99.337,40.505,32.225 +2020-11-04 04:15:00,84.43,110.712,40.505,32.225 +2020-11-04 04:30:00,87.82,111.073,40.505,32.225 +2020-11-04 04:45:00,88.51,113.22,40.505,32.225 +2020-11-04 05:00:00,88.07,146.239,43.397,32.225 +2020-11-04 05:15:00,92.6,175.447,43.397,32.225 +2020-11-04 05:30:00,99.67,169.497,43.397,32.225 +2020-11-04 05:45:00,101.58,159.82,43.397,32.225 +2020-11-04 06:00:00,103.46,157.997,55.218,32.225 +2020-11-04 06:15:00,112.23,162.908,55.218,32.225 +2020-11-04 06:30:00,114.12,162.44,55.218,32.225 +2020-11-04 06:45:00,117.78,163.09799999999998,55.218,32.225 +2020-11-04 07:00:00,121.0,164.22,67.39,32.225 +2020-11-04 07:15:00,123.07,167.551,67.39,32.225 +2020-11-04 07:30:00,121.54,168.52599999999998,67.39,32.225 +2020-11-04 07:45:00,123.46,168.72799999999998,67.39,32.225 +2020-11-04 08:00:00,126.53,168.62400000000002,74.345,32.225 +2020-11-04 08:15:00,124.13,168.00099999999998,74.345,32.225 +2020-11-04 08:30:00,124.39,165.68,74.345,32.225 +2020-11-04 08:45:00,125.06,163.233,74.345,32.225 +2020-11-04 09:00:00,120.85,158.594,69.336,32.225 +2020-11-04 09:15:00,122.69,155.651,69.336,32.225 +2020-11-04 09:30:00,127.17,154.84,69.336,32.225 +2020-11-04 09:45:00,119.89,153.649,69.336,32.225 +2020-11-04 10:00:00,117.85,150.679,64.291,32.225 +2020-11-04 10:15:00,119.47,148.695,64.291,32.225 +2020-11-04 10:30:00,118.23,146.60299999999998,64.291,32.225 +2020-11-04 10:45:00,117.75,145.809,64.291,32.225 +2020-11-04 11:00:00,117.87,141.967,62.20399999999999,32.225 +2020-11-04 11:15:00,117.63,142.02100000000002,62.20399999999999,32.225 +2020-11-04 11:30:00,117.7,142.192,62.20399999999999,32.225 +2020-11-04 11:45:00,119.73,143.015,62.20399999999999,32.225 +2020-11-04 12:00:00,117.89,138.71,59.042,32.225 +2020-11-04 12:15:00,116.62,137.545,59.042,32.225 +2020-11-04 12:30:00,119.41,137.797,59.042,32.225 +2020-11-04 12:45:00,114.95,138.29399999999998,59.042,32.225 +2020-11-04 13:00:00,118.41,137.885,57.907,32.225 +2020-11-04 13:15:00,117.16,137.295,57.907,32.225 +2020-11-04 13:30:00,115.27,136.624,57.907,32.225 +2020-11-04 13:45:00,116.77,136.303,57.907,32.225 +2020-11-04 14:00:00,114.41,135.916,58.358000000000004,32.225 +2020-11-04 14:15:00,113.25,135.64700000000002,58.358000000000004,32.225 +2020-11-04 14:30:00,113.2,135.291,58.358000000000004,32.225 +2020-11-04 14:45:00,116.71,135.17600000000002,58.358000000000004,32.225 +2020-11-04 15:00:00,118.53,134.237,59.348,32.225 +2020-11-04 15:15:00,117.22,134.471,59.348,32.225 +2020-11-04 15:30:00,115.9,134.694,59.348,32.225 +2020-11-04 15:45:00,117.62,134.718,59.348,32.225 +2020-11-04 16:00:00,119.43,137.075,61.413999999999994,32.225 +2020-11-04 16:15:00,119.61,138.499,61.413999999999994,32.225 +2020-11-04 16:30:00,124.61,138.737,61.413999999999994,32.225 +2020-11-04 16:45:00,127.54,137.829,61.413999999999994,32.225 +2020-11-04 17:00:00,135.41,139.454,67.107,32.225 +2020-11-04 17:15:00,134.88,140.226,67.107,32.225 +2020-11-04 17:30:00,134.97,140.349,67.107,32.225 +2020-11-04 17:45:00,134.76,139.954,67.107,32.225 +2020-11-04 18:00:00,134.73,142.542,71.92,32.225 +2020-11-04 18:15:00,133.15,141.637,71.92,32.225 +2020-11-04 18:30:00,132.19,139.892,71.92,32.225 +2020-11-04 18:45:00,131.93,143.155,71.92,32.225 +2020-11-04 19:00:00,128.13,144.165,75.09,32.225 +2020-11-04 19:15:00,126.83,142.259,75.09,32.225 +2020-11-04 19:30:00,125.21,141.106,75.09,32.225 +2020-11-04 19:45:00,126.42,139.845,75.09,32.225 +2020-11-04 20:00:00,120.08,135.208,65.977,32.225 +2020-11-04 20:15:00,113.28,131.525,65.977,32.225 +2020-11-04 20:30:00,111.02,130.87,65.977,32.225 +2020-11-04 20:45:00,112.13,128.757,65.977,32.225 +2020-11-04 21:00:00,108.11,124.35700000000001,58.798,32.225 +2020-11-04 21:15:00,107.66,123.671,58.798,32.225 +2020-11-04 21:30:00,109.28,122.713,58.798,32.225 +2020-11-04 21:45:00,103.93,120.087,58.798,32.225 +2020-11-04 22:00:00,97.92,114.374,54.486000000000004,32.225 +2020-11-04 22:15:00,93.55,110.039,54.486000000000004,32.225 +2020-11-04 22:30:00,95.7,96.777,54.486000000000004,32.225 +2020-11-04 22:45:00,98.22,89.345,54.486000000000004,32.225 +2020-11-04 23:00:00,93.29,83.88600000000001,47.783,32.225 +2020-11-04 23:15:00,92.59,82.83200000000001,47.783,32.225 +2020-11-04 23:30:00,89.1,82.152,47.783,32.225 +2020-11-04 23:45:00,89.25,82.274,47.783,32.225 +2020-11-05 00:00:00,85.13,77.245,43.88,32.225 +2020-11-05 00:15:00,81.55,78.372,43.88,32.225 +2020-11-05 00:30:00,84.52,78.18,43.88,32.225 +2020-11-05 00:45:00,83.95,78.225,43.88,32.225 +2020-11-05 01:00:00,78.92,79.571,42.242,32.225 +2020-11-05 01:15:00,78.1,79.663,42.242,32.225 +2020-11-05 01:30:00,81.46,79.433,42.242,32.225 +2020-11-05 01:45:00,82.78,79.43,42.242,32.225 +2020-11-05 02:00:00,79.05,80.63,40.918,32.225 +2020-11-05 02:15:00,78.82,81.15899999999999,40.918,32.225 +2020-11-05 02:30:00,81.15,81.407,40.918,32.225 +2020-11-05 02:45:00,85.55,82.351,40.918,32.225 +2020-11-05 03:00:00,79.23,85.072,40.411,32.225 +2020-11-05 03:15:00,74.81,86.42,40.411,32.225 +2020-11-05 03:30:00,82.28,87.23700000000001,40.411,32.225 +2020-11-05 03:45:00,82.7,87.84700000000001,40.411,32.225 +2020-11-05 04:00:00,84.49,99.74700000000001,41.246,32.225 +2020-11-05 04:15:00,80.91,111.145,41.246,32.225 +2020-11-05 04:30:00,84.02,111.501,41.246,32.225 +2020-11-05 04:45:00,88.63,113.65700000000001,41.246,32.225 +2020-11-05 05:00:00,89.89,146.725,44.533,32.225 +2020-11-05 05:15:00,85.19,175.975,44.533,32.225 +2020-11-05 05:30:00,88.1,170.021,44.533,32.225 +2020-11-05 05:45:00,93.43,160.321,44.533,32.225 +2020-11-05 06:00:00,102.82,158.494,55.005,32.225 +2020-11-05 06:15:00,110.75,163.417,55.005,32.225 +2020-11-05 06:30:00,115.53,162.97799999999998,55.005,32.225 +2020-11-05 06:45:00,126.41,163.656,55.005,32.225 +2020-11-05 07:00:00,128.69,164.77700000000002,64.597,32.225 +2020-11-05 07:15:00,123.86,168.12,64.597,32.225 +2020-11-05 07:30:00,127.33,169.122,64.597,32.225 +2020-11-05 07:45:00,126.84,169.326,64.597,32.225 +2020-11-05 08:00:00,128.0,169.237,71.71600000000001,32.225 +2020-11-05 08:15:00,129.17,168.595,71.71600000000001,32.225 +2020-11-05 08:30:00,128.65,166.301,71.71600000000001,32.225 +2020-11-05 08:45:00,128.37,163.826,71.71600000000001,32.225 +2020-11-05 09:00:00,127.46,159.174,66.51899999999999,32.225 +2020-11-05 09:15:00,128.89,156.231,66.51899999999999,32.225 +2020-11-05 09:30:00,127.36,155.411,66.51899999999999,32.225 +2020-11-05 09:45:00,127.69,154.197,66.51899999999999,32.225 +2020-11-05 10:00:00,124.33,151.218,63.04,32.225 +2020-11-05 10:15:00,121.71,149.195,63.04,32.225 +2020-11-05 10:30:00,118.24,147.08,63.04,32.225 +2020-11-05 10:45:00,119.63,146.269,63.04,32.225 +2020-11-05 11:00:00,119.67,142.42700000000002,60.998000000000005,32.225 +2020-11-05 11:15:00,125.95,142.46,60.998000000000005,32.225 +2020-11-05 11:30:00,128.57,142.63,60.998000000000005,32.225 +2020-11-05 11:45:00,128.14,143.44,60.998000000000005,32.225 +2020-11-05 12:00:00,125.75,139.116,58.27,32.225 +2020-11-05 12:15:00,124.99,137.951,58.27,32.225 +2020-11-05 12:30:00,126.45,138.237,58.27,32.225 +2020-11-05 12:45:00,127.2,138.736,58.27,32.225 +2020-11-05 13:00:00,125.78,138.29,57.196000000000005,32.225 +2020-11-05 13:15:00,126.04,137.708,57.196000000000005,32.225 +2020-11-05 13:30:00,123.81,137.035,57.196000000000005,32.225 +2020-11-05 13:45:00,124.92,136.709,57.196000000000005,32.225 +2020-11-05 14:00:00,127.64,136.27,57.38399999999999,32.225 +2020-11-05 14:15:00,128.62,136.016,57.38399999999999,32.225 +2020-11-05 14:30:00,130.34,135.69899999999998,57.38399999999999,32.225 +2020-11-05 14:45:00,127.29,135.585,57.38399999999999,32.225 +2020-11-05 15:00:00,129.48,134.634,58.647,32.225 +2020-11-05 15:15:00,129.34,134.884,58.647,32.225 +2020-11-05 15:30:00,127.64,135.14600000000002,58.647,32.225 +2020-11-05 15:45:00,127.96,135.18200000000002,58.647,32.225 +2020-11-05 16:00:00,131.92,137.515,60.083999999999996,32.225 +2020-11-05 16:15:00,131.35,138.964,60.083999999999996,32.225 +2020-11-05 16:30:00,131.61,139.2,60.083999999999996,32.225 +2020-11-05 16:45:00,134.19,138.344,60.083999999999996,32.225 +2020-11-05 17:00:00,137.92,139.92700000000002,65.85600000000001,32.225 +2020-11-05 17:15:00,136.96,140.72,65.85600000000001,32.225 +2020-11-05 17:30:00,137.47,140.84799999999998,65.85600000000001,32.225 +2020-11-05 17:45:00,138.29,140.466,65.85600000000001,32.225 +2020-11-05 18:00:00,139.48,143.053,69.855,32.225 +2020-11-05 18:15:00,134.15,142.115,69.855,32.225 +2020-11-05 18:30:00,133.51,140.38,69.855,32.225 +2020-11-05 18:45:00,132.8,143.64,69.855,32.225 +2020-11-05 19:00:00,131.09,144.657,74.015,32.225 +2020-11-05 19:15:00,127.89,142.741,74.015,32.225 +2020-11-05 19:30:00,128.05,141.57299999999998,74.015,32.225 +2020-11-05 19:45:00,123.88,140.283,74.015,32.225 +2020-11-05 20:00:00,116.5,135.664,65.316,32.225 +2020-11-05 20:15:00,113.48,131.972,65.316,32.225 +2020-11-05 20:30:00,112.84,131.285,65.316,32.225 +2020-11-05 20:45:00,114.31,129.164,65.316,32.225 +2020-11-05 21:00:00,103.83,124.757,58.403999999999996,32.225 +2020-11-05 21:15:00,105.69,124.059,58.403999999999996,32.225 +2020-11-05 21:30:00,103.3,123.11,58.403999999999996,32.225 +2020-11-05 21:45:00,102.93,120.469,58.403999999999996,32.225 +2020-11-05 22:00:00,97.89,114.755,54.092,32.225 +2020-11-05 22:15:00,97.8,110.404,54.092,32.225 +2020-11-05 22:30:00,92.88,97.184,54.092,32.225 +2020-11-05 22:45:00,97.46,89.76,54.092,32.225 +2020-11-05 23:00:00,92.45,84.305,48.18600000000001,32.225 +2020-11-05 23:15:00,91.31,83.226,48.18600000000001,32.225 +2020-11-05 23:30:00,83.3,82.54899999999999,48.18600000000001,32.225 +2020-11-05 23:45:00,84.68,82.65700000000001,48.18600000000001,32.225 +2020-11-06 00:00:00,84.7,76.2,45.18899999999999,32.225 +2020-11-06 00:15:00,85.23,77.523,45.18899999999999,32.225 +2020-11-06 00:30:00,83.88,77.358,45.18899999999999,32.225 +2020-11-06 00:45:00,80.5,77.637,45.18899999999999,32.225 +2020-11-06 01:00:00,80.34,78.648,43.256,32.225 +2020-11-06 01:15:00,81.37,79.095,43.256,32.225 +2020-11-06 01:30:00,77.99,79.031,43.256,32.225 +2020-11-06 01:45:00,77.75,78.985,43.256,32.225 +2020-11-06 02:00:00,79.46,80.635,42.312,32.225 +2020-11-06 02:15:00,81.87,81.078,42.312,32.225 +2020-11-06 02:30:00,75.94,81.99,42.312,32.225 +2020-11-06 02:45:00,73.82,82.675,42.312,32.225 +2020-11-06 03:00:00,72.73,85.07600000000001,41.833,32.225 +2020-11-06 03:15:00,81.36,86.537,41.833,32.225 +2020-11-06 03:30:00,82.16,87.24600000000001,41.833,32.225 +2020-11-06 03:45:00,82.46,88.412,41.833,32.225 +2020-11-06 04:00:00,78.14,100.52,42.732,32.225 +2020-11-06 04:15:00,80.31,111.135,42.732,32.225 +2020-11-06 04:30:00,83.97,112.02799999999999,42.732,32.225 +2020-11-06 04:45:00,89.09,113.204,42.732,32.225 +2020-11-06 05:00:00,92.15,145.32399999999998,46.254,32.225 +2020-11-06 05:15:00,86.95,175.96,46.254,32.225 +2020-11-06 05:30:00,90.34,170.75799999999998,46.254,32.225 +2020-11-06 05:45:00,99.94,160.809,46.254,32.225 +2020-11-06 06:00:00,111.88,159.36700000000002,56.76,32.225 +2020-11-06 06:15:00,114.2,163.43200000000002,56.76,32.225 +2020-11-06 06:30:00,122.58,162.452,56.76,32.225 +2020-11-06 06:45:00,120.37,164.13,56.76,32.225 +2020-11-06 07:00:00,125.85,164.983,66.029,32.225 +2020-11-06 07:15:00,126.83,169.363,66.029,32.225 +2020-11-06 07:30:00,126.69,169.503,66.029,32.225 +2020-11-06 07:45:00,128.85,169.049,66.029,32.225 +2020-11-06 08:00:00,132.52,168.55,73.128,32.225 +2020-11-06 08:15:00,131.81,167.88299999999998,73.128,32.225 +2020-11-06 08:30:00,131.12,166.232,73.128,32.225 +2020-11-06 08:45:00,131.33,162.619,73.128,32.225 +2020-11-06 09:00:00,132.45,157.394,68.23100000000001,32.225 +2020-11-06 09:15:00,134.02,155.541,68.23100000000001,32.225 +2020-11-06 09:30:00,135.36,154.16899999999998,68.23100000000001,32.225 +2020-11-06 09:45:00,135.72,152.999,68.23100000000001,32.225 +2020-11-06 10:00:00,135.71,149.14,64.733,32.225 +2020-11-06 10:15:00,136.19,147.489,64.733,32.225 +2020-11-06 10:30:00,134.9,145.493,64.733,32.225 +2020-11-06 10:45:00,134.66,144.32399999999998,64.733,32.225 +2020-11-06 11:00:00,134.81,140.532,62.0,32.225 +2020-11-06 11:15:00,135.06,139.54399999999998,62.0,32.225 +2020-11-06 11:30:00,134.53,140.829,62.0,32.225 +2020-11-06 11:45:00,137.14,141.335,62.0,32.225 +2020-11-06 12:00:00,134.12,137.924,57.876999999999995,32.225 +2020-11-06 12:15:00,133.37,135.04399999999998,57.876999999999995,32.225 +2020-11-06 12:30:00,132.61,135.516,57.876999999999995,32.225 +2020-11-06 12:45:00,131.3,136.129,57.876999999999995,32.225 +2020-11-06 13:00:00,128.69,136.524,55.585,32.225 +2020-11-06 13:15:00,127.97,136.601,55.585,32.225 +2020-11-06 13:30:00,124.2,136.194,55.585,32.225 +2020-11-06 13:45:00,123.92,135.91299999999998,55.585,32.225 +2020-11-06 14:00:00,118.95,134.342,54.5,32.225 +2020-11-06 14:15:00,120.9,134.113,54.5,32.225 +2020-11-06 14:30:00,121.51,134.691,54.5,32.225 +2020-11-06 14:45:00,120.58,134.575,54.5,32.225 +2020-11-06 15:00:00,122.67,133.238,55.131,32.225 +2020-11-06 15:15:00,118.7,133.108,55.131,32.225 +2020-11-06 15:30:00,121.37,132.126,55.131,32.225 +2020-11-06 15:45:00,120.79,132.526,55.131,32.225 +2020-11-06 16:00:00,122.13,133.756,56.8,32.225 +2020-11-06 16:15:00,122.5,135.602,56.8,32.225 +2020-11-06 16:30:00,127.43,135.842,56.8,32.225 +2020-11-06 16:45:00,129.4,134.688,56.8,32.225 +2020-11-06 17:00:00,133.06,137.007,63.428999999999995,32.225 +2020-11-06 17:15:00,134.26,137.476,63.428999999999995,32.225 +2020-11-06 17:30:00,132.78,137.447,63.428999999999995,32.225 +2020-11-06 17:45:00,131.33,136.857,63.428999999999995,32.225 +2020-11-06 18:00:00,134.08,139.959,67.915,32.225 +2020-11-06 18:15:00,130.5,138.378,67.915,32.225 +2020-11-06 18:30:00,129.8,136.88299999999998,67.915,32.225 +2020-11-06 18:45:00,133.03,140.289,67.915,32.225 +2020-11-06 19:00:00,126.8,142.261,69.428,32.225 +2020-11-06 19:15:00,125.27,141.47899999999998,69.428,32.225 +2020-11-06 19:30:00,122.86,140.039,69.428,32.225 +2020-11-06 19:45:00,124.09,138.034,69.428,32.225 +2020-11-06 20:00:00,114.98,133.422,60.56100000000001,32.225 +2020-11-06 20:15:00,113.0,130.02100000000002,60.56100000000001,32.225 +2020-11-06 20:30:00,109.07,129.093,60.56100000000001,32.225 +2020-11-06 20:45:00,109.58,127.03299999999999,60.56100000000001,32.225 +2020-11-06 21:00:00,102.28,123.459,55.18600000000001,32.225 +2020-11-06 21:15:00,106.27,123.67200000000001,55.18600000000001,32.225 +2020-11-06 21:30:00,104.99,122.706,55.18600000000001,32.225 +2020-11-06 21:45:00,96.99,120.491,55.18600000000001,32.225 +2020-11-06 22:00:00,88.19,115.375,51.433,32.225 +2020-11-06 22:15:00,86.68,110.825,51.433,32.225 +2020-11-06 22:30:00,82.9,103.704,51.433,32.225 +2020-11-06 22:45:00,82.11,98.971,51.433,32.225 +2020-11-06 23:00:00,77.89,93.86200000000001,46.201,32.225 +2020-11-06 23:15:00,81.36,90.863,46.201,32.225 +2020-11-06 23:30:00,75.34,88.525,46.201,32.225 +2020-11-06 23:45:00,74.67,88.12899999999999,46.201,32.225 +2020-11-07 00:00:00,72.4,75.25399999999999,42.576,32.047 +2020-11-07 00:15:00,76.3,73.567,42.576,32.047 +2020-11-07 00:30:00,76.56,74.063,42.576,32.047 +2020-11-07 00:45:00,74.82,74.486,42.576,32.047 +2020-11-07 01:00:00,70.47,76.05,39.34,32.047 +2020-11-07 01:15:00,73.1,76.13,39.34,32.047 +2020-11-07 01:30:00,72.65,75.42,39.34,32.047 +2020-11-07 01:45:00,66.71,75.745,39.34,32.047 +2020-11-07 02:00:00,66.31,77.455,37.582,32.047 +2020-11-07 02:15:00,71.52,77.346,37.582,32.047 +2020-11-07 02:30:00,68.13,77.218,37.582,32.047 +2020-11-07 02:45:00,68.12,78.291,37.582,32.047 +2020-11-07 03:00:00,64.49,80.518,36.523,32.047 +2020-11-07 03:15:00,70.44,80.949,36.523,32.047 +2020-11-07 03:30:00,71.27,80.803,36.523,32.047 +2020-11-07 03:45:00,70.23,82.641,36.523,32.047 +2020-11-07 04:00:00,62.21,91.421,36.347,32.047 +2020-11-07 04:15:00,62.81,100.102,36.347,32.047 +2020-11-07 04:30:00,63.23,98.935,36.347,32.047 +2020-11-07 04:45:00,63.56,99.926,36.347,32.047 +2020-11-07 05:00:00,64.36,118.78299999999999,36.407,32.047 +2020-11-07 05:15:00,65.17,133.07,36.407,32.047 +2020-11-07 05:30:00,64.94,128.732,36.407,32.047 +2020-11-07 05:45:00,64.25,124.027,36.407,32.047 +2020-11-07 06:00:00,70.3,138.67,38.228,32.047 +2020-11-07 06:15:00,70.95,156.255,38.228,32.047 +2020-11-07 06:30:00,68.59,150.749,38.228,32.047 +2020-11-07 06:45:00,70.43,145.471,38.228,32.047 +2020-11-07 07:00:00,75.46,143.015,41.905,32.047 +2020-11-07 07:15:00,76.33,146.082,41.905,32.047 +2020-11-07 07:30:00,78.63,148.593,41.905,32.047 +2020-11-07 07:45:00,81.45,151.06799999999998,41.905,32.047 +2020-11-07 08:00:00,83.9,153.178,46.051,32.047 +2020-11-07 08:15:00,83.65,154.811,46.051,32.047 +2020-11-07 08:30:00,84.05,154.248,46.051,32.047 +2020-11-07 08:45:00,85.96,153.055,46.051,32.047 +2020-11-07 09:00:00,87.34,150.039,46.683,32.047 +2020-11-07 09:15:00,87.81,148.872,46.683,32.047 +2020-11-07 09:30:00,86.74,148.283,46.683,32.047 +2020-11-07 09:45:00,86.17,147.033,46.683,32.047 +2020-11-07 10:00:00,86.08,143.433,44.425,32.047 +2020-11-07 10:15:00,85.25,141.98,44.425,32.047 +2020-11-07 10:30:00,85.52,139.95600000000002,44.425,32.047 +2020-11-07 10:45:00,85.84,139.55,44.425,32.047 +2020-11-07 11:00:00,86.36,135.795,42.148999999999994,32.047 +2020-11-07 11:15:00,87.42,134.597,42.148999999999994,32.047 +2020-11-07 11:30:00,87.9,135.195,42.148999999999994,32.047 +2020-11-07 11:45:00,89.42,135.241,42.148999999999994,32.047 +2020-11-07 12:00:00,86.24,131.194,39.683,32.047 +2020-11-07 12:15:00,84.54,129.049,39.683,32.047 +2020-11-07 12:30:00,83.03,129.739,39.683,32.047 +2020-11-07 12:45:00,82.28,130.041,39.683,32.047 +2020-11-07 13:00:00,80.02,129.766,37.154,32.047 +2020-11-07 13:15:00,79.68,128.201,37.154,32.047 +2020-11-07 13:30:00,78.82,127.529,37.154,32.047 +2020-11-07 13:45:00,78.57,127.15,37.154,32.047 +2020-11-07 14:00:00,77.79,126.416,36.457,32.047 +2020-11-07 14:15:00,78.58,125.419,36.457,32.047 +2020-11-07 14:30:00,78.89,124.588,36.457,32.047 +2020-11-07 14:45:00,80.87,124.78399999999999,36.457,32.047 +2020-11-07 15:00:00,80.77,124.04299999999999,38.257,32.047 +2020-11-07 15:15:00,82.29,124.734,38.257,32.047 +2020-11-07 15:30:00,83.92,124.965,38.257,32.047 +2020-11-07 15:45:00,86.04,125.09299999999999,38.257,32.047 +2020-11-07 16:00:00,87.33,126.383,41.181000000000004,32.047 +2020-11-07 16:15:00,88.36,128.577,41.181000000000004,32.047 +2020-11-07 16:30:00,90.62,128.856,41.181000000000004,32.047 +2020-11-07 16:45:00,95.89,128.364,41.181000000000004,32.047 +2020-11-07 17:00:00,101.97,130.005,46.806000000000004,32.047 +2020-11-07 17:15:00,101.76,130.86700000000002,46.806000000000004,32.047 +2020-11-07 17:30:00,104.34,130.73,46.806000000000004,32.047 +2020-11-07 17:45:00,104.83,130.05,46.806000000000004,32.047 +2020-11-07 18:00:00,105.8,133.376,52.073,32.047 +2020-11-07 18:15:00,105.2,133.58700000000002,52.073,32.047 +2020-11-07 18:30:00,103.89,133.498,52.073,32.047 +2020-11-07 18:45:00,101.96,133.372,52.073,32.047 +2020-11-07 19:00:00,100.94,135.424,53.608000000000004,32.047 +2020-11-07 19:15:00,98.9,133.929,53.608000000000004,32.047 +2020-11-07 19:30:00,97.79,133.262,53.608000000000004,32.047 +2020-11-07 19:45:00,96.56,131.755,53.608000000000004,32.047 +2020-11-07 20:00:00,91.1,128.975,50.265,32.047 +2020-11-07 20:15:00,85.93,126.74799999999999,50.265,32.047 +2020-11-07 20:30:00,85.7,125.228,50.265,32.047 +2020-11-07 20:45:00,83.77,123.581,50.265,32.047 +2020-11-07 21:00:00,79.67,120.964,45.766000000000005,32.047 +2020-11-07 21:15:00,79.69,121.325,45.766000000000005,32.047 +2020-11-07 21:30:00,78.88,121.25399999999999,45.766000000000005,32.047 +2020-11-07 21:45:00,77.61,118.55,45.766000000000005,32.047 +2020-11-07 22:00:00,75.18,114.319,45.97,32.047 +2020-11-07 22:15:00,76.21,111.5,45.97,32.047 +2020-11-07 22:30:00,73.1,108.414,45.97,32.047 +2020-11-07 22:45:00,72.11,105.071,45.97,32.047 +2020-11-07 23:00:00,68.65,101.348,40.415,32.047 +2020-11-07 23:15:00,68.16,97.39200000000001,40.415,32.047 +2020-11-07 23:30:00,64.73,94.814,40.415,32.047 +2020-11-07 23:45:00,63.74,92.92399999999999,40.415,32.047 +2020-11-08 00:00:00,56.78,76.44800000000001,36.376,32.047 +2020-11-08 00:15:00,59.59,74.069,36.376,32.047 +2020-11-08 00:30:00,59.04,74.27,36.376,32.047 +2020-11-08 00:45:00,58.74,75.078,36.376,32.047 +2020-11-08 01:00:00,56.0,76.681,32.992,32.047 +2020-11-08 01:15:00,56.15,77.38,32.992,32.047 +2020-11-08 01:30:00,53.94,76.958,32.992,32.047 +2020-11-08 01:45:00,55.96,76.921,32.992,32.047 +2020-11-08 02:00:00,53.18,78.202,32.327,32.047 +2020-11-08 02:15:00,54.85,77.861,32.327,32.047 +2020-11-08 02:30:00,53.77,78.33800000000001,32.327,32.047 +2020-11-08 02:45:00,54.28,79.574,32.327,32.047 +2020-11-08 03:00:00,54.05,82.25399999999999,31.169,32.047 +2020-11-08 03:15:00,54.15,82.494,31.169,32.047 +2020-11-08 03:30:00,54.49,82.912,31.169,32.047 +2020-11-08 03:45:00,52.1,84.354,31.169,32.047 +2020-11-08 04:00:00,54.3,92.965,30.796,32.047 +2020-11-08 04:15:00,54.34,100.863,30.796,32.047 +2020-11-08 04:30:00,55.34,100.29799999999999,30.796,32.047 +2020-11-08 04:45:00,52.7,101.26799999999999,30.796,32.047 +2020-11-08 05:00:00,55.15,118.09200000000001,30.848000000000003,32.047 +2020-11-08 05:15:00,56.75,130.61,30.848000000000003,32.047 +2020-11-08 05:30:00,56.68,125.994,30.848000000000003,32.047 +2020-11-08 05:45:00,57.37,121.301,30.848000000000003,32.047 +2020-11-08 06:00:00,58.77,134.852,31.166,32.047 +2020-11-08 06:15:00,56.79,151.719,31.166,32.047 +2020-11-08 06:30:00,59.58,145.264,31.166,32.047 +2020-11-08 06:45:00,61.46,138.931,31.166,32.047 +2020-11-08 07:00:00,63.74,138.10299999999998,33.527,32.047 +2020-11-08 07:15:00,64.26,139.97799999999998,33.527,32.047 +2020-11-08 07:30:00,66.51,142.24200000000002,33.527,32.047 +2020-11-08 07:45:00,65.6,144.219,33.527,32.047 +2020-11-08 08:00:00,71.16,147.767,36.616,32.047 +2020-11-08 08:15:00,72.66,149.776,36.616,32.047 +2020-11-08 08:30:00,73.14,150.658,36.616,32.047 +2020-11-08 08:45:00,75.05,150.73,36.616,32.047 +2020-11-08 09:00:00,75.61,147.37,37.857,32.047 +2020-11-08 09:15:00,73.91,146.389,37.857,32.047 +2020-11-08 09:30:00,74.91,145.84799999999998,37.857,32.047 +2020-11-08 09:45:00,74.9,144.869,37.857,32.047 +2020-11-08 10:00:00,75.28,143.132,36.319,32.047 +2020-11-08 10:15:00,81.36,142.055,36.319,32.047 +2020-11-08 10:30:00,83.52,140.497,36.319,32.047 +2020-11-08 10:45:00,84.81,139.138,36.319,32.047 +2020-11-08 11:00:00,89.21,135.871,37.236999999999995,32.047 +2020-11-08 11:15:00,90.66,134.592,37.236999999999995,32.047 +2020-11-08 11:30:00,90.88,134.764,37.236999999999995,32.047 +2020-11-08 11:45:00,89.95,135.306,37.236999999999995,32.047 +2020-11-08 12:00:00,87.53,131.175,34.871,32.047 +2020-11-08 12:15:00,85.32,130.161,34.871,32.047 +2020-11-08 12:30:00,83.34,129.931,34.871,32.047 +2020-11-08 12:45:00,78.63,129.341,34.871,32.047 +2020-11-08 13:00:00,69.57,128.405,29.738000000000003,32.047 +2020-11-08 13:15:00,69.16,128.797,29.738000000000003,32.047 +2020-11-08 13:30:00,66.72,127.609,29.738000000000003,32.047 +2020-11-08 13:45:00,67.62,127.12299999999999,29.738000000000003,32.047 +2020-11-08 14:00:00,66.83,126.906,27.333000000000002,32.047 +2020-11-08 14:15:00,66.82,126.916,27.333000000000002,32.047 +2020-11-08 14:30:00,67.73,126.59700000000001,27.333000000000002,32.047 +2020-11-08 14:45:00,69.57,126.15100000000001,27.333000000000002,32.047 +2020-11-08 15:00:00,71.36,124.36,28.232,32.047 +2020-11-08 15:15:00,70.85,125.339,28.232,32.047 +2020-11-08 15:30:00,72.33,125.96799999999999,28.232,32.047 +2020-11-08 15:45:00,74.18,126.693,28.232,32.047 +2020-11-08 16:00:00,77.95,128.625,32.815,32.047 +2020-11-08 16:15:00,78.12,130.292,32.815,32.047 +2020-11-08 16:30:00,81.11,131.095,32.815,32.047 +2020-11-08 16:45:00,86.31,130.768,32.815,32.047 +2020-11-08 17:00:00,91.61,132.41899999999998,43.068999999999996,32.047 +2020-11-08 17:15:00,92.71,133.623,43.068999999999996,32.047 +2020-11-08 17:30:00,94.77,133.976,43.068999999999996,32.047 +2020-11-08 17:45:00,96.36,135.0,43.068999999999996,32.047 +2020-11-08 18:00:00,97.41,138.14700000000002,50.498999999999995,32.047 +2020-11-08 18:15:00,96.11,139.069,50.498999999999995,32.047 +2020-11-08 18:30:00,96.01,137.498,50.498999999999995,32.047 +2020-11-08 18:45:00,94.28,138.656,50.498999999999995,32.047 +2020-11-08 19:00:00,92.22,141.273,53.481,32.047 +2020-11-08 19:15:00,90.52,139.755,53.481,32.047 +2020-11-08 19:30:00,89.88,138.872,53.481,32.047 +2020-11-08 19:45:00,88.47,138.131,53.481,32.047 +2020-11-08 20:00:00,86.19,135.379,51.687,32.047 +2020-11-08 20:15:00,84.49,133.74200000000002,51.687,32.047 +2020-11-08 20:30:00,83.73,133.315,51.687,32.047 +2020-11-08 20:45:00,83.27,130.233,51.687,32.047 +2020-11-08 21:00:00,79.79,125.823,47.674,32.047 +2020-11-08 21:15:00,80.23,125.645,47.674,32.047 +2020-11-08 21:30:00,79.81,125.53,47.674,32.047 +2020-11-08 21:45:00,80.44,123.037,47.674,32.047 +2020-11-08 22:00:00,78.11,118.802,48.178000000000004,32.047 +2020-11-08 22:15:00,78.45,114.829,48.178000000000004,32.047 +2020-11-08 22:30:00,76.78,109.508,48.178000000000004,32.047 +2020-11-08 22:45:00,77.39,105.12899999999999,48.178000000000004,32.047 +2020-11-08 23:00:00,74.59,99.45,42.553999999999995,32.047 +2020-11-08 23:15:00,73.28,97.175,42.553999999999995,32.047 +2020-11-08 23:30:00,72.41,94.932,42.553999999999995,32.047 +2020-11-08 23:45:00,72.06,93.675,42.553999999999995,32.047 +2020-11-09 00:00:00,68.05,80.07300000000001,37.177,32.225 +2020-11-09 00:15:00,68.1,79.87,37.177,32.225 +2020-11-09 00:30:00,68.17,79.997,37.177,32.225 +2020-11-09 00:45:00,67.31,80.294,37.177,32.225 +2020-11-09 01:00:00,65.98,82.068,35.358000000000004,32.225 +2020-11-09 01:15:00,65.25,82.456,35.358000000000004,32.225 +2020-11-09 01:30:00,64.89,82.23299999999999,35.358000000000004,32.225 +2020-11-09 01:45:00,65.37,82.22,35.358000000000004,32.225 +2020-11-09 02:00:00,63.02,83.67399999999999,35.03,32.225 +2020-11-09 02:15:00,64.11,83.83,35.03,32.225 +2020-11-09 02:30:00,64.54,84.574,35.03,32.225 +2020-11-09 02:45:00,64.76,85.359,35.03,32.225 +2020-11-09 03:00:00,64.28,89.001,34.394,32.225 +2020-11-09 03:15:00,66.01,90.586,34.394,32.225 +2020-11-09 03:30:00,66.41,91.126,34.394,32.225 +2020-11-09 03:45:00,67.37,92.016,34.394,32.225 +2020-11-09 04:00:00,69.97,104.598,34.421,32.225 +2020-11-09 04:15:00,71.09,116.309,34.421,32.225 +2020-11-09 04:30:00,71.91,116.883,34.421,32.225 +2020-11-09 04:45:00,79.32,118.117,34.421,32.225 +2020-11-09 05:00:00,82.71,147.628,39.435,32.225 +2020-11-09 05:15:00,87.16,176.90900000000002,39.435,32.225 +2020-11-09 05:30:00,87.32,171.798,39.435,32.225 +2020-11-09 05:45:00,101.72,162.31799999999998,39.435,32.225 +2020-11-09 06:00:00,110.45,160.78,55.685,32.225 +2020-11-09 06:15:00,113.86,164.645,55.685,32.225 +2020-11-09 06:30:00,115.39,164.861,55.685,32.225 +2020-11-09 06:45:00,118.64,166.217,55.685,32.225 +2020-11-09 07:00:00,122.58,167.396,66.837,32.225 +2020-11-09 07:15:00,122.82,170.988,66.837,32.225 +2020-11-09 07:30:00,126.22,172.438,66.837,32.225 +2020-11-09 07:45:00,125.54,172.898,66.837,32.225 +2020-11-09 08:00:00,127.34,172.813,72.217,32.225 +2020-11-09 08:15:00,127.18,172.938,72.217,32.225 +2020-11-09 08:30:00,126.88,170.8,72.217,32.225 +2020-11-09 08:45:00,127.51,168.736,72.217,32.225 +2020-11-09 09:00:00,128.25,164.46599999999998,66.117,32.225 +2020-11-09 09:15:00,130.74,160.498,66.117,32.225 +2020-11-09 09:30:00,135.41,158.94,66.117,32.225 +2020-11-09 09:45:00,128.44,157.295,66.117,32.225 +2020-11-09 10:00:00,121.64,155.171,62.1,32.225 +2020-11-09 10:15:00,122.64,153.78,62.1,32.225 +2020-11-09 10:30:00,121.37,151.439,62.1,32.225 +2020-11-09 10:45:00,120.12,150.023,62.1,32.225 +2020-11-09 11:00:00,119.17,145.142,60.021,32.225 +2020-11-09 11:15:00,118.05,145.196,60.021,32.225 +2020-11-09 11:30:00,120.45,146.566,60.021,32.225 +2020-11-09 11:45:00,127.24,146.98,60.021,32.225 +2020-11-09 12:00:00,123.12,143.612,56.75899999999999,32.225 +2020-11-09 12:15:00,119.35,142.637,56.75899999999999,32.225 +2020-11-09 12:30:00,119.39,142.264,56.75899999999999,32.225 +2020-11-09 12:45:00,119.84,142.754,56.75899999999999,32.225 +2020-11-09 13:00:00,116.9,142.586,56.04600000000001,32.225 +2020-11-09 13:15:00,116.79,141.71200000000002,56.04600000000001,32.225 +2020-11-09 13:30:00,113.73,140.2,56.04600000000001,32.225 +2020-11-09 13:45:00,114.61,140.019,56.04600000000001,32.225 +2020-11-09 14:00:00,115.9,139.042,55.475,32.225 +2020-11-09 14:15:00,116.58,138.798,55.475,32.225 +2020-11-09 14:30:00,120.75,138.077,55.475,32.225 +2020-11-09 14:45:00,120.18,138.275,55.475,32.225 +2020-11-09 15:00:00,123.17,137.644,57.048,32.225 +2020-11-09 15:15:00,122.33,137.42,57.048,32.225 +2020-11-09 15:30:00,124.39,137.74,57.048,32.225 +2020-11-09 15:45:00,125.72,138.004,57.048,32.225 +2020-11-09 16:00:00,126.17,140.192,59.06,32.225 +2020-11-09 16:15:00,125.85,141.375,59.06,32.225 +2020-11-09 16:30:00,127.02,141.27700000000002,59.06,32.225 +2020-11-09 16:45:00,131.92,140.181,59.06,32.225 +2020-11-09 17:00:00,136.09,141.148,65.419,32.225 +2020-11-09 17:15:00,136.48,141.859,65.419,32.225 +2020-11-09 17:30:00,137.83,141.708,65.419,32.225 +2020-11-09 17:45:00,137.56,141.585,65.419,32.225 +2020-11-09 18:00:00,135.48,144.59,69.345,32.225 +2020-11-09 18:15:00,133.71,143.328,69.345,32.225 +2020-11-09 18:30:00,132.59,141.934,69.345,32.225 +2020-11-09 18:45:00,132.3,144.716,69.345,32.225 +2020-11-09 19:00:00,127.93,146.127,73.825,32.225 +2020-11-09 19:15:00,126.34,144.3,73.825,32.225 +2020-11-09 19:30:00,123.57,143.6,73.825,32.225 +2020-11-09 19:45:00,121.6,142.05,73.825,32.225 +2020-11-09 20:00:00,115.11,137.256,64.027,32.225 +2020-11-09 20:15:00,112.04,134.487,64.027,32.225 +2020-11-09 20:30:00,108.08,132.988,64.027,32.225 +2020-11-09 20:45:00,107.81,131.05700000000002,64.027,32.225 +2020-11-09 21:00:00,107.92,126.76799999999999,57.952,32.225 +2020-11-09 21:15:00,109.06,125.969,57.952,32.225 +2020-11-09 21:30:00,106.65,125.475,57.952,32.225 +2020-11-09 21:45:00,100.41,122.554,57.952,32.225 +2020-11-09 22:00:00,92.6,115.631,53.031000000000006,32.225 +2020-11-09 22:15:00,93.12,111.51899999999999,53.031000000000006,32.225 +2020-11-09 22:30:00,95.32,98.29700000000001,53.031000000000006,32.225 +2020-11-09 22:45:00,94.64,90.72200000000001,53.031000000000006,32.225 +2020-11-09 23:00:00,90.53,85.54799999999999,45.085,32.225 +2020-11-09 23:15:00,89.52,84.38799999999999,45.085,32.225 +2020-11-09 23:30:00,88.09,83.963,45.085,32.225 +2020-11-09 23:45:00,87.29,84.242,45.085,32.225 +2020-11-10 00:00:00,79.63,79.074,42.843,32.225 +2020-11-10 00:15:00,76.93,80.104,42.843,32.225 +2020-11-10 00:30:00,82.65,79.929,42.843,32.225 +2020-11-10 00:45:00,81.56,79.919,42.843,32.225 +2020-11-10 01:00:00,80.25,81.372,41.542,32.225 +2020-11-10 01:15:00,76.77,81.541,41.542,32.225 +2020-11-10 01:30:00,79.55,81.388,41.542,32.225 +2020-11-10 01:45:00,79.66,81.351,41.542,32.225 +2020-11-10 02:00:00,78.05,82.617,40.19,32.225 +2020-11-10 02:15:00,73.84,83.189,40.19,32.225 +2020-11-10 02:30:00,77.2,83.39,40.19,32.225 +2020-11-10 02:45:00,79.47,84.315,40.19,32.225 +2020-11-10 03:00:00,79.27,86.96600000000001,39.626,32.225 +2020-11-10 03:15:00,78.58,88.43700000000001,39.626,32.225 +2020-11-10 03:30:00,80.16,89.265,39.626,32.225 +2020-11-10 03:45:00,80.07,89.79799999999999,39.626,32.225 +2020-11-10 04:00:00,78.72,101.744,40.196999999999996,32.225 +2020-11-10 04:15:00,81.21,113.26899999999999,40.196999999999996,32.225 +2020-11-10 04:30:00,83.65,113.595,40.196999999999996,32.225 +2020-11-10 04:45:00,86.52,115.79,40.196999999999996,32.225 +2020-11-10 05:00:00,87.82,149.101,43.378,32.225 +2020-11-10 05:15:00,90.74,178.55599999999998,43.378,32.225 +2020-11-10 05:30:00,99.8,172.574,43.378,32.225 +2020-11-10 05:45:00,101.83,162.769,43.378,32.225 +2020-11-10 06:00:00,103.85,160.92600000000002,55.691,32.225 +2020-11-10 06:15:00,109.52,165.903,55.691,32.225 +2020-11-10 06:30:00,115.89,165.612,55.691,32.225 +2020-11-10 06:45:00,121.67,166.38299999999998,55.691,32.225 +2020-11-10 07:00:00,125.99,167.50599999999997,65.567,32.225 +2020-11-10 07:15:00,125.23,170.90400000000002,65.567,32.225 +2020-11-10 07:30:00,125.48,172.033,65.567,32.225 +2020-11-10 07:45:00,127.15,172.24599999999998,65.567,32.225 +2020-11-10 08:00:00,130.42,172.225,73.001,32.225 +2020-11-10 08:15:00,129.33,171.486,73.001,32.225 +2020-11-10 08:30:00,128.62,169.31900000000002,73.001,32.225 +2020-11-10 08:45:00,130.72,166.703,73.001,32.225 +2020-11-10 09:00:00,130.86,161.987,67.08800000000001,32.225 +2020-11-10 09:15:00,133.46,159.045,67.08800000000001,32.225 +2020-11-10 09:30:00,134.07,158.188,67.08800000000001,32.225 +2020-11-10 09:45:00,134.92,156.86,67.08800000000001,32.225 +2020-11-10 10:00:00,138.69,153.835,62.803000000000004,32.225 +2020-11-10 10:15:00,136.4,151.625,62.803000000000004,32.225 +2020-11-10 10:30:00,138.3,149.394,62.803000000000004,32.225 +2020-11-10 10:45:00,138.45,148.505,62.803000000000004,32.225 +2020-11-10 11:00:00,138.8,144.653,60.155,32.225 +2020-11-10 11:15:00,140.26,144.589,60.155,32.225 +2020-11-10 11:30:00,138.98,144.757,60.155,32.225 +2020-11-10 11:45:00,138.44,145.502,60.155,32.225 +2020-11-10 12:00:00,136.24,141.084,56.845,32.225 +2020-11-10 12:15:00,132.78,139.921,56.845,32.225 +2020-11-10 12:30:00,132.82,140.374,56.845,32.225 +2020-11-10 12:45:00,133.55,140.88,56.845,32.225 +2020-11-10 13:00:00,133.51,140.255,56.163000000000004,32.225 +2020-11-10 13:15:00,133.18,139.71,56.163000000000004,32.225 +2020-11-10 13:30:00,132.48,139.026,56.163000000000004,32.225 +2020-11-10 13:45:00,135.29,138.667,56.163000000000004,32.225 +2020-11-10 14:00:00,132.64,137.986,55.934,32.225 +2020-11-10 14:15:00,133.0,137.806,55.934,32.225 +2020-11-10 14:30:00,130.66,137.678,55.934,32.225 +2020-11-10 14:45:00,129.66,137.57,55.934,32.225 +2020-11-10 15:00:00,129.09,136.569,57.43899999999999,32.225 +2020-11-10 15:15:00,128.92,136.889,57.43899999999999,32.225 +2020-11-10 15:30:00,125.2,137.341,57.43899999999999,32.225 +2020-11-10 15:45:00,127.27,137.435,57.43899999999999,32.225 +2020-11-10 16:00:00,129.73,139.649,59.968999999999994,32.225 +2020-11-10 16:15:00,128.3,141.219,59.968999999999994,32.225 +2020-11-10 16:30:00,129.86,141.44799999999998,59.968999999999994,32.225 +2020-11-10 16:45:00,133.6,140.843,59.968999999999994,32.225 +2020-11-10 17:00:00,138.8,142.224,67.428,32.225 +2020-11-10 17:15:00,136.91,143.123,67.428,32.225 +2020-11-10 17:30:00,136.54,143.278,67.428,32.225 +2020-11-10 17:45:00,137.56,142.963,67.428,32.225 +2020-11-10 18:00:00,136.51,145.55,71.533,32.225 +2020-11-10 18:15:00,134.26,144.453,71.533,32.225 +2020-11-10 18:30:00,133.81,142.766,71.533,32.225 +2020-11-10 18:45:00,133.71,146.015,71.533,32.225 +2020-11-10 19:00:00,131.4,147.056,73.32300000000001,32.225 +2020-11-10 19:15:00,128.61,145.097,73.32300000000001,32.225 +2020-11-10 19:30:00,126.59,143.855,73.32300000000001,32.225 +2020-11-10 19:45:00,125.18,142.424,73.32300000000001,32.225 +2020-11-10 20:00:00,121.52,137.89600000000002,64.166,32.225 +2020-11-10 20:15:00,115.1,134.157,64.166,32.225 +2020-11-10 20:30:00,113.81,133.317,64.166,32.225 +2020-11-10 20:45:00,112.32,131.153,64.166,32.225 +2020-11-10 21:00:00,107.06,126.714,57.891999999999996,32.225 +2020-11-10 21:15:00,105.3,125.95200000000001,57.891999999999996,32.225 +2020-11-10 21:30:00,102.67,125.04799999999999,57.891999999999996,32.225 +2020-11-10 21:45:00,101.94,122.339,57.891999999999996,32.225 +2020-11-10 22:00:00,98.35,116.615,53.242,32.225 +2020-11-10 22:15:00,100.16,112.191,53.242,32.225 +2020-11-10 22:30:00,98.28,99.18,53.242,32.225 +2020-11-10 22:45:00,96.68,91.795,53.242,32.225 +2020-11-10 23:00:00,88.01,86.35799999999999,46.665,32.225 +2020-11-10 23:15:00,84.04,85.154,46.665,32.225 +2020-11-10 23:30:00,84.59,84.492,46.665,32.225 +2020-11-10 23:45:00,81.05,84.535,46.665,32.225 +2020-11-11 00:00:00,83.4,79.433,43.16,32.225 +2020-11-11 00:15:00,83.26,80.444,43.16,32.225 +2020-11-11 00:30:00,84.67,80.27,43.16,32.225 +2020-11-11 00:45:00,81.96,80.248,43.16,32.225 +2020-11-11 01:00:00,78.35,81.723,40.972,32.225 +2020-11-11 01:15:00,81.15,81.906,40.972,32.225 +2020-11-11 01:30:00,81.08,81.768,40.972,32.225 +2020-11-11 01:45:00,81.16,81.72399999999999,40.972,32.225 +2020-11-11 02:00:00,78.61,83.00299999999999,39.749,32.225 +2020-11-11 02:15:00,80.73,83.584,39.749,32.225 +2020-11-11 02:30:00,81.45,83.777,39.749,32.225 +2020-11-11 02:45:00,79.88,84.698,39.749,32.225 +2020-11-11 03:00:00,77.2,87.337,39.422,32.225 +2020-11-11 03:15:00,82.68,88.831,39.422,32.225 +2020-11-11 03:30:00,82.22,89.661,39.422,32.225 +2020-11-11 03:45:00,83.11,90.179,39.422,32.225 +2020-11-11 04:00:00,83.29,102.134,40.505,32.225 +2020-11-11 04:15:00,85.37,113.684,40.505,32.225 +2020-11-11 04:30:00,86.75,114.00299999999999,40.505,32.225 +2020-11-11 04:45:00,86.34,116.20700000000001,40.505,32.225 +2020-11-11 05:00:00,93.0,149.563,43.397,32.225 +2020-11-11 05:15:00,96.5,179.05900000000003,43.397,32.225 +2020-11-11 05:30:00,96.82,173.071,43.397,32.225 +2020-11-11 05:45:00,95.16,163.246,43.397,32.225 +2020-11-11 06:00:00,104.31,161.401,55.218,32.225 +2020-11-11 06:15:00,112.72,166.389,55.218,32.225 +2020-11-11 06:30:00,122.08,166.125,55.218,32.225 +2020-11-11 06:45:00,124.33,166.916,55.218,32.225 +2020-11-11 07:00:00,126.72,168.04,67.39,32.225 +2020-11-11 07:15:00,123.92,171.447,67.39,32.225 +2020-11-11 07:30:00,127.19,172.601,67.39,32.225 +2020-11-11 07:45:00,125.8,172.813,67.39,32.225 +2020-11-11 08:00:00,126.17,172.80700000000002,74.345,32.225 +2020-11-11 08:15:00,124.97,172.047,74.345,32.225 +2020-11-11 08:30:00,126.2,169.903,74.345,32.225 +2020-11-11 08:45:00,125.23,167.25900000000001,74.345,32.225 +2020-11-11 09:00:00,124.4,162.53,69.336,32.225 +2020-11-11 09:15:00,125.54,159.589,69.336,32.225 +2020-11-11 09:30:00,123.59,158.725,69.336,32.225 +2020-11-11 09:45:00,121.93,157.375,69.336,32.225 +2020-11-11 10:00:00,121.82,154.341,64.291,32.225 +2020-11-11 10:15:00,120.73,152.095,64.291,32.225 +2020-11-11 10:30:00,118.92,149.842,64.291,32.225 +2020-11-11 10:45:00,121.37,148.938,64.291,32.225 +2020-11-11 11:00:00,120.95,145.084,62.20399999999999,32.225 +2020-11-11 11:15:00,122.46,145.001,62.20399999999999,32.225 +2020-11-11 11:30:00,121.56,145.16899999999998,62.20399999999999,32.225 +2020-11-11 11:45:00,121.47,145.9,62.20399999999999,32.225 +2020-11-11 12:00:00,120.37,141.465,59.042,32.225 +2020-11-11 12:15:00,119.79,140.303,59.042,32.225 +2020-11-11 12:30:00,119.63,140.78799999999998,59.042,32.225 +2020-11-11 12:45:00,121.96,141.296,59.042,32.225 +2020-11-11 13:00:00,120.59,140.635,57.907,32.225 +2020-11-11 13:15:00,122.95,140.09799999999998,57.907,32.225 +2020-11-11 13:30:00,118.81,139.411,57.907,32.225 +2020-11-11 13:45:00,117.0,139.046,57.907,32.225 +2020-11-11 14:00:00,116.01,138.31799999999998,58.358000000000004,32.225 +2020-11-11 14:15:00,115.36,138.151,58.358000000000004,32.225 +2020-11-11 14:30:00,116.38,138.061,58.358000000000004,32.225 +2020-11-11 14:45:00,119.55,137.955,58.358000000000004,32.225 +2020-11-11 15:00:00,123.96,136.944,59.348,32.225 +2020-11-11 15:15:00,123.92,137.278,59.348,32.225 +2020-11-11 15:30:00,125.52,137.766,59.348,32.225 +2020-11-11 15:45:00,123.32,137.872,59.348,32.225 +2020-11-11 16:00:00,125.75,140.063,61.413999999999994,32.225 +2020-11-11 16:15:00,126.76,141.656,61.413999999999994,32.225 +2020-11-11 16:30:00,128.31,141.88299999999998,61.413999999999994,32.225 +2020-11-11 16:45:00,133.13,141.327,61.413999999999994,32.225 +2020-11-11 17:00:00,138.86,142.668,67.107,32.225 +2020-11-11 17:15:00,136.54,143.589,67.107,32.225 +2020-11-11 17:30:00,137.81,143.75,67.107,32.225 +2020-11-11 17:45:00,137.09,143.44899999999998,67.107,32.225 +2020-11-11 18:00:00,136.75,146.037,71.92,32.225 +2020-11-11 18:15:00,134.87,144.91,71.92,32.225 +2020-11-11 18:30:00,134.79,143.232,71.92,32.225 +2020-11-11 18:45:00,135.01,146.47899999999998,71.92,32.225 +2020-11-11 19:00:00,130.77,147.524,75.09,32.225 +2020-11-11 19:15:00,128.61,145.55700000000002,75.09,32.225 +2020-11-11 19:30:00,126.23,144.30100000000002,75.09,32.225 +2020-11-11 19:45:00,129.72,142.842,75.09,32.225 +2020-11-11 20:00:00,120.35,138.33,65.977,32.225 +2020-11-11 20:15:00,114.91,134.583,65.977,32.225 +2020-11-11 20:30:00,111.86,133.71200000000002,65.977,32.225 +2020-11-11 20:45:00,111.47,131.542,65.977,32.225 +2020-11-11 21:00:00,106.24,127.095,58.798,32.225 +2020-11-11 21:15:00,111.59,126.32,58.798,32.225 +2020-11-11 21:30:00,109.24,125.426,58.798,32.225 +2020-11-11 21:45:00,110.46,122.704,58.798,32.225 +2020-11-11 22:00:00,100.23,116.978,54.486000000000004,32.225 +2020-11-11 22:15:00,95.43,112.539,54.486000000000004,32.225 +2020-11-11 22:30:00,93.35,99.57,54.486000000000004,32.225 +2020-11-11 22:45:00,97.79,92.194,54.486000000000004,32.225 +2020-11-11 23:00:00,94.38,86.758,47.783,32.225 +2020-11-11 23:15:00,93.93,85.53,47.783,32.225 +2020-11-11 23:30:00,85.88,84.87299999999999,47.783,32.225 +2020-11-11 23:45:00,84.95,84.90299999999999,47.783,32.225 +2020-11-12 00:00:00,85.17,79.789,43.88,32.225 +2020-11-12 00:15:00,87.34,80.78,43.88,32.225 +2020-11-12 00:30:00,85.98,80.609,43.88,32.225 +2020-11-12 00:45:00,80.22,80.575,43.88,32.225 +2020-11-12 01:00:00,83.59,82.071,42.242,32.225 +2020-11-12 01:15:00,84.86,82.26899999999999,42.242,32.225 +2020-11-12 01:30:00,83.06,82.146,42.242,32.225 +2020-11-12 01:45:00,81.74,82.094,42.242,32.225 +2020-11-12 02:00:00,83.52,83.387,40.918,32.225 +2020-11-12 02:15:00,83.31,83.976,40.918,32.225 +2020-11-12 02:30:00,78.25,84.161,40.918,32.225 +2020-11-12 02:45:00,78.19,85.07700000000001,40.918,32.225 +2020-11-12 03:00:00,75.8,87.70299999999999,40.411,32.225 +2020-11-12 03:15:00,78.38,89.221,40.411,32.225 +2020-11-12 03:30:00,82.84,90.053,40.411,32.225 +2020-11-12 03:45:00,85.06,90.555,40.411,32.225 +2020-11-12 04:00:00,83.04,102.51899999999999,41.246,32.225 +2020-11-12 04:15:00,79.36,114.094,41.246,32.225 +2020-11-12 04:30:00,80.43,114.40899999999999,41.246,32.225 +2020-11-12 04:45:00,81.3,116.619,41.246,32.225 +2020-11-12 05:00:00,85.8,150.023,44.533,32.225 +2020-11-12 05:15:00,88.26,179.558,44.533,32.225 +2020-11-12 05:30:00,92.04,173.56400000000002,44.533,32.225 +2020-11-12 05:45:00,97.64,163.719,44.533,32.225 +2020-11-12 06:00:00,113.62,161.872,55.005,32.225 +2020-11-12 06:15:00,116.6,166.87,55.005,32.225 +2020-11-12 06:30:00,120.04,166.635,55.005,32.225 +2020-11-12 06:45:00,120.88,167.44400000000002,55.005,32.225 +2020-11-12 07:00:00,127.95,168.56900000000002,64.597,32.225 +2020-11-12 07:15:00,124.59,171.987,64.597,32.225 +2020-11-12 07:30:00,125.29,173.16299999999998,64.597,32.225 +2020-11-12 07:45:00,127.13,173.375,64.597,32.225 +2020-11-12 08:00:00,129.63,173.38099999999997,71.71600000000001,32.225 +2020-11-12 08:15:00,127.3,172.602,71.71600000000001,32.225 +2020-11-12 08:30:00,127.82,170.482,71.71600000000001,32.225 +2020-11-12 08:45:00,127.07,167.80900000000003,71.71600000000001,32.225 +2020-11-12 09:00:00,123.74,163.067,66.51899999999999,32.225 +2020-11-12 09:15:00,124.7,160.127,66.51899999999999,32.225 +2020-11-12 09:30:00,127.83,159.257,66.51899999999999,32.225 +2020-11-12 09:45:00,129.16,157.885,66.51899999999999,32.225 +2020-11-12 10:00:00,129.07,154.842,63.04,32.225 +2020-11-12 10:15:00,128.54,152.56,63.04,32.225 +2020-11-12 10:30:00,127.42,150.284,63.04,32.225 +2020-11-12 10:45:00,124.95,149.365,63.04,32.225 +2020-11-12 11:00:00,124.62,145.50799999999998,60.998000000000005,32.225 +2020-11-12 11:15:00,124.2,145.406,60.998000000000005,32.225 +2020-11-12 11:30:00,123.72,145.57399999999998,60.998000000000005,32.225 +2020-11-12 11:45:00,125.51,146.29399999999998,60.998000000000005,32.225 +2020-11-12 12:00:00,126.91,141.84,58.27,32.225 +2020-11-12 12:15:00,122.96,140.68,58.27,32.225 +2020-11-12 12:30:00,121.82,141.197,58.27,32.225 +2020-11-12 12:45:00,116.29,141.707,58.27,32.225 +2020-11-12 13:00:00,117.07,141.012,57.196000000000005,32.225 +2020-11-12 13:15:00,119.85,140.481,57.196000000000005,32.225 +2020-11-12 13:30:00,119.33,139.791,57.196000000000005,32.225 +2020-11-12 13:45:00,120.26,139.41899999999998,57.196000000000005,32.225 +2020-11-12 14:00:00,118.83,138.64600000000002,57.38399999999999,32.225 +2020-11-12 14:15:00,120.08,138.493,57.38399999999999,32.225 +2020-11-12 14:30:00,121.28,138.439,57.38399999999999,32.225 +2020-11-12 14:45:00,124.13,138.335,57.38399999999999,32.225 +2020-11-12 15:00:00,125.14,137.315,58.647,32.225 +2020-11-12 15:15:00,123.4,137.661,58.647,32.225 +2020-11-12 15:30:00,122.12,138.186,58.647,32.225 +2020-11-12 15:45:00,123.51,138.30200000000002,58.647,32.225 +2020-11-12 16:00:00,126.72,140.471,60.083999999999996,32.225 +2020-11-12 16:15:00,125.87,142.08700000000002,60.083999999999996,32.225 +2020-11-12 16:30:00,129.87,142.313,60.083999999999996,32.225 +2020-11-12 16:45:00,133.87,141.805,60.083999999999996,32.225 +2020-11-12 17:00:00,137.7,143.105,65.85600000000001,32.225 +2020-11-12 17:15:00,137.13,144.049,65.85600000000001,32.225 +2020-11-12 17:30:00,139.61,144.218,65.85600000000001,32.225 +2020-11-12 17:45:00,138.78,143.93,65.85600000000001,32.225 +2020-11-12 18:00:00,137.47,146.519,69.855,32.225 +2020-11-12 18:15:00,136.57,145.361,69.855,32.225 +2020-11-12 18:30:00,135.22,143.692,69.855,32.225 +2020-11-12 18:45:00,135.89,146.94,69.855,32.225 +2020-11-12 19:00:00,131.65,147.986,74.015,32.225 +2020-11-12 19:15:00,131.85,146.012,74.015,32.225 +2020-11-12 19:30:00,129.37,144.74200000000002,74.015,32.225 +2020-11-12 19:45:00,126.94,143.257,74.015,32.225 +2020-11-12 20:00:00,119.74,138.762,65.316,32.225 +2020-11-12 20:15:00,117.13,135.005,65.316,32.225 +2020-11-12 20:30:00,115.09,134.105,65.316,32.225 +2020-11-12 20:45:00,111.94,131.92600000000002,65.316,32.225 +2020-11-12 21:00:00,107.77,127.473,58.403999999999996,32.225 +2020-11-12 21:15:00,113.2,126.684,58.403999999999996,32.225 +2020-11-12 21:30:00,112.61,125.8,58.403999999999996,32.225 +2020-11-12 21:45:00,110.3,123.066,58.403999999999996,32.225 +2020-11-12 22:00:00,100.12,117.338,54.092,32.225 +2020-11-12 22:15:00,96.26,112.885,54.092,32.225 +2020-11-12 22:30:00,99.58,99.958,54.092,32.225 +2020-11-12 22:45:00,99.58,92.59,54.092,32.225 +2020-11-12 23:00:00,93.92,87.156,48.18600000000001,32.225 +2020-11-12 23:15:00,90.66,85.905,48.18600000000001,32.225 +2020-11-12 23:30:00,82.77,85.25,48.18600000000001,32.225 +2020-11-12 23:45:00,93.39,85.26700000000001,48.18600000000001,32.225 +2020-11-13 00:00:00,88.54,78.727,45.18899999999999,32.225 +2020-11-13 00:15:00,87.54,79.914,45.18899999999999,32.225 +2020-11-13 00:30:00,83.28,79.76899999999999,45.18899999999999,32.225 +2020-11-13 00:45:00,75.68,79.967,45.18899999999999,32.225 +2020-11-13 01:00:00,83.0,81.128,43.256,32.225 +2020-11-13 01:15:00,83.41,81.67699999999999,43.256,32.225 +2020-11-13 01:30:00,80.05,81.719,43.256,32.225 +2020-11-13 01:45:00,78.52,81.625,43.256,32.225 +2020-11-13 02:00:00,74.26,83.368,42.312,32.225 +2020-11-13 02:15:00,81.05,83.87,42.312,32.225 +2020-11-13 02:30:00,84.7,84.721,42.312,32.225 +2020-11-13 02:45:00,85.03,85.37799999999999,42.312,32.225 +2020-11-13 03:00:00,79.15,87.684,41.833,32.225 +2020-11-13 03:15:00,80.84,89.316,41.833,32.225 +2020-11-13 03:30:00,82.96,90.04,41.833,32.225 +2020-11-13 03:45:00,87.43,91.09899999999999,41.833,32.225 +2020-11-13 04:00:00,85.9,103.27,42.732,32.225 +2020-11-13 04:15:00,84.02,114.059,42.732,32.225 +2020-11-13 04:30:00,88.32,114.913,42.732,32.225 +2020-11-13 04:45:00,91.69,116.14200000000001,42.732,32.225 +2020-11-13 05:00:00,90.57,148.593,46.254,32.225 +2020-11-13 05:15:00,89.24,179.515,46.254,32.225 +2020-11-13 05:30:00,100.06,174.269,46.254,32.225 +2020-11-13 05:45:00,105.63,164.176,46.254,32.225 +2020-11-13 06:00:00,111.79,162.717,56.76,32.225 +2020-11-13 06:15:00,111.89,166.858,56.76,32.225 +2020-11-13 06:30:00,118.4,166.08,56.76,32.225 +2020-11-13 06:45:00,122.39,167.889,56.76,32.225 +2020-11-13 07:00:00,129.36,168.747,66.029,32.225 +2020-11-13 07:15:00,127.21,173.19799999999998,66.029,32.225 +2020-11-13 07:30:00,127.28,173.50900000000001,66.029,32.225 +2020-11-13 07:45:00,128.94,173.06,66.029,32.225 +2020-11-13 08:00:00,131.25,172.65400000000002,73.128,32.225 +2020-11-13 08:15:00,129.89,171.84900000000002,73.128,32.225 +2020-11-13 08:30:00,128.17,170.36700000000002,73.128,32.225 +2020-11-13 08:45:00,128.29,166.558,73.128,32.225 +2020-11-13 09:00:00,123.97,161.243,68.23100000000001,32.225 +2020-11-13 09:15:00,128.09,159.393,68.23100000000001,32.225 +2020-11-13 09:30:00,127.96,157.974,68.23100000000001,32.225 +2020-11-13 09:45:00,126.9,156.645,68.23100000000001,32.225 +2020-11-13 10:00:00,123.72,152.725,64.733,32.225 +2020-11-13 10:15:00,124.12,150.817,64.733,32.225 +2020-11-13 10:30:00,121.64,148.661,64.733,32.225 +2020-11-13 10:45:00,122.3,147.386,64.733,32.225 +2020-11-13 11:00:00,119.98,143.577,62.0,32.225 +2020-11-13 11:15:00,121.51,142.455,62.0,32.225 +2020-11-13 11:30:00,120.54,143.74,62.0,32.225 +2020-11-13 11:45:00,120.13,144.156,62.0,32.225 +2020-11-13 12:00:00,119.43,140.618,57.876999999999995,32.225 +2020-11-13 12:15:00,119.97,137.744,57.876999999999995,32.225 +2020-11-13 12:30:00,120.64,138.444,57.876999999999995,32.225 +2020-11-13 12:45:00,119.97,139.069,57.876999999999995,32.225 +2020-11-13 13:00:00,117.26,139.217,55.585,32.225 +2020-11-13 13:15:00,116.98,139.343,55.585,32.225 +2020-11-13 13:30:00,114.0,138.91899999999998,55.585,32.225 +2020-11-13 13:45:00,115.51,138.592,55.585,32.225 +2020-11-13 14:00:00,113.7,136.691,54.5,32.225 +2020-11-13 14:15:00,116.76,136.561,54.5,32.225 +2020-11-13 14:30:00,115.06,137.4,54.5,32.225 +2020-11-13 14:45:00,117.33,137.297,54.5,32.225 +2020-11-13 15:00:00,118.57,135.893,55.131,32.225 +2020-11-13 15:15:00,121.83,135.856,55.131,32.225 +2020-11-13 15:30:00,118.63,135.135,55.131,32.225 +2020-11-13 15:45:00,120.04,135.612,55.131,32.225 +2020-11-13 16:00:00,123.01,136.678,56.8,32.225 +2020-11-13 16:15:00,123.05,138.69,56.8,32.225 +2020-11-13 16:30:00,127.02,138.92,56.8,32.225 +2020-11-13 16:45:00,132.91,138.112,56.8,32.225 +2020-11-13 17:00:00,136.31,140.149,63.428999999999995,32.225 +2020-11-13 17:15:00,134.14,140.769,63.428999999999995,32.225 +2020-11-13 17:30:00,135.6,140.783,63.428999999999995,32.225 +2020-11-13 17:45:00,136.85,140.28799999999998,63.428999999999995,32.225 +2020-11-13 18:00:00,132.99,143.393,67.915,32.225 +2020-11-13 18:15:00,132.55,141.596,67.915,32.225 +2020-11-13 18:30:00,131.85,140.16899999999998,67.915,32.225 +2020-11-13 18:45:00,131.77,143.563,67.915,32.225 +2020-11-13 19:00:00,127.72,145.562,69.428,32.225 +2020-11-13 19:15:00,126.33,144.722,69.428,32.225 +2020-11-13 19:30:00,123.37,143.181,69.428,32.225 +2020-11-13 19:45:00,122.12,140.983,69.428,32.225 +2020-11-13 20:00:00,116.08,136.49200000000002,60.56100000000001,32.225 +2020-11-13 20:15:00,112.51,133.029,60.56100000000001,32.225 +2020-11-13 20:30:00,109.7,131.888,60.56100000000001,32.225 +2020-11-13 20:45:00,109.08,129.773,60.56100000000001,32.225 +2020-11-13 21:00:00,102.38,126.15,55.18600000000001,32.225 +2020-11-13 21:15:00,102.35,126.273,55.18600000000001,32.225 +2020-11-13 21:30:00,103.54,125.37200000000001,55.18600000000001,32.225 +2020-11-13 21:45:00,102.4,123.066,55.18600000000001,32.225 +2020-11-13 22:00:00,94.88,117.936,51.433,32.225 +2020-11-13 22:15:00,90.57,113.288,51.433,32.225 +2020-11-13 22:30:00,83.68,106.458,51.433,32.225 +2020-11-13 22:45:00,88.07,101.781,51.433,32.225 +2020-11-13 23:00:00,87.2,96.691,46.201,32.225 +2020-11-13 23:15:00,86.93,93.52,46.201,32.225 +2020-11-13 23:30:00,81.06,91.205,46.201,32.225 +2020-11-13 23:45:00,75.58,90.72,46.201,32.225 +2020-11-14 00:00:00,73.58,77.764,42.576,32.047 +2020-11-14 00:15:00,74.26,75.94,42.576,32.047 +2020-11-14 00:30:00,76.41,76.453,42.576,32.047 +2020-11-14 00:45:00,75.17,76.795,42.576,32.047 +2020-11-14 01:00:00,67.85,78.506,39.34,32.047 +2020-11-14 01:15:00,73.52,78.689,39.34,32.047 +2020-11-14 01:30:00,72.25,78.084,39.34,32.047 +2020-11-14 01:45:00,73.31,78.359,39.34,32.047 +2020-11-14 02:00:00,65.19,80.163,37.582,32.047 +2020-11-14 02:15:00,71.47,80.111,37.582,32.047 +2020-11-14 02:30:00,72.68,79.926,37.582,32.047 +2020-11-14 02:45:00,72.1,80.97,37.582,32.047 +2020-11-14 03:00:00,65.86,83.103,36.523,32.047 +2020-11-14 03:15:00,72.14,83.70299999999999,36.523,32.047 +2020-11-14 03:30:00,71.64,83.572,36.523,32.047 +2020-11-14 03:45:00,67.64,85.304,36.523,32.047 +2020-11-14 04:00:00,64.63,94.146,36.347,32.047 +2020-11-14 04:15:00,66.11,103.001,36.347,32.047 +2020-11-14 04:30:00,66.46,101.794,36.347,32.047 +2020-11-14 04:45:00,67.22,102.839,36.347,32.047 +2020-11-14 05:00:00,66.75,122.023,36.407,32.047 +2020-11-14 05:15:00,66.43,136.593,36.407,32.047 +2020-11-14 05:30:00,66.52,132.209,36.407,32.047 +2020-11-14 05:45:00,69.84,127.36399999999999,36.407,32.047 +2020-11-14 06:00:00,68.77,141.99200000000002,38.228,32.047 +2020-11-14 06:15:00,71.68,159.651,38.228,32.047 +2020-11-14 06:30:00,74.01,154.347,38.228,32.047 +2020-11-14 06:45:00,75.87,149.19799999999998,38.228,32.047 +2020-11-14 07:00:00,77.55,146.75,41.905,32.047 +2020-11-14 07:15:00,78.66,149.886,41.905,32.047 +2020-11-14 07:30:00,84.59,152.562,41.905,32.047 +2020-11-14 07:45:00,85.21,155.03799999999998,41.905,32.047 +2020-11-14 08:00:00,88.67,157.241,46.051,32.047 +2020-11-14 08:15:00,88.43,158.736,46.051,32.047 +2020-11-14 08:30:00,88.2,158.338,46.051,32.047 +2020-11-14 08:45:00,89.95,156.94899999999998,46.051,32.047 +2020-11-14 09:00:00,90.45,153.843,46.683,32.047 +2020-11-14 09:15:00,92.7,152.679,46.683,32.047 +2020-11-14 09:30:00,89.36,152.045,46.683,32.047 +2020-11-14 09:45:00,91.08,150.639,46.683,32.047 +2020-11-14 10:00:00,92.47,146.975,44.425,32.047 +2020-11-14 10:15:00,89.41,145.27,44.425,32.047 +2020-11-14 10:30:00,89.74,143.088,44.425,32.047 +2020-11-14 10:45:00,89.93,142.576,44.425,32.047 +2020-11-14 11:00:00,88.64,138.803,42.148999999999994,32.047 +2020-11-14 11:15:00,89.78,137.474,42.148999999999994,32.047 +2020-11-14 11:30:00,87.93,138.07,42.148999999999994,32.047 +2020-11-14 11:45:00,90.55,138.03,42.148999999999994,32.047 +2020-11-14 12:00:00,88.09,133.856,39.683,32.047 +2020-11-14 12:15:00,84.91,131.719,39.683,32.047 +2020-11-14 12:30:00,85.92,132.635,39.683,32.047 +2020-11-14 12:45:00,81.63,132.94799999999998,39.683,32.047 +2020-11-14 13:00:00,83.08,132.429,37.154,32.047 +2020-11-14 13:15:00,79.91,130.911,37.154,32.047 +2020-11-14 13:30:00,78.46,130.222,37.154,32.047 +2020-11-14 13:45:00,81.0,129.796,37.154,32.047 +2020-11-14 14:00:00,78.11,128.738,36.457,32.047 +2020-11-14 14:15:00,80.05,127.838,36.457,32.047 +2020-11-14 14:30:00,79.25,127.26700000000001,36.457,32.047 +2020-11-14 14:45:00,81.47,127.476,36.457,32.047 +2020-11-14 15:00:00,82.81,126.669,38.257,32.047 +2020-11-14 15:15:00,83.68,127.45100000000001,38.257,32.047 +2020-11-14 15:30:00,86.67,127.93799999999999,38.257,32.047 +2020-11-14 15:45:00,89.46,128.145,38.257,32.047 +2020-11-14 16:00:00,93.0,129.27100000000002,41.181000000000004,32.047 +2020-11-14 16:15:00,94.17,131.631,41.181000000000004,32.047 +2020-11-14 16:30:00,97.49,131.899,41.181000000000004,32.047 +2020-11-14 16:45:00,98.74,131.75,41.181000000000004,32.047 +2020-11-14 17:00:00,103.86,133.11,46.806000000000004,32.047 +2020-11-14 17:15:00,102.08,134.124,46.806000000000004,32.047 +2020-11-14 17:30:00,106.38,134.031,46.806000000000004,32.047 +2020-11-14 17:45:00,104.65,133.447,46.806000000000004,32.047 +2020-11-14 18:00:00,105.57,136.77700000000002,52.073,32.047 +2020-11-14 18:15:00,107.67,136.77700000000002,52.073,32.047 +2020-11-14 18:30:00,107.18,136.754,52.073,32.047 +2020-11-14 18:45:00,104.68,136.619,52.073,32.047 +2020-11-14 19:00:00,106.23,138.694,53.608000000000004,32.047 +2020-11-14 19:15:00,102.11,137.142,53.608000000000004,32.047 +2020-11-14 19:30:00,101.48,136.377,53.608000000000004,32.047 +2020-11-14 19:45:00,102.58,134.679,53.608000000000004,32.047 +2020-11-14 20:00:00,93.26,132.019,50.265,32.047 +2020-11-14 20:15:00,91.95,129.72899999999998,50.265,32.047 +2020-11-14 20:30:00,89.67,127.99700000000001,50.265,32.047 +2020-11-14 20:45:00,87.38,126.29799999999999,50.265,32.047 +2020-11-14 21:00:00,82.46,123.631,45.766000000000005,32.047 +2020-11-14 21:15:00,82.49,123.90100000000001,45.766000000000005,32.047 +2020-11-14 21:30:00,81.16,123.895,45.766000000000005,32.047 +2020-11-14 21:45:00,81.79,121.103,45.766000000000005,32.047 +2020-11-14 22:00:00,76.57,116.85799999999999,45.97,32.047 +2020-11-14 22:15:00,78.47,113.943,45.97,32.047 +2020-11-14 22:30:00,73.69,111.147,45.97,32.047 +2020-11-14 22:45:00,74.23,107.86,45.97,32.047 +2020-11-14 23:00:00,71.23,104.15299999999999,40.415,32.047 +2020-11-14 23:15:00,73.31,100.02799999999999,40.415,32.047 +2020-11-14 23:30:00,67.49,97.47399999999999,40.415,32.047 +2020-11-14 23:45:00,69.47,95.495,40.415,32.047 +2020-11-15 00:00:00,64.33,78.939,36.376,32.047 +2020-11-15 00:15:00,64.14,76.423,36.376,32.047 +2020-11-15 00:30:00,62.75,76.64,36.376,32.047 +2020-11-15 00:45:00,61.39,77.367,36.376,32.047 +2020-11-15 01:00:00,59.62,79.115,32.992,32.047 +2020-11-15 01:15:00,59.48,79.914,32.992,32.047 +2020-11-15 01:30:00,59.29,79.596,32.992,32.047 +2020-11-15 01:45:00,58.73,79.51,32.992,32.047 +2020-11-15 02:00:00,57.48,80.884,32.327,32.047 +2020-11-15 02:15:00,58.06,80.6,32.327,32.047 +2020-11-15 02:30:00,56.99,81.02,32.327,32.047 +2020-11-15 02:45:00,57.41,82.229,32.327,32.047 +2020-11-15 03:00:00,56.71,84.815,31.169,32.047 +2020-11-15 03:15:00,58.22,85.223,31.169,32.047 +2020-11-15 03:30:00,57.82,85.655,31.169,32.047 +2020-11-15 03:45:00,60.95,86.992,31.169,32.047 +2020-11-15 04:00:00,58.41,95.665,30.796,32.047 +2020-11-15 04:15:00,58.67,103.736,30.796,32.047 +2020-11-15 04:30:00,59.85,103.132,30.796,32.047 +2020-11-15 04:45:00,60.47,104.154,30.796,32.047 +2020-11-15 05:00:00,61.1,121.301,30.848000000000003,32.047 +2020-11-15 05:15:00,62.05,134.10299999999998,30.848000000000003,32.047 +2020-11-15 05:30:00,61.78,129.438,30.848000000000003,32.047 +2020-11-15 05:45:00,62.1,124.605,30.848000000000003,32.047 +2020-11-15 06:00:00,63.73,138.144,31.166,32.047 +2020-11-15 06:15:00,64.63,155.086,31.166,32.047 +2020-11-15 06:30:00,63.71,148.829,31.166,32.047 +2020-11-15 06:45:00,66.0,142.626,31.166,32.047 +2020-11-15 07:00:00,69.28,141.808,33.527,32.047 +2020-11-15 07:15:00,69.23,143.747,33.527,32.047 +2020-11-15 07:30:00,70.66,146.174,33.527,32.047 +2020-11-15 07:45:00,72.74,148.149,33.527,32.047 +2020-11-15 08:00:00,76.18,151.78799999999998,36.616,32.047 +2020-11-15 08:15:00,75.92,153.657,36.616,32.047 +2020-11-15 08:30:00,76.26,154.701,36.616,32.047 +2020-11-15 08:45:00,76.69,154.578,36.616,32.047 +2020-11-15 09:00:00,75.86,151.127,37.857,32.047 +2020-11-15 09:15:00,76.01,150.151,37.857,32.047 +2020-11-15 09:30:00,76.34,149.567,37.857,32.047 +2020-11-15 09:45:00,75.23,148.433,37.857,32.047 +2020-11-15 10:00:00,74.72,146.63299999999998,36.319,32.047 +2020-11-15 10:15:00,76.47,145.306,36.319,32.047 +2020-11-15 10:30:00,77.15,143.592,36.319,32.047 +2020-11-15 10:45:00,81.23,142.128,36.319,32.047 +2020-11-15 11:00:00,80.21,138.842,37.236999999999995,32.047 +2020-11-15 11:15:00,83.25,137.43200000000002,37.236999999999995,32.047 +2020-11-15 11:30:00,84.16,137.60299999999998,37.236999999999995,32.047 +2020-11-15 11:45:00,84.67,138.061,37.236999999999995,32.047 +2020-11-15 12:00:00,81.09,133.804,34.871,32.047 +2020-11-15 12:15:00,79.37,132.8,34.871,32.047 +2020-11-15 12:30:00,75.92,132.793,34.871,32.047 +2020-11-15 12:45:00,80.89,132.215,34.871,32.047 +2020-11-15 13:00:00,77.87,131.03799999999998,29.738000000000003,32.047 +2020-11-15 13:15:00,77.45,131.476,29.738000000000003,32.047 +2020-11-15 13:30:00,80.57,130.268,29.738000000000003,32.047 +2020-11-15 13:45:00,80.75,129.736,29.738000000000003,32.047 +2020-11-15 14:00:00,77.13,129.19799999999998,27.333000000000002,32.047 +2020-11-15 14:15:00,77.57,129.305,27.333000000000002,32.047 +2020-11-15 14:30:00,76.34,129.243,27.333000000000002,32.047 +2020-11-15 14:45:00,81.67,128.811,27.333000000000002,32.047 +2020-11-15 15:00:00,80.78,126.95700000000001,28.232,32.047 +2020-11-15 15:15:00,82.26,128.025,28.232,32.047 +2020-11-15 15:30:00,82.54,128.907,28.232,32.047 +2020-11-15 15:45:00,83.8,129.707,28.232,32.047 +2020-11-15 16:00:00,84.97,131.477,32.815,32.047 +2020-11-15 16:15:00,87.01,133.31,32.815,32.047 +2020-11-15 16:30:00,90.92,134.102,32.815,32.047 +2020-11-15 16:45:00,94.28,134.114,32.815,32.047 +2020-11-15 17:00:00,99.69,135.486,43.068999999999996,32.047 +2020-11-15 17:15:00,99.2,136.842,43.068999999999996,32.047 +2020-11-15 17:30:00,101.41,137.244,43.068999999999996,32.047 +2020-11-15 17:45:00,102.52,138.362,43.068999999999996,32.047 +2020-11-15 18:00:00,103.76,141.516,50.498999999999995,32.047 +2020-11-15 18:15:00,103.31,142.23,50.498999999999995,32.047 +2020-11-15 18:30:00,103.12,140.725,50.498999999999995,32.047 +2020-11-15 18:45:00,100.94,141.876,50.498999999999995,32.047 +2020-11-15 19:00:00,98.74,144.512,53.481,32.047 +2020-11-15 19:15:00,97.83,142.938,53.481,32.047 +2020-11-15 19:30:00,95.48,141.958,53.481,32.047 +2020-11-15 19:45:00,95.27,141.03,53.481,32.047 +2020-11-15 20:00:00,97.93,138.394,51.687,32.047 +2020-11-15 20:15:00,98.82,136.695,51.687,32.047 +2020-11-15 20:30:00,95.61,136.059,51.687,32.047 +2020-11-15 20:45:00,88.72,132.92600000000002,51.687,32.047 +2020-11-15 21:00:00,85.57,128.464,47.674,32.047 +2020-11-15 21:15:00,87.2,128.194,47.674,32.047 +2020-11-15 21:30:00,86.41,128.145,47.674,32.047 +2020-11-15 21:45:00,91.7,125.568,47.674,32.047 +2020-11-15 22:00:00,90.91,121.318,48.178000000000004,32.047 +2020-11-15 22:15:00,94.56,117.251,48.178000000000004,32.047 +2020-11-15 22:30:00,86.94,112.219,48.178000000000004,32.047 +2020-11-15 22:45:00,86.52,107.897,48.178000000000004,32.047 +2020-11-15 23:00:00,86.59,102.23200000000001,42.553999999999995,32.047 +2020-11-15 23:15:00,89.06,99.788,42.553999999999995,32.047 +2020-11-15 23:30:00,87.84,97.57,42.553999999999995,32.047 +2020-11-15 23:45:00,83.38,96.226,42.553999999999995,32.047 +2020-11-16 00:00:00,77.1,99.905,37.177,32.225 +2020-11-16 00:15:00,77.33,98.662,37.177,32.225 +2020-11-16 00:30:00,82.22,99.729,37.177,32.225 +2020-11-16 00:45:00,82.47,101.445,37.177,32.225 +2020-11-16 01:00:00,81.36,102.945,35.358000000000004,32.225 +2020-11-16 01:15:00,75.91,104.265,35.358000000000004,32.225 +2020-11-16 01:30:00,77.02,104.413,35.358000000000004,32.225 +2020-11-16 01:45:00,82.03,104.87100000000001,35.358000000000004,32.225 +2020-11-16 02:00:00,81.18,106.01299999999999,35.03,32.225 +2020-11-16 02:15:00,77.98,106.837,35.03,32.225 +2020-11-16 02:30:00,75.5,107.139,35.03,32.225 +2020-11-16 02:45:00,80.51,108.699,35.03,32.225 +2020-11-16 03:00:00,81.9,112.40299999999999,34.394,32.225 +2020-11-16 03:15:00,83.19,113.646,34.394,32.225 +2020-11-16 03:30:00,82.04,115.22200000000001,34.394,32.225 +2020-11-16 03:45:00,82.89,115.84,34.394,32.225 +2020-11-16 04:00:00,84.77,130.126,34.421,32.225 +2020-11-16 04:15:00,83.73,143.125,34.421,32.225 +2020-11-16 04:30:00,79.15,144.792,34.421,32.225 +2020-11-16 04:45:00,87.58,145.659,34.421,32.225 +2020-11-16 05:00:00,93.5,175.607,39.435,32.225 +2020-11-16 05:15:00,96.13,205.88400000000001,39.435,32.225 +2020-11-16 05:30:00,95.49,202.571,39.435,32.225 +2020-11-16 05:45:00,96.86,193.637,39.435,32.225 +2020-11-16 06:00:00,106.96,190.03,55.685,32.225 +2020-11-16 06:15:00,117.57,193.713,55.685,32.225 +2020-11-16 06:30:00,124.3,195.49400000000003,55.685,32.225 +2020-11-16 06:45:00,128.75,196.998,55.685,32.225 +2020-11-16 07:00:00,127.88,198.408,66.837,32.225 +2020-11-16 07:15:00,130.09,202.535,66.837,32.225 +2020-11-16 07:30:00,131.28,204.43900000000002,66.837,32.225 +2020-11-16 07:45:00,132.13,204.878,66.837,32.225 +2020-11-16 08:00:00,134.59,203.71,72.217,32.225 +2020-11-16 08:15:00,135.13,204.238,72.217,32.225 +2020-11-16 08:30:00,134.93,202.28799999999998,72.217,32.225 +2020-11-16 08:45:00,135.48,199.72,72.217,32.225 +2020-11-16 09:00:00,137.04,194.734,66.117,32.225 +2020-11-16 09:15:00,137.72,189.67,66.117,32.225 +2020-11-16 09:30:00,139.23,186.65099999999998,66.117,32.225 +2020-11-16 09:45:00,140.89,184.546,66.117,32.225 +2020-11-16 10:00:00,140.75,182.533,62.1,32.225 +2020-11-16 10:15:00,140.27,180.21200000000002,62.1,32.225 +2020-11-16 10:30:00,139.75,178.195,62.1,32.225 +2020-11-16 10:45:00,141.87,176.75400000000002,62.1,32.225 +2020-11-16 11:00:00,139.36,173.687,60.021,32.225 +2020-11-16 11:15:00,138.76,173.46599999999998,60.021,32.225 +2020-11-16 11:30:00,136.87,174.088,60.021,32.225 +2020-11-16 11:45:00,137.88,173.037,60.021,32.225 +2020-11-16 12:00:00,136.95,168.87,56.75899999999999,32.225 +2020-11-16 12:15:00,136.17,167.93599999999998,56.75899999999999,32.225 +2020-11-16 12:30:00,138.07,167.206,56.75899999999999,32.225 +2020-11-16 12:45:00,139.07,167.998,56.75899999999999,32.225 +2020-11-16 13:00:00,135.65,167.93099999999998,56.04600000000001,32.225 +2020-11-16 13:15:00,138.73,167.377,56.04600000000001,32.225 +2020-11-16 13:30:00,134.79,165.9,56.04600000000001,32.225 +2020-11-16 13:45:00,133.83,165.859,56.04600000000001,32.225 +2020-11-16 14:00:00,130.83,165.37900000000002,55.475,32.225 +2020-11-16 14:15:00,130.65,165.18099999999998,55.475,32.225 +2020-11-16 14:30:00,129.75,164.22,55.475,32.225 +2020-11-16 14:45:00,129.95,164.2,55.475,32.225 +2020-11-16 15:00:00,130.58,164.608,57.048,32.225 +2020-11-16 15:15:00,129.71,164.543,57.048,32.225 +2020-11-16 15:30:00,129.12,165.127,57.048,32.225 +2020-11-16 15:45:00,129.23,166.125,57.048,32.225 +2020-11-16 16:00:00,130.93,169.43,59.06,32.225 +2020-11-16 16:15:00,132.04,170.313,59.06,32.225 +2020-11-16 16:30:00,136.67,171.088,59.06,32.225 +2020-11-16 16:45:00,138.56,170.58900000000003,59.06,32.225 +2020-11-16 17:00:00,139.38,173.562,65.419,32.225 +2020-11-16 17:15:00,139.96,173.8,65.419,32.225 +2020-11-16 17:30:00,140.06,173.597,65.419,32.225 +2020-11-16 17:45:00,139.57,173.247,65.419,32.225 +2020-11-16 18:00:00,137.5,175.109,69.345,32.225 +2020-11-16 18:15:00,136.2,174.597,69.345,32.225 +2020-11-16 18:30:00,135.18,173.084,69.345,32.225 +2020-11-16 18:45:00,135.88,173.261,69.345,32.225 +2020-11-16 19:00:00,133.06,174.851,73.825,32.225 +2020-11-16 19:15:00,129.71,172.138,73.825,32.225 +2020-11-16 19:30:00,128.36,171.513,73.825,32.225 +2020-11-16 19:45:00,126.9,168.456,73.825,32.225 +2020-11-16 20:00:00,121.58,164.43200000000002,64.027,32.225 +2020-11-16 20:15:00,117.62,160.344,64.027,32.225 +2020-11-16 20:30:00,113.7,156.911,64.027,32.225 +2020-11-16 20:45:00,112.48,154.009,64.027,32.225 +2020-11-16 21:00:00,108.97,152.451,57.952,32.225 +2020-11-16 21:15:00,107.75,150.361,57.952,32.225 +2020-11-16 21:30:00,110.85,148.789,57.952,32.225 +2020-11-16 21:45:00,111.19,146.843,57.952,32.225 +2020-11-16 22:00:00,106.86,138.77200000000002,53.031000000000006,32.225 +2020-11-16 22:15:00,98.64,134.02100000000002,53.031000000000006,32.225 +2020-11-16 22:30:00,94.58,118.351,53.031000000000006,32.225 +2020-11-16 22:45:00,92.11,110.148,53.031000000000006,32.225 +2020-11-16 23:00:00,89.04,105.603,45.085,32.225 +2020-11-16 23:15:00,88.11,104.111,45.085,32.225 +2020-11-16 23:30:00,84.69,104.76700000000001,45.085,32.225 +2020-11-16 23:45:00,83.57,104.964,45.085,32.225 +2020-11-17 00:00:00,84.58,99.30799999999999,42.843,32.225 +2020-11-17 00:15:00,87.16,99.41,42.843,32.225 +2020-11-17 00:30:00,88.47,99.79700000000001,42.843,32.225 +2020-11-17 00:45:00,83.12,100.8,42.843,32.225 +2020-11-17 01:00:00,78.7,102.07799999999999,41.542,32.225 +2020-11-17 01:15:00,84.09,103.046,41.542,32.225 +2020-11-17 01:30:00,84.23,103.331,41.542,32.225 +2020-11-17 01:45:00,83.62,103.946,41.542,32.225 +2020-11-17 02:00:00,76.91,105.01700000000001,40.19,32.225 +2020-11-17 02:15:00,80.46,105.959,40.19,32.225 +2020-11-17 02:30:00,83.38,105.678,40.19,32.225 +2020-11-17 02:45:00,84.48,107.306,40.19,32.225 +2020-11-17 03:00:00,82.24,109.87200000000001,39.626,32.225 +2020-11-17 03:15:00,84.6,110.59299999999999,39.626,32.225 +2020-11-17 03:30:00,86.1,112.57600000000001,39.626,32.225 +2020-11-17 03:45:00,86.14,113.147,39.626,32.225 +2020-11-17 04:00:00,82.44,127.023,40.196999999999996,32.225 +2020-11-17 04:15:00,86.13,139.739,40.196999999999996,32.225 +2020-11-17 04:30:00,89.79,141.108,40.196999999999996,32.225 +2020-11-17 04:45:00,90.47,143.09799999999998,40.196999999999996,32.225 +2020-11-17 05:00:00,88.89,177.57299999999998,43.378,32.225 +2020-11-17 05:15:00,91.86,207.77200000000002,43.378,32.225 +2020-11-17 05:30:00,94.62,203.21400000000003,43.378,32.225 +2020-11-17 05:45:00,98.85,194.155,43.378,32.225 +2020-11-17 06:00:00,105.56,189.748,55.691,32.225 +2020-11-17 06:15:00,112.04,194.85299999999998,55.691,32.225 +2020-11-17 06:30:00,118.42,196.07299999999998,55.691,32.225 +2020-11-17 06:45:00,121.99,197.13400000000001,55.691,32.225 +2020-11-17 07:00:00,126.36,198.41299999999998,65.567,32.225 +2020-11-17 07:15:00,128.77,202.37,65.567,32.225 +2020-11-17 07:30:00,129.41,203.832,65.567,32.225 +2020-11-17 07:45:00,131.36,204.28900000000002,65.567,32.225 +2020-11-17 08:00:00,134.03,203.22099999999998,73.001,32.225 +2020-11-17 08:15:00,133.22,202.782,73.001,32.225 +2020-11-17 08:30:00,137.27,200.732,73.001,32.225 +2020-11-17 08:45:00,136.45,197.748,73.001,32.225 +2020-11-17 09:00:00,137.66,192.072,67.08800000000001,32.225 +2020-11-17 09:15:00,138.23,188.37599999999998,67.08800000000001,32.225 +2020-11-17 09:30:00,141.61,186.05599999999998,67.08800000000001,32.225 +2020-11-17 09:45:00,141.87,183.963,67.08800000000001,32.225 +2020-11-17 10:00:00,142.21,181.206,62.803000000000004,32.225 +2020-11-17 10:15:00,140.92,177.903,62.803000000000004,32.225 +2020-11-17 10:30:00,143.35,176.02200000000002,62.803000000000004,32.225 +2020-11-17 10:45:00,142.09,174.975,62.803000000000004,32.225 +2020-11-17 11:00:00,140.39,173.19299999999998,60.155,32.225 +2020-11-17 11:15:00,139.74,172.726,60.155,32.225 +2020-11-17 11:30:00,140.21,172.148,60.155,32.225 +2020-11-17 11:45:00,140.97,171.627,60.155,32.225 +2020-11-17 12:00:00,139.79,166.22099999999998,56.845,32.225 +2020-11-17 12:15:00,138.99,164.982,56.845,32.225 +2020-11-17 12:30:00,143.01,165.047,56.845,32.225 +2020-11-17 12:45:00,146.71,165.69400000000002,56.845,32.225 +2020-11-17 13:00:00,144.04,165.172,56.163000000000004,32.225 +2020-11-17 13:15:00,138.69,164.601,56.163000000000004,32.225 +2020-11-17 13:30:00,137.01,164.142,56.163000000000004,32.225 +2020-11-17 13:45:00,136.33,164.106,56.163000000000004,32.225 +2020-11-17 14:00:00,136.4,163.898,55.934,32.225 +2020-11-17 14:15:00,130.93,163.82,55.934,32.225 +2020-11-17 14:30:00,127.86,163.495,55.934,32.225 +2020-11-17 14:45:00,127.46,163.289,55.934,32.225 +2020-11-17 15:00:00,129.68,163.296,57.43899999999999,32.225 +2020-11-17 15:15:00,129.91,163.672,57.43899999999999,32.225 +2020-11-17 15:30:00,130.07,164.459,57.43899999999999,32.225 +2020-11-17 15:45:00,132.74,165.16299999999998,57.43899999999999,32.225 +2020-11-17 16:00:00,135.56,168.65099999999998,59.968999999999994,32.225 +2020-11-17 16:15:00,137.0,169.99099999999999,59.968999999999994,32.225 +2020-11-17 16:30:00,137.55,171.27900000000002,59.968999999999994,32.225 +2020-11-17 16:45:00,137.39,171.187,59.968999999999994,32.225 +2020-11-17 17:00:00,139.11,174.64,67.428,32.225 +2020-11-17 17:15:00,139.71,174.99,67.428,32.225 +2020-11-17 17:30:00,139.17,175.31,67.428,32.225 +2020-11-17 17:45:00,137.63,174.801,67.428,32.225 +2020-11-17 18:00:00,135.8,176.44,71.533,32.225 +2020-11-17 18:15:00,134.06,175.666,71.533,32.225 +2020-11-17 18:30:00,133.05,173.852,71.533,32.225 +2020-11-17 18:45:00,133.67,174.7,71.533,32.225 +2020-11-17 19:00:00,130.58,176.188,73.32300000000001,32.225 +2020-11-17 19:15:00,128.02,173.24200000000002,73.32300000000001,32.225 +2020-11-17 19:30:00,125.82,171.97799999999998,73.32300000000001,32.225 +2020-11-17 19:45:00,124.09,168.967,73.32300000000001,32.225 +2020-11-17 20:00:00,119.92,165.153,64.166,32.225 +2020-11-17 20:15:00,115.35,160.27200000000002,64.166,32.225 +2020-11-17 20:30:00,112.08,157.69899999999998,64.166,32.225 +2020-11-17 20:45:00,109.65,154.369,64.166,32.225 +2020-11-17 21:00:00,106.35,152.325,57.891999999999996,32.225 +2020-11-17 21:15:00,101.33,150.787,57.891999999999996,32.225 +2020-11-17 21:30:00,97.94,148.607,57.891999999999996,32.225 +2020-11-17 21:45:00,94.87,146.888,57.891999999999996,32.225 +2020-11-17 22:00:00,90.4,140.35,53.242,32.225 +2020-11-17 22:15:00,87.25,135.31,53.242,32.225 +2020-11-17 22:30:00,83.17,119.822,53.242,32.225 +2020-11-17 22:45:00,81.13,111.868,53.242,32.225 +2020-11-17 23:00:00,77.54,107.215,46.665,32.225 +2020-11-17 23:15:00,75.31,105.156,46.665,32.225 +2020-11-17 23:30:00,73.28,105.50200000000001,46.665,32.225 +2020-11-17 23:45:00,71.49,105.323,46.665,32.225 +2020-11-18 00:00:00,68.06,96.84,43.16,32.225 +2020-11-18 00:15:00,66.65,92.867,43.16,32.225 +2020-11-18 00:30:00,66.05,93.897,43.16,32.225 +2020-11-18 00:45:00,65.38,96.11,43.16,32.225 +2020-11-18 01:00:00,63.81,97.618,40.972,32.225 +2020-11-18 01:15:00,65.53,99.4,40.972,32.225 +2020-11-18 01:30:00,61.92,99.46,40.972,32.225 +2020-11-18 01:45:00,62.1,99.815,40.972,32.225 +2020-11-18 02:00:00,61.67,100.939,39.749,32.225 +2020-11-18 02:15:00,62.09,100.72399999999999,39.749,32.225 +2020-11-18 02:30:00,61.4,100.70299999999999,39.749,32.225 +2020-11-18 02:45:00,61.82,102.81200000000001,39.749,32.225 +2020-11-18 03:00:00,61.2,105.334,39.422,32.225 +2020-11-18 03:15:00,61.6,105.109,39.422,32.225 +2020-11-18 03:30:00,61.32,106.791,39.422,32.225 +2020-11-18 03:45:00,62.03,107.945,39.422,32.225 +2020-11-18 04:00:00,62.95,117.961,40.505,32.225 +2020-11-18 04:15:00,62.82,126.931,40.505,32.225 +2020-11-18 04:30:00,63.09,126.81700000000001,40.505,32.225 +2020-11-18 04:45:00,63.58,127.492,40.505,32.225 +2020-11-18 05:00:00,64.61,142.859,43.397,32.225 +2020-11-18 05:15:00,64.56,154.186,43.397,32.225 +2020-11-18 05:30:00,64.42,150.976,43.397,32.225 +2020-11-18 05:45:00,65.18,147.387,43.397,32.225 +2020-11-18 06:00:00,65.99,160.76,55.218,32.225 +2020-11-18 06:15:00,66.23,178.679,55.218,32.225 +2020-11-18 06:30:00,67.7,173.043,55.218,32.225 +2020-11-18 06:45:00,69.63,166.206,55.218,32.225 +2020-11-18 07:00:00,72.15,165.429,67.39,32.225 +2020-11-18 07:15:00,73.16,168.10299999999998,67.39,32.225 +2020-11-18 07:30:00,73.58,170.863,67.39,32.225 +2020-11-18 07:45:00,76.05,173.437,67.39,32.225 +2020-11-18 08:00:00,79.97,176.707,74.345,32.225 +2020-11-18 08:15:00,81.61,179.26,74.345,32.225 +2020-11-18 08:30:00,82.38,181.017,74.345,32.225 +2020-11-18 08:45:00,84.44,181.19799999999998,74.345,32.225 +2020-11-18 09:00:00,86.73,177.138,69.336,32.225 +2020-11-18 09:15:00,88.15,175.387,69.336,32.225 +2020-11-18 09:30:00,88.01,173.38099999999997,69.336,32.225 +2020-11-18 09:45:00,88.77,171.382,69.336,32.225 +2020-11-18 10:00:00,88.6,170.173,64.291,32.225 +2020-11-18 10:15:00,89.69,168.09,64.291,32.225 +2020-11-18 10:30:00,91.83,166.854,64.291,32.225 +2020-11-18 10:45:00,93.65,165.011,64.291,32.225 +2020-11-18 11:00:00,93.82,164.09099999999998,62.20399999999999,32.225 +2020-11-18 11:15:00,96.88,162.225,62.20399999999999,32.225 +2020-11-18 11:30:00,97.62,161.532,62.20399999999999,32.225 +2020-11-18 11:45:00,98.81,160.72899999999998,62.20399999999999,32.225 +2020-11-18 12:00:00,98.43,155.30100000000002,59.042,32.225 +2020-11-18 12:15:00,96.83,154.36,59.042,32.225 +2020-11-18 12:30:00,94.69,153.64600000000002,59.042,32.225 +2020-11-18 12:45:00,92.52,153.125,59.042,32.225 +2020-11-18 13:00:00,91.17,152.308,57.907,32.225 +2020-11-18 13:15:00,90.28,153.11700000000002,57.907,32.225 +2020-11-18 13:30:00,89.49,152.067,57.907,32.225 +2020-11-18 13:45:00,89.41,151.834,57.907,32.225 +2020-11-18 14:00:00,88.17,151.933,58.358000000000004,32.225 +2020-11-18 14:15:00,87.97,152.22299999999998,58.358000000000004,32.225 +2020-11-18 14:30:00,88.15,151.829,58.358000000000004,32.225 +2020-11-18 14:45:00,89.22,151.511,58.358000000000004,32.225 +2020-11-18 15:00:00,91.04,150.407,59.348,32.225 +2020-11-18 15:15:00,90.98,151.707,59.348,32.225 +2020-11-18 15:30:00,90.33,152.963,59.348,32.225 +2020-11-18 15:45:00,91.75,154.442,59.348,32.225 +2020-11-18 16:00:00,93.83,157.414,61.413999999999994,32.225 +2020-11-18 16:15:00,96.36,159.002,61.413999999999994,32.225 +2020-11-18 16:30:00,97.49,160.732,61.413999999999994,32.225 +2020-11-18 16:45:00,98.72,161.352,61.413999999999994,32.225 +2020-11-18 17:00:00,101.46,164.60299999999998,67.107,32.225 +2020-11-18 17:15:00,103.06,165.655,67.107,32.225 +2020-11-18 17:30:00,107.61,166.00599999999997,67.107,32.225 +2020-11-18 17:45:00,105.53,167.03400000000002,67.107,32.225 +2020-11-18 18:00:00,105.8,168.706,71.92,32.225 +2020-11-18 18:15:00,104.59,170.32,71.92,32.225 +2020-11-18 18:30:00,103.28,168.372,71.92,32.225 +2020-11-18 18:45:00,104.04,167.43,71.92,32.225 +2020-11-18 19:00:00,102.53,170.468,75.09,32.225 +2020-11-18 19:15:00,100.99,168.55,75.09,32.225 +2020-11-18 19:30:00,99.54,167.50900000000001,75.09,32.225 +2020-11-18 19:45:00,98.32,165.204,75.09,32.225 +2020-11-18 20:00:00,101.42,163.47799999999998,65.977,32.225 +2020-11-18 20:15:00,101.77,161.30200000000002,65.977,32.225 +2020-11-18 20:30:00,98.91,159.345,65.977,32.225 +2020-11-18 20:45:00,94.0,155.009,65.977,32.225 +2020-11-18 21:00:00,91.32,153.06799999999998,58.798,32.225 +2020-11-18 21:15:00,94.27,151.904,58.798,32.225 +2020-11-18 21:30:00,94.17,150.993,58.798,32.225 +2020-11-18 21:45:00,90.94,149.516,58.798,32.225 +2020-11-18 22:00:00,87.11,144.303,54.486000000000004,32.225 +2020-11-18 22:15:00,86.53,140.368,54.486000000000004,32.225 +2020-11-18 22:30:00,87.31,133.83,54.486000000000004,32.225 +2020-11-18 22:45:00,83.89,129.859,54.486000000000004,32.225 +2020-11-18 23:00:00,79.96,124.615,47.783,32.225 +2020-11-18 23:15:00,80.17,121.041,47.783,32.225 +2020-11-18 23:30:00,78.39,119.30799999999999,47.783,32.225 +2020-11-18 23:45:00,77.89,117.245,47.783,32.225 +2020-11-19 00:00:00,74.83,100.085,43.88,32.225 +2020-11-19 00:15:00,74.27,100.132,43.88,32.225 +2020-11-19 00:30:00,72.66,100.522,43.88,32.225 +2020-11-19 00:45:00,72.09,101.492,43.88,32.225 +2020-11-19 01:00:00,71.02,102.845,42.242,32.225 +2020-11-19 01:15:00,71.4,103.829,42.242,32.225 +2020-11-19 01:30:00,70.62,104.14,42.242,32.225 +2020-11-19 01:45:00,71.65,104.73,42.242,32.225 +2020-11-19 02:00:00,74.0,105.839,40.918,32.225 +2020-11-19 02:15:00,71.51,106.79,40.918,32.225 +2020-11-19 02:30:00,71.25,106.499,40.918,32.225 +2020-11-19 02:45:00,72.0,108.12299999999999,40.918,32.225 +2020-11-19 03:00:00,71.53,110.661,40.411,32.225 +2020-11-19 03:15:00,71.33,111.44,40.411,32.225 +2020-11-19 03:30:00,71.54,113.429,40.411,32.225 +2020-11-19 03:45:00,73.18,113.98200000000001,40.411,32.225 +2020-11-19 04:00:00,74.7,127.82600000000001,41.246,32.225 +2020-11-19 04:15:00,75.98,140.563,41.246,32.225 +2020-11-19 04:30:00,77.0,141.905,41.246,32.225 +2020-11-19 04:45:00,79.12,143.909,41.246,32.225 +2020-11-19 05:00:00,84.14,178.382,44.533,32.225 +2020-11-19 05:15:00,87.47,208.544,44.533,32.225 +2020-11-19 05:30:00,90.75,204.02200000000002,44.533,32.225 +2020-11-19 05:45:00,96.44,194.972,44.533,32.225 +2020-11-19 06:00:00,104.01,190.593,55.005,32.225 +2020-11-19 06:15:00,109.76,195.706,55.005,32.225 +2020-11-19 06:30:00,116.54,197.018,55.005,32.225 +2020-11-19 06:45:00,120.52,198.15200000000002,55.005,32.225 +2020-11-19 07:00:00,124.39,199.429,64.597,32.225 +2020-11-19 07:15:00,126.36,203.41299999999998,64.597,32.225 +2020-11-19 07:30:00,127.26,204.91,64.597,32.225 +2020-11-19 07:45:00,130.79,205.38299999999998,64.597,32.225 +2020-11-19 08:00:00,133.32,204.34599999999998,71.71600000000001,32.225 +2020-11-19 08:15:00,133.27,203.889,71.71600000000001,32.225 +2020-11-19 08:30:00,133.0,201.88400000000001,71.71600000000001,32.225 +2020-11-19 08:45:00,133.02,198.83599999999998,71.71600000000001,32.225 +2020-11-19 09:00:00,137.0,193.114,66.51899999999999,32.225 +2020-11-19 09:15:00,138.81,189.428,66.51899999999999,32.225 +2020-11-19 09:30:00,139.87,187.102,66.51899999999999,32.225 +2020-11-19 09:45:00,136.56,184.975,66.51899999999999,32.225 +2020-11-19 10:00:00,136.84,182.19400000000002,63.04,32.225 +2020-11-19 10:15:00,136.24,178.824,63.04,32.225 +2020-11-19 10:30:00,136.58,176.892,63.04,32.225 +2020-11-19 10:45:00,138.2,175.81799999999998,63.04,32.225 +2020-11-19 11:00:00,143.48,174.016,60.998000000000005,32.225 +2020-11-19 11:15:00,145.08,173.512,60.998000000000005,32.225 +2020-11-19 11:30:00,141.99,172.93099999999998,60.998000000000005,32.225 +2020-11-19 11:45:00,139.15,172.388,60.998000000000005,32.225 +2020-11-19 12:00:00,137.73,166.959,58.27,32.225 +2020-11-19 12:15:00,136.88,165.732,58.27,32.225 +2020-11-19 12:30:00,137.43,165.851,58.27,32.225 +2020-11-19 12:45:00,136.49,166.507,58.27,32.225 +2020-11-19 13:00:00,134.8,165.90599999999998,57.196000000000005,32.225 +2020-11-19 13:15:00,134.79,165.35,57.196000000000005,32.225 +2020-11-19 13:30:00,132.52,164.889,57.196000000000005,32.225 +2020-11-19 13:45:00,131.77,164.835,57.196000000000005,32.225 +2020-11-19 14:00:00,133.0,164.542,57.38399999999999,32.225 +2020-11-19 14:15:00,133.06,164.49,57.38399999999999,32.225 +2020-11-19 14:30:00,131.21,164.235,57.38399999999999,32.225 +2020-11-19 14:45:00,131.57,164.03900000000002,57.38399999999999,32.225 +2020-11-19 15:00:00,132.6,164.05700000000002,58.647,32.225 +2020-11-19 15:15:00,131.83,164.451,58.647,32.225 +2020-11-19 15:30:00,132.4,165.30900000000003,58.647,32.225 +2020-11-19 15:45:00,133.34,166.02900000000002,58.647,32.225 +2020-11-19 16:00:00,136.61,169.5,60.083999999999996,32.225 +2020-11-19 16:15:00,135.17,170.892,60.083999999999996,32.225 +2020-11-19 16:30:00,137.53,172.18599999999998,60.083999999999996,32.225 +2020-11-19 16:45:00,137.22,172.18200000000002,60.083999999999996,32.225 +2020-11-19 17:00:00,138.52,175.574,65.85600000000001,32.225 +2020-11-19 17:15:00,138.89,175.96900000000002,65.85600000000001,32.225 +2020-11-19 17:30:00,140.31,176.305,65.85600000000001,32.225 +2020-11-19 17:45:00,138.58,175.81,65.85600000000001,32.225 +2020-11-19 18:00:00,136.88,177.47400000000002,69.855,32.225 +2020-11-19 18:15:00,135.08,176.61,69.855,32.225 +2020-11-19 18:30:00,134.94,174.812,69.855,32.225 +2020-11-19 18:45:00,135.05,175.66299999999998,69.855,32.225 +2020-11-19 19:00:00,132.41,177.15200000000002,74.015,32.225 +2020-11-19 19:15:00,129.97,174.18200000000002,74.015,32.225 +2020-11-19 19:30:00,128.17,172.87900000000002,74.015,32.225 +2020-11-19 19:45:00,126.77,169.799,74.015,32.225 +2020-11-19 20:00:00,121.56,166.00799999999998,65.316,32.225 +2020-11-19 20:15:00,116.74,161.10299999999998,65.316,32.225 +2020-11-19 20:30:00,113.64,158.468,65.316,32.225 +2020-11-19 20:45:00,117.52,155.156,65.316,32.225 +2020-11-19 21:00:00,113.74,153.093,58.403999999999996,32.225 +2020-11-19 21:15:00,113.82,151.528,58.403999999999996,32.225 +2020-11-19 21:30:00,108.94,149.357,58.403999999999996,32.225 +2020-11-19 21:45:00,105.09,147.631,58.403999999999996,32.225 +2020-11-19 22:00:00,99.66,141.107,54.092,32.225 +2020-11-19 22:15:00,102.85,136.05100000000002,54.092,32.225 +2020-11-19 22:30:00,100.78,120.677,54.092,32.225 +2020-11-19 22:45:00,99.23,112.73700000000001,54.092,32.225 +2020-11-19 23:00:00,91.21,108.055,48.18600000000001,32.225 +2020-11-19 23:15:00,88.51,105.96700000000001,48.18600000000001,32.225 +2020-11-19 23:30:00,92.47,106.329,48.18600000000001,32.225 +2020-11-19 23:45:00,91.07,106.109,48.18600000000001,32.225 +2020-11-20 00:00:00,87.75,99.23,45.18899999999999,32.225 +2020-11-20 00:15:00,81.05,99.459,45.18899999999999,32.225 +2020-11-20 00:30:00,85.97,99.78200000000001,45.18899999999999,32.225 +2020-11-20 00:45:00,85.53,100.905,45.18899999999999,32.225 +2020-11-20 01:00:00,83.27,101.958,43.256,32.225 +2020-11-20 01:15:00,80.61,103.631,43.256,32.225 +2020-11-20 01:30:00,82.75,103.89,43.256,32.225 +2020-11-20 01:45:00,83.03,104.514,43.256,32.225 +2020-11-20 02:00:00,78.63,105.882,42.312,32.225 +2020-11-20 02:15:00,77.14,106.729,42.312,32.225 +2020-11-20 02:30:00,77.65,107.031,42.312,32.225 +2020-11-20 02:45:00,82.12,108.568,42.312,32.225 +2020-11-20 03:00:00,83.0,110.37899999999999,41.833,32.225 +2020-11-20 03:15:00,81.44,111.771,41.833,32.225 +2020-11-20 03:30:00,78.04,113.704,41.833,32.225 +2020-11-20 03:45:00,81.83,114.689,41.833,32.225 +2020-11-20 04:00:00,85.52,128.736,42.732,32.225 +2020-11-20 04:15:00,86.71,140.985,42.732,32.225 +2020-11-20 04:30:00,82.74,142.685,42.732,32.225 +2020-11-20 04:45:00,85.24,143.602,42.732,32.225 +2020-11-20 05:00:00,87.34,176.903,46.254,32.225 +2020-11-20 05:15:00,95.1,208.50799999999998,46.254,32.225 +2020-11-20 05:30:00,100.38,204.958,46.254,32.225 +2020-11-20 05:45:00,104.5,195.774,46.254,32.225 +2020-11-20 06:00:00,105.66,191.827,56.76,32.225 +2020-11-20 06:15:00,110.32,195.717,56.76,32.225 +2020-11-20 06:30:00,114.29,196.326,56.76,32.225 +2020-11-20 06:45:00,119.4,198.864,56.76,32.225 +2020-11-20 07:00:00,126.14,199.542,66.029,32.225 +2020-11-20 07:15:00,127.73,204.579,66.029,32.225 +2020-11-20 07:30:00,127.93,205.59099999999998,66.029,32.225 +2020-11-20 07:45:00,128.79,205.271,66.029,32.225 +2020-11-20 08:00:00,131.62,203.435,73.128,32.225 +2020-11-20 08:15:00,131.62,202.748,73.128,32.225 +2020-11-20 08:30:00,131.81,201.59400000000002,73.128,32.225 +2020-11-20 08:45:00,132.09,197.12599999999998,73.128,32.225 +2020-11-20 09:00:00,132.08,191.378,68.23100000000001,32.225 +2020-11-20 09:15:00,132.7,188.517,68.23100000000001,32.225 +2020-11-20 09:30:00,130.51,185.7,68.23100000000001,32.225 +2020-11-20 09:45:00,130.25,183.52700000000002,68.23100000000001,32.225 +2020-11-20 10:00:00,127.82,179.71400000000003,64.733,32.225 +2020-11-20 10:15:00,126.85,176.882,64.733,32.225 +2020-11-20 10:30:00,125.62,174.955,64.733,32.225 +2020-11-20 10:45:00,125.03,173.46599999999998,64.733,32.225 +2020-11-20 11:00:00,126.8,171.67,62.0,32.225 +2020-11-20 11:15:00,126.91,170.18599999999998,62.0,32.225 +2020-11-20 11:30:00,124.26,171.074,62.0,32.225 +2020-11-20 11:45:00,122.85,170.40400000000002,62.0,32.225 +2020-11-20 12:00:00,121.61,165.986,57.876999999999995,32.225 +2020-11-20 12:15:00,121.21,162.827,57.876999999999995,32.225 +2020-11-20 12:30:00,121.35,163.131,57.876999999999995,32.225 +2020-11-20 12:45:00,122.36,164.113,57.876999999999995,32.225 +2020-11-20 13:00:00,119.72,164.393,55.585,32.225 +2020-11-20 13:15:00,118.77,164.592,55.585,32.225 +2020-11-20 13:30:00,118.89,164.27200000000002,55.585,32.225 +2020-11-20 13:45:00,117.82,164.204,55.585,32.225 +2020-11-20 14:00:00,119.04,162.74,54.5,32.225 +2020-11-20 14:15:00,118.42,162.615,54.5,32.225 +2020-11-20 14:30:00,118.28,163.089,54.5,32.225 +2020-11-20 14:45:00,119.7,163.05,54.5,32.225 +2020-11-20 15:00:00,119.39,162.639,55.131,32.225 +2020-11-20 15:15:00,120.02,162.61700000000002,55.131,32.225 +2020-11-20 15:30:00,120.71,162.084,55.131,32.225 +2020-11-20 15:45:00,122.19,163.062,55.131,32.225 +2020-11-20 16:00:00,124.56,165.365,56.8,32.225 +2020-11-20 16:15:00,127.81,167.11599999999999,56.8,32.225 +2020-11-20 16:30:00,132.47,168.468,56.8,32.225 +2020-11-20 16:45:00,134.58,168.27900000000002,56.8,32.225 +2020-11-20 17:00:00,135.53,172.09799999999998,63.428999999999995,32.225 +2020-11-20 17:15:00,135.41,172.127,63.428999999999995,32.225 +2020-11-20 17:30:00,135.86,172.21599999999998,63.428999999999995,32.225 +2020-11-20 17:45:00,135.03,171.495,63.428999999999995,32.225 +2020-11-20 18:00:00,133.0,173.782,67.915,32.225 +2020-11-20 18:15:00,132.6,172.382,67.915,32.225 +2020-11-20 18:30:00,132.16,170.923,67.915,32.225 +2020-11-20 18:45:00,133.09,171.83599999999998,67.915,32.225 +2020-11-20 19:00:00,129.97,174.26,69.428,32.225 +2020-11-20 19:15:00,127.95,172.574,69.428,32.225 +2020-11-20 19:30:00,125.39,170.898,69.428,32.225 +2020-11-20 19:45:00,122.11,167.21900000000002,69.428,32.225 +2020-11-20 20:00:00,117.37,163.468,60.56100000000001,32.225 +2020-11-20 20:15:00,113.89,158.686,60.56100000000001,32.225 +2020-11-20 20:30:00,110.74,155.905,60.56100000000001,32.225 +2020-11-20 20:45:00,109.52,152.961,60.56100000000001,32.225 +2020-11-20 21:00:00,106.9,151.546,55.18600000000001,32.225 +2020-11-20 21:15:00,105.87,150.619,55.18600000000001,32.225 +2020-11-20 21:30:00,106.31,148.468,55.18600000000001,32.225 +2020-11-20 21:45:00,103.66,147.249,55.18600000000001,32.225 +2020-11-20 22:00:00,92.56,141.57,51.433,32.225 +2020-11-20 22:15:00,90.8,136.34799999999998,51.433,32.225 +2020-11-20 22:30:00,84.72,127.389,51.433,32.225 +2020-11-20 22:45:00,86.32,122.689,51.433,32.225 +2020-11-20 23:00:00,87.62,117.875,46.201,32.225 +2020-11-20 23:15:00,86.93,113.809,46.201,32.225 +2020-11-20 23:30:00,82.7,112.605,46.201,32.225 +2020-11-20 23:45:00,78.82,111.764,46.201,32.225 +2020-11-21 00:00:00,79.22,97.382,42.576,32.047 +2020-11-21 00:15:00,78.96,93.809,42.576,32.047 +2020-11-21 00:30:00,73.94,95.18799999999999,42.576,32.047 +2020-11-21 00:45:00,70.84,96.777,42.576,32.047 +2020-11-21 01:00:00,71.67,98.469,39.34,32.047 +2020-11-21 01:15:00,74.73,99.413,39.34,32.047 +2020-11-21 01:30:00,74.22,99.094,39.34,32.047 +2020-11-21 01:45:00,71.33,99.74799999999999,39.34,32.047 +2020-11-21 02:00:00,71.97,101.554,37.582,32.047 +2020-11-21 02:15:00,73.0,101.944,37.582,32.047 +2020-11-21 02:30:00,71.44,101.148,37.582,32.047 +2020-11-21 02:45:00,69.8,102.914,37.582,32.047 +2020-11-21 03:00:00,70.35,105.005,36.523,32.047 +2020-11-21 03:15:00,71.12,105.26100000000001,36.523,32.047 +2020-11-21 03:30:00,68.69,105.90799999999999,36.523,32.047 +2020-11-21 03:45:00,65.43,107.25299999999999,36.523,32.047 +2020-11-21 04:00:00,63.81,117.439,36.347,32.047 +2020-11-21 04:15:00,64.05,127.375,36.347,32.047 +2020-11-21 04:30:00,65.13,126.90899999999999,36.347,32.047 +2020-11-21 04:45:00,65.92,127.463,36.347,32.047 +2020-11-21 05:00:00,66.64,145.74,36.407,32.047 +2020-11-21 05:15:00,66.61,159.19,36.407,32.047 +2020-11-21 05:30:00,66.7,156.248,36.407,32.047 +2020-11-21 05:45:00,68.33,152.523,36.407,32.047 +2020-11-21 06:00:00,70.41,166.495,38.228,32.047 +2020-11-21 06:15:00,71.52,185.717,38.228,32.047 +2020-11-21 06:30:00,73.49,181.28099999999998,38.228,32.047 +2020-11-21 06:45:00,75.76,175.635,38.228,32.047 +2020-11-21 07:00:00,79.69,172.74200000000002,41.905,32.047 +2020-11-21 07:15:00,81.93,176.484,41.905,32.047 +2020-11-21 07:30:00,82.59,180.097,41.905,32.047 +2020-11-21 07:45:00,86.02,183.34099999999998,41.905,32.047 +2020-11-21 08:00:00,89.63,184.977,46.051,32.047 +2020-11-21 08:15:00,90.83,187.362,46.051,32.047 +2020-11-21 08:30:00,93.03,187.613,46.051,32.047 +2020-11-21 08:45:00,96.12,185.99200000000002,46.051,32.047 +2020-11-21 09:00:00,100.89,182.237,46.683,32.047 +2020-11-21 09:15:00,98.64,180.114,46.683,32.047 +2020-11-21 09:30:00,100.25,178.16,46.683,32.047 +2020-11-21 09:45:00,100.87,176.032,46.683,32.047 +2020-11-21 10:00:00,100.67,172.535,44.425,32.047 +2020-11-21 10:15:00,99.74,169.87400000000002,44.425,32.047 +2020-11-21 10:30:00,99.32,168.007,44.425,32.047 +2020-11-21 10:45:00,99.73,167.572,44.425,32.047 +2020-11-21 11:00:00,100.59,165.90200000000002,42.148999999999994,32.047 +2020-11-21 11:15:00,101.81,163.94400000000002,42.148999999999994,32.047 +2020-11-21 11:30:00,104.19,163.908,42.148999999999994,32.047 +2020-11-21 11:45:00,103.4,162.507,42.148999999999994,32.047 +2020-11-21 12:00:00,103.31,157.35299999999998,39.683,32.047 +2020-11-21 12:15:00,103.72,154.89600000000002,39.683,32.047 +2020-11-21 12:30:00,101.44,155.484,39.683,32.047 +2020-11-21 12:45:00,101.91,155.91899999999998,39.683,32.047 +2020-11-21 13:00:00,97.83,155.659,37.154,32.047 +2020-11-21 13:15:00,97.45,153.987,37.154,32.047 +2020-11-21 13:30:00,97.31,153.30200000000002,37.154,32.047 +2020-11-21 13:45:00,96.77,153.42,37.154,32.047 +2020-11-21 14:00:00,94.23,152.981,36.457,32.047 +2020-11-21 14:15:00,92.82,152.19899999999998,36.457,32.047 +2020-11-21 14:30:00,91.21,151.05100000000002,36.457,32.047 +2020-11-21 14:45:00,90.74,151.286,36.457,32.047 +2020-11-21 15:00:00,89.08,151.498,38.257,32.047 +2020-11-21 15:15:00,90.29,152.30200000000002,38.257,32.047 +2020-11-21 15:30:00,90.87,153.183,38.257,32.047 +2020-11-21 15:45:00,91.79,154.035,38.257,32.047 +2020-11-21 16:00:00,94.22,155.641,41.181000000000004,32.047 +2020-11-21 16:15:00,97.63,158.055,41.181000000000004,32.047 +2020-11-21 16:30:00,100.73,159.40200000000002,41.181000000000004,32.047 +2020-11-21 16:45:00,102.19,160.024,41.181000000000004,32.047 +2020-11-21 17:00:00,104.89,163.127,46.806000000000004,32.047 +2020-11-21 17:15:00,108.34,164.282,46.806000000000004,32.047 +2020-11-21 17:30:00,107.18,164.275,46.806000000000004,32.047 +2020-11-21 17:45:00,107.64,163.287,46.806000000000004,32.047 +2020-11-21 18:00:00,107.72,165.36599999999999,52.073,32.047 +2020-11-21 18:15:00,107.31,165.763,52.073,32.047 +2020-11-21 18:30:00,105.98,165.696,52.073,32.047 +2020-11-21 18:45:00,105.32,163.127,52.073,32.047 +2020-11-21 19:00:00,103.23,166.097,53.608000000000004,32.047 +2020-11-21 19:15:00,101.82,163.806,53.608000000000004,32.047 +2020-11-21 19:30:00,100.14,162.893,53.608000000000004,32.047 +2020-11-21 19:45:00,98.85,159.30200000000002,53.608000000000004,32.047 +2020-11-21 20:00:00,94.8,157.629,50.265,32.047 +2020-11-21 20:15:00,91.04,154.589,50.265,32.047 +2020-11-21 20:30:00,87.39,151.33,50.265,32.047 +2020-11-21 20:45:00,85.88,148.344,50.265,32.047 +2020-11-21 21:00:00,83.83,148.64700000000002,45.766000000000005,32.047 +2020-11-21 21:15:00,82.44,148.033,45.766000000000005,32.047 +2020-11-21 21:30:00,81.16,146.989,45.766000000000005,32.047 +2020-11-21 21:45:00,82.28,145.325,45.766000000000005,32.047 +2020-11-21 22:00:00,77.41,140.82399999999998,45.97,32.047 +2020-11-21 22:15:00,75.61,137.806,45.97,32.047 +2020-11-21 22:30:00,72.97,134.268,45.97,32.047 +2020-11-21 22:45:00,72.35,131.268,45.97,32.047 +2020-11-21 23:00:00,69.85,128.459,40.415,32.047 +2020-11-21 23:15:00,68.87,123.02,40.415,32.047 +2020-11-21 23:30:00,66.45,120.694,40.415,32.047 +2020-11-21 23:45:00,64.91,117.774,40.415,32.047 +2020-11-22 00:00:00,62.39,98.36399999999999,36.376,32.047 +2020-11-22 00:15:00,61.59,94.28299999999999,36.376,32.047 +2020-11-22 00:30:00,60.39,95.315,36.376,32.047 +2020-11-22 00:45:00,59.43,97.46,36.376,32.047 +2020-11-22 01:00:00,60.22,99.115,32.992,32.047 +2020-11-22 01:15:00,57.85,100.927,32.992,32.047 +2020-11-22 01:30:00,56.88,101.039,32.992,32.047 +2020-11-22 01:45:00,56.3,101.34299999999999,32.992,32.047 +2020-11-22 02:00:00,55.45,102.54299999999999,32.327,32.047 +2020-11-22 02:15:00,55.69,102.346,32.327,32.047 +2020-11-22 02:30:00,55.72,102.307,32.327,32.047 +2020-11-22 02:45:00,55.6,104.40899999999999,32.327,32.047 +2020-11-22 03:00:00,55.22,106.87299999999999,31.169,32.047 +2020-11-22 03:15:00,55.28,106.764,31.169,32.047 +2020-11-22 03:30:00,55.5,108.458,31.169,32.047 +2020-11-22 03:45:00,55.87,109.579,31.169,32.047 +2020-11-22 04:00:00,55.68,119.53,30.796,32.047 +2020-11-22 04:15:00,56.25,128.539,30.796,32.047 +2020-11-22 04:30:00,56.7,128.373,30.796,32.047 +2020-11-22 04:45:00,57.4,129.07399999999998,30.796,32.047 +2020-11-22 05:00:00,57.89,144.434,30.848000000000003,32.047 +2020-11-22 05:15:00,58.33,155.689,30.848000000000003,32.047 +2020-11-22 05:30:00,58.4,152.545,30.848000000000003,32.047 +2020-11-22 05:45:00,58.91,148.976,30.848000000000003,32.047 +2020-11-22 06:00:00,60.42,162.407,31.166,32.047 +2020-11-22 06:15:00,61.02,180.342,31.166,32.047 +2020-11-22 06:30:00,61.68,174.887,31.166,32.047 +2020-11-22 06:45:00,63.19,168.197,31.166,32.047 +2020-11-22 07:00:00,66.58,167.417,33.527,32.047 +2020-11-22 07:15:00,67.92,170.138,33.527,32.047 +2020-11-22 07:30:00,68.4,172.968,33.527,32.047 +2020-11-22 07:45:00,70.27,175.56599999999997,33.527,32.047 +2020-11-22 08:00:00,73.27,178.896,36.616,32.047 +2020-11-22 08:15:00,74.74,181.408,36.616,32.047 +2020-11-22 08:30:00,76.33,183.252,36.616,32.047 +2020-11-22 08:45:00,76.44,183.30700000000002,36.616,32.047 +2020-11-22 09:00:00,77.09,179.15400000000002,37.857,32.047 +2020-11-22 09:15:00,78.76,177.422,37.857,32.047 +2020-11-22 09:30:00,79.57,175.412,37.857,32.047 +2020-11-22 09:45:00,79.12,173.343,37.857,32.047 +2020-11-22 10:00:00,78.1,172.08700000000002,36.319,32.047 +2020-11-22 10:15:00,80.56,169.87400000000002,36.319,32.047 +2020-11-22 10:30:00,83.15,168.53900000000002,36.319,32.047 +2020-11-22 10:45:00,84.7,166.643,36.319,32.047 +2020-11-22 11:00:00,85.59,165.68099999999998,37.236999999999995,32.047 +2020-11-22 11:15:00,87.48,163.744,37.236999999999995,32.047 +2020-11-22 11:30:00,89.9,163.045,37.236999999999995,32.047 +2020-11-22 11:45:00,94.79,162.2,37.236999999999995,32.047 +2020-11-22 12:00:00,90.94,156.726,34.871,32.047 +2020-11-22 12:15:00,89.01,155.812,34.871,32.047 +2020-11-22 12:30:00,86.85,155.20600000000002,34.871,32.047 +2020-11-22 12:45:00,85.53,154.701,34.871,32.047 +2020-11-22 13:00:00,81.64,153.731,29.738000000000003,32.047 +2020-11-22 13:15:00,78.73,154.57,29.738000000000003,32.047 +2020-11-22 13:30:00,78.52,153.511,29.738000000000003,32.047 +2020-11-22 13:45:00,76.68,153.241,29.738000000000003,32.047 +2020-11-22 14:00:00,76.68,153.178,27.333000000000002,32.047 +2020-11-22 14:15:00,75.93,153.518,27.333000000000002,32.047 +2020-11-22 14:30:00,75.96,153.26,27.333000000000002,32.047 +2020-11-22 14:45:00,75.98,152.963,27.333000000000002,32.047 +2020-11-22 15:00:00,80.48,151.885,28.232,32.047 +2020-11-22 15:15:00,77.4,153.216,28.232,32.047 +2020-11-22 15:30:00,79.06,154.611,28.232,32.047 +2020-11-22 15:45:00,81.3,156.118,28.232,32.047 +2020-11-22 16:00:00,84.0,159.056,32.815,32.047 +2020-11-22 16:15:00,86.71,160.746,32.815,32.047 +2020-11-22 16:30:00,91.42,162.487,32.815,32.047 +2020-11-22 16:45:00,94.2,163.279,32.815,32.047 +2020-11-22 17:00:00,97.01,166.412,43.068999999999996,32.047 +2020-11-22 17:15:00,99.13,167.55599999999998,43.068999999999996,32.047 +2020-11-22 17:30:00,106.82,167.94099999999997,43.068999999999996,32.047 +2020-11-22 17:45:00,108.43,168.998,43.068999999999996,32.047 +2020-11-22 18:00:00,106.98,170.72,50.498999999999995,32.047 +2020-11-22 18:15:00,102.7,172.16400000000002,50.498999999999995,32.047 +2020-11-22 18:30:00,103.87,170.24900000000002,50.498999999999995,32.047 +2020-11-22 18:45:00,101.15,169.312,50.498999999999995,32.047 +2020-11-22 19:00:00,101.38,172.34799999999998,53.481,32.047 +2020-11-22 19:15:00,97.91,170.38400000000001,53.481,32.047 +2020-11-22 19:30:00,97.08,169.269,53.481,32.047 +2020-11-22 19:45:00,94.87,166.83,53.481,32.047 +2020-11-22 20:00:00,100.26,165.14700000000002,51.687,32.047 +2020-11-22 20:15:00,100.16,162.92600000000002,51.687,32.047 +2020-11-22 20:30:00,97.79,160.846,51.687,32.047 +2020-11-22 20:45:00,89.12,156.549,51.687,32.047 +2020-11-22 21:00:00,89.33,154.567,47.674,32.047 +2020-11-22 21:15:00,90.04,153.346,47.674,32.047 +2020-11-22 21:30:00,87.6,152.453,47.674,32.047 +2020-11-22 21:45:00,87.83,150.965,47.674,32.047 +2020-11-22 22:00:00,86.54,145.78,48.178000000000004,32.047 +2020-11-22 22:15:00,87.22,141.814,48.178000000000004,32.047 +2020-11-22 22:30:00,85.35,135.501,48.178000000000004,32.047 +2020-11-22 22:45:00,86.7,131.559,48.178000000000004,32.047 +2020-11-22 23:00:00,87.2,126.255,42.553999999999995,32.047 +2020-11-22 23:15:00,88.4,122.626,42.553999999999995,32.047 +2020-11-22 23:30:00,84.48,120.92200000000001,42.553999999999995,32.047 +2020-11-22 23:45:00,81.66,118.785,42.553999999999995,32.047 +2020-11-23 00:00:00,81.01,102.585,37.177,32.225 +2020-11-23 00:15:00,80.79,101.152,37.177,32.225 +2020-11-23 00:30:00,79.11,102.22200000000001,37.177,32.225 +2020-11-23 00:45:00,74.45,103.821,37.177,32.225 +2020-11-23 01:00:00,77.84,105.58,35.358000000000004,32.225 +2020-11-23 01:15:00,77.83,106.954,35.358000000000004,32.225 +2020-11-23 01:30:00,77.76,107.191,35.358000000000004,32.225 +2020-11-23 01:45:00,72.18,107.56299999999999,35.358000000000004,32.225 +2020-11-23 02:00:00,77.53,108.836,35.03,32.225 +2020-11-23 02:15:00,77.99,109.693,35.03,32.225 +2020-11-23 02:30:00,77.7,109.962,35.03,32.225 +2020-11-23 02:45:00,71.51,111.509,35.03,32.225 +2020-11-23 03:00:00,77.86,115.11200000000001,34.394,32.225 +2020-11-23 03:15:00,77.98,116.559,34.394,32.225 +2020-11-23 03:30:00,79.9,118.154,34.394,32.225 +2020-11-23 03:45:00,76.4,118.714,34.394,32.225 +2020-11-23 04:00:00,75.65,132.888,34.421,32.225 +2020-11-23 04:15:00,75.21,145.955,34.421,32.225 +2020-11-23 04:30:00,80.58,147.53,34.421,32.225 +2020-11-23 04:45:00,86.22,148.444,34.421,32.225 +2020-11-23 05:00:00,89.91,178.38099999999997,39.435,32.225 +2020-11-23 05:15:00,93.08,208.532,39.435,32.225 +2020-11-23 05:30:00,91.38,205.338,39.435,32.225 +2020-11-23 05:45:00,101.04,196.43599999999998,39.435,32.225 +2020-11-23 06:00:00,111.88,192.929,55.685,32.225 +2020-11-23 06:15:00,119.66,196.643,55.685,32.225 +2020-11-23 06:30:00,117.56,198.74,55.685,32.225 +2020-11-23 06:45:00,126.52,200.50099999999998,55.685,32.225 +2020-11-23 07:00:00,127.0,201.90599999999998,66.837,32.225 +2020-11-23 07:15:00,130.33,206.11599999999999,66.837,32.225 +2020-11-23 07:30:00,133.16,208.145,66.837,32.225 +2020-11-23 07:45:00,134.79,208.628,66.837,32.225 +2020-11-23 08:00:00,135.1,207.56599999999997,72.217,32.225 +2020-11-23 08:15:00,134.95,208.025,72.217,32.225 +2020-11-23 08:30:00,135.26,206.227,72.217,32.225 +2020-11-23 08:45:00,135.33,203.44,72.217,32.225 +2020-11-23 09:00:00,137.24,198.29,66.117,32.225 +2020-11-23 09:15:00,141.14,193.26,66.117,32.225 +2020-11-23 09:30:00,142.69,190.23,66.117,32.225 +2020-11-23 09:45:00,142.86,188.003,66.117,32.225 +2020-11-23 10:00:00,143.04,185.90900000000002,62.1,32.225 +2020-11-23 10:15:00,143.04,183.359,62.1,32.225 +2020-11-23 10:30:00,143.62,181.167,62.1,32.225 +2020-11-23 10:45:00,139.51,179.63299999999998,62.1,32.225 +2020-11-23 11:00:00,144.08,176.495,60.021,32.225 +2020-11-23 11:15:00,159.0,176.148,60.021,32.225 +2020-11-23 11:30:00,160.47,176.75799999999998,60.021,32.225 +2020-11-23 11:45:00,160.25,175.63299999999998,60.021,32.225 +2020-11-23 12:00:00,147.32,171.385,56.75899999999999,32.225 +2020-11-23 12:15:00,145.08,170.497,56.75899999999999,32.225 +2020-11-23 12:30:00,148.09,169.957,56.75899999999999,32.225 +2020-11-23 12:45:00,145.6,170.778,56.75899999999999,32.225 +2020-11-23 13:00:00,143.46,170.442,56.04600000000001,32.225 +2020-11-23 13:15:00,142.41,169.93900000000002,56.04600000000001,32.225 +2020-11-23 13:30:00,140.37,168.447,56.04600000000001,32.225 +2020-11-23 13:45:00,135.08,168.343,56.04600000000001,32.225 +2020-11-23 14:00:00,135.61,167.577,55.475,32.225 +2020-11-23 14:15:00,135.86,167.467,55.475,32.225 +2020-11-23 14:30:00,135.91,166.745,55.475,32.225 +2020-11-23 14:45:00,136.54,166.763,55.475,32.225 +2020-11-23 15:00:00,136.12,167.21400000000003,57.048,32.225 +2020-11-23 15:15:00,135.68,167.205,57.048,32.225 +2020-11-23 15:30:00,135.08,168.033,57.048,32.225 +2020-11-23 15:45:00,135.81,169.081,57.048,32.225 +2020-11-23 16:00:00,136.9,172.327,59.06,32.225 +2020-11-23 16:15:00,137.68,173.388,59.06,32.225 +2020-11-23 16:30:00,139.13,174.18400000000003,59.06,32.225 +2020-11-23 16:45:00,141.15,173.987,59.06,32.225 +2020-11-23 17:00:00,155.46,176.75400000000002,65.419,32.225 +2020-11-23 17:15:00,157.73,177.153,65.419,32.225 +2020-11-23 17:30:00,158.42,177.005,65.419,32.225 +2020-11-23 17:45:00,145.53,176.706,65.419,32.225 +2020-11-23 18:00:00,137.98,178.655,69.345,32.225 +2020-11-23 18:15:00,139.34,177.84,69.345,32.225 +2020-11-23 18:30:00,138.47,176.386,69.345,32.225 +2020-11-23 18:45:00,139.68,176.57299999999998,69.345,32.225 +2020-11-23 19:00:00,137.89,178.16,73.825,32.225 +2020-11-23 19:15:00,134.19,175.365,73.825,32.225 +2020-11-23 19:30:00,131.82,174.611,73.825,32.225 +2020-11-23 19:45:00,129.59,171.31599999999997,73.825,32.225 +2020-11-23 20:00:00,125.6,167.36900000000003,64.027,32.225 +2020-11-23 20:15:00,119.28,163.202,64.027,32.225 +2020-11-23 20:30:00,122.12,159.554,64.027,32.225 +2020-11-23 20:45:00,121.81,156.717,64.027,32.225 +2020-11-23 21:00:00,118.81,155.09,57.952,32.225 +2020-11-23 21:15:00,109.73,152.90200000000002,57.952,32.225 +2020-11-23 21:30:00,108.05,151.36,57.952,32.225 +2020-11-23 21:45:00,106.57,149.395,57.952,32.225 +2020-11-23 22:00:00,105.12,141.372,53.031000000000006,32.225 +2020-11-23 22:15:00,106.96,136.566,53.031000000000006,32.225 +2020-11-23 22:30:00,104.93,121.29,53.031000000000006,32.225 +2020-11-23 22:45:00,101.2,113.13799999999999,53.031000000000006,32.225 +2020-11-23 23:00:00,92.0,108.491,45.085,32.225 +2020-11-23 23:15:00,94.85,106.9,45.085,32.225 +2020-11-23 23:30:00,94.19,107.60799999999999,45.085,32.225 +2020-11-23 23:45:00,91.56,107.67299999999999,45.085,32.225 +2020-11-24 00:00:00,86.66,101.96,42.843,32.225 +2020-11-24 00:15:00,81.99,101.87299999999999,42.843,32.225 +2020-11-24 00:30:00,80.81,102.262,42.843,32.225 +2020-11-24 00:45:00,83.16,103.147,42.843,32.225 +2020-11-24 01:00:00,86.21,104.68,41.542,32.225 +2020-11-24 01:15:00,86.93,105.70100000000001,41.542,32.225 +2020-11-24 01:30:00,83.03,106.073,41.542,32.225 +2020-11-24 01:45:00,80.91,106.603,41.542,32.225 +2020-11-24 02:00:00,80.28,107.804,40.19,32.225 +2020-11-24 02:15:00,85.04,108.777,40.19,32.225 +2020-11-24 02:30:00,80.94,108.46600000000001,40.19,32.225 +2020-11-24 02:45:00,83.11,110.08,40.19,32.225 +2020-11-24 03:00:00,82.33,112.54700000000001,39.626,32.225 +2020-11-24 03:15:00,81.11,113.471,39.626,32.225 +2020-11-24 03:30:00,80.6,115.473,39.626,32.225 +2020-11-24 03:45:00,81.65,115.988,39.626,32.225 +2020-11-24 04:00:00,89.16,129.751,40.196999999999996,32.225 +2020-11-24 04:15:00,89.98,142.534,40.196999999999996,32.225 +2020-11-24 04:30:00,91.48,143.811,40.196999999999996,32.225 +2020-11-24 04:45:00,88.32,145.847,40.196999999999996,32.225 +2020-11-24 05:00:00,95.04,180.31,43.378,32.225 +2020-11-24 05:15:00,100.05,210.38299999999998,43.378,32.225 +2020-11-24 05:30:00,105.49,205.94099999999997,43.378,32.225 +2020-11-24 05:45:00,104.35,196.915,43.378,32.225 +2020-11-24 06:00:00,111.75,192.609,55.691,32.225 +2020-11-24 06:15:00,115.57,197.745,55.691,32.225 +2020-11-24 06:30:00,120.7,199.278,55.691,32.225 +2020-11-24 06:45:00,125.63,200.59400000000002,55.691,32.225 +2020-11-24 07:00:00,131.53,201.87,65.567,32.225 +2020-11-24 07:15:00,134.38,205.908,65.567,32.225 +2020-11-24 07:30:00,135.59,207.489,65.567,32.225 +2020-11-24 07:45:00,136.05,207.987,65.567,32.225 +2020-11-24 08:00:00,133.21,207.02200000000002,73.001,32.225 +2020-11-24 08:15:00,132.47,206.514,73.001,32.225 +2020-11-24 08:30:00,131.85,204.61,73.001,32.225 +2020-11-24 08:45:00,132.0,201.40599999999998,73.001,32.225 +2020-11-24 09:00:00,133.22,195.56900000000002,67.08800000000001,32.225 +2020-11-24 09:15:00,134.13,191.907,67.08800000000001,32.225 +2020-11-24 09:30:00,136.8,189.577,67.08800000000001,32.225 +2020-11-24 09:45:00,138.03,187.364,67.08800000000001,32.225 +2020-11-24 10:00:00,143.53,184.52599999999998,62.803000000000004,32.225 +2020-11-24 10:15:00,143.26,181.0,62.803000000000004,32.225 +2020-11-24 10:30:00,144.11,178.94400000000002,62.803000000000004,32.225 +2020-11-24 10:45:00,142.0,177.80599999999998,62.803000000000004,32.225 +2020-11-24 11:00:00,145.32,175.952,60.155,32.225 +2020-11-24 11:15:00,159.14,175.36,60.155,32.225 +2020-11-24 11:30:00,159.74,174.77200000000002,60.155,32.225 +2020-11-24 11:45:00,158.94,174.179,60.155,32.225 +2020-11-24 12:00:00,144.91,168.69400000000002,56.845,32.225 +2020-11-24 12:15:00,143.79,167.502,56.845,32.225 +2020-11-24 12:30:00,144.9,167.753,56.845,32.225 +2020-11-24 12:45:00,148.31,168.429,56.845,32.225 +2020-11-24 13:00:00,143.29,167.641,56.163000000000004,32.225 +2020-11-24 13:15:00,142.71,167.11900000000003,56.163000000000004,32.225 +2020-11-24 13:30:00,138.34,166.644,56.163000000000004,32.225 +2020-11-24 13:45:00,137.62,166.546,56.163000000000004,32.225 +2020-11-24 14:00:00,137.28,166.058,55.934,32.225 +2020-11-24 14:15:00,136.5,166.065,55.934,32.225 +2020-11-24 14:30:00,136.48,165.97799999999998,55.934,32.225 +2020-11-24 14:45:00,135.39,165.81,55.934,32.225 +2020-11-24 15:00:00,134.87,165.862,57.43899999999999,32.225 +2020-11-24 15:15:00,134.85,166.291,57.43899999999999,32.225 +2020-11-24 15:30:00,135.23,167.31599999999997,57.43899999999999,32.225 +2020-11-24 15:45:00,134.55,168.06900000000002,57.43899999999999,32.225 +2020-11-24 16:00:00,136.4,171.49900000000002,59.968999999999994,32.225 +2020-11-24 16:15:00,141.88,173.015,59.968999999999994,32.225 +2020-11-24 16:30:00,145.09,174.326,59.968999999999994,32.225 +2020-11-24 16:45:00,146.27,174.53099999999998,59.968999999999994,32.225 +2020-11-24 17:00:00,147.1,177.77900000000002,67.428,32.225 +2020-11-24 17:15:00,160.92,178.29,67.428,32.225 +2020-11-24 17:30:00,161.04,178.67,67.428,32.225 +2020-11-24 17:45:00,161.02,178.213,67.428,32.225 +2020-11-24 18:00:00,147.32,179.94099999999997,71.533,32.225 +2020-11-24 18:15:00,138.99,178.87099999999998,71.533,32.225 +2020-11-24 18:30:00,137.9,177.114,71.533,32.225 +2020-11-24 18:45:00,141.41,177.975,71.533,32.225 +2020-11-24 19:00:00,139.01,179.456,73.32300000000001,32.225 +2020-11-24 19:15:00,136.46,176.43,73.32300000000001,32.225 +2020-11-24 19:30:00,133.33,175.03799999999998,73.32300000000001,32.225 +2020-11-24 19:45:00,132.31,171.794,73.32300000000001,32.225 +2020-11-24 20:00:00,128.54,168.054,64.166,32.225 +2020-11-24 20:15:00,122.92,163.094,64.166,32.225 +2020-11-24 20:30:00,122.74,160.308,64.166,32.225 +2020-11-24 20:45:00,124.43,157.046,64.166,32.225 +2020-11-24 21:00:00,120.21,154.929,57.891999999999996,32.225 +2020-11-24 21:15:00,116.54,153.293,57.891999999999996,32.225 +2020-11-24 21:30:00,111.79,151.145,57.891999999999996,32.225 +2020-11-24 21:45:00,110.12,149.408,57.891999999999996,32.225 +2020-11-24 22:00:00,104.81,142.917,53.242,32.225 +2020-11-24 22:15:00,106.93,137.826,53.242,32.225 +2020-11-24 22:30:00,109.19,122.727,53.242,32.225 +2020-11-24 22:45:00,106.58,114.824,53.242,32.225 +2020-11-24 23:00:00,99.59,110.068,46.665,32.225 +2020-11-24 23:15:00,93.79,107.912,46.665,32.225 +2020-11-24 23:30:00,97.04,108.311,46.665,32.225 +2020-11-24 23:45:00,95.69,108.001,46.665,32.225 +2020-11-25 00:00:00,94.25,102.323,43.16,32.225 +2020-11-25 00:15:00,89.05,102.209,43.16,32.225 +2020-11-25 00:30:00,88.28,102.59700000000001,43.16,32.225 +2020-11-25 00:45:00,90.4,103.465,43.16,32.225 +2020-11-25 01:00:00,88.67,105.03299999999999,40.972,32.225 +2020-11-25 01:15:00,83.25,106.059,40.972,32.225 +2020-11-25 01:30:00,82.33,106.444,40.972,32.225 +2020-11-25 01:45:00,79.08,106.96,40.972,32.225 +2020-11-25 02:00:00,81.52,108.182,39.749,32.225 +2020-11-25 02:15:00,83.98,109.15799999999999,39.749,32.225 +2020-11-25 02:30:00,87.31,108.84299999999999,39.749,32.225 +2020-11-25 02:45:00,88.71,110.45700000000001,39.749,32.225 +2020-11-25 03:00:00,85.46,112.91,39.422,32.225 +2020-11-25 03:15:00,84.79,113.861,39.422,32.225 +2020-11-25 03:30:00,89.21,115.866,39.422,32.225 +2020-11-25 03:45:00,90.74,116.37200000000001,39.422,32.225 +2020-11-25 04:00:00,92.44,130.12,40.505,32.225 +2020-11-25 04:15:00,89.59,142.911,40.505,32.225 +2020-11-25 04:30:00,92.56,144.178,40.505,32.225 +2020-11-25 04:45:00,96.12,146.219,40.505,32.225 +2020-11-25 05:00:00,97.39,180.678,43.397,32.225 +2020-11-25 05:15:00,101.36,210.732,43.397,32.225 +2020-11-25 05:30:00,106.35,206.30599999999998,43.397,32.225 +2020-11-25 05:45:00,111.83,197.285,43.397,32.225 +2020-11-25 06:00:00,112.45,192.995,55.218,32.225 +2020-11-25 06:15:00,119.29,198.136,55.218,32.225 +2020-11-25 06:30:00,122.62,199.71200000000002,55.218,32.225 +2020-11-25 06:45:00,127.32,201.063,55.218,32.225 +2020-11-25 07:00:00,129.42,202.33900000000003,67.39,32.225 +2020-11-25 07:15:00,133.53,206.388,67.39,32.225 +2020-11-25 07:30:00,133.46,207.983,67.39,32.225 +2020-11-25 07:45:00,133.22,208.485,67.39,32.225 +2020-11-25 08:00:00,134.72,207.533,74.345,32.225 +2020-11-25 08:15:00,134.93,207.014,74.345,32.225 +2020-11-25 08:30:00,133.18,205.128,74.345,32.225 +2020-11-25 08:45:00,136.06,201.892,74.345,32.225 +2020-11-25 09:00:00,136.12,196.03400000000002,69.336,32.225 +2020-11-25 09:15:00,136.89,192.37599999999998,69.336,32.225 +2020-11-25 09:30:00,137.83,190.047,69.336,32.225 +2020-11-25 09:45:00,136.21,187.817,69.336,32.225 +2020-11-25 10:00:00,134.1,184.968,64.291,32.225 +2020-11-25 10:15:00,134.21,181.41099999999997,64.291,32.225 +2020-11-25 10:30:00,132.81,179.333,64.291,32.225 +2020-11-25 10:45:00,132.27,178.18200000000002,64.291,32.225 +2020-11-25 11:00:00,130.7,176.317,62.20399999999999,32.225 +2020-11-25 11:15:00,130.65,175.709,62.20399999999999,32.225 +2020-11-25 11:30:00,129.63,175.11900000000003,62.20399999999999,32.225 +2020-11-25 11:45:00,128.59,174.517,62.20399999999999,32.225 +2020-11-25 12:00:00,127.98,169.023,59.042,32.225 +2020-11-25 12:15:00,127.46,167.83700000000002,59.042,32.225 +2020-11-25 12:30:00,129.41,168.113,59.042,32.225 +2020-11-25 12:45:00,127.0,168.794,59.042,32.225 +2020-11-25 13:00:00,126.51,167.97,57.907,32.225 +2020-11-25 13:15:00,126.11,167.454,57.907,32.225 +2020-11-25 13:30:00,126.17,166.975,57.907,32.225 +2020-11-25 13:45:00,126.16,166.868,57.907,32.225 +2020-11-25 14:00:00,128.27,166.345,58.358000000000004,32.225 +2020-11-25 14:15:00,129.25,166.362,58.358000000000004,32.225 +2020-11-25 14:30:00,129.64,166.30700000000002,58.358000000000004,32.225 +2020-11-25 14:45:00,130.02,166.146,58.358000000000004,32.225 +2020-11-25 15:00:00,131.43,166.205,59.348,32.225 +2020-11-25 15:15:00,133.51,166.64,59.348,32.225 +2020-11-25 15:30:00,133.44,167.696,59.348,32.225 +2020-11-25 15:45:00,134.78,168.456,59.348,32.225 +2020-11-25 16:00:00,136.6,171.878,61.413999999999994,32.225 +2020-11-25 16:15:00,139.28,173.418,61.413999999999994,32.225 +2020-11-25 16:30:00,143.76,174.731,61.413999999999994,32.225 +2020-11-25 16:45:00,145.53,174.976,61.413999999999994,32.225 +2020-11-25 17:00:00,146.91,178.197,67.107,32.225 +2020-11-25 17:15:00,147.41,178.731,67.107,32.225 +2020-11-25 17:30:00,147.84,179.12099999999998,67.107,32.225 +2020-11-25 17:45:00,147.01,178.673,67.107,32.225 +2020-11-25 18:00:00,144.4,180.41400000000002,71.92,32.225 +2020-11-25 18:15:00,143.14,179.304,71.92,32.225 +2020-11-25 18:30:00,141.88,177.55700000000002,71.92,32.225 +2020-11-25 18:45:00,142.19,178.42,71.92,32.225 +2020-11-25 19:00:00,141.09,179.89700000000002,75.09,32.225 +2020-11-25 19:15:00,137.11,176.861,75.09,32.225 +2020-11-25 19:30:00,134.84,175.452,75.09,32.225 +2020-11-25 19:45:00,133.69,172.17700000000002,75.09,32.225 +2020-11-25 20:00:00,130.33,168.446,65.977,32.225 +2020-11-25 20:15:00,122.19,163.476,65.977,32.225 +2020-11-25 20:30:00,123.02,160.661,65.977,32.225 +2020-11-25 20:45:00,124.89,157.409,65.977,32.225 +2020-11-25 21:00:00,121.48,155.282,58.798,32.225 +2020-11-25 21:15:00,116.62,153.631,58.798,32.225 +2020-11-25 21:30:00,108.88,151.487,58.798,32.225 +2020-11-25 21:45:00,109.31,149.749,58.798,32.225 +2020-11-25 22:00:00,105.77,143.263,54.486000000000004,32.225 +2020-11-25 22:15:00,109.26,138.168,54.486000000000004,32.225 +2020-11-25 22:30:00,106.25,123.12200000000001,54.486000000000004,32.225 +2020-11-25 22:45:00,104.95,115.227,54.486000000000004,32.225 +2020-11-25 23:00:00,95.13,110.454,47.783,32.225 +2020-11-25 23:15:00,98.94,108.286,47.783,32.225 +2020-11-25 23:30:00,97.56,108.693,47.783,32.225 +2020-11-25 23:45:00,94.42,108.366,47.783,32.225 +2020-11-26 00:00:00,86.96,102.682,43.88,32.225 +2020-11-26 00:15:00,83.82,102.54,43.88,32.225 +2020-11-26 00:30:00,85.5,102.928,43.88,32.225 +2020-11-26 00:45:00,88.72,103.77799999999999,43.88,32.225 +2020-11-26 01:00:00,88.89,105.38,42.242,32.225 +2020-11-26 01:15:00,85.06,106.413,42.242,32.225 +2020-11-26 01:30:00,83.57,106.809,42.242,32.225 +2020-11-26 01:45:00,85.67,107.31299999999999,42.242,32.225 +2020-11-26 02:00:00,86.12,108.552,40.918,32.225 +2020-11-26 02:15:00,83.43,109.53299999999999,40.918,32.225 +2020-11-26 02:30:00,83.05,109.21600000000001,40.918,32.225 +2020-11-26 02:45:00,85.0,110.82700000000001,40.918,32.225 +2020-11-26 03:00:00,85.05,113.26700000000001,40.411,32.225 +2020-11-26 03:15:00,83.54,114.24600000000001,40.411,32.225 +2020-11-26 03:30:00,84.59,116.25299999999999,40.411,32.225 +2020-11-26 03:45:00,88.11,116.75299999999999,40.411,32.225 +2020-11-26 04:00:00,89.54,130.483,41.246,32.225 +2020-11-26 04:15:00,86.6,143.284,41.246,32.225 +2020-11-26 04:30:00,88.88,144.537,41.246,32.225 +2020-11-26 04:45:00,93.86,146.585,41.246,32.225 +2020-11-26 05:00:00,97.17,181.04,44.533,32.225 +2020-11-26 05:15:00,98.12,211.076,44.533,32.225 +2020-11-26 05:30:00,99.16,206.66400000000002,44.533,32.225 +2020-11-26 05:45:00,105.0,197.649,44.533,32.225 +2020-11-26 06:00:00,117.69,193.375,55.005,32.225 +2020-11-26 06:15:00,124.77,198.52,55.005,32.225 +2020-11-26 06:30:00,126.33,200.138,55.005,32.225 +2020-11-26 06:45:00,125.77,201.525,55.005,32.225 +2020-11-26 07:00:00,130.95,202.803,64.597,32.225 +2020-11-26 07:15:00,134.46,206.86,64.597,32.225 +2020-11-26 07:30:00,133.78,208.47,64.597,32.225 +2020-11-26 07:45:00,135.55,208.97400000000002,64.597,32.225 +2020-11-26 08:00:00,136.97,208.035,71.71600000000001,32.225 +2020-11-26 08:15:00,136.62,207.50400000000002,71.71600000000001,32.225 +2020-11-26 08:30:00,135.96,205.636,71.71600000000001,32.225 +2020-11-26 08:45:00,134.99,202.37,71.71600000000001,32.225 +2020-11-26 09:00:00,134.73,196.488,66.51899999999999,32.225 +2020-11-26 09:15:00,134.01,192.83599999999998,66.51899999999999,32.225 +2020-11-26 09:30:00,134.51,190.507,66.51899999999999,32.225 +2020-11-26 09:45:00,133.76,188.261,66.51899999999999,32.225 +2020-11-26 10:00:00,133.11,185.40099999999998,63.04,32.225 +2020-11-26 10:15:00,133.63,181.81599999999997,63.04,32.225 +2020-11-26 10:30:00,132.09,179.71400000000003,63.04,32.225 +2020-11-26 10:45:00,131.91,178.551,63.04,32.225 +2020-11-26 11:00:00,130.31,176.675,60.998000000000005,32.225 +2020-11-26 11:15:00,129.82,176.05,60.998000000000005,32.225 +2020-11-26 11:30:00,129.38,175.459,60.998000000000005,32.225 +2020-11-26 11:45:00,129.19,174.84900000000002,60.998000000000005,32.225 +2020-11-26 12:00:00,129.43,169.345,58.27,32.225 +2020-11-26 12:15:00,129.92,168.167,58.27,32.225 +2020-11-26 12:30:00,130.77,168.467,58.27,32.225 +2020-11-26 12:45:00,128.99,169.15200000000002,58.27,32.225 +2020-11-26 13:00:00,127.51,168.292,57.196000000000005,32.225 +2020-11-26 13:15:00,127.43,167.783,57.196000000000005,32.225 +2020-11-26 13:30:00,128.14,167.3,57.196000000000005,32.225 +2020-11-26 13:45:00,128.96,167.18400000000003,57.196000000000005,32.225 +2020-11-26 14:00:00,131.54,166.625,57.38399999999999,32.225 +2020-11-26 14:15:00,132.48,166.65400000000002,57.38399999999999,32.225 +2020-11-26 14:30:00,133.07,166.63,57.38399999999999,32.225 +2020-11-26 14:45:00,134.6,166.475,57.38399999999999,32.225 +2020-11-26 15:00:00,135.57,166.542,58.647,32.225 +2020-11-26 15:15:00,134.79,166.982,58.647,32.225 +2020-11-26 15:30:00,133.95,168.06900000000002,58.647,32.225 +2020-11-26 15:45:00,134.77,168.833,58.647,32.225 +2020-11-26 16:00:00,135.09,172.248,60.083999999999996,32.225 +2020-11-26 16:15:00,139.08,173.813,60.083999999999996,32.225 +2020-11-26 16:30:00,142.52,175.12900000000002,60.083999999999996,32.225 +2020-11-26 16:45:00,144.11,175.41400000000002,60.083999999999996,32.225 +2020-11-26 17:00:00,145.11,178.605,65.85600000000001,32.225 +2020-11-26 17:15:00,145.39,179.16299999999998,65.85600000000001,32.225 +2020-11-26 17:30:00,144.99,179.56400000000002,65.85600000000001,32.225 +2020-11-26 17:45:00,144.73,179.125,65.85600000000001,32.225 +2020-11-26 18:00:00,142.36,180.87900000000002,69.855,32.225 +2020-11-26 18:15:00,139.12,179.732,69.855,32.225 +2020-11-26 18:30:00,138.76,177.99200000000002,69.855,32.225 +2020-11-26 18:45:00,139.25,178.859,69.855,32.225 +2020-11-26 19:00:00,138.59,180.332,74.015,32.225 +2020-11-26 19:15:00,135.27,177.285,74.015,32.225 +2020-11-26 19:30:00,133.83,175.861,74.015,32.225 +2020-11-26 19:45:00,132.18,172.55599999999998,74.015,32.225 +2020-11-26 20:00:00,127.04,168.832,65.316,32.225 +2020-11-26 20:15:00,124.0,163.852,65.316,32.225 +2020-11-26 20:30:00,120.42,161.009,65.316,32.225 +2020-11-26 20:45:00,117.9,157.766,65.316,32.225 +2020-11-26 21:00:00,116.75,155.628,58.403999999999996,32.225 +2020-11-26 21:15:00,114.04,153.963,58.403999999999996,32.225 +2020-11-26 21:30:00,110.09,151.82299999999998,58.403999999999996,32.225 +2020-11-26 21:45:00,106.75,150.084,58.403999999999996,32.225 +2020-11-26 22:00:00,103.22,143.605,54.092,32.225 +2020-11-26 22:15:00,99.89,138.503,54.092,32.225 +2020-11-26 22:30:00,96.72,123.51100000000001,54.092,32.225 +2020-11-26 22:45:00,95.07,115.624,54.092,32.225 +2020-11-26 23:00:00,93.68,110.834,48.18600000000001,32.225 +2020-11-26 23:15:00,90.11,108.655,48.18600000000001,32.225 +2020-11-26 23:30:00,88.8,109.069,48.18600000000001,32.225 +2020-11-26 23:45:00,89.14,108.726,48.18600000000001,32.225 +2020-11-27 00:00:00,84.43,101.79899999999999,45.18899999999999,32.225 +2020-11-27 00:15:00,89.72,101.839,45.18899999999999,32.225 +2020-11-27 00:30:00,89.24,102.156,45.18899999999999,32.225 +2020-11-27 00:45:00,87.86,103.16,45.18899999999999,32.225 +2020-11-27 01:00:00,81.04,104.459,43.256,32.225 +2020-11-27 01:15:00,79.47,106.179,43.256,32.225 +2020-11-27 01:30:00,83.22,106.521,43.256,32.225 +2020-11-27 01:45:00,85.75,107.06,43.256,32.225 +2020-11-27 02:00:00,86.44,108.556,42.312,32.225 +2020-11-27 02:15:00,83.54,109.431,42.312,32.225 +2020-11-27 02:30:00,79.41,109.71,42.312,32.225 +2020-11-27 02:45:00,79.4,111.234,42.312,32.225 +2020-11-27 03:00:00,80.77,112.949,41.833,32.225 +2020-11-27 03:15:00,88.63,114.539,41.833,32.225 +2020-11-27 03:30:00,90.12,116.492,41.833,32.225 +2020-11-27 03:45:00,88.67,117.42200000000001,41.833,32.225 +2020-11-27 04:00:00,85.04,131.356,42.732,32.225 +2020-11-27 04:15:00,84.49,143.668,42.732,32.225 +2020-11-27 04:30:00,91.48,145.282,42.732,32.225 +2020-11-27 04:45:00,95.37,146.24,42.732,32.225 +2020-11-27 05:00:00,98.6,179.519,46.254,32.225 +2020-11-27 05:15:00,98.74,211.0,46.254,32.225 +2020-11-27 05:30:00,104.82,207.55599999999998,46.254,32.225 +2020-11-27 05:45:00,107.0,198.40900000000002,46.254,32.225 +2020-11-27 06:00:00,109.72,194.56900000000002,56.76,32.225 +2020-11-27 06:15:00,114.28,198.49099999999999,56.76,32.225 +2020-11-27 06:30:00,121.1,199.40200000000002,56.76,32.225 +2020-11-27 06:45:00,125.73,202.19099999999997,56.76,32.225 +2020-11-27 07:00:00,132.85,202.873,66.029,32.225 +2020-11-27 07:15:00,137.07,207.98,66.029,32.225 +2020-11-27 07:30:00,138.18,209.1,66.029,32.225 +2020-11-27 07:45:00,139.13,208.80700000000002,66.029,32.225 +2020-11-27 08:00:00,140.02,207.06799999999998,73.128,32.225 +2020-11-27 08:15:00,141.53,206.30599999999998,73.128,32.225 +2020-11-27 08:30:00,141.95,205.28,73.128,32.225 +2020-11-27 08:45:00,141.74,200.595,73.128,32.225 +2020-11-27 09:00:00,143.12,194.68900000000002,68.23100000000001,32.225 +2020-11-27 09:15:00,144.97,191.862,68.23100000000001,32.225 +2020-11-27 09:30:00,146.38,189.045,68.23100000000001,32.225 +2020-11-27 09:45:00,148.07,186.753,68.23100000000001,32.225 +2020-11-27 10:00:00,148.43,182.864,64.733,32.225 +2020-11-27 10:15:00,149.35,179.82,64.733,32.225 +2020-11-27 10:30:00,148.75,177.725,64.733,32.225 +2020-11-27 10:45:00,149.72,176.15099999999998,64.733,32.225 +2020-11-27 11:00:00,148.49,174.278,62.0,32.225 +2020-11-27 11:15:00,148.43,172.675,62.0,32.225 +2020-11-27 11:30:00,148.91,173.55599999999998,62.0,32.225 +2020-11-27 11:45:00,148.96,172.81900000000002,62.0,32.225 +2020-11-27 12:00:00,148.12,168.327,57.876999999999995,32.225 +2020-11-27 12:15:00,148.75,165.21900000000002,57.876999999999995,32.225 +2020-11-27 12:30:00,151.8,165.7,57.876999999999995,32.225 +2020-11-27 12:45:00,148.18,166.71200000000002,57.876999999999995,32.225 +2020-11-27 13:00:00,147.61,166.737,55.585,32.225 +2020-11-27 13:15:00,145.8,166.979,55.585,32.225 +2020-11-27 13:30:00,143.06,166.637,55.585,32.225 +2020-11-27 13:45:00,142.49,166.505,55.585,32.225 +2020-11-27 14:00:00,140.86,164.783,54.5,32.225 +2020-11-27 14:15:00,140.22,164.737,54.5,32.225 +2020-11-27 14:30:00,139.38,165.44,54.5,32.225 +2020-11-27 14:45:00,139.36,165.44400000000002,54.5,32.225 +2020-11-27 15:00:00,138.46,165.082,55.131,32.225 +2020-11-27 15:15:00,138.42,165.104,55.131,32.225 +2020-11-27 15:30:00,138.56,164.794,55.131,32.225 +2020-11-27 15:45:00,138.86,165.815,55.131,32.225 +2020-11-27 16:00:00,140.34,168.062,56.8,32.225 +2020-11-27 16:15:00,142.71,169.984,56.8,32.225 +2020-11-27 16:30:00,143.85,171.358,56.8,32.225 +2020-11-27 16:45:00,144.15,171.454,56.8,32.225 +2020-11-27 17:00:00,144.16,175.07299999999998,63.428999999999995,32.225 +2020-11-27 17:15:00,144.21,175.265,63.428999999999995,32.225 +2020-11-27 17:30:00,143.91,175.423,63.428999999999995,32.225 +2020-11-27 17:45:00,143.48,174.76,63.428999999999995,32.225 +2020-11-27 18:00:00,141.75,177.138,67.915,32.225 +2020-11-27 18:15:00,141.19,175.46099999999998,67.915,32.225 +2020-11-27 18:30:00,140.31,174.05900000000003,67.915,32.225 +2020-11-27 18:45:00,140.84,174.99200000000002,67.915,32.225 +2020-11-27 19:00:00,139.02,177.395,69.428,32.225 +2020-11-27 19:15:00,136.92,175.63299999999998,69.428,32.225 +2020-11-27 19:30:00,133.97,173.84,69.428,32.225 +2020-11-27 19:45:00,132.93,169.93900000000002,69.428,32.225 +2020-11-27 20:00:00,127.08,166.252,60.56100000000001,32.225 +2020-11-27 20:15:00,123.22,161.39700000000002,60.56100000000001,32.225 +2020-11-27 20:30:00,120.97,158.409,60.56100000000001,32.225 +2020-11-27 20:45:00,121.98,155.536,60.56100000000001,32.225 +2020-11-27 21:00:00,120.28,154.04399999999998,55.18600000000001,32.225 +2020-11-27 21:15:00,115.06,153.016,55.18600000000001,32.225 +2020-11-27 21:30:00,106.27,150.89700000000002,55.18600000000001,32.225 +2020-11-27 21:45:00,104.92,149.668,55.18600000000001,32.225 +2020-11-27 22:00:00,98.21,144.031,51.433,32.225 +2020-11-27 22:15:00,95.41,138.768,51.433,32.225 +2020-11-27 22:30:00,92.29,130.185,51.433,32.225 +2020-11-27 22:45:00,89.5,125.54,51.433,32.225 +2020-11-27 23:00:00,88.76,120.616,46.201,32.225 +2020-11-27 23:15:00,90.76,116.462,46.201,32.225 +2020-11-27 23:30:00,88.25,115.31200000000001,46.201,32.225 +2020-11-27 23:45:00,84.02,114.34899999999999,46.201,32.225 +2020-11-28 00:00:00,77.33,99.921,42.576,32.047 +2020-11-28 00:15:00,75.71,96.16,42.576,32.047 +2020-11-28 00:30:00,79.64,97.53200000000001,42.576,32.047 +2020-11-28 00:45:00,79.58,99.0,42.576,32.047 +2020-11-28 01:00:00,78.45,100.935,39.34,32.047 +2020-11-28 01:15:00,69.57,101.92299999999999,39.34,32.047 +2020-11-28 01:30:00,71.73,101.686,39.34,32.047 +2020-11-28 01:45:00,75.49,102.255,39.34,32.047 +2020-11-28 02:00:00,75.22,104.189,37.582,32.047 +2020-11-28 02:15:00,73.99,104.60799999999999,37.582,32.047 +2020-11-28 02:30:00,67.25,103.791,37.582,32.047 +2020-11-28 02:45:00,71.5,105.54299999999999,37.582,32.047 +2020-11-28 03:00:00,74.56,107.538,36.523,32.047 +2020-11-28 03:15:00,74.51,107.991,36.523,32.047 +2020-11-28 03:30:00,72.04,108.655,36.523,32.047 +2020-11-28 03:45:00,70.18,109.949,36.523,32.047 +2020-11-28 04:00:00,74.75,120.022,36.347,32.047 +2020-11-28 04:15:00,70.48,130.019,36.347,32.047 +2020-11-28 04:30:00,69.82,129.468,36.347,32.047 +2020-11-28 04:45:00,68.36,130.064,36.347,32.047 +2020-11-28 05:00:00,68.09,148.316,36.407,32.047 +2020-11-28 05:15:00,67.78,161.641,36.407,32.047 +2020-11-28 05:30:00,68.17,158.80200000000002,36.407,32.047 +2020-11-28 05:45:00,70.47,155.115,36.407,32.047 +2020-11-28 06:00:00,72.18,169.19400000000002,38.228,32.047 +2020-11-28 06:15:00,75.04,188.45,38.228,32.047 +2020-11-28 06:30:00,76.95,184.31099999999998,38.228,32.047 +2020-11-28 06:45:00,78.95,178.916,38.228,32.047 +2020-11-28 07:00:00,81.61,176.02900000000002,41.905,32.047 +2020-11-28 07:15:00,85.49,179.83700000000002,41.905,32.047 +2020-11-28 07:30:00,86.76,183.553,41.905,32.047 +2020-11-28 07:45:00,89.52,186.821,41.905,32.047 +2020-11-28 08:00:00,92.6,188.55,46.051,32.047 +2020-11-28 08:15:00,93.47,190.86,46.051,32.047 +2020-11-28 08:30:00,94.42,191.234,46.051,32.047 +2020-11-28 08:45:00,96.07,189.398,46.051,32.047 +2020-11-28 09:00:00,97.99,185.484,46.683,32.047 +2020-11-28 09:15:00,98.87,183.395,46.683,32.047 +2020-11-28 09:30:00,100.11,181.44400000000002,46.683,32.047 +2020-11-28 09:45:00,100.75,179.19799999999998,46.683,32.047 +2020-11-28 10:00:00,100.87,175.627,44.425,32.047 +2020-11-28 10:15:00,100.95,172.757,44.425,32.047 +2020-11-28 10:30:00,101.61,170.725,44.425,32.047 +2020-11-28 10:45:00,102.02,170.206,44.425,32.047 +2020-11-28 11:00:00,102.71,168.458,42.148999999999994,32.047 +2020-11-28 11:15:00,102.34,166.38299999999998,42.148999999999994,32.047 +2020-11-28 11:30:00,102.7,166.33900000000003,42.148999999999994,32.047 +2020-11-28 11:45:00,102.53,164.873,42.148999999999994,32.047 +2020-11-28 12:00:00,101.89,159.649,39.683,32.047 +2020-11-28 12:15:00,99.63,157.246,39.683,32.047 +2020-11-28 12:30:00,98.83,158.006,39.683,32.047 +2020-11-28 12:45:00,95.47,158.469,39.683,32.047 +2020-11-28 13:00:00,91.62,157.96,37.154,32.047 +2020-11-28 13:15:00,89.78,156.328,37.154,32.047 +2020-11-28 13:30:00,88.89,155.621,37.154,32.047 +2020-11-28 13:45:00,89.56,155.675,37.154,32.047 +2020-11-28 14:00:00,88.24,154.984,36.457,32.047 +2020-11-28 14:15:00,89.2,154.278,36.457,32.047 +2020-11-28 14:30:00,90.17,153.356,36.457,32.047 +2020-11-28 14:45:00,90.93,153.635,36.457,32.047 +2020-11-28 15:00:00,91.29,153.899,38.257,32.047 +2020-11-28 15:15:00,91.79,154.743,38.257,32.047 +2020-11-28 15:30:00,92.89,155.843,38.257,32.047 +2020-11-28 15:45:00,94.51,156.736,38.257,32.047 +2020-11-28 16:00:00,96.87,158.286,41.181000000000004,32.047 +2020-11-28 16:15:00,100.02,160.869,41.181000000000004,32.047 +2020-11-28 16:30:00,104.43,162.238,41.181000000000004,32.047 +2020-11-28 16:45:00,105.58,163.142,41.181000000000004,32.047 +2020-11-28 17:00:00,107.55,166.046,46.806000000000004,32.047 +2020-11-28 17:15:00,108.65,167.36599999999999,46.806000000000004,32.047 +2020-11-28 17:30:00,109.6,167.429,46.806000000000004,32.047 +2020-11-28 17:45:00,109.96,166.5,46.806000000000004,32.047 +2020-11-28 18:00:00,110.46,168.673,52.073,32.047 +2020-11-28 18:15:00,109.84,168.799,52.073,32.047 +2020-11-28 18:30:00,109.58,168.78900000000002,52.073,32.047 +2020-11-28 18:45:00,109.05,166.24200000000002,52.073,32.047 +2020-11-28 19:00:00,106.97,169.187,53.608000000000004,32.047 +2020-11-28 19:15:00,105.88,166.82299999999998,53.608000000000004,32.047 +2020-11-28 19:30:00,104.51,165.793,53.608000000000004,32.047 +2020-11-28 19:45:00,103.77,161.985,53.608000000000004,32.047 +2020-11-28 20:00:00,99.85,160.374,50.265,32.047 +2020-11-28 20:15:00,95.69,157.26,50.265,32.047 +2020-11-28 20:30:00,93.1,153.798,50.265,32.047 +2020-11-28 20:45:00,91.53,150.88299999999998,50.265,32.047 +2020-11-28 21:00:00,89.7,151.108,45.766000000000005,32.047 +2020-11-28 21:15:00,87.06,150.393,45.766000000000005,32.047 +2020-11-28 21:30:00,85.08,149.381,45.766000000000005,32.047 +2020-11-28 21:45:00,84.12,147.709,45.766000000000005,32.047 +2020-11-28 22:00:00,82.85,143.25,45.97,32.047 +2020-11-28 22:15:00,80.31,140.19299999999998,45.97,32.047 +2020-11-28 22:30:00,77.52,137.02700000000002,45.97,32.047 +2020-11-28 22:45:00,75.91,134.08100000000002,45.97,32.047 +2020-11-28 23:00:00,73.58,131.162,40.415,32.047 +2020-11-28 23:15:00,72.14,125.635,40.415,32.047 +2020-11-28 23:30:00,69.33,123.365,40.415,32.047 +2020-11-28 23:45:00,66.87,120.325,40.415,32.047 +2020-11-29 00:00:00,64.94,100.87299999999999,36.376,32.047 +2020-11-29 00:15:00,62.98,96.604,36.376,32.047 +2020-11-29 00:30:00,61.95,97.626,36.376,32.047 +2020-11-29 00:45:00,60.96,99.65100000000001,36.376,32.047 +2020-11-29 01:00:00,59.8,101.545,32.992,32.047 +2020-11-29 01:15:00,59.26,103.4,32.992,32.047 +2020-11-29 01:30:00,58.37,103.59,32.992,32.047 +2020-11-29 01:45:00,57.51,103.811,32.992,32.047 +2020-11-29 02:00:00,57.27,105.13799999999999,32.327,32.047 +2020-11-29 02:15:00,56.22,104.96799999999999,32.327,32.047 +2020-11-29 02:30:00,55.73,104.90899999999999,32.327,32.047 +2020-11-29 02:45:00,55.26,106.99799999999999,32.327,32.047 +2020-11-29 03:00:00,54.75,109.369,31.169,32.047 +2020-11-29 03:15:00,55.27,109.456,31.169,32.047 +2020-11-29 03:30:00,55.26,111.166,31.169,32.047 +2020-11-29 03:45:00,55.47,112.23700000000001,31.169,32.047 +2020-11-29 04:00:00,56.47,122.075,30.796,32.047 +2020-11-29 04:15:00,56.6,131.143,30.796,32.047 +2020-11-29 04:30:00,56.82,130.894,30.796,32.047 +2020-11-29 04:45:00,57.66,131.634,30.796,32.047 +2020-11-29 05:00:00,58.4,146.967,30.848000000000003,32.047 +2020-11-29 05:15:00,59.09,158.09799999999998,30.848000000000003,32.047 +2020-11-29 05:30:00,59.43,155.056,30.848000000000003,32.047 +2020-11-29 05:45:00,60.08,151.525,30.848000000000003,32.047 +2020-11-29 06:00:00,61.05,165.06400000000002,31.166,32.047 +2020-11-29 06:15:00,61.8,183.03400000000002,31.166,32.047 +2020-11-29 06:30:00,62.33,177.872,31.166,32.047 +2020-11-29 06:45:00,64.49,171.43099999999998,31.166,32.047 +2020-11-29 07:00:00,66.6,170.65900000000002,33.527,32.047 +2020-11-29 07:15:00,68.82,173.44400000000002,33.527,32.047 +2020-11-29 07:30:00,70.6,176.37099999999998,33.527,32.047 +2020-11-29 07:45:00,72.8,178.989,33.527,32.047 +2020-11-29 08:00:00,75.93,182.40900000000002,36.616,32.047 +2020-11-29 08:15:00,78.15,184.84400000000002,36.616,32.047 +2020-11-29 08:30:00,79.59,186.805,36.616,32.047 +2020-11-29 08:45:00,81.98,186.64700000000002,36.616,32.047 +2020-11-29 09:00:00,83.56,182.33700000000002,37.857,32.047 +2020-11-29 09:15:00,85.67,180.64,37.857,32.047 +2020-11-29 09:30:00,86.37,178.63299999999998,37.857,32.047 +2020-11-29 09:45:00,87.65,176.449,37.857,32.047 +2020-11-29 10:00:00,88.71,175.118,36.319,32.047 +2020-11-29 10:15:00,89.33,172.702,36.319,32.047 +2020-11-29 10:30:00,89.75,171.203,36.319,32.047 +2020-11-29 10:45:00,91.28,169.226,36.319,32.047 +2020-11-29 11:00:00,94.21,168.18400000000003,37.236999999999995,32.047 +2020-11-29 11:15:00,98.71,166.132,37.236999999999995,32.047 +2020-11-29 11:30:00,100.96,165.42700000000002,37.236999999999995,32.047 +2020-11-29 11:45:00,103.01,164.52,37.236999999999995,32.047 +2020-11-29 12:00:00,100.88,158.976,34.871,32.047 +2020-11-29 12:15:00,98.94,158.11700000000002,34.871,32.047 +2020-11-29 12:30:00,96.38,157.68,34.871,32.047 +2020-11-29 12:45:00,94.52,157.204,34.871,32.047 +2020-11-29 13:00:00,94.12,155.987,29.738000000000003,32.047 +2020-11-29 13:15:00,92.16,156.864,29.738000000000003,32.047 +2020-11-29 13:30:00,90.98,155.782,29.738000000000003,32.047 +2020-11-29 13:45:00,89.74,155.44899999999998,29.738000000000003,32.047 +2020-11-29 14:00:00,89.32,155.141,27.333000000000002,32.047 +2020-11-29 14:15:00,88.36,155.555,27.333000000000002,32.047 +2020-11-29 14:30:00,90.19,155.519,27.333000000000002,32.047 +2020-11-29 14:45:00,90.43,155.267,27.333000000000002,32.047 +2020-11-29 15:00:00,89.89,154.24200000000002,28.232,32.047 +2020-11-29 15:15:00,90.56,155.61,28.232,32.047 +2020-11-29 15:30:00,91.22,157.219,28.232,32.047 +2020-11-29 15:45:00,92.39,158.764,28.232,32.047 +2020-11-29 16:00:00,95.42,161.649,32.815,32.047 +2020-11-29 16:15:00,96.9,163.506,32.815,32.047 +2020-11-29 16:30:00,98.92,165.269,32.815,32.047 +2020-11-29 16:45:00,101.02,166.33900000000003,32.815,32.047 +2020-11-29 17:00:00,102.53,169.273,43.068999999999996,32.047 +2020-11-29 17:15:00,104.58,170.584,43.068999999999996,32.047 +2020-11-29 17:30:00,105.89,171.041,43.068999999999996,32.047 +2020-11-29 17:45:00,107.7,172.16,43.068999999999996,32.047 +2020-11-29 18:00:00,106.71,173.975,50.498999999999995,32.047 +2020-11-29 18:15:00,105.17,175.155,50.498999999999995,32.047 +2020-11-29 18:30:00,103.9,173.296,50.498999999999995,32.047 +2020-11-29 18:45:00,102.8,172.385,50.498999999999995,32.047 +2020-11-29 19:00:00,101.44,175.391,53.481,32.047 +2020-11-29 19:15:00,99.87,173.355,53.481,32.047 +2020-11-29 19:30:00,98.61,172.127,53.481,32.047 +2020-11-29 19:45:00,96.7,169.475,53.481,32.047 +2020-11-29 20:00:00,95.81,167.84900000000002,51.687,32.047 +2020-11-29 20:15:00,93.42,165.55900000000003,51.687,32.047 +2020-11-29 20:30:00,91.99,163.27700000000002,51.687,32.047 +2020-11-29 20:45:00,90.53,159.05200000000002,51.687,32.047 +2020-11-29 21:00:00,89.47,156.99200000000002,47.674,32.047 +2020-11-29 21:15:00,87.86,155.668,47.674,32.047 +2020-11-29 21:30:00,87.32,154.80700000000002,47.674,32.047 +2020-11-29 21:45:00,87.42,153.313,47.674,32.047 +2020-11-29 22:00:00,87.27,148.168,48.178000000000004,32.047 +2020-11-29 22:15:00,87.13,144.168,48.178000000000004,32.047 +2020-11-29 22:30:00,85.14,138.221,48.178000000000004,32.047 +2020-11-29 22:45:00,83.37,134.334,48.178000000000004,32.047 +2020-11-29 23:00:00,80.39,128.91899999999998,42.553999999999995,32.047 +2020-11-29 23:15:00,78.24,125.204,42.553999999999995,32.047 +2020-11-29 23:30:00,76.63,123.557,42.553999999999995,32.047 +2020-11-29 23:45:00,74.94,121.303,42.553999999999995,32.047 +2020-11-30 00:00:00,70.81,105.06200000000001,37.177,32.225 +2020-11-30 00:15:00,71.25,103.441,37.177,32.225 +2020-11-30 00:30:00,71.29,104.5,37.177,32.225 +2020-11-30 00:45:00,72.06,105.978,37.177,32.225 +2020-11-30 01:00:00,67.48,107.973,35.358000000000004,32.225 +2020-11-30 01:15:00,69.08,109.38799999999999,35.358000000000004,32.225 +2020-11-30 01:30:00,68.47,109.70200000000001,35.358000000000004,32.225 +2020-11-30 01:45:00,68.71,109.99,35.358000000000004,32.225 +2020-11-30 02:00:00,66.0,111.391,35.03,32.225 +2020-11-30 02:15:00,67.54,112.273,35.03,32.225 +2020-11-30 02:30:00,67.4,112.52600000000001,35.03,32.225 +2020-11-30 02:45:00,68.06,114.06,35.03,32.225 +2020-11-30 03:00:00,66.68,117.569,34.394,32.225 +2020-11-30 03:15:00,67.69,119.211,34.394,32.225 +2020-11-30 03:30:00,68.14,120.822,34.394,32.225 +2020-11-30 03:45:00,69.36,121.333,34.394,32.225 +2020-11-30 04:00:00,71.04,135.393,34.421,32.225 +2020-11-30 04:15:00,71.36,148.519,34.421,32.225 +2020-11-30 04:30:00,80.36,150.013,34.421,32.225 +2020-11-30 04:45:00,83.58,150.964,34.421,32.225 +2020-11-30 05:00:00,84.07,180.87099999999998,39.435,32.225 +2020-11-30 05:15:00,83.14,210.899,39.435,32.225 +2020-11-30 05:30:00,93.56,207.80200000000002,39.435,32.225 +2020-11-30 05:45:00,101.91,198.94,39.435,32.225 +2020-11-30 06:00:00,110.42,195.543,55.685,32.225 +2020-11-30 06:15:00,109.5,199.292,55.685,32.225 +2020-11-30 06:30:00,112.78,201.679,55.685,32.225 +2020-11-30 06:45:00,120.16,203.687,55.685,32.225 +2020-11-30 07:00:00,123.28,205.102,66.837,32.225 +2020-11-30 07:15:00,128.77,209.372,66.837,32.225 +2020-11-30 07:30:00,128.06,211.49400000000003,66.837,32.225 +2020-11-30 07:45:00,128.21,211.993,66.837,32.225 +2020-11-30 08:00:00,135.05,211.018,72.217,32.225 +2020-11-30 08:15:00,132.17,211.399,72.217,32.225 +2020-11-30 08:30:00,130.69,209.71200000000002,72.217,32.225 +2020-11-30 08:45:00,130.49,206.71200000000002,72.217,32.225 +2020-11-30 09:00:00,131.71,201.407,66.117,32.225 +2020-11-30 09:15:00,132.93,196.41099999999997,66.117,32.225 +2020-11-30 09:30:00,133.88,193.389,66.117,32.225 +2020-11-30 09:45:00,136.35,191.048,66.117,32.225 +2020-11-30 10:00:00,133.09,188.88,62.1,32.225 +2020-11-30 10:15:00,133.18,186.13,62.1,32.225 +2020-11-30 10:30:00,131.07,183.77599999999998,62.1,32.225 +2020-11-30 10:45:00,130.86,182.16299999999998,62.1,32.225 +2020-11-30 11:00:00,129.58,178.945,60.021,32.225 +2020-11-30 11:15:00,127.7,178.484,60.021,32.225 +2020-11-30 11:30:00,128.06,179.08900000000003,60.021,32.225 +2020-11-30 11:45:00,127.63,177.903,60.021,32.225 +2020-11-30 12:00:00,127.01,173.58900000000003,56.75899999999999,32.225 +2020-11-30 12:15:00,129.44,172.75599999999997,56.75899999999999,32.225 +2020-11-30 12:30:00,126.09,172.38299999999998,56.75899999999999,32.225 +2020-11-30 12:45:00,130.5,173.231,56.75899999999999,32.225 +2020-11-30 13:00:00,127.8,172.65200000000002,56.04600000000001,32.225 +2020-11-30 13:15:00,135.15,172.187,56.04600000000001,32.225 +2020-11-30 13:30:00,133.2,170.669,56.04600000000001,32.225 +2020-11-30 13:45:00,134.94,170.502,56.04600000000001,32.225 +2020-11-30 14:00:00,132.77,169.498,55.475,32.225 +2020-11-30 14:15:00,134.22,169.459,55.475,32.225 +2020-11-30 14:30:00,134.93,168.958,55.475,32.225 +2020-11-30 14:45:00,139.22,169.021,55.475,32.225 +2020-11-30 15:00:00,139.45,169.52599999999998,57.048,32.225 +2020-11-30 15:15:00,138.24,169.551,57.048,32.225 +2020-11-30 15:30:00,137.99,170.58900000000003,57.048,32.225 +2020-11-30 15:45:00,136.61,171.674,57.048,32.225 +2020-11-30 16:00:00,139.97,174.865,59.06,32.225 +2020-11-30 16:15:00,141.95,176.092,59.06,32.225 +2020-11-30 16:30:00,140.58,176.91,59.06,32.225 +2020-11-30 16:45:00,140.15,176.986,59.06,32.225 +2020-11-30 17:00:00,142.38,179.55700000000002,65.419,32.225 +2020-11-30 17:15:00,140.03,180.122,65.419,32.225 +2020-11-30 17:30:00,141.11,180.05200000000002,65.419,32.225 +2020-11-30 17:45:00,140.16,179.81400000000002,65.419,32.225 +2020-11-30 18:00:00,138.78,181.859,69.345,32.225 +2020-11-30 18:15:00,137.07,180.787,69.345,32.225 +2020-11-30 18:30:00,136.41,179.389,69.345,32.225 +2020-11-30 18:45:00,137.66,179.602,69.345,32.225 +2020-11-30 19:00:00,134.46,181.15599999999998,73.825,32.225 +2020-11-30 19:15:00,133.63,178.29,73.825,32.225 +2020-11-30 19:30:00,137.08,177.426,73.825,32.225 +2020-11-30 19:45:00,135.8,173.923,73.825,32.225 +2020-11-30 20:00:00,129.29,170.03,64.027,32.225 +2020-11-30 20:15:00,123.1,165.794,64.027,32.225 +2020-11-30 20:30:00,117.76,161.946,64.027,32.225 +2020-11-30 20:45:00,115.12,159.184,64.027,32.225 +2020-11-30 21:00:00,116.06,157.475,57.952,32.225 +2020-11-30 21:15:00,113.82,155.185,57.952,32.225 +2020-11-30 21:30:00,112.58,153.67600000000002,57.952,32.225 +2020-11-30 21:45:00,105.49,151.708,57.952,32.225 +2020-11-30 22:00:00,101.57,143.72299999999998,53.031000000000006,32.225 +2020-11-30 22:15:00,95.52,138.885,53.031000000000006,32.225 +2020-11-30 22:30:00,97.58,123.971,53.031000000000006,32.225 +2020-11-30 22:45:00,97.01,115.875,53.031000000000006,32.225 +2020-11-30 23:00:00,92.05,111.11399999999999,45.085,32.225 +2020-11-30 23:15:00,89.71,109.441,45.085,32.225 +2020-11-30 23:30:00,83.96,110.206,45.085,32.225 +2020-11-30 23:45:00,81.23,110.156,45.085,32.225 +2020-12-01 00:00:00,77.38,114.376,43.537,32.65 +2020-12-01 00:15:00,76.24,114.65799999999999,43.537,32.65 +2020-12-01 00:30:00,76.87,115.84100000000001,43.537,32.65 +2020-12-01 00:45:00,75.04,117.59700000000001,43.537,32.65 +2020-12-01 01:00:00,73.08,119.316,41.854,32.65 +2020-12-01 01:15:00,73.82,120.051,41.854,32.65 +2020-12-01 01:30:00,71.9,120.405,41.854,32.65 +2020-12-01 01:45:00,73.54,121.234,41.854,32.65 +2020-12-01 02:00:00,71.89,122.501,40.321,32.65 +2020-12-01 02:15:00,73.96,123.98899999999999,40.321,32.65 +2020-12-01 02:30:00,71.76,124.215,40.321,32.65 +2020-12-01 02:45:00,72.0,126.095,40.321,32.65 +2020-12-01 03:00:00,71.75,128.914,39.632,32.65 +2020-12-01 03:15:00,75.34,129.286,39.632,32.65 +2020-12-01 03:30:00,74.29,131.14,39.632,32.65 +2020-12-01 03:45:00,75.41,132.171,39.632,32.65 +2020-12-01 04:00:00,76.26,145.259,40.183,32.65 +2020-12-01 04:15:00,77.11,157.393,40.183,32.65 +2020-12-01 04:30:00,79.19,160.136,40.183,32.65 +2020-12-01 04:45:00,80.44,162.73,40.183,32.65 +2020-12-01 05:00:00,84.76,197.88,43.945,32.65 +2020-12-01 05:15:00,88.06,227.541,43.945,32.65 +2020-12-01 05:30:00,90.98,222.683,43.945,32.65 +2020-12-01 05:45:00,99.38,214.511,43.945,32.65 +2020-12-01 06:00:00,107.58,210.252,56.048,32.65 +2020-12-01 06:15:00,111.62,215.68,56.048,32.65 +2020-12-01 06:30:00,119.23,217.26,56.048,32.65 +2020-12-01 06:45:00,121.67,219.46099999999998,56.048,32.65 +2020-12-01 07:00:00,129.63,218.843,65.74,32.65 +2020-12-01 07:15:00,130.84,223.71400000000003,65.74,32.65 +2020-12-01 07:30:00,137.48,226.422,65.74,32.65 +2020-12-01 07:45:00,137.37,227.78900000000002,65.74,32.65 +2020-12-01 08:00:00,140.6,226.493,72.757,32.65 +2020-12-01 08:15:00,141.1,226.658,72.757,32.65 +2020-12-01 08:30:00,140.35,224.92700000000002,72.757,32.65 +2020-12-01 08:45:00,140.13,222.5,72.757,32.65 +2020-12-01 09:00:00,141.71,216.40599999999998,67.692,32.65 +2020-12-01 09:15:00,141.8,212.893,67.692,32.65 +2020-12-01 09:30:00,142.81,210.30700000000002,67.692,32.65 +2020-12-01 09:45:00,145.16,207.66299999999998,67.692,32.65 +2020-12-01 10:00:00,142.57,203.207,63.506,32.65 +2020-12-01 10:15:00,141.13,199.245,63.506,32.65 +2020-12-01 10:30:00,139.34,197.101,63.506,32.65 +2020-12-01 10:45:00,143.54,195.78,63.506,32.65 +2020-12-01 11:00:00,143.81,194.96099999999998,60.758,32.65 +2020-12-01 11:15:00,146.07,194.06400000000002,60.758,32.65 +2020-12-01 11:30:00,145.51,192.882,60.758,32.65 +2020-12-01 11:45:00,143.54,191.393,60.758,32.65 +2020-12-01 12:00:00,141.07,185.77599999999998,57.519,32.65 +2020-12-01 12:15:00,140.56,184.696,57.519,32.65 +2020-12-01 12:30:00,136.23,184.53,57.519,32.65 +2020-12-01 12:45:00,133.91,185.405,57.519,32.65 +2020-12-01 13:00:00,132.2,184.952,56.46,32.65 +2020-12-01 13:15:00,136.5,184.533,56.46,32.65 +2020-12-01 13:30:00,133.44,184.457,56.46,32.65 +2020-12-01 13:45:00,129.4,184.488,56.46,32.65 +2020-12-01 14:00:00,127.85,183.696,56.207,32.65 +2020-12-01 14:15:00,132.64,184.13400000000001,56.207,32.65 +2020-12-01 14:30:00,135.84,184.32299999999998,56.207,32.65 +2020-12-01 14:45:00,135.32,184.10299999999998,56.207,32.65 +2020-12-01 15:00:00,135.69,184.78400000000002,57.391999999999996,32.65 +2020-12-01 15:15:00,135.61,185.36599999999999,57.391999999999996,32.65 +2020-12-01 15:30:00,133.27,187.30200000000002,57.391999999999996,32.65 +2020-12-01 15:45:00,133.91,189.111,57.391999999999996,32.65 +2020-12-01 16:00:00,137.4,190.207,59.955,32.65 +2020-12-01 16:15:00,139.6,191.11599999999999,59.955,32.65 +2020-12-01 16:30:00,142.04,193.775,59.955,32.65 +2020-12-01 16:45:00,141.31,194.75400000000002,59.955,32.65 +2020-12-01 17:00:00,144.35,197.62,67.063,32.65 +2020-12-01 17:15:00,144.05,197.739,67.063,32.65 +2020-12-01 17:30:00,146.6,198.13,67.063,32.65 +2020-12-01 17:45:00,145.09,197.708,67.063,32.65 +2020-12-01 18:00:00,144.03,198.524,71.477,32.65 +2020-12-01 18:15:00,142.72,196.93200000000002,71.477,32.65 +2020-12-01 18:30:00,142.42,195.355,71.477,32.65 +2020-12-01 18:45:00,141.42,195.035,71.477,32.65 +2020-12-01 19:00:00,139.2,196.077,74.32,32.65 +2020-12-01 19:15:00,138.58,192.56,74.32,32.65 +2020-12-01 19:30:00,136.59,190.588,74.32,32.65 +2020-12-01 19:45:00,136.62,187.38299999999998,74.32,32.65 +2020-12-01 20:00:00,127.68,183.91299999999998,66.157,32.65 +2020-12-01 20:15:00,124.64,178.15099999999998,66.157,32.65 +2020-12-01 20:30:00,120.77,174.831,66.157,32.65 +2020-12-01 20:45:00,119.59,171.986,66.157,32.65 +2020-12-01 21:00:00,113.2,170.00900000000001,59.806000000000004,32.65 +2020-12-01 21:15:00,113.24,168.122,59.806000000000004,32.65 +2020-12-01 21:30:00,115.21,166.013,59.806000000000004,32.65 +2020-12-01 21:45:00,113.56,164.245,59.806000000000004,32.65 +2020-12-01 22:00:00,109.11,157.439,54.785,32.65 +2020-12-01 22:15:00,104.46,151.769,54.785,32.65 +2020-12-01 22:30:00,97.9,136.998,54.785,32.65 +2020-12-01 22:45:00,101.22,128.881,54.785,32.65 +2020-12-01 23:00:00,96.47,123.572,47.176,32.65 +2020-12-01 23:15:00,95.75,121.62100000000001,47.176,32.65 +2020-12-01 23:30:00,88.96,121.59299999999999,47.176,32.65 +2020-12-01 23:45:00,85.14,121.363,47.176,32.65 +2020-12-02 00:00:00,84.73,114.73200000000001,43.42,32.65 +2020-12-02 00:15:00,88.25,114.98200000000001,43.42,32.65 +2020-12-02 00:30:00,88.93,116.162,43.42,32.65 +2020-12-02 00:45:00,86.11,117.9,43.42,32.65 +2020-12-02 01:00:00,82.71,119.66,40.869,32.65 +2020-12-02 01:15:00,87.22,120.396,40.869,32.65 +2020-12-02 01:30:00,85.57,120.758,40.869,32.65 +2020-12-02 01:45:00,81.75,121.573,40.869,32.65 +2020-12-02 02:00:00,83.12,122.861,39.541,32.65 +2020-12-02 02:15:00,84.79,124.352,39.541,32.65 +2020-12-02 02:30:00,82.28,124.575,39.541,32.65 +2020-12-02 02:45:00,82.21,126.456,39.541,32.65 +2020-12-02 03:00:00,84.94,129.262,39.052,32.65 +2020-12-02 03:15:00,86.09,129.664,39.052,32.65 +2020-12-02 03:30:00,83.57,131.52100000000002,39.052,32.65 +2020-12-02 03:45:00,84.34,132.55200000000002,39.052,32.65 +2020-12-02 04:00:00,87.42,145.606,40.36,32.65 +2020-12-02 04:15:00,90.24,157.736,40.36,32.65 +2020-12-02 04:30:00,90.6,160.465,40.36,32.65 +2020-12-02 04:45:00,88.51,163.063,40.36,32.65 +2020-12-02 05:00:00,94.36,198.175,43.133,32.65 +2020-12-02 05:15:00,100.52,227.77700000000002,43.133,32.65 +2020-12-02 05:30:00,105.35,222.95,43.133,32.65 +2020-12-02 05:45:00,105.35,214.801,43.133,32.65 +2020-12-02 06:00:00,109.55,210.567,54.953,32.65 +2020-12-02 06:15:00,115.06,215.998,54.953,32.65 +2020-12-02 06:30:00,123.34,217.628,54.953,32.65 +2020-12-02 06:45:00,127.78,219.878,54.953,32.65 +2020-12-02 07:00:00,134.98,219.26,66.566,32.65 +2020-12-02 07:15:00,137.75,224.141,66.566,32.65 +2020-12-02 07:30:00,136.68,226.858,66.566,32.65 +2020-12-02 07:45:00,138.02,228.231,66.566,32.65 +2020-12-02 08:00:00,141.25,226.949,72.902,32.65 +2020-12-02 08:15:00,138.63,227.109,72.902,32.65 +2020-12-02 08:30:00,137.77,225.389,72.902,32.65 +2020-12-02 08:45:00,136.96,222.929,72.902,32.65 +2020-12-02 09:00:00,134.22,216.808,68.465,32.65 +2020-12-02 09:15:00,138.14,213.30200000000002,68.465,32.65 +2020-12-02 09:30:00,138.14,210.72099999999998,68.465,32.65 +2020-12-02 09:45:00,137.02,208.063,68.465,32.65 +2020-12-02 10:00:00,134.5,203.59900000000002,63.625,32.65 +2020-12-02 10:15:00,134.21,199.612,63.625,32.65 +2020-12-02 10:30:00,134.09,197.44299999999998,63.625,32.65 +2020-12-02 10:45:00,133.17,196.112,63.625,32.65 +2020-12-02 11:00:00,128.69,195.27599999999998,61.628,32.65 +2020-12-02 11:15:00,131.51,194.36599999999999,61.628,32.65 +2020-12-02 11:30:00,130.92,193.18099999999998,61.628,32.65 +2020-12-02 11:45:00,130.63,191.68400000000003,61.628,32.65 +2020-12-02 12:00:00,128.7,186.065,58.708999999999996,32.65 +2020-12-02 12:15:00,125.44,184.99599999999998,58.708999999999996,32.65 +2020-12-02 12:30:00,126.63,184.84900000000002,58.708999999999996,32.65 +2020-12-02 12:45:00,128.48,185.729,58.708999999999996,32.65 +2020-12-02 13:00:00,126.35,185.24,57.373000000000005,32.65 +2020-12-02 13:15:00,128.95,184.826,57.373000000000005,32.65 +2020-12-02 13:30:00,127.1,184.747,57.373000000000005,32.65 +2020-12-02 13:45:00,127.31,184.768,57.373000000000005,32.65 +2020-12-02 14:00:00,125.8,183.947,57.684,32.65 +2020-12-02 14:15:00,128.72,184.392,57.684,32.65 +2020-12-02 14:30:00,128.58,184.61,57.684,32.65 +2020-12-02 14:45:00,128.73,184.4,57.684,32.65 +2020-12-02 15:00:00,132.19,185.09799999999998,58.03,32.65 +2020-12-02 15:15:00,132.73,185.68099999999998,58.03,32.65 +2020-12-02 15:30:00,132.49,187.644,58.03,32.65 +2020-12-02 15:45:00,133.74,189.456,58.03,32.65 +2020-12-02 16:00:00,137.37,190.55200000000002,59.97,32.65 +2020-12-02 16:15:00,138.66,191.484,59.97,32.65 +2020-12-02 16:30:00,141.97,194.15,59.97,32.65 +2020-12-02 16:45:00,142.93,195.16400000000002,59.97,32.65 +2020-12-02 17:00:00,147.26,198.00400000000002,65.661,32.65 +2020-12-02 17:15:00,146.69,198.15099999999998,65.661,32.65 +2020-12-02 17:30:00,147.72,198.554,65.661,32.65 +2020-12-02 17:45:00,146.61,198.139,65.661,32.65 +2020-12-02 18:00:00,144.03,198.97299999999998,70.96300000000001,32.65 +2020-12-02 18:15:00,143.55,197.338,70.96300000000001,32.65 +2020-12-02 18:30:00,141.98,195.77,70.96300000000001,32.65 +2020-12-02 18:45:00,139.97,195.455,70.96300000000001,32.65 +2020-12-02 19:00:00,141.8,196.488,74.133,32.65 +2020-12-02 19:15:00,140.75,192.959,74.133,32.65 +2020-12-02 19:30:00,137.4,190.96900000000002,74.133,32.65 +2020-12-02 19:45:00,137.36,187.734,74.133,32.65 +2020-12-02 20:00:00,130.36,184.267,65.613,32.65 +2020-12-02 20:15:00,122.11,178.49400000000003,65.613,32.65 +2020-12-02 20:30:00,123.19,175.14700000000002,65.613,32.65 +2020-12-02 20:45:00,122.73,172.321,65.613,32.65 +2020-12-02 21:00:00,117.07,170.332,58.583,32.65 +2020-12-02 21:15:00,110.96,168.428,58.583,32.65 +2020-12-02 21:30:00,110.94,166.321,58.583,32.65 +2020-12-02 21:45:00,111.13,164.558,58.583,32.65 +2020-12-02 22:00:00,105.41,157.761,54.411,32.65 +2020-12-02 22:15:00,105.31,152.091,54.411,32.65 +2020-12-02 22:30:00,102.13,137.376,54.411,32.65 +2020-12-02 22:45:00,104.64,129.267,54.411,32.65 +2020-12-02 23:00:00,96.98,123.932,47.878,32.65 +2020-12-02 23:15:00,94.32,121.976,47.878,32.65 +2020-12-02 23:30:00,94.93,121.96,47.878,32.65 +2020-12-02 23:45:00,93.72,121.713,47.878,32.65 +2020-12-03 00:00:00,87.84,115.08200000000001,44.513000000000005,32.65 +2020-12-03 00:15:00,83.11,115.303,44.513000000000005,32.65 +2020-12-03 00:30:00,84.51,116.478,44.513000000000005,32.65 +2020-12-03 00:45:00,87.95,118.196,44.513000000000005,32.65 +2020-12-03 01:00:00,84.16,119.99700000000001,43.169,32.65 +2020-12-03 01:15:00,83.09,120.73299999999999,43.169,32.65 +2020-12-03 01:30:00,85.34,121.10600000000001,43.169,32.65 +2020-12-03 01:45:00,86.49,121.904,43.169,32.65 +2020-12-03 02:00:00,82.06,123.214,41.763999999999996,32.65 +2020-12-03 02:15:00,82.6,124.706,41.763999999999996,32.65 +2020-12-03 02:30:00,85.09,124.931,41.763999999999996,32.65 +2020-12-03 02:45:00,86.07,126.811,41.763999999999996,32.65 +2020-12-03 03:00:00,80.57,129.602,41.155,32.65 +2020-12-03 03:15:00,80.92,130.036,41.155,32.65 +2020-12-03 03:30:00,83.75,131.89700000000002,41.155,32.65 +2020-12-03 03:45:00,87.62,132.925,41.155,32.65 +2020-12-03 04:00:00,89.28,145.94799999999998,41.96,32.65 +2020-12-03 04:15:00,85.37,158.075,41.96,32.65 +2020-12-03 04:30:00,83.55,160.786,41.96,32.65 +2020-12-03 04:45:00,85.67,163.389,41.96,32.65 +2020-12-03 05:00:00,90.67,198.46200000000002,45.206,32.65 +2020-12-03 05:15:00,94.78,228.007,45.206,32.65 +2020-12-03 05:30:00,95.98,223.21200000000002,45.206,32.65 +2020-12-03 05:45:00,101.33,215.085,45.206,32.65 +2020-12-03 06:00:00,109.52,210.877,55.398999999999994,32.65 +2020-12-03 06:15:00,116.92,216.308,55.398999999999994,32.65 +2020-12-03 06:30:00,120.32,217.99,55.398999999999994,32.65 +2020-12-03 06:45:00,124.73,220.28599999999997,55.398999999999994,32.65 +2020-12-03 07:00:00,131.84,219.67,64.627,32.65 +2020-12-03 07:15:00,134.4,224.56099999999998,64.627,32.65 +2020-12-03 07:30:00,137.22,227.28400000000002,64.627,32.65 +2020-12-03 07:45:00,137.69,228.66400000000002,64.627,32.65 +2020-12-03 08:00:00,141.29,227.395,70.895,32.65 +2020-12-03 08:15:00,139.35,227.55,70.895,32.65 +2020-12-03 08:30:00,139.65,225.842,70.895,32.65 +2020-12-03 08:45:00,139.71,223.34900000000002,70.895,32.65 +2020-12-03 09:00:00,139.33,217.199,66.382,32.65 +2020-12-03 09:15:00,140.17,213.701,66.382,32.65 +2020-12-03 09:30:00,140.93,211.125,66.382,32.65 +2020-12-03 09:45:00,141.66,208.455,66.382,32.65 +2020-12-03 10:00:00,143.6,203.981,62.739,32.65 +2020-12-03 10:15:00,145.07,199.96900000000002,62.739,32.65 +2020-12-03 10:30:00,144.64,197.77700000000002,62.739,32.65 +2020-12-03 10:45:00,144.41,196.437,62.739,32.65 +2020-12-03 11:00:00,143.34,195.584,60.843,32.65 +2020-12-03 11:15:00,145.71,194.658,60.843,32.65 +2020-12-03 11:30:00,144.57,193.47099999999998,60.843,32.65 +2020-12-03 11:45:00,144.85,191.968,60.843,32.65 +2020-12-03 12:00:00,142.96,186.34599999999998,58.466,32.65 +2020-12-03 12:15:00,140.5,185.28900000000002,58.466,32.65 +2020-12-03 12:30:00,139.07,185.16,58.466,32.65 +2020-12-03 12:45:00,139.98,186.046,58.466,32.65 +2020-12-03 13:00:00,137.73,185.521,56.883,32.65 +2020-12-03 13:15:00,138.78,185.111,56.883,32.65 +2020-12-03 13:30:00,136.57,185.028,56.883,32.65 +2020-12-03 13:45:00,136.43,185.03799999999998,56.883,32.65 +2020-12-03 14:00:00,135.01,184.19099999999997,56.503,32.65 +2020-12-03 14:15:00,135.23,184.644,56.503,32.65 +2020-12-03 14:30:00,135.78,184.89,56.503,32.65 +2020-12-03 14:45:00,136.83,184.68900000000002,56.503,32.65 +2020-12-03 15:00:00,136.93,185.407,57.803999999999995,32.65 +2020-12-03 15:15:00,136.54,185.989,57.803999999999995,32.65 +2020-12-03 15:30:00,135.85,187.979,57.803999999999995,32.65 +2020-12-03 15:45:00,135.94,189.792,57.803999999999995,32.65 +2020-12-03 16:00:00,139.8,190.887,59.379,32.65 +2020-12-03 16:15:00,140.97,191.84400000000002,59.379,32.65 +2020-12-03 16:30:00,142.86,194.516,59.379,32.65 +2020-12-03 16:45:00,142.14,195.565,59.379,32.65 +2020-12-03 17:00:00,143.33,198.38,64.71600000000001,32.65 +2020-12-03 17:15:00,143.46,198.551,64.71600000000001,32.65 +2020-12-03 17:30:00,145.46,198.96900000000002,64.71600000000001,32.65 +2020-12-03 17:45:00,144.91,198.55900000000003,64.71600000000001,32.65 +2020-12-03 18:00:00,141.72,199.41299999999998,68.803,32.65 +2020-12-03 18:15:00,139.73,197.738,68.803,32.65 +2020-12-03 18:30:00,138.04,196.17700000000002,68.803,32.65 +2020-12-03 18:45:00,139.53,195.868,68.803,32.65 +2020-12-03 19:00:00,136.48,196.892,72.934,32.65 +2020-12-03 19:15:00,136.16,193.352,72.934,32.65 +2020-12-03 19:30:00,132.96,191.345,72.934,32.65 +2020-12-03 19:45:00,132.54,188.079,72.934,32.65 +2020-12-03 20:00:00,124.03,184.614,65.175,32.65 +2020-12-03 20:15:00,120.74,178.831,65.175,32.65 +2020-12-03 20:30:00,117.79,175.456,65.175,32.65 +2020-12-03 20:45:00,115.66,172.65099999999998,65.175,32.65 +2020-12-03 21:00:00,113.13,170.648,58.55,32.65 +2020-12-03 21:15:00,111.13,168.72799999999998,58.55,32.65 +2020-12-03 21:30:00,107.15,166.62099999999998,58.55,32.65 +2020-12-03 21:45:00,104.82,164.864,58.55,32.65 +2020-12-03 22:00:00,98.64,158.077,55.041000000000004,32.65 +2020-12-03 22:15:00,100.35,152.408,55.041000000000004,32.65 +2020-12-03 22:30:00,99.69,137.747,55.041000000000004,32.65 +2020-12-03 22:45:00,99.18,129.645,55.041000000000004,32.65 +2020-12-03 23:00:00,96.32,124.286,48.258,32.65 +2020-12-03 23:15:00,90.21,122.32600000000001,48.258,32.65 +2020-12-03 23:30:00,91.13,122.322,48.258,32.65 +2020-12-03 23:45:00,90.28,122.055,48.258,32.65 +2020-12-04 00:00:00,88.17,114.368,45.02,32.65 +2020-12-04 00:15:00,80.56,114.755,45.02,32.65 +2020-12-04 00:30:00,74.99,115.79,45.02,32.65 +2020-12-04 00:45:00,78.87,117.596,45.02,32.65 +2020-12-04 01:00:00,80.8,119.12799999999999,42.695,32.65 +2020-12-04 01:15:00,81.08,120.788,42.695,32.65 +2020-12-04 01:30:00,79.01,120.94,42.695,32.65 +2020-12-04 01:45:00,77.31,121.831,42.695,32.65 +2020-12-04 02:00:00,74.12,123.24,41.511,32.65 +2020-12-04 02:15:00,74.97,124.619,41.511,32.65 +2020-12-04 02:30:00,79.2,125.36200000000001,41.511,32.65 +2020-12-04 02:45:00,82.28,127.291,41.511,32.65 +2020-12-04 03:00:00,79.37,129.062,41.162,32.65 +2020-12-04 03:15:00,79.26,130.482,41.162,32.65 +2020-12-04 03:30:00,82.2,132.33100000000002,41.162,32.65 +2020-12-04 03:45:00,85.02,133.678,41.162,32.65 +2020-12-04 04:00:00,84.4,146.89600000000002,42.226000000000006,32.65 +2020-12-04 04:15:00,78.87,158.79,42.226000000000006,32.65 +2020-12-04 04:30:00,75.73,161.70600000000002,42.226000000000006,32.65 +2020-12-04 04:45:00,80.98,163.172,42.226000000000006,32.65 +2020-12-04 05:00:00,85.06,196.937,45.597,32.65 +2020-12-04 05:15:00,88.99,227.93,45.597,32.65 +2020-12-04 05:30:00,89.73,224.248,45.597,32.65 +2020-12-04 05:45:00,96.09,216.083,45.597,32.65 +2020-12-04 06:00:00,108.01,212.333,56.263999999999996,32.65 +2020-12-04 06:15:00,112.74,216.285,56.263999999999996,32.65 +2020-12-04 06:30:00,117.29,217.148,56.263999999999996,32.65 +2020-12-04 06:45:00,121.66,221.128,56.263999999999996,32.65 +2020-12-04 07:00:00,130.21,219.666,66.888,32.65 +2020-12-04 07:15:00,129.15,225.585,66.888,32.65 +2020-12-04 07:30:00,132.61,228.15400000000002,66.888,32.65 +2020-12-04 07:45:00,134.61,228.649,66.888,32.65 +2020-12-04 08:00:00,137.01,226.28,73.459,32.65 +2020-12-04 08:15:00,136.94,226.03599999999997,73.459,32.65 +2020-12-04 08:30:00,137.62,225.31599999999997,73.459,32.65 +2020-12-04 08:45:00,137.29,221.213,73.459,32.65 +2020-12-04 09:00:00,138.54,215.50599999999997,69.087,32.65 +2020-12-04 09:15:00,139.96,212.579,69.087,32.65 +2020-12-04 09:30:00,139.56,209.581,69.087,32.65 +2020-12-04 09:45:00,138.84,206.78799999999998,69.087,32.65 +2020-12-04 10:00:00,136.1,201.173,65.404,32.65 +2020-12-04 10:15:00,138.43,197.835,65.404,32.65 +2020-12-04 10:30:00,137.86,195.547,65.404,32.65 +2020-12-04 10:45:00,138.39,193.75599999999997,65.404,32.65 +2020-12-04 11:00:00,136.83,192.87,63.0,32.65 +2020-12-04 11:15:00,137.96,191.03599999999997,63.0,32.65 +2020-12-04 11:30:00,138.11,191.58900000000003,63.0,32.65 +2020-12-04 11:45:00,138.08,190.125,63.0,32.65 +2020-12-04 12:00:00,136.37,185.582,59.083,32.65 +2020-12-04 12:15:00,134.96,182.451,59.083,32.65 +2020-12-04 12:30:00,134.55,182.497,59.083,32.65 +2020-12-04 12:45:00,137.65,183.898,59.083,32.65 +2020-12-04 13:00:00,132.64,184.27200000000002,56.611999999999995,32.65 +2020-12-04 13:15:00,133.9,184.679,56.611999999999995,32.65 +2020-12-04 13:30:00,132.84,184.613,56.611999999999995,32.65 +2020-12-04 13:45:00,133.43,184.551,56.611999999999995,32.65 +2020-12-04 14:00:00,130.52,182.54,55.161,32.65 +2020-12-04 14:15:00,129.92,182.826,55.161,32.65 +2020-12-04 14:30:00,127.64,183.615,55.161,32.65 +2020-12-04 14:45:00,127.22,183.72099999999998,55.161,32.65 +2020-12-04 15:00:00,125.79,183.976,55.583,32.65 +2020-12-04 15:15:00,126.08,184.122,55.583,32.65 +2020-12-04 15:30:00,126.65,184.62599999999998,55.583,32.65 +2020-12-04 15:45:00,131.71,186.58900000000003,55.583,32.65 +2020-12-04 16:00:00,133.56,186.52,57.611999999999995,32.65 +2020-12-04 16:15:00,135.6,187.78900000000002,57.611999999999995,32.65 +2020-12-04 16:30:00,138.73,190.56400000000002,57.611999999999995,32.65 +2020-12-04 16:45:00,136.33,191.52700000000002,57.611999999999995,32.65 +2020-12-04 17:00:00,138.83,194.524,64.14,32.65 +2020-12-04 17:15:00,137.41,194.31099999999998,64.14,32.65 +2020-12-04 17:30:00,138.46,194.424,64.14,32.65 +2020-12-04 17:45:00,138.3,193.78900000000002,64.14,32.65 +2020-12-04 18:00:00,135.16,195.34900000000002,68.086,32.65 +2020-12-04 18:15:00,133.86,193.245,68.086,32.65 +2020-12-04 18:30:00,134.09,192.083,68.086,32.65 +2020-12-04 18:45:00,137.38,191.77700000000002,68.086,32.65 +2020-12-04 19:00:00,132.52,193.69400000000002,69.915,32.65 +2020-12-04 19:15:00,129.24,191.503,69.915,32.65 +2020-12-04 19:30:00,128.95,189.06900000000002,69.915,32.65 +2020-12-04 19:45:00,126.18,185.313,69.915,32.65 +2020-12-04 20:00:00,118.12,181.905,61.695,32.65 +2020-12-04 20:15:00,116.18,176.11900000000003,61.695,32.65 +2020-12-04 20:30:00,113.76,172.675,61.695,32.65 +2020-12-04 20:45:00,111.07,170.445,61.695,32.65 +2020-12-04 21:00:00,107.3,168.935,56.041000000000004,32.65 +2020-12-04 21:15:00,103.46,167.43400000000003,56.041000000000004,32.65 +2020-12-04 21:30:00,100.57,165.37400000000002,56.041000000000004,32.65 +2020-12-04 21:45:00,103.14,164.165,56.041000000000004,32.65 +2020-12-04 22:00:00,98.06,158.365,51.888999999999996,32.65 +2020-12-04 22:15:00,96.92,152.564,51.888999999999996,32.65 +2020-12-04 22:30:00,84.82,144.314,51.888999999999996,32.65 +2020-12-04 22:45:00,84.39,139.736,51.888999999999996,32.65 +2020-12-04 23:00:00,84.2,133.908,45.787,32.65 +2020-12-04 23:15:00,84.93,130.002,45.787,32.65 +2020-12-04 23:30:00,82.36,128.553,45.787,32.65 +2020-12-04 23:45:00,78.57,127.60700000000001,45.787,32.65 +2020-12-05 00:00:00,72.37,111.85600000000001,41.815,32.468 +2020-12-05 00:15:00,73.35,107.964,41.815,32.468 +2020-12-05 00:30:00,73.52,110.323,41.815,32.468 +2020-12-05 00:45:00,69.58,112.825,41.815,32.468 +2020-12-05 01:00:00,65.32,115.024,38.645,32.468 +2020-12-05 01:15:00,67.07,115.698,38.645,32.468 +2020-12-05 01:30:00,70.05,115.344,38.645,32.468 +2020-12-05 01:45:00,72.67,116.0,38.645,32.468 +2020-12-05 02:00:00,67.0,118.118,36.696,32.468 +2020-12-05 02:15:00,65.94,119.133,36.696,32.468 +2020-12-05 02:30:00,62.92,118.76799999999999,36.696,32.468 +2020-12-05 02:45:00,67.89,120.795,36.696,32.468 +2020-12-05 03:00:00,68.09,123.185,35.42,32.468 +2020-12-05 03:15:00,69.91,123.42200000000001,35.42,32.468 +2020-12-05 03:30:00,67.12,123.686,35.42,32.468 +2020-12-05 03:45:00,70.54,125.135,35.42,32.468 +2020-12-05 04:00:00,70.06,134.191,35.167,32.468 +2020-12-05 04:15:00,64.46,143.546,35.167,32.468 +2020-12-05 04:30:00,62.74,144.289,35.167,32.468 +2020-12-05 04:45:00,63.37,145.262,35.167,32.468 +2020-12-05 05:00:00,63.4,163.07299999999998,35.311,32.468 +2020-12-05 05:15:00,63.89,175.081,35.311,32.468 +2020-12-05 05:30:00,64.64,171.774,35.311,32.468 +2020-12-05 05:45:00,66.98,169.051,35.311,32.468 +2020-12-05 06:00:00,68.66,184.03099999999998,37.117,32.468 +2020-12-05 06:15:00,70.86,204.283,37.117,32.468 +2020-12-05 06:30:00,72.62,199.84,37.117,32.468 +2020-12-05 06:45:00,75.93,194.908,37.117,32.468 +2020-12-05 07:00:00,78.46,189.69299999999998,40.948,32.468 +2020-12-05 07:15:00,81.06,194.37400000000002,40.948,32.468 +2020-12-05 07:30:00,83.25,199.646,40.948,32.468 +2020-12-05 07:45:00,85.28,204.113,40.948,32.468 +2020-12-05 08:00:00,88.29,205.796,44.903,32.468 +2020-12-05 08:15:00,88.3,209.168,44.903,32.468 +2020-12-05 08:30:00,89.21,210.06599999999997,44.903,32.468 +2020-12-05 08:45:00,91.96,209.077,44.903,32.468 +2020-12-05 09:00:00,93.43,205.105,46.283,32.468 +2020-12-05 09:15:00,93.03,202.935,46.283,32.468 +2020-12-05 09:30:00,95.57,200.84099999999998,46.283,32.468 +2020-12-05 09:45:00,93.63,198.196,46.283,32.468 +2020-12-05 10:00:00,95.54,192.859,44.103,32.468 +2020-12-05 10:15:00,96.38,189.66,44.103,32.468 +2020-12-05 10:30:00,98.31,187.513,44.103,32.468 +2020-12-05 10:45:00,98.64,187.00599999999997,44.103,32.468 +2020-12-05 11:00:00,101.27,186.32299999999998,42.373999999999995,32.468 +2020-12-05 11:15:00,101.54,183.795,42.373999999999995,32.468 +2020-12-05 11:30:00,99.33,183.24,42.373999999999995,32.468 +2020-12-05 11:45:00,97.85,180.82299999999998,42.373999999999995,32.468 +2020-12-05 12:00:00,99.19,175.40900000000002,39.937,32.468 +2020-12-05 12:15:00,96.78,172.928,39.937,32.468 +2020-12-05 12:30:00,95.3,173.30599999999998,39.937,32.468 +2020-12-05 12:45:00,95.84,173.953,39.937,32.468 +2020-12-05 13:00:00,90.76,173.893,37.138000000000005,32.468 +2020-12-05 13:15:00,88.94,172.267,37.138000000000005,32.468 +2020-12-05 13:30:00,88.02,171.75099999999998,37.138000000000005,32.468 +2020-12-05 13:45:00,84.59,172.145,37.138000000000005,32.468 +2020-12-05 14:00:00,86.03,171.328,36.141999999999996,32.468 +2020-12-05 14:15:00,87.73,171.08599999999998,36.141999999999996,32.468 +2020-12-05 14:30:00,88.66,170.1,36.141999999999996,32.468 +2020-12-05 14:45:00,88.57,170.43400000000003,36.141999999999996,32.468 +2020-12-05 15:00:00,90.45,171.362,37.964,32.468 +2020-12-05 15:15:00,92.3,172.308,37.964,32.468 +2020-12-05 15:30:00,95.92,174.362,37.964,32.468 +2020-12-05 15:45:00,95.54,176.33700000000002,37.964,32.468 +2020-12-05 16:00:00,99.12,175.083,40.699,32.468 +2020-12-05 16:15:00,103.2,177.25900000000001,40.699,32.468 +2020-12-05 16:30:00,101.85,179.989,40.699,32.468 +2020-12-05 16:45:00,102.17,181.863,40.699,32.468 +2020-12-05 17:00:00,105.16,184.262,46.216,32.468 +2020-12-05 17:15:00,104.75,185.71099999999998,46.216,32.468 +2020-12-05 17:30:00,109.26,185.743,46.216,32.468 +2020-12-05 17:45:00,106.26,184.71400000000003,46.216,32.468 +2020-12-05 18:00:00,107.15,185.812,51.123999999999995,32.468 +2020-12-05 18:15:00,107.16,185.459,51.123999999999995,32.468 +2020-12-05 18:30:00,106.1,185.628,51.123999999999995,32.468 +2020-12-05 18:45:00,105.22,182.0,51.123999999999995,32.468 +2020-12-05 19:00:00,103.27,184.81900000000002,52.336000000000006,32.468 +2020-12-05 19:15:00,102.54,182.122,52.336000000000006,32.468 +2020-12-05 19:30:00,101.72,180.421,52.336000000000006,32.468 +2020-12-05 19:45:00,99.84,176.465,52.336000000000006,32.468 +2020-12-05 20:00:00,95.78,175.215,48.825,32.468 +2020-12-05 20:15:00,92.88,171.52599999999998,48.825,32.468 +2020-12-05 20:30:00,90.67,167.706,48.825,32.468 +2020-12-05 20:45:00,89.27,165.101,48.825,32.468 +2020-12-05 21:00:00,84.32,165.80599999999998,43.729,32.468 +2020-12-05 21:15:00,84.83,164.725,43.729,32.468 +2020-12-05 21:30:00,83.33,163.88400000000001,43.729,32.468 +2020-12-05 21:45:00,81.56,162.278,43.729,32.468 +2020-12-05 22:00:00,77.6,157.82299999999998,44.126000000000005,32.468 +2020-12-05 22:15:00,76.48,154.488,44.126000000000005,32.468 +2020-12-05 22:30:00,73.47,152.457,44.126000000000005,32.468 +2020-12-05 22:45:00,72.33,149.74200000000002,44.126000000000005,32.468 +2020-12-05 23:00:00,67.46,146.29399999999998,38.169000000000004,32.468 +2020-12-05 23:15:00,67.5,140.766,38.169000000000004,32.468 +2020-12-05 23:30:00,65.09,137.606,38.169000000000004,32.468 +2020-12-05 23:45:00,63.28,134.237,38.169000000000004,32.468 +2020-12-06 00:00:00,59.2,112.60799999999999,35.232,32.468 +2020-12-06 00:15:00,58.21,108.366,35.232,32.468 +2020-12-06 00:30:00,58.38,110.34700000000001,35.232,32.468 +2020-12-06 00:45:00,57.76,113.522,35.232,32.468 +2020-12-06 01:00:00,53.35,115.618,31.403000000000002,32.468 +2020-12-06 01:15:00,54.31,117.323,31.403000000000002,32.468 +2020-12-06 01:30:00,53.96,117.494,31.403000000000002,32.468 +2020-12-06 01:45:00,53.89,117.824,31.403000000000002,32.468 +2020-12-06 02:00:00,52.08,119.219,30.69,32.468 +2020-12-06 02:15:00,53.48,119.389,30.69,32.468 +2020-12-06 02:30:00,52.62,119.875,30.69,32.468 +2020-12-06 02:45:00,52.89,122.36,30.69,32.468 +2020-12-06 03:00:00,50.89,125.052,29.516,32.468 +2020-12-06 03:15:00,52.01,124.8,29.516,32.468 +2020-12-06 03:30:00,49.54,126.455,29.516,32.468 +2020-12-06 03:45:00,51.95,127.82600000000001,29.516,32.468 +2020-12-06 04:00:00,51.32,136.608,29.148000000000003,32.468 +2020-12-06 04:15:00,52.22,144.953,29.148000000000003,32.468 +2020-12-06 04:30:00,53.06,145.747,29.148000000000003,32.468 +2020-12-06 04:45:00,54.12,146.994,29.148000000000003,32.468 +2020-12-06 05:00:00,54.06,161.283,28.706,32.468 +2020-12-06 05:15:00,55.45,170.83599999999998,28.706,32.468 +2020-12-06 05:30:00,55.14,167.392,28.706,32.468 +2020-12-06 05:45:00,55.66,164.93400000000003,28.706,32.468 +2020-12-06 06:00:00,57.08,179.829,28.771,32.468 +2020-12-06 06:15:00,58.52,198.37599999999998,28.771,32.468 +2020-12-06 06:30:00,60.18,192.88299999999998,28.771,32.468 +2020-12-06 06:45:00,62.27,186.947,28.771,32.468 +2020-12-06 07:00:00,64.58,184.169,31.39,32.468 +2020-12-06 07:15:00,66.66,188.005,31.39,32.468 +2020-12-06 07:30:00,70.38,192.06599999999997,31.39,32.468 +2020-12-06 07:45:00,71.0,195.782,31.39,32.468 +2020-12-06 08:00:00,72.79,199.30599999999998,34.972,32.468 +2020-12-06 08:15:00,75.41,202.59,34.972,32.468 +2020-12-06 08:30:00,74.46,205.142,34.972,32.468 +2020-12-06 08:45:00,78.84,206.12,34.972,32.468 +2020-12-06 09:00:00,80.64,201.72299999999998,36.709,32.468 +2020-12-06 09:15:00,82.63,200.10299999999998,36.709,32.468 +2020-12-06 09:30:00,84.01,197.863,36.709,32.468 +2020-12-06 09:45:00,85.27,195.09799999999998,36.709,32.468 +2020-12-06 10:00:00,87.21,192.24400000000003,35.812,32.468 +2020-12-06 10:15:00,88.37,189.549,35.812,32.468 +2020-12-06 10:30:00,89.09,187.975,35.812,32.468 +2020-12-06 10:45:00,91.13,185.59900000000002,35.812,32.468 +2020-12-06 11:00:00,92.13,185.801,36.746,32.468 +2020-12-06 11:15:00,94.24,183.386,36.746,32.468 +2020-12-06 11:30:00,96.69,181.97799999999998,36.746,32.468 +2020-12-06 11:45:00,97.52,180.15400000000002,36.746,32.468 +2020-12-06 12:00:00,95.11,174.195,35.048,32.468 +2020-12-06 12:15:00,93.73,173.592,35.048,32.468 +2020-12-06 12:30:00,90.61,172.55700000000002,35.048,32.468 +2020-12-06 12:45:00,90.11,172.25,35.048,32.468 +2020-12-06 13:00:00,86.89,171.447,29.987,32.468 +2020-12-06 13:15:00,86.08,172.782,29.987,32.468 +2020-12-06 13:30:00,85.02,172.041,29.987,32.468 +2020-12-06 13:45:00,84.86,171.791,29.987,32.468 +2020-12-06 14:00:00,83.89,171.206,27.21,32.468 +2020-12-06 14:15:00,85.55,172.157,27.21,32.468 +2020-12-06 14:30:00,85.84,172.392,27.21,32.468 +2020-12-06 14:45:00,85.27,172.317,27.21,32.468 +2020-12-06 15:00:00,86.37,171.761,27.726999999999997,32.468 +2020-12-06 15:15:00,86.71,173.438,27.726999999999997,32.468 +2020-12-06 15:30:00,87.73,176.092,27.726999999999997,32.468 +2020-12-06 15:45:00,90.08,178.752,27.726999999999997,32.468 +2020-12-06 16:00:00,91.02,179.32,32.23,32.468 +2020-12-06 16:15:00,92.78,180.62599999999998,32.23,32.468 +2020-12-06 16:30:00,94.77,183.63,32.23,32.468 +2020-12-06 16:45:00,95.94,185.667,32.23,32.468 +2020-12-06 17:00:00,99.97,188.047,42.016999999999996,32.468 +2020-12-06 17:15:00,100.43,189.213,42.016999999999996,32.468 +2020-12-06 17:30:00,101.22,189.55,42.016999999999996,32.468 +2020-12-06 17:45:00,103.06,190.769,42.016999999999996,32.468 +2020-12-06 18:00:00,104.02,191.354,49.338,32.468 +2020-12-06 18:15:00,103.43,192.268,49.338,32.468 +2020-12-06 18:30:00,103.55,190.385,49.338,32.468 +2020-12-06 18:45:00,102.55,188.59400000000002,49.338,32.468 +2020-12-06 19:00:00,101.83,191.08900000000003,52.369,32.468 +2020-12-06 19:15:00,100.33,188.959,52.369,32.468 +2020-12-06 19:30:00,99.34,187.078,52.369,32.468 +2020-12-06 19:45:00,96.53,184.515,52.369,32.468 +2020-12-06 20:00:00,95.12,183.24099999999999,50.405,32.468 +2020-12-06 20:15:00,95.13,180.514,50.405,32.468 +2020-12-06 20:30:00,94.12,177.896,50.405,32.468 +2020-12-06 20:45:00,99.95,174.113,50.405,32.468 +2020-12-06 21:00:00,96.19,172.24400000000003,46.235,32.468 +2020-12-06 21:15:00,96.46,170.52599999999998,46.235,32.468 +2020-12-06 21:30:00,87.84,169.972,46.235,32.468 +2020-12-06 21:45:00,89.67,168.50900000000001,46.235,32.468 +2020-12-06 22:00:00,87.62,162.901,46.861000000000004,32.468 +2020-12-06 22:15:00,90.69,158.804,46.861000000000004,32.468 +2020-12-06 22:30:00,89.86,153.694,46.861000000000004,32.468 +2020-12-06 22:45:00,88.9,150.136,46.861000000000004,32.468 +2020-12-06 23:00:00,78.62,143.899,41.302,32.468 +2020-12-06 23:15:00,77.12,140.21,41.302,32.468 +2020-12-06 23:30:00,79.89,137.859,41.302,32.468 +2020-12-06 23:45:00,77.11,135.356,41.302,32.468 +2020-12-07 00:00:00,72.47,117.10600000000001,37.164,32.65 +2020-12-07 00:15:00,69.76,115.786,37.164,32.65 +2020-12-07 00:30:00,69.85,117.88799999999999,37.164,32.65 +2020-12-07 00:45:00,69.38,120.509,37.164,32.65 +2020-12-07 01:00:00,67.99,122.65899999999999,34.994,32.65 +2020-12-07 01:15:00,70.59,123.839,34.994,32.65 +2020-12-07 01:30:00,73.49,124.073,34.994,32.65 +2020-12-07 01:45:00,70.94,124.50200000000001,34.994,32.65 +2020-12-07 02:00:00,66.53,125.896,34.571,32.65 +2020-12-07 02:15:00,67.94,127.52,34.571,32.65 +2020-12-07 02:30:00,66.77,128.33700000000002,34.571,32.65 +2020-12-07 02:45:00,67.8,130.205,34.571,32.65 +2020-12-07 03:00:00,68.56,134.14700000000002,33.934,32.65 +2020-12-07 03:15:00,68.92,135.562,33.934,32.65 +2020-12-07 03:30:00,72.78,136.951,33.934,32.65 +2020-12-07 03:45:00,77.58,137.773,33.934,32.65 +2020-12-07 04:00:00,78.96,150.85,34.107,32.65 +2020-12-07 04:15:00,76.3,163.312,34.107,32.65 +2020-12-07 04:30:00,75.15,166.274,34.107,32.65 +2020-12-07 04:45:00,76.91,167.683,34.107,32.65 +2020-12-07 05:00:00,80.43,197.60299999999998,39.575,32.65 +2020-12-07 05:15:00,83.47,227.14,39.575,32.65 +2020-12-07 05:30:00,88.42,224.0,39.575,32.65 +2020-12-07 05:45:00,93.74,215.947,39.575,32.65 +2020-12-07 06:00:00,104.42,213.045,56.156000000000006,32.65 +2020-12-07 06:15:00,111.84,216.856,56.156000000000006,32.65 +2020-12-07 06:30:00,115.43,219.375,56.156000000000006,32.65 +2020-12-07 06:45:00,121.15,222.22299999999998,56.156000000000006,32.65 +2020-12-07 07:00:00,126.05,221.801,67.926,32.65 +2020-12-07 07:15:00,129.31,226.882,67.926,32.65 +2020-12-07 07:30:00,132.06,230.178,67.926,32.65 +2020-12-07 07:45:00,131.74,231.34599999999998,67.926,32.65 +2020-12-07 08:00:00,134.22,230.016,72.58,32.65 +2020-12-07 08:15:00,134.5,231.165,72.58,32.65 +2020-12-07 08:30:00,134.15,229.669,72.58,32.65 +2020-12-07 08:45:00,132.35,227.305,72.58,32.65 +2020-12-07 09:00:00,135.85,221.885,66.984,32.65 +2020-12-07 09:15:00,133.39,216.815,66.984,32.65 +2020-12-07 09:30:00,137.13,213.58900000000003,66.984,32.65 +2020-12-07 09:45:00,137.46,211.10299999999998,66.984,32.65 +2020-12-07 10:00:00,137.29,207.167,63.158,32.65 +2020-12-07 10:15:00,137.05,204.125,63.158,32.65 +2020-12-07 10:30:00,137.9,201.66,63.158,32.65 +2020-12-07 10:45:00,135.05,200.014,63.158,32.65 +2020-12-07 11:00:00,137.29,197.605,61.141000000000005,32.65 +2020-12-07 11:15:00,137.89,196.96099999999998,61.141000000000005,32.65 +2020-12-07 11:30:00,136.52,196.918,61.141000000000005,32.65 +2020-12-07 11:45:00,136.73,194.68599999999998,61.141000000000005,32.65 +2020-12-07 12:00:00,135.1,190.422,57.961000000000006,32.65 +2020-12-07 12:15:00,132.49,189.833,57.961000000000006,32.65 +2020-12-07 12:30:00,133.75,189.05700000000002,57.961000000000006,32.65 +2020-12-07 12:45:00,132.35,190.261,57.961000000000006,32.65 +2020-12-07 13:00:00,131.69,190.002,56.843,32.65 +2020-12-07 13:15:00,130.03,189.96599999999998,56.843,32.65 +2020-12-07 13:30:00,129.19,188.69799999999998,56.843,32.65 +2020-12-07 13:45:00,129.62,188.476,56.843,32.65 +2020-12-07 14:00:00,131.03,187.24900000000002,55.992,32.65 +2020-12-07 14:15:00,133.01,187.574,55.992,32.65 +2020-12-07 14:30:00,132.23,187.301,55.992,32.65 +2020-12-07 14:45:00,130.84,187.215,55.992,32.65 +2020-12-07 15:00:00,132.97,188.435,57.523,32.65 +2020-12-07 15:15:00,132.34,188.688,57.523,32.65 +2020-12-07 15:30:00,133.31,190.551,57.523,32.65 +2020-12-07 15:45:00,133.8,192.769,57.523,32.65 +2020-12-07 16:00:00,136.26,193.521,59.471000000000004,32.65 +2020-12-07 16:15:00,137.17,194.09599999999998,59.471000000000004,32.65 +2020-12-07 16:30:00,133.45,196.149,59.471000000000004,32.65 +2020-12-07 16:45:00,141.66,197.045,59.471000000000004,32.65 +2020-12-07 17:00:00,155.57,199.225,65.066,32.65 +2020-12-07 17:15:00,156.59,199.476,65.066,32.65 +2020-12-07 17:30:00,158.11,199.28400000000002,65.066,32.65 +2020-12-07 17:45:00,143.66,199.03099999999998,65.066,32.65 +2020-12-07 18:00:00,137.35,200.05900000000003,69.581,32.65 +2020-12-07 18:15:00,140.33,198.747,69.581,32.65 +2020-12-07 18:30:00,138.53,197.518,69.581,32.65 +2020-12-07 18:45:00,138.89,196.44299999999998,69.581,32.65 +2020-12-07 19:00:00,138.22,197.331,73.771,32.65 +2020-12-07 19:15:00,133.86,194.03,73.771,32.65 +2020-12-07 19:30:00,132.32,192.628,73.771,32.65 +2020-12-07 19:45:00,130.15,189.213,73.771,32.65 +2020-12-07 20:00:00,125.89,185.597,65.035,32.65 +2020-12-07 20:15:00,120.27,180.40099999999998,65.035,32.65 +2020-12-07 20:30:00,116.83,175.91099999999997,65.035,32.65 +2020-12-07 20:45:00,115.19,173.76,65.035,32.65 +2020-12-07 21:00:00,113.15,172.405,58.7,32.65 +2020-12-07 21:15:00,109.2,169.50599999999997,58.7,32.65 +2020-12-07 21:30:00,106.77,168.132,58.7,32.65 +2020-12-07 21:45:00,102.99,166.173,58.7,32.65 +2020-12-07 22:00:00,100.77,157.714,53.888000000000005,32.65 +2020-12-07 22:15:00,98.17,152.30700000000002,53.888000000000005,32.65 +2020-12-07 22:30:00,96.13,137.769,53.888000000000005,32.65 +2020-12-07 22:45:00,92.55,129.42,53.888000000000005,32.65 +2020-12-07 23:00:00,91.82,123.929,45.501999999999995,32.65 +2020-12-07 23:15:00,89.24,122.87299999999999,45.501999999999995,32.65 +2020-12-07 23:30:00,86.63,123.27600000000001,45.501999999999995,32.65 +2020-12-07 23:45:00,83.86,123.38,45.501999999999995,32.65 +2020-12-08 00:00:00,81.48,116.75399999999999,43.537,32.65 +2020-12-08 00:15:00,80.94,116.822,43.537,32.65 +2020-12-08 00:30:00,78.77,117.975,43.537,32.65 +2020-12-08 00:45:00,79.27,119.594,43.537,32.65 +2020-12-08 01:00:00,79.97,121.589,41.854,32.65 +2020-12-08 01:15:00,75.56,122.323,41.854,32.65 +2020-12-08 01:30:00,74.99,122.73700000000001,41.854,32.65 +2020-12-08 01:45:00,74.98,123.461,41.854,32.65 +2020-12-08 02:00:00,75.24,124.875,40.321,32.65 +2020-12-08 02:15:00,76.79,126.37799999999999,40.321,32.65 +2020-12-08 02:30:00,77.69,126.604,40.321,32.65 +2020-12-08 02:45:00,75.39,128.481,40.321,32.65 +2020-12-08 03:00:00,75.09,131.209,39.632,32.65 +2020-12-08 03:15:00,77.11,131.793,39.632,32.65 +2020-12-08 03:30:00,77.43,133.668,39.632,32.65 +2020-12-08 03:45:00,78.5,134.694,39.632,32.65 +2020-12-08 04:00:00,80.28,147.555,40.183,32.65 +2020-12-08 04:15:00,79.41,159.66299999999998,40.183,32.65 +2020-12-08 04:30:00,80.97,162.30100000000002,40.183,32.65 +2020-12-08 04:45:00,83.79,164.922,40.183,32.65 +2020-12-08 05:00:00,87.76,199.80200000000002,43.945,32.65 +2020-12-08 05:15:00,91.47,229.07,43.945,32.65 +2020-12-08 05:30:00,96.61,224.416,43.945,32.65 +2020-12-08 05:45:00,100.78,216.40099999999998,43.945,32.65 +2020-12-08 06:00:00,109.95,212.322,56.048,32.65 +2020-12-08 06:15:00,114.88,217.763,56.048,32.65 +2020-12-08 06:30:00,120.13,219.68400000000003,56.048,32.65 +2020-12-08 06:45:00,125.43,222.21400000000003,56.048,32.65 +2020-12-08 07:00:00,131.43,221.608,65.74,32.65 +2020-12-08 07:15:00,135.23,226.53900000000002,65.74,32.65 +2020-12-08 07:30:00,137.34,229.292,65.74,32.65 +2020-12-08 07:45:00,140.61,230.69,65.74,32.65 +2020-12-08 08:00:00,140.68,229.48,72.757,32.65 +2020-12-08 08:15:00,141.83,229.604,72.757,32.65 +2020-12-08 08:30:00,140.68,227.933,72.757,32.65 +2020-12-08 08:45:00,140.89,225.28400000000002,72.757,32.65 +2020-12-08 09:00:00,137.03,218.998,67.692,32.65 +2020-12-08 09:15:00,139.39,215.53799999999998,67.692,32.65 +2020-12-08 09:30:00,139.92,212.989,67.692,32.65 +2020-12-08 09:45:00,139.63,210.25900000000001,67.692,32.65 +2020-12-08 10:00:00,141.01,205.74200000000002,63.506,32.65 +2020-12-08 10:15:00,141.07,201.618,63.506,32.65 +2020-12-08 10:30:00,142.01,199.31,63.506,32.65 +2020-12-08 10:45:00,148.52,197.928,63.506,32.65 +2020-12-08 11:00:00,159.29,196.99099999999999,60.758,32.65 +2020-12-08 11:15:00,158.54,195.997,60.758,32.65 +2020-12-08 11:30:00,159.27,194.8,60.758,32.65 +2020-12-08 11:45:00,146.55,193.266,60.758,32.65 +2020-12-08 12:00:00,143.87,187.637,57.519,32.65 +2020-12-08 12:15:00,142.59,186.643,57.519,32.65 +2020-12-08 12:30:00,142.23,186.595,57.519,32.65 +2020-12-08 12:45:00,140.71,187.507,57.519,32.65 +2020-12-08 13:00:00,136.17,186.812,56.46,32.65 +2020-12-08 13:15:00,135.08,186.417,56.46,32.65 +2020-12-08 13:30:00,133.39,186.313,56.46,32.65 +2020-12-08 13:45:00,132.97,186.27599999999998,56.46,32.65 +2020-12-08 14:00:00,133.26,185.308,56.207,32.65 +2020-12-08 14:15:00,134.5,185.797,56.207,32.65 +2020-12-08 14:30:00,134.65,186.173,56.207,32.65 +2020-12-08 14:45:00,134.64,186.024,56.207,32.65 +2020-12-08 15:00:00,135.74,186.833,57.391999999999996,32.65 +2020-12-08 15:15:00,137.12,187.408,57.391999999999996,32.65 +2020-12-08 15:30:00,138.16,189.517,57.391999999999996,32.65 +2020-12-08 15:45:00,139.22,191.333,57.391999999999996,32.65 +2020-12-08 16:00:00,144.76,192.426,59.955,32.65 +2020-12-08 16:15:00,147.35,193.497,59.955,32.65 +2020-12-08 16:30:00,147.5,196.205,59.955,32.65 +2020-12-08 16:45:00,149.58,197.412,59.955,32.65 +2020-12-08 17:00:00,161.96,200.109,67.063,32.65 +2020-12-08 17:15:00,162.06,200.412,67.063,32.65 +2020-12-08 17:30:00,163.06,200.90400000000002,67.063,32.65 +2020-12-08 17:45:00,150.37,200.532,67.063,32.65 +2020-12-08 18:00:00,142.45,201.481,71.477,32.65 +2020-12-08 18:15:00,144.58,199.622,71.477,32.65 +2020-12-08 18:30:00,143.42,198.09599999999998,71.477,32.65 +2020-12-08 18:45:00,143.59,197.825,71.477,32.65 +2020-12-08 19:00:00,141.4,198.78900000000002,74.32,32.65 +2020-12-08 19:15:00,138.68,195.19799999999998,74.32,32.65 +2020-12-08 19:30:00,136.14,193.11599999999999,74.32,32.65 +2020-12-08 19:45:00,135.11,189.708,74.32,32.65 +2020-12-08 20:00:00,130.38,186.248,66.157,32.65 +2020-12-08 20:15:00,125.0,180.418,66.157,32.65 +2020-12-08 20:30:00,121.61,176.912,66.157,32.65 +2020-12-08 20:45:00,119.36,174.207,66.157,32.65 +2020-12-08 21:00:00,116.99,172.13299999999998,59.806000000000004,32.65 +2020-12-08 21:15:00,116.17,170.135,59.806000000000004,32.65 +2020-12-08 21:30:00,118.31,168.03,59.806000000000004,32.65 +2020-12-08 21:45:00,115.49,166.30599999999998,59.806000000000004,32.65 +2020-12-08 22:00:00,106.63,159.56,54.785,32.65 +2020-12-08 22:15:00,103.62,153.899,54.785,32.65 +2020-12-08 22:30:00,104.21,139.499,54.785,32.65 +2020-12-08 22:45:00,103.55,131.433,54.785,32.65 +2020-12-08 23:00:00,100.51,125.949,47.176,32.65 +2020-12-08 23:15:00,93.05,123.975,47.176,32.65 +2020-12-08 23:30:00,89.98,124.031,47.176,32.65 +2020-12-08 23:45:00,88.91,123.681,47.176,32.65 +2020-12-09 00:00:00,91.27,117.072,43.42,32.65 +2020-12-09 00:15:00,91.07,117.11,43.42,32.65 +2020-12-09 00:30:00,87.44,118.256,43.42,32.65 +2020-12-09 00:45:00,84.26,119.85700000000001,43.42,32.65 +2020-12-09 01:00:00,86.4,121.887,40.869,32.65 +2020-12-09 01:15:00,87.29,122.62,40.869,32.65 +2020-12-09 01:30:00,87.03,123.04299999999999,40.869,32.65 +2020-12-09 01:45:00,82.74,123.751,40.869,32.65 +2020-12-09 02:00:00,80.31,125.185,39.541,32.65 +2020-12-09 02:15:00,81.19,126.69,39.541,32.65 +2020-12-09 02:30:00,86.0,126.91799999999999,39.541,32.65 +2020-12-09 02:45:00,86.44,128.79399999999998,39.541,32.65 +2020-12-09 03:00:00,85.55,131.509,39.052,32.65 +2020-12-09 03:15:00,80.17,132.123,39.052,32.65 +2020-12-09 03:30:00,84.96,134.0,39.052,32.65 +2020-12-09 03:45:00,88.74,135.02700000000002,39.052,32.65 +2020-12-09 04:00:00,90.1,147.856,40.36,32.65 +2020-12-09 04:15:00,86.33,159.96,40.36,32.65 +2020-12-09 04:30:00,85.2,162.585,40.36,32.65 +2020-12-09 04:45:00,86.88,165.207,40.36,32.65 +2020-12-09 05:00:00,93.26,200.05,43.133,32.65 +2020-12-09 05:15:00,99.1,229.263,43.133,32.65 +2020-12-09 05:30:00,104.98,224.636,43.133,32.65 +2020-12-09 05:45:00,108.07,216.644,43.133,32.65 +2020-12-09 06:00:00,110.28,212.59,54.953,32.65 +2020-12-09 06:15:00,116.51,218.035,54.953,32.65 +2020-12-09 06:30:00,119.87,220.00099999999998,54.953,32.65 +2020-12-09 06:45:00,127.56,222.575,54.953,32.65 +2020-12-09 07:00:00,130.61,221.97299999999998,66.566,32.65 +2020-12-09 07:15:00,131.47,226.91099999999997,66.566,32.65 +2020-12-09 07:30:00,133.34,229.667,66.566,32.65 +2020-12-09 07:45:00,136.34,231.06599999999997,66.566,32.65 +2020-12-09 08:00:00,138.72,229.86599999999999,72.902,32.65 +2020-12-09 08:15:00,140.05,229.984,72.902,32.65 +2020-12-09 08:30:00,140.81,228.317,72.902,32.65 +2020-12-09 08:45:00,140.14,225.637,72.902,32.65 +2020-12-09 09:00:00,141.66,219.325,68.465,32.65 +2020-12-09 09:15:00,142.55,215.872,68.465,32.65 +2020-12-09 09:30:00,144.01,213.329,68.465,32.65 +2020-12-09 09:45:00,148.03,210.588,68.465,32.65 +2020-12-09 10:00:00,148.43,206.063,63.625,32.65 +2020-12-09 10:15:00,148.69,201.92,63.625,32.65 +2020-12-09 10:30:00,147.3,199.59,63.625,32.65 +2020-12-09 10:45:00,150.11,198.201,63.625,32.65 +2020-12-09 11:00:00,165.23,197.245,61.628,32.65 +2020-12-09 11:15:00,167.64,196.24,61.628,32.65 +2020-12-09 11:30:00,171.35,195.041,61.628,32.65 +2020-12-09 11:45:00,158.93,193.50099999999998,61.628,32.65 +2020-12-09 12:00:00,150.69,187.872,58.708999999999996,32.65 +2020-12-09 12:15:00,147.05,186.891,58.708999999999996,32.65 +2020-12-09 12:30:00,147.05,186.858,58.708999999999996,32.65 +2020-12-09 12:45:00,144.87,187.774,58.708999999999996,32.65 +2020-12-09 13:00:00,143.99,187.047,57.373000000000005,32.65 +2020-12-09 13:15:00,143.5,186.655,57.373000000000005,32.65 +2020-12-09 13:30:00,140.33,186.545,57.373000000000005,32.65 +2020-12-09 13:45:00,139.48,186.498,57.373000000000005,32.65 +2020-12-09 14:00:00,139.53,185.511,57.684,32.65 +2020-12-09 14:15:00,138.98,186.005,57.684,32.65 +2020-12-09 14:30:00,138.49,186.405,57.684,32.65 +2020-12-09 14:45:00,137.14,186.267,57.684,32.65 +2020-12-09 15:00:00,135.82,187.095,58.03,32.65 +2020-12-09 15:15:00,135.39,187.666,58.03,32.65 +2020-12-09 15:30:00,138.38,189.798,58.03,32.65 +2020-12-09 15:45:00,138.54,191.613,58.03,32.65 +2020-12-09 16:00:00,139.67,192.706,59.97,32.65 +2020-12-09 16:15:00,145.31,193.799,59.97,32.65 +2020-12-09 16:30:00,147.26,196.513,59.97,32.65 +2020-12-09 16:45:00,151.24,197.75099999999998,59.97,32.65 +2020-12-09 17:00:00,169.05,200.424,65.661,32.65 +2020-12-09 17:15:00,169.17,200.75400000000002,65.661,32.65 +2020-12-09 17:30:00,170.79,201.261,65.661,32.65 +2020-12-09 17:45:00,149.97,200.899,65.661,32.65 +2020-12-09 18:00:00,141.53,201.86599999999999,70.96300000000001,32.65 +2020-12-09 18:15:00,143.4,199.97400000000002,70.96300000000001,32.65 +2020-12-09 18:30:00,142.84,198.456,70.96300000000001,32.65 +2020-12-09 18:45:00,143.61,198.19400000000002,70.96300000000001,32.65 +2020-12-09 19:00:00,142.88,199.144,74.133,32.65 +2020-12-09 19:15:00,140.01,195.545,74.133,32.65 +2020-12-09 19:30:00,138.02,193.44799999999998,74.133,32.65 +2020-12-09 19:45:00,135.99,190.014,74.133,32.65 +2020-12-09 20:00:00,129.82,186.553,65.613,32.65 +2020-12-09 20:15:00,124.78,180.71400000000003,65.613,32.65 +2020-12-09 20:30:00,122.6,177.18400000000003,65.613,32.65 +2020-12-09 20:45:00,120.04,174.5,65.613,32.65 +2020-12-09 21:00:00,116.74,172.41,58.583,32.65 +2020-12-09 21:15:00,113.73,170.396,58.583,32.65 +2020-12-09 21:30:00,111.56,168.293,58.583,32.65 +2020-12-09 21:45:00,112.32,166.574,58.583,32.65 +2020-12-09 22:00:00,111.72,159.83700000000002,54.411,32.65 +2020-12-09 22:15:00,110.88,154.179,54.411,32.65 +2020-12-09 22:30:00,104.66,139.827,54.411,32.65 +2020-12-09 22:45:00,99.54,131.77,54.411,32.65 +2020-12-09 23:00:00,93.47,126.26100000000001,47.878,32.65 +2020-12-09 23:15:00,92.23,124.285,47.878,32.65 +2020-12-09 23:30:00,90.56,124.353,47.878,32.65 +2020-12-09 23:45:00,89.81,123.98700000000001,47.878,32.65 +2020-12-10 00:00:00,92.49,117.383,44.513000000000005,32.65 +2020-12-10 00:15:00,87.7,117.391,44.513000000000005,32.65 +2020-12-10 00:30:00,90.23,118.53200000000001,44.513000000000005,32.65 +2020-12-10 00:45:00,85.04,120.113,44.513000000000005,32.65 +2020-12-10 01:00:00,82.13,122.18,43.169,32.65 +2020-12-10 01:15:00,80.96,122.911,43.169,32.65 +2020-12-10 01:30:00,85.8,123.34,43.169,32.65 +2020-12-10 01:45:00,87.1,124.03299999999999,43.169,32.65 +2020-12-10 02:00:00,87.01,125.48899999999999,41.763999999999996,32.65 +2020-12-10 02:15:00,82.06,126.995,41.763999999999996,32.65 +2020-12-10 02:30:00,85.45,127.22399999999999,41.763999999999996,32.65 +2020-12-10 02:45:00,87.43,129.1,41.763999999999996,32.65 +2020-12-10 03:00:00,87.6,131.804,41.155,32.65 +2020-12-10 03:15:00,83.72,132.446,41.155,32.65 +2020-12-10 03:30:00,87.63,134.326,41.155,32.65 +2020-12-10 03:45:00,90.33,135.352,41.155,32.65 +2020-12-10 04:00:00,91.8,148.15,41.96,32.65 +2020-12-10 04:15:00,88.57,160.25,41.96,32.65 +2020-12-10 04:30:00,87.86,162.861,41.96,32.65 +2020-12-10 04:45:00,87.24,165.486,41.96,32.65 +2020-12-10 05:00:00,94.72,200.29,45.206,32.65 +2020-12-10 05:15:00,98.25,229.452,45.206,32.65 +2020-12-10 05:30:00,98.01,224.84900000000002,45.206,32.65 +2020-12-10 05:45:00,102.63,216.87900000000002,45.206,32.65 +2020-12-10 06:00:00,109.41,212.851,55.398999999999994,32.65 +2020-12-10 06:15:00,116.05,218.298,55.398999999999994,32.65 +2020-12-10 06:30:00,120.99,220.30900000000003,55.398999999999994,32.65 +2020-12-10 06:45:00,125.66,222.928,55.398999999999994,32.65 +2020-12-10 07:00:00,129.75,222.33,64.627,32.65 +2020-12-10 07:15:00,134.39,227.273,64.627,32.65 +2020-12-10 07:30:00,136.07,230.03400000000002,64.627,32.65 +2020-12-10 07:45:00,137.41,231.43200000000002,64.627,32.65 +2020-12-10 08:00:00,138.31,230.24200000000002,70.895,32.65 +2020-12-10 08:15:00,138.27,230.352,70.895,32.65 +2020-12-10 08:30:00,139.4,228.68900000000002,70.895,32.65 +2020-12-10 08:45:00,139.75,225.979,70.895,32.65 +2020-12-10 09:00:00,140.06,219.641,66.382,32.65 +2020-12-10 09:15:00,143.71,216.195,66.382,32.65 +2020-12-10 09:30:00,145.38,213.66,66.382,32.65 +2020-12-10 09:45:00,143.55,210.907,66.382,32.65 +2020-12-10 10:00:00,144.96,206.37400000000002,62.739,32.65 +2020-12-10 10:15:00,143.67,202.21099999999998,62.739,32.65 +2020-12-10 10:30:00,142.44,199.86,62.739,32.65 +2020-12-10 10:45:00,142.02,198.46400000000003,62.739,32.65 +2020-12-10 11:00:00,140.23,197.49099999999999,60.843,32.65 +2020-12-10 11:15:00,141.63,196.47299999999998,60.843,32.65 +2020-12-10 11:30:00,140.83,195.273,60.843,32.65 +2020-12-10 11:45:00,140.68,193.729,60.843,32.65 +2020-12-10 12:00:00,140.94,188.09900000000002,58.466,32.65 +2020-12-10 12:15:00,140.46,187.13,58.466,32.65 +2020-12-10 12:30:00,138.97,187.112,58.466,32.65 +2020-12-10 12:45:00,136.01,188.033,58.466,32.65 +2020-12-10 13:00:00,137.69,187.275,56.883,32.65 +2020-12-10 13:15:00,137.48,186.88400000000001,56.883,32.65 +2020-12-10 13:30:00,132.54,186.77,56.883,32.65 +2020-12-10 13:45:00,133.09,186.713,56.883,32.65 +2020-12-10 14:00:00,135.21,185.706,56.503,32.65 +2020-12-10 14:15:00,136.53,186.206,56.503,32.65 +2020-12-10 14:30:00,137.12,186.63,56.503,32.65 +2020-12-10 14:45:00,137.05,186.502,56.503,32.65 +2020-12-10 15:00:00,137.96,187.34900000000002,57.803999999999995,32.65 +2020-12-10 15:15:00,139.18,187.918,57.803999999999995,32.65 +2020-12-10 15:30:00,136.67,190.06900000000002,57.803999999999995,32.65 +2020-12-10 15:45:00,137.64,191.88400000000001,57.803999999999995,32.65 +2020-12-10 16:00:00,138.45,192.976,59.379,32.65 +2020-12-10 16:15:00,142.96,194.09,59.379,32.65 +2020-12-10 16:30:00,146.32,196.812,59.379,32.65 +2020-12-10 16:45:00,153.38,198.078,59.379,32.65 +2020-12-10 17:00:00,168.95,200.729,64.71600000000001,32.65 +2020-12-10 17:15:00,169.75,201.08599999999998,64.71600000000001,32.65 +2020-12-10 17:30:00,169.32,201.61,64.71600000000001,32.65 +2020-12-10 17:45:00,147.93,201.25599999999997,64.71600000000001,32.65 +2020-12-10 18:00:00,141.13,202.24400000000003,68.803,32.65 +2020-12-10 18:15:00,143.26,200.32,68.803,32.65 +2020-12-10 18:30:00,143.39,198.808,68.803,32.65 +2020-12-10 18:45:00,144.16,198.555,68.803,32.65 +2020-12-10 19:00:00,142.57,199.49099999999999,72.934,32.65 +2020-12-10 19:15:00,140.71,195.882,72.934,32.65 +2020-12-10 19:30:00,137.64,193.773,72.934,32.65 +2020-12-10 19:45:00,135.04,190.31400000000002,72.934,32.65 +2020-12-10 20:00:00,132.42,186.852,65.175,32.65 +2020-12-10 20:15:00,126.79,181.005,65.175,32.65 +2020-12-10 20:30:00,122.81,177.45,65.175,32.65 +2020-12-10 20:45:00,120.88,174.785,65.175,32.65 +2020-12-10 21:00:00,117.89,172.68099999999998,58.55,32.65 +2020-12-10 21:15:00,115.42,170.65,58.55,32.65 +2020-12-10 21:30:00,118.82,168.548,58.55,32.65 +2020-12-10 21:45:00,116.05,166.838,58.55,32.65 +2020-12-10 22:00:00,113.48,160.107,55.041000000000004,32.65 +2020-12-10 22:15:00,110.17,154.452,55.041000000000004,32.65 +2020-12-10 22:30:00,99.6,140.149,55.041000000000004,32.65 +2020-12-10 22:45:00,100.62,132.09799999999998,55.041000000000004,32.65 +2020-12-10 23:00:00,94.51,126.565,48.258,32.65 +2020-12-10 23:15:00,91.88,124.586,48.258,32.65 +2020-12-10 23:30:00,89.58,124.66799999999999,48.258,32.65 +2020-12-10 23:45:00,91.27,124.288,48.258,32.65 +2020-12-11 00:00:00,91.85,116.62899999999999,45.02,32.65 +2020-12-11 00:15:00,90.83,116.805,45.02,32.65 +2020-12-11 00:30:00,85.32,117.803,45.02,32.65 +2020-12-11 00:45:00,82.24,119.473,45.02,32.65 +2020-12-11 01:00:00,79.17,121.26299999999999,42.695,32.65 +2020-12-11 01:15:00,81.0,122.91799999999999,42.695,32.65 +2020-12-11 01:30:00,77.89,123.124,42.695,32.65 +2020-12-11 01:45:00,78.18,123.911,42.695,32.65 +2020-12-11 02:00:00,81.93,125.464,41.511,32.65 +2020-12-11 02:15:00,86.0,126.85700000000001,41.511,32.65 +2020-12-11 02:30:00,86.34,127.60600000000001,41.511,32.65 +2020-12-11 02:45:00,81.69,129.531,41.511,32.65 +2020-12-11 03:00:00,81.72,131.216,41.162,32.65 +2020-12-11 03:15:00,86.02,132.842,41.162,32.65 +2020-12-11 03:30:00,88.54,134.709,41.162,32.65 +2020-12-11 03:45:00,89.73,136.055,41.162,32.65 +2020-12-11 04:00:00,84.52,149.05200000000002,42.226000000000006,32.65 +2020-12-11 04:15:00,82.83,160.917,42.226000000000006,32.65 +2020-12-11 04:30:00,84.73,163.737,42.226000000000006,32.65 +2020-12-11 04:45:00,86.99,165.222,42.226000000000006,32.65 +2020-12-11 05:00:00,89.74,198.715,45.597,32.65 +2020-12-11 05:15:00,93.03,229.33,45.597,32.65 +2020-12-11 05:30:00,96.79,225.83599999999998,45.597,32.65 +2020-12-11 05:45:00,101.33,217.828,45.597,32.65 +2020-12-11 06:00:00,110.09,214.25900000000001,56.263999999999996,32.65 +2020-12-11 06:15:00,118.24,218.227,56.263999999999996,32.65 +2020-12-11 06:30:00,122.75,219.41400000000002,56.263999999999996,32.65 +2020-12-11 06:45:00,127.63,223.713,56.263999999999996,32.65 +2020-12-11 07:00:00,132.56,222.27200000000002,66.888,32.65 +2020-12-11 07:15:00,135.59,228.239,66.888,32.65 +2020-12-11 07:30:00,138.93,230.83900000000003,66.888,32.65 +2020-12-11 07:45:00,140.11,231.35,66.888,32.65 +2020-12-11 08:00:00,140.74,229.05599999999998,73.459,32.65 +2020-12-11 08:15:00,138.35,228.766,73.459,32.65 +2020-12-11 08:30:00,138.94,228.084,73.459,32.65 +2020-12-11 08:45:00,138.68,223.765,73.459,32.65 +2020-12-11 09:00:00,138.76,217.87099999999998,69.087,32.65 +2020-12-11 09:15:00,142.43,214.997,69.087,32.65 +2020-12-11 09:30:00,145.27,212.041,69.087,32.65 +2020-12-11 09:45:00,145.82,209.167,69.087,32.65 +2020-12-11 10:00:00,146.67,203.49599999999998,65.404,32.65 +2020-12-11 10:15:00,146.04,200.011,65.404,32.65 +2020-12-11 10:30:00,146.52,197.567,65.404,32.65 +2020-12-11 10:45:00,146.39,195.722,65.404,32.65 +2020-12-11 11:00:00,144.72,194.715,63.0,32.65 +2020-12-11 11:15:00,144.28,192.791,63.0,32.65 +2020-12-11 11:30:00,145.11,193.333,63.0,32.65 +2020-12-11 11:45:00,143.67,191.829,63.0,32.65 +2020-12-11 12:00:00,143.59,187.28,59.083,32.65 +2020-12-11 12:15:00,143.45,184.239,59.083,32.65 +2020-12-11 12:30:00,142.82,184.391,59.083,32.65 +2020-12-11 12:45:00,140.7,185.827,59.083,32.65 +2020-12-11 13:00:00,139.92,185.97299999999998,56.611999999999995,32.65 +2020-12-11 13:15:00,139.16,186.396,56.611999999999995,32.65 +2020-12-11 13:30:00,136.21,186.297,56.611999999999995,32.65 +2020-12-11 13:45:00,136.82,186.169,56.611999999999995,32.65 +2020-12-11 14:00:00,134.19,184.005,55.161,32.65 +2020-12-11 14:15:00,134.68,184.335,55.161,32.65 +2020-12-11 14:30:00,134.81,185.299,55.161,32.65 +2020-12-11 14:45:00,133.75,185.48,55.161,32.65 +2020-12-11 15:00:00,133.3,185.865,55.583,32.65 +2020-12-11 15:15:00,132.89,185.993,55.583,32.65 +2020-12-11 15:30:00,133.49,186.65400000000002,55.583,32.65 +2020-12-11 15:45:00,133.98,188.61599999999999,55.583,32.65 +2020-12-11 16:00:00,135.78,188.544,57.611999999999995,32.65 +2020-12-11 16:15:00,139.22,189.968,57.611999999999995,32.65 +2020-12-11 16:30:00,141.38,192.791,57.611999999999995,32.65 +2020-12-11 16:45:00,141.75,193.968,57.611999999999995,32.65 +2020-12-11 17:00:00,142.8,196.8,64.14,32.65 +2020-12-11 17:15:00,142.37,196.774,64.14,32.65 +2020-12-11 17:30:00,142.8,196.99900000000002,64.14,32.65 +2020-12-11 17:45:00,141.37,196.421,64.14,32.65 +2020-12-11 18:00:00,139.86,198.11599999999999,68.086,32.65 +2020-12-11 18:15:00,137.98,195.77200000000002,68.086,32.65 +2020-12-11 18:30:00,137.12,194.65900000000002,68.086,32.65 +2020-12-11 18:45:00,137.09,194.41099999999997,68.086,32.65 +2020-12-11 19:00:00,135.72,196.236,69.915,32.65 +2020-12-11 19:15:00,133.39,193.97799999999998,69.915,32.65 +2020-12-11 19:30:00,131.85,191.445,69.915,32.65 +2020-12-11 19:45:00,128.74,187.503,69.915,32.65 +2020-12-11 20:00:00,123.92,184.093,61.695,32.65 +2020-12-11 20:15:00,119.13,178.245,61.695,32.65 +2020-12-11 20:30:00,116.21,174.625,61.695,32.65 +2020-12-11 20:45:00,114.15,172.53400000000002,61.695,32.65 +2020-12-11 21:00:00,114.45,170.922,56.041000000000004,32.65 +2020-12-11 21:15:00,112.95,169.31,56.041000000000004,32.65 +2020-12-11 21:30:00,111.79,167.25400000000002,56.041000000000004,32.65 +2020-12-11 21:45:00,111.52,166.093,56.041000000000004,32.65 +2020-12-11 22:00:00,100.51,160.34799999999998,51.888999999999996,32.65 +2020-12-11 22:15:00,97.98,154.564,51.888999999999996,32.65 +2020-12-11 22:30:00,92.39,146.664,51.888999999999996,32.65 +2020-12-11 22:45:00,92.97,142.139,51.888999999999996,32.65 +2020-12-11 23:00:00,94.85,136.137,45.787,32.65 +2020-12-11 23:15:00,93.72,132.213,45.787,32.65 +2020-12-11 23:30:00,88.87,130.852,45.787,32.65 +2020-12-11 23:45:00,84.74,129.795,45.787,32.65 +2020-12-12 00:00:00,84.97,114.07600000000001,41.815,32.468 +2020-12-12 00:15:00,83.87,109.97399999999999,41.815,32.468 +2020-12-12 00:30:00,79.35,112.294,41.815,32.468 +2020-12-12 00:45:00,76.4,114.661,41.815,32.468 +2020-12-12 01:00:00,78.93,117.113,38.645,32.468 +2020-12-12 01:15:00,79.22,117.779,38.645,32.468 +2020-12-12 01:30:00,74.91,117.477,38.645,32.468 +2020-12-12 01:45:00,70.19,118.03,38.645,32.468 +2020-12-12 02:00:00,69.07,120.29,36.696,32.468 +2020-12-12 02:15:00,68.62,121.31700000000001,36.696,32.468 +2020-12-12 02:30:00,76.58,120.963,36.696,32.468 +2020-12-12 02:45:00,71.16,122.986,36.696,32.468 +2020-12-12 03:00:00,72.83,125.289,35.42,32.468 +2020-12-12 03:15:00,75.73,125.73200000000001,35.42,32.468 +2020-12-12 03:30:00,75.68,126.01299999999999,35.42,32.468 +2020-12-12 03:45:00,71.18,127.463,35.42,32.468 +2020-12-12 04:00:00,69.45,136.298,35.167,32.468 +2020-12-12 04:15:00,68.25,145.624,35.167,32.468 +2020-12-12 04:30:00,69.44,146.27200000000002,35.167,32.468 +2020-12-12 04:45:00,69.89,147.263,35.167,32.468 +2020-12-12 05:00:00,71.14,164.803,35.311,32.468 +2020-12-12 05:15:00,71.23,176.438,35.311,32.468 +2020-12-12 05:30:00,71.29,173.31400000000002,35.311,32.468 +2020-12-12 05:45:00,72.91,170.747,35.311,32.468 +2020-12-12 06:00:00,74.72,185.908,37.117,32.468 +2020-12-12 06:15:00,76.39,206.178,37.117,32.468 +2020-12-12 06:30:00,78.26,202.05200000000002,37.117,32.468 +2020-12-12 06:45:00,80.93,197.43599999999998,37.117,32.468 +2020-12-12 07:00:00,84.33,192.245,40.948,32.468 +2020-12-12 07:15:00,86.73,196.97,40.948,32.468 +2020-12-12 07:30:00,90.02,202.268,40.948,32.468 +2020-12-12 07:45:00,93.49,206.74400000000003,40.948,32.468 +2020-12-12 08:00:00,95.85,208.5,44.903,32.468 +2020-12-12 08:15:00,97.55,211.82299999999998,44.903,32.468 +2020-12-12 08:30:00,99.61,212.753,44.903,32.468 +2020-12-12 08:45:00,103.07,211.549,44.903,32.468 +2020-12-12 09:00:00,104.78,207.393,46.283,32.468 +2020-12-12 09:15:00,105.94,205.275,46.283,32.468 +2020-12-12 09:30:00,106.91,203.227,46.283,32.468 +2020-12-12 09:45:00,107.66,200.5,46.283,32.468 +2020-12-12 10:00:00,108.32,195.109,44.103,32.468 +2020-12-12 10:15:00,109.17,191.769,44.103,32.468 +2020-12-12 10:30:00,109.72,189.468,44.103,32.468 +2020-12-12 10:45:00,110.26,188.91,44.103,32.468 +2020-12-12 11:00:00,111.78,188.105,42.373999999999995,32.468 +2020-12-12 11:15:00,113.31,185.49,42.373999999999995,32.468 +2020-12-12 11:30:00,113.65,184.924,42.373999999999995,32.468 +2020-12-12 11:45:00,113.32,182.46900000000002,42.373999999999995,32.468 +2020-12-12 12:00:00,111.9,177.05200000000002,39.937,32.468 +2020-12-12 12:15:00,111.01,174.662,39.937,32.468 +2020-12-12 12:30:00,108.83,175.142,39.937,32.468 +2020-12-12 12:45:00,106.63,175.82299999999998,39.937,32.468 +2020-12-12 13:00:00,103.27,175.53900000000002,37.138000000000005,32.468 +2020-12-12 13:15:00,101.79,173.928,37.138000000000005,32.468 +2020-12-12 13:30:00,101.3,173.377,37.138000000000005,32.468 +2020-12-12 13:45:00,101.1,173.704,37.138000000000005,32.468 +2020-12-12 14:00:00,100.88,172.74400000000003,36.141999999999996,32.468 +2020-12-12 14:15:00,100.41,172.542,36.141999999999996,32.468 +2020-12-12 14:30:00,100.19,171.72799999999998,36.141999999999996,32.468 +2020-12-12 14:45:00,99.5,172.138,36.141999999999996,32.468 +2020-12-12 15:00:00,98.71,173.196,37.964,32.468 +2020-12-12 15:15:00,98.98,174.12,37.964,32.468 +2020-12-12 15:30:00,99.94,176.325,37.964,32.468 +2020-12-12 15:45:00,100.8,178.297,37.964,32.468 +2020-12-12 16:00:00,104.51,177.041,40.699,32.468 +2020-12-12 16:15:00,107.74,179.368,40.699,32.468 +2020-12-12 16:30:00,108.74,182.148,40.699,32.468 +2020-12-12 16:45:00,109.29,184.231,40.699,32.468 +2020-12-12 17:00:00,110.17,186.467,46.216,32.468 +2020-12-12 17:15:00,110.88,188.104,46.216,32.468 +2020-12-12 17:30:00,111.64,188.24900000000002,46.216,32.468 +2020-12-12 17:45:00,112.51,187.28099999999998,46.216,32.468 +2020-12-12 18:00:00,112.37,188.514,51.123999999999995,32.468 +2020-12-12 18:15:00,112.02,187.929,51.123999999999995,32.468 +2020-12-12 18:30:00,111.33,188.148,51.123999999999995,32.468 +2020-12-12 18:45:00,111.02,184.58,51.123999999999995,32.468 +2020-12-12 19:00:00,110.03,187.303,52.336000000000006,32.468 +2020-12-12 19:15:00,108.32,184.541,52.336000000000006,32.468 +2020-12-12 19:30:00,107.24,182.74400000000003,52.336000000000006,32.468 +2020-12-12 19:45:00,106.61,178.607,52.336000000000006,32.468 +2020-12-12 20:00:00,102.62,177.35299999999998,48.825,32.468 +2020-12-12 20:15:00,97.95,173.604,48.825,32.468 +2020-12-12 20:30:00,96.03,169.61,48.825,32.468 +2020-12-12 20:45:00,94.62,167.145,48.825,32.468 +2020-12-12 21:00:00,92.77,167.74599999999998,43.729,32.468 +2020-12-12 21:15:00,90.0,166.553,43.729,32.468 +2020-12-12 21:30:00,87.78,165.718,43.729,32.468 +2020-12-12 21:45:00,87.21,164.16,43.729,32.468 +2020-12-12 22:00:00,86.33,159.75799999999998,44.126000000000005,32.468 +2020-12-12 22:15:00,84.41,156.444,44.126000000000005,32.468 +2020-12-12 22:30:00,82.37,154.756,44.126000000000005,32.468 +2020-12-12 22:45:00,80.33,152.092,44.126000000000005,32.468 +2020-12-12 23:00:00,76.41,148.47299999999998,38.169000000000004,32.468 +2020-12-12 23:15:00,74.99,142.928,38.169000000000004,32.468 +2020-12-12 23:30:00,72.58,139.857,38.169000000000004,32.468 +2020-12-12 23:45:00,70.05,136.382,38.169000000000004,32.468 +2020-12-13 00:00:00,67.83,114.786,35.232,32.468 +2020-12-13 00:15:00,65.77,110.337,35.232,32.468 +2020-12-13 00:30:00,64.76,112.277,35.232,32.468 +2020-12-13 00:45:00,63.83,115.316,35.232,32.468 +2020-12-13 01:00:00,62.51,117.65799999999999,31.403000000000002,32.468 +2020-12-13 01:15:00,61.48,119.354,31.403000000000002,32.468 +2020-12-13 01:30:00,61.2,119.575,31.403000000000002,32.468 +2020-12-13 01:45:00,60.78,119.804,31.403000000000002,32.468 +2020-12-13 02:00:00,59.95,121.339,30.69,32.468 +2020-12-13 02:15:00,59.57,121.521,30.69,32.468 +2020-12-13 02:30:00,59.18,122.01899999999999,30.69,32.468 +2020-12-13 02:45:00,59.27,124.5,30.69,32.468 +2020-12-13 03:00:00,59.3,127.10700000000001,29.516,32.468 +2020-12-13 03:15:00,59.76,127.05799999999999,29.516,32.468 +2020-12-13 03:30:00,60.59,128.73,29.516,32.468 +2020-12-13 03:45:00,60.29,130.10399999999998,29.516,32.468 +2020-12-13 04:00:00,59.87,138.665,29.148000000000003,32.468 +2020-12-13 04:15:00,59.82,146.981,29.148000000000003,32.468 +2020-12-13 04:30:00,60.61,147.683,29.148000000000003,32.468 +2020-12-13 04:45:00,60.83,148.946,29.148000000000003,32.468 +2020-12-13 05:00:00,61.5,162.963,28.706,32.468 +2020-12-13 05:15:00,62.25,172.148,28.706,32.468 +2020-12-13 05:30:00,63.05,168.88299999999998,28.706,32.468 +2020-12-13 05:45:00,63.76,166.579,28.706,32.468 +2020-12-13 06:00:00,64.65,181.65599999999998,28.771,32.468 +2020-12-13 06:15:00,65.56,200.222,28.771,32.468 +2020-12-13 06:30:00,65.84,195.041,28.771,32.468 +2020-12-13 06:45:00,67.8,189.417,28.771,32.468 +2020-12-13 07:00:00,69.8,186.665,31.39,32.468 +2020-12-13 07:15:00,72.1,190.542,31.39,32.468 +2020-12-13 07:30:00,74.71,194.625,31.39,32.468 +2020-12-13 07:45:00,76.75,198.34400000000002,31.39,32.468 +2020-12-13 08:00:00,78.86,201.938,34.972,32.468 +2020-12-13 08:15:00,81.37,205.171,34.972,32.468 +2020-12-13 08:30:00,82.52,207.74599999999998,34.972,32.468 +2020-12-13 08:45:00,84.69,208.512,34.972,32.468 +2020-12-13 09:00:00,85.83,203.933,36.709,32.468 +2020-12-13 09:15:00,87.39,202.365,36.709,32.468 +2020-12-13 09:30:00,88.29,200.173,36.709,32.468 +2020-12-13 09:45:00,89.37,197.327,36.709,32.468 +2020-12-13 10:00:00,91.42,194.422,35.812,32.468 +2020-12-13 10:15:00,92.02,191.58900000000003,35.812,32.468 +2020-12-13 10:30:00,93.21,189.86599999999999,35.812,32.468 +2020-12-13 10:45:00,95.56,187.44,35.812,32.468 +2020-12-13 11:00:00,97.35,187.52,36.746,32.468 +2020-12-13 11:15:00,99.58,185.02,36.746,32.468 +2020-12-13 11:30:00,99.04,183.602,36.746,32.468 +2020-12-13 11:45:00,101.44,181.74200000000002,36.746,32.468 +2020-12-13 12:00:00,102.29,175.783,35.048,32.468 +2020-12-13 12:15:00,100.68,175.27200000000002,35.048,32.468 +2020-12-13 12:30:00,97.69,174.334,35.048,32.468 +2020-12-13 12:45:00,93.5,174.06099999999998,35.048,32.468 +2020-12-13 13:00:00,93.18,173.04,29.987,32.468 +2020-12-13 13:15:00,91.25,174.38400000000001,29.987,32.468 +2020-12-13 13:30:00,90.27,173.607,29.987,32.468 +2020-12-13 13:45:00,90.93,173.293,29.987,32.468 +2020-12-13 14:00:00,91.2,172.57299999999998,27.21,32.468 +2020-12-13 14:15:00,91.53,173.56,27.21,32.468 +2020-12-13 14:30:00,92.77,173.96400000000003,27.21,32.468 +2020-12-13 14:45:00,92.68,173.96599999999998,27.21,32.468 +2020-12-13 15:00:00,92.95,173.54,27.726999999999997,32.468 +2020-12-13 15:15:00,93.32,175.192,27.726999999999997,32.468 +2020-12-13 15:30:00,93.65,177.99,27.726999999999997,32.468 +2020-12-13 15:45:00,94.82,180.645,27.726999999999997,32.468 +2020-12-13 16:00:00,97.84,181.21,32.23,32.468 +2020-12-13 16:15:00,102.34,182.666,32.23,32.468 +2020-12-13 16:30:00,104.88,185.718,32.23,32.468 +2020-12-13 16:45:00,106.38,187.96200000000002,32.23,32.468 +2020-12-13 17:00:00,108.65,190.179,42.016999999999996,32.468 +2020-12-13 17:15:00,110.19,191.53400000000002,42.016999999999996,32.468 +2020-12-13 17:30:00,112.07,191.988,42.016999999999996,32.468 +2020-12-13 17:45:00,113.2,193.269,42.016999999999996,32.468 +2020-12-13 18:00:00,112.55,193.99099999999999,49.338,32.468 +2020-12-13 18:15:00,110.56,194.68200000000002,49.338,32.468 +2020-12-13 18:30:00,110.25,192.84799999999998,49.338,32.468 +2020-12-13 18:45:00,110.1,191.11900000000003,49.338,32.468 +2020-12-13 19:00:00,108.26,193.512,52.369,32.468 +2020-12-13 19:15:00,106.2,191.321,52.369,32.468 +2020-12-13 19:30:00,105.15,189.34799999999998,52.369,32.468 +2020-12-13 19:45:00,104.42,186.611,52.369,32.468 +2020-12-13 20:00:00,108.33,185.328,50.405,32.468 +2020-12-13 20:15:00,108.05,182.543,50.405,32.468 +2020-12-13 20:30:00,103.92,179.755,50.405,32.468 +2020-12-13 20:45:00,96.6,176.112,50.405,32.468 +2020-12-13 21:00:00,98.84,174.137,46.235,32.468 +2020-12-13 21:15:00,101.2,172.30700000000002,46.235,32.468 +2020-12-13 21:30:00,100.09,171.757,46.235,32.468 +2020-12-13 21:45:00,99.54,170.34599999999998,46.235,32.468 +2020-12-13 22:00:00,91.06,164.78900000000002,46.861000000000004,32.468 +2020-12-13 22:15:00,90.94,160.716,46.861000000000004,32.468 +2020-12-13 22:30:00,85.27,155.94,46.861000000000004,32.468 +2020-12-13 22:45:00,86.11,152.435,46.861000000000004,32.468 +2020-12-13 23:00:00,87.91,146.025,41.302,32.468 +2020-12-13 23:15:00,88.21,142.32299999999998,41.302,32.468 +2020-12-13 23:30:00,84.64,140.062,41.302,32.468 +2020-12-13 23:45:00,77.19,137.455,41.302,32.468 +2020-12-14 00:00:00,73.85,119.242,37.164,32.65 +2020-12-14 00:15:00,76.66,117.71700000000001,37.164,32.65 +2020-12-14 00:30:00,78.78,119.77600000000001,37.164,32.65 +2020-12-14 00:45:00,77.4,122.26100000000001,37.164,32.65 +2020-12-14 01:00:00,68.45,124.65100000000001,34.994,32.65 +2020-12-14 01:15:00,73.71,125.82,34.994,32.65 +2020-12-14 01:30:00,75.05,126.103,34.994,32.65 +2020-12-14 01:45:00,74.17,126.43,34.994,32.65 +2020-12-14 02:00:00,71.84,127.964,34.571,32.65 +2020-12-14 02:15:00,74.01,129.599,34.571,32.65 +2020-12-14 02:30:00,74.73,130.429,34.571,32.65 +2020-12-14 02:45:00,74.65,132.29399999999998,34.571,32.65 +2020-12-14 03:00:00,70.83,136.15200000000002,33.934,32.65 +2020-12-14 03:15:00,76.74,137.769,33.934,32.65 +2020-12-14 03:30:00,78.07,139.173,33.934,32.65 +2020-12-14 03:45:00,77.08,139.999,33.934,32.65 +2020-12-14 04:00:00,73.01,152.859,34.107,32.65 +2020-12-14 04:15:00,78.81,165.28900000000002,34.107,32.65 +2020-12-14 04:30:00,80.92,168.162,34.107,32.65 +2020-12-14 04:45:00,82.24,169.58599999999998,34.107,32.65 +2020-12-14 05:00:00,82.67,199.233,39.575,32.65 +2020-12-14 05:15:00,82.48,228.407,39.575,32.65 +2020-12-14 05:30:00,87.89,225.44099999999997,39.575,32.65 +2020-12-14 05:45:00,93.59,217.542,39.575,32.65 +2020-12-14 06:00:00,104.33,214.821,56.156000000000006,32.65 +2020-12-14 06:15:00,110.84,218.653,56.156000000000006,32.65 +2020-12-14 06:30:00,116.28,221.476,56.156000000000006,32.65 +2020-12-14 06:45:00,125.61,224.635,56.156000000000006,32.65 +2020-12-14 07:00:00,128.28,224.24200000000002,67.926,32.65 +2020-12-14 07:15:00,129.58,229.359,67.926,32.65 +2020-12-14 07:30:00,133.95,232.672,67.926,32.65 +2020-12-14 07:45:00,132.49,233.838,67.926,32.65 +2020-12-14 08:00:00,136.16,232.574,72.58,32.65 +2020-12-14 08:15:00,133.51,233.67,72.58,32.65 +2020-12-14 08:30:00,136.78,232.19,72.58,32.65 +2020-12-14 08:45:00,132.47,229.61599999999999,72.58,32.65 +2020-12-14 09:00:00,134.5,224.016,66.984,32.65 +2020-12-14 09:15:00,136.6,218.998,66.984,32.65 +2020-12-14 09:30:00,141.47,215.822,66.984,32.65 +2020-12-14 09:45:00,137.79,213.25799999999998,66.984,32.65 +2020-12-14 10:00:00,135.32,209.27,63.158,32.65 +2020-12-14 10:15:00,131.67,206.097,63.158,32.65 +2020-12-14 10:30:00,131.48,203.485,63.158,32.65 +2020-12-14 10:45:00,133.72,201.791,63.158,32.65 +2020-12-14 11:00:00,129.21,199.26,61.141000000000005,32.65 +2020-12-14 11:15:00,129.73,198.532,61.141000000000005,32.65 +2020-12-14 11:30:00,131.24,198.482,61.141000000000005,32.65 +2020-12-14 11:45:00,132.76,196.21599999999998,61.141000000000005,32.65 +2020-12-14 12:00:00,129.52,191.954,57.961000000000006,32.65 +2020-12-14 12:15:00,133.74,191.457,57.961000000000006,32.65 +2020-12-14 12:30:00,133.03,190.775,57.961000000000006,32.65 +2020-12-14 12:45:00,135.84,192.012,57.961000000000006,32.65 +2020-12-14 13:00:00,131.88,191.54,56.843,32.65 +2020-12-14 13:15:00,130.95,191.511,56.843,32.65 +2020-12-14 13:30:00,128.48,190.205,56.843,32.65 +2020-12-14 13:45:00,128.95,189.919,56.843,32.65 +2020-12-14 14:00:00,123.91,188.565,55.992,32.65 +2020-12-14 14:15:00,124.92,188.925,55.992,32.65 +2020-12-14 14:30:00,124.35,188.81599999999997,55.992,32.65 +2020-12-14 14:45:00,126.22,188.80700000000002,55.992,32.65 +2020-12-14 15:00:00,127.2,190.158,57.523,32.65 +2020-12-14 15:15:00,128.82,190.382,57.523,32.65 +2020-12-14 15:30:00,126.77,192.38400000000001,57.523,32.65 +2020-12-14 15:45:00,128.99,194.595,57.523,32.65 +2020-12-14 16:00:00,133.74,195.34400000000002,59.471000000000004,32.65 +2020-12-14 16:15:00,135.18,196.06599999999997,59.471000000000004,32.65 +2020-12-14 16:30:00,136.84,198.168,59.471000000000004,32.65 +2020-12-14 16:45:00,137.31,199.264,59.471000000000004,32.65 +2020-12-14 17:00:00,139.82,201.285,65.066,32.65 +2020-12-14 17:15:00,139.13,201.725,65.066,32.65 +2020-12-14 17:30:00,139.59,201.65200000000002,65.066,32.65 +2020-12-14 17:45:00,139.83,201.465,65.066,32.65 +2020-12-14 18:00:00,137.48,202.62900000000002,69.581,32.65 +2020-12-14 18:15:00,135.23,201.102,69.581,32.65 +2020-12-14 18:30:00,134.3,199.922,69.581,32.65 +2020-12-14 18:45:00,134.53,198.91299999999998,69.581,32.65 +2020-12-14 19:00:00,132.27,199.696,73.771,32.65 +2020-12-14 19:15:00,137.65,196.335,73.771,32.65 +2020-12-14 19:30:00,137.55,194.843,73.771,32.65 +2020-12-14 19:45:00,136.21,191.26,73.771,32.65 +2020-12-14 20:00:00,121.11,187.632,65.035,32.65 +2020-12-14 20:15:00,117.82,182.38099999999997,65.035,32.65 +2020-12-14 20:30:00,115.14,177.72400000000002,65.035,32.65 +2020-12-14 20:45:00,115.32,175.71200000000002,65.035,32.65 +2020-12-14 21:00:00,109.37,174.25,58.7,32.65 +2020-12-14 21:15:00,108.96,171.238,58.7,32.65 +2020-12-14 21:30:00,104.73,169.86900000000003,58.7,32.65 +2020-12-14 21:45:00,104.04,167.965,58.7,32.65 +2020-12-14 22:00:00,99.84,159.553,53.888000000000005,32.65 +2020-12-14 22:15:00,96.41,154.173,53.888000000000005,32.65 +2020-12-14 22:30:00,96.15,139.96200000000002,53.888000000000005,32.65 +2020-12-14 22:45:00,97.48,131.666,53.888000000000005,32.65 +2020-12-14 23:00:00,92.99,126.00399999999999,45.501999999999995,32.65 +2020-12-14 23:15:00,90.86,124.936,45.501999999999995,32.65 +2020-12-14 23:30:00,86.06,125.43,45.501999999999995,32.65 +2020-12-14 23:45:00,88.58,125.434,45.501999999999995,32.65 +2020-12-15 00:00:00,83.52,115.656,43.537,32.65 +2020-12-15 00:15:00,83.19,115.315,43.537,32.65 +2020-12-15 00:30:00,81.66,116.205,43.537,32.65 +2020-12-15 00:45:00,84.41,117.449,43.537,32.65 +2020-12-15 01:00:00,80.92,119.626,41.854,32.65 +2020-12-15 01:15:00,78.66,120.473,41.854,32.65 +2020-12-15 01:30:00,78.58,120.975,41.854,32.65 +2020-12-15 01:45:00,80.79,121.521,41.854,32.65 +2020-12-15 02:00:00,78.33,123.03200000000001,40.321,32.65 +2020-12-15 02:15:00,79.13,124.414,40.321,32.65 +2020-12-15 02:30:00,79.46,124.492,40.321,32.65 +2020-12-15 02:45:00,81.65,126.29299999999999,40.321,32.65 +2020-12-15 03:00:00,80.09,128.789,39.632,32.65 +2020-12-15 03:15:00,79.26,129.722,39.632,32.65 +2020-12-15 03:30:00,80.46,131.651,39.632,32.65 +2020-12-15 03:45:00,82.17,132.52100000000002,39.632,32.65 +2020-12-15 04:00:00,82.38,145.236,40.183,32.65 +2020-12-15 04:15:00,81.57,157.416,40.183,32.65 +2020-12-15 04:30:00,85.91,159.6,40.183,32.65 +2020-12-15 04:45:00,85.18,162.086,40.183,32.65 +2020-12-15 05:00:00,85.38,196.30599999999998,43.945,32.65 +2020-12-15 05:15:00,84.75,225.167,43.945,32.65 +2020-12-15 05:30:00,94.66,220.77900000000002,43.945,32.65 +2020-12-15 05:45:00,104.6,212.74599999999998,43.945,32.65 +2020-12-15 06:00:00,114.07,208.825,56.048,32.65 +2020-12-15 06:15:00,115.06,214.197,56.048,32.65 +2020-12-15 06:30:00,115.45,216.41,56.048,32.65 +2020-12-15 06:45:00,118.13,219.02599999999998,56.048,32.65 +2020-12-15 07:00:00,127.23,218.861,65.74,32.65 +2020-12-15 07:15:00,130.09,223.575,65.74,32.65 +2020-12-15 07:30:00,130.79,226.06599999999997,65.74,32.65 +2020-12-15 07:45:00,134.25,227.21,65.74,32.65 +2020-12-15 08:00:00,137.81,226.11599999999999,72.757,32.65 +2020-12-15 08:15:00,135.91,225.97099999999998,72.757,32.65 +2020-12-15 08:30:00,136.55,224.303,72.757,32.65 +2020-12-15 08:45:00,137.31,221.18200000000002,72.757,32.65 +2020-12-15 09:00:00,136.97,214.54,67.692,32.65 +2020-12-15 09:15:00,141.95,211.025,67.692,32.65 +2020-12-15 09:30:00,142.11,208.47,67.692,32.65 +2020-12-15 09:45:00,142.8,205.74200000000002,67.692,32.65 +2020-12-15 10:00:00,141.36,201.498,63.506,32.65 +2020-12-15 10:15:00,141.94,197.08599999999998,63.506,32.65 +2020-12-15 10:30:00,141.49,194.91400000000002,63.506,32.65 +2020-12-15 10:45:00,143.1,193.605,63.506,32.65 +2020-12-15 11:00:00,141.22,192.74099999999999,60.758,32.65 +2020-12-15 11:15:00,142.97,191.713,60.758,32.65 +2020-12-15 11:30:00,142.93,190.668,60.758,32.65 +2020-12-15 11:45:00,141.87,189.172,60.758,32.65 +2020-12-15 12:00:00,141.11,183.533,57.519,32.65 +2020-12-15 12:15:00,141.16,182.61700000000002,57.519,32.65 +2020-12-15 12:30:00,140.9,182.8,57.519,32.65 +2020-12-15 12:45:00,138.4,183.675,57.519,32.65 +2020-12-15 13:00:00,133.81,182.6,56.46,32.65 +2020-12-15 13:15:00,131.95,182.195,56.46,32.65 +2020-12-15 13:30:00,129.26,181.915,56.46,32.65 +2020-12-15 13:45:00,129.67,181.796,56.46,32.65 +2020-12-15 14:00:00,123.7,180.81599999999997,56.207,32.65 +2020-12-15 14:15:00,124.77,181.243,56.207,32.65 +2020-12-15 14:30:00,124.87,181.71400000000003,56.207,32.65 +2020-12-15 14:45:00,125.31,181.653,56.207,32.65 +2020-12-15 15:00:00,127.76,182.535,57.391999999999996,32.65 +2020-12-15 15:15:00,126.63,183.02599999999998,57.391999999999996,32.65 +2020-12-15 15:30:00,126.46,185.03799999999998,57.391999999999996,32.65 +2020-12-15 15:45:00,128.96,186.55,57.391999999999996,32.65 +2020-12-15 16:00:00,131.98,188.59799999999998,59.955,32.65 +2020-12-15 16:15:00,135.15,189.975,59.955,32.65 +2020-12-15 16:30:00,137.94,192.37400000000002,59.955,32.65 +2020-12-15 16:45:00,139.15,193.635,59.955,32.65 +2020-12-15 17:00:00,141.27,196.472,67.063,32.65 +2020-12-15 17:15:00,141.7,197.14700000000002,67.063,32.65 +2020-12-15 17:30:00,143.06,197.852,67.063,32.65 +2020-12-15 17:45:00,142.57,197.611,67.063,32.65 +2020-12-15 18:00:00,140.73,198.834,71.477,32.65 +2020-12-15 18:15:00,139.06,197.072,71.477,32.65 +2020-12-15 18:30:00,137.45,195.517,71.477,32.65 +2020-12-15 18:45:00,137.98,195.32299999999998,71.477,32.65 +2020-12-15 19:00:00,135.69,196.59099999999998,74.32,32.65 +2020-12-15 19:15:00,134.51,193.03599999999997,74.32,32.65 +2020-12-15 19:30:00,132.57,191.079,74.32,32.65 +2020-12-15 19:45:00,131.33,187.28799999999998,74.32,32.65 +2020-12-15 20:00:00,123.93,183.83700000000002,66.157,32.65 +2020-12-15 20:15:00,121.36,178.165,66.157,32.65 +2020-12-15 20:30:00,114.46,174.50599999999997,66.157,32.65 +2020-12-15 20:45:00,115.6,171.75400000000002,66.157,32.65 +2020-12-15 21:00:00,115.82,169.782,59.806000000000004,32.65 +2020-12-15 21:15:00,114.44,167.699,59.806000000000004,32.65 +2020-12-15 21:30:00,113.31,165.55900000000003,59.806000000000004,32.65 +2020-12-15 21:45:00,103.61,163.97,59.806000000000004,32.65 +2020-12-15 22:00:00,102.01,157.408,54.785,32.65 +2020-12-15 22:15:00,98.37,151.947,54.785,32.65 +2020-12-15 22:30:00,92.73,137.92,54.785,32.65 +2020-12-15 22:45:00,94.84,130.05100000000002,54.785,32.65 +2020-12-15 23:00:00,93.0,124.61200000000001,47.176,32.65 +2020-12-15 23:15:00,94.63,122.538,47.176,32.65 +2020-12-15 23:30:00,87.81,122.829,47.176,32.65 +2020-12-15 23:45:00,85.28,122.35,47.176,32.65 +2020-12-16 00:00:00,80.62,115.92399999999999,43.42,32.65 +2020-12-16 00:15:00,83.74,115.554,43.42,32.65 +2020-12-16 00:30:00,81.51,116.43799999999999,43.42,32.65 +2020-12-16 00:45:00,80.18,117.663,43.42,32.65 +2020-12-16 01:00:00,69.83,119.867,40.869,32.65 +2020-12-16 01:15:00,80.0,120.713,40.869,32.65 +2020-12-16 01:30:00,80.76,121.22,40.869,32.65 +2020-12-16 01:45:00,80.19,121.75399999999999,40.869,32.65 +2020-12-16 02:00:00,74.32,123.281,39.541,32.65 +2020-12-16 02:15:00,78.32,124.664,39.541,32.65 +2020-12-16 02:30:00,77.27,124.74799999999999,39.541,32.65 +2020-12-16 02:45:00,80.86,126.546,39.541,32.65 +2020-12-16 03:00:00,75.94,129.032,39.052,32.65 +2020-12-16 03:15:00,79.31,129.991,39.052,32.65 +2020-12-16 03:30:00,79.64,131.922,39.052,32.65 +2020-12-16 03:45:00,82.22,132.792,39.052,32.65 +2020-12-16 04:00:00,79.05,145.48,40.36,32.65 +2020-12-16 04:15:00,81.08,157.658,40.36,32.65 +2020-12-16 04:30:00,85.18,159.833,40.36,32.65 +2020-12-16 04:45:00,82.1,162.319,40.36,32.65 +2020-12-16 05:00:00,83.77,196.507,43.133,32.65 +2020-12-16 05:15:00,87.36,225.327,43.133,32.65 +2020-12-16 05:30:00,91.05,220.956,43.133,32.65 +2020-12-16 05:45:00,95.36,212.94299999999998,43.133,32.65 +2020-12-16 06:00:00,105.17,209.046,54.953,32.65 +2020-12-16 06:15:00,108.16,214.421,54.953,32.65 +2020-12-16 06:30:00,114.63,216.671,54.953,32.65 +2020-12-16 06:45:00,117.72,219.325,54.953,32.65 +2020-12-16 07:00:00,127.82,219.166,66.566,32.65 +2020-12-16 07:15:00,130.45,223.882,66.566,32.65 +2020-12-16 07:30:00,131.15,226.372,66.566,32.65 +2020-12-16 07:45:00,132.85,227.511,66.566,32.65 +2020-12-16 08:00:00,137.7,226.423,72.902,32.65 +2020-12-16 08:15:00,135.94,226.269,72.902,32.65 +2020-12-16 08:30:00,136.44,224.597,72.902,32.65 +2020-12-16 08:45:00,137.62,221.44799999999998,72.902,32.65 +2020-12-16 09:00:00,137.95,214.785,68.465,32.65 +2020-12-16 09:15:00,139.43,211.27599999999998,68.465,32.65 +2020-12-16 09:30:00,140.51,208.729,68.465,32.65 +2020-12-16 09:45:00,139.57,205.99,68.465,32.65 +2020-12-16 10:00:00,137.58,201.74,63.625,32.65 +2020-12-16 10:15:00,137.97,197.31400000000002,63.625,32.65 +2020-12-16 10:30:00,140.2,195.123,63.625,32.65 +2020-12-16 10:45:00,138.04,193.81,63.625,32.65 +2020-12-16 11:00:00,138.03,192.929,61.628,32.65 +2020-12-16 11:15:00,136.32,191.89,61.628,32.65 +2020-12-16 11:30:00,136.53,190.845,61.628,32.65 +2020-12-16 11:45:00,136.0,189.34599999999998,61.628,32.65 +2020-12-16 12:00:00,135.94,183.708,58.708999999999996,32.65 +2020-12-16 12:15:00,135.61,182.80599999999998,58.708999999999996,32.65 +2020-12-16 12:30:00,133.9,182.998,58.708999999999996,32.65 +2020-12-16 12:45:00,135.77,183.877,58.708999999999996,32.65 +2020-12-16 13:00:00,133.66,182.77900000000002,57.373000000000005,32.65 +2020-12-16 13:15:00,133.97,182.372,57.373000000000005,32.65 +2020-12-16 13:30:00,134.94,182.085,57.373000000000005,32.65 +2020-12-16 13:45:00,130.4,181.957,57.373000000000005,32.65 +2020-12-16 14:00:00,129.27,180.96599999999998,57.684,32.65 +2020-12-16 14:15:00,131.13,181.395,57.684,32.65 +2020-12-16 14:30:00,131.55,181.887,57.684,32.65 +2020-12-16 14:45:00,131.65,181.83599999999998,57.684,32.65 +2020-12-16 15:00:00,133.62,182.735,58.03,32.65 +2020-12-16 15:15:00,133.02,183.22,58.03,32.65 +2020-12-16 15:30:00,131.8,185.247,58.03,32.65 +2020-12-16 15:45:00,133.43,186.75799999999998,58.03,32.65 +2020-12-16 16:00:00,137.44,188.804,59.97,32.65 +2020-12-16 16:15:00,138.97,190.2,59.97,32.65 +2020-12-16 16:30:00,140.77,192.604,59.97,32.65 +2020-12-16 16:45:00,140.66,193.891,59.97,32.65 +2020-12-16 17:00:00,143.13,196.707,65.661,32.65 +2020-12-16 17:15:00,142.28,197.408,65.661,32.65 +2020-12-16 17:30:00,143.12,198.132,65.661,32.65 +2020-12-16 17:45:00,142.9,197.90200000000002,65.661,32.65 +2020-12-16 18:00:00,139.93,199.144,70.96300000000001,32.65 +2020-12-16 18:15:00,139.4,197.36,70.96300000000001,32.65 +2020-12-16 18:30:00,138.28,195.812,70.96300000000001,32.65 +2020-12-16 18:45:00,137.34,195.62900000000002,70.96300000000001,32.65 +2020-12-16 19:00:00,135.22,196.88,74.133,32.65 +2020-12-16 19:15:00,133.45,193.31799999999998,74.133,32.65 +2020-12-16 19:30:00,130.36,191.351,74.133,32.65 +2020-12-16 19:45:00,130.37,187.542,74.133,32.65 +2020-12-16 20:00:00,125.93,184.088,65.613,32.65 +2020-12-16 20:15:00,119.64,178.40900000000002,65.613,32.65 +2020-12-16 20:30:00,118.92,174.72799999999998,65.613,32.65 +2020-12-16 20:45:00,115.48,171.995,65.613,32.65 +2020-12-16 21:00:00,115.75,170.007,58.583,32.65 +2020-12-16 21:15:00,117.64,167.907,58.583,32.65 +2020-12-16 21:30:00,113.57,165.769,58.583,32.65 +2020-12-16 21:45:00,107.19,164.188,58.583,32.65 +2020-12-16 22:00:00,102.1,157.631,54.411,32.65 +2020-12-16 22:15:00,102.82,152.17600000000002,54.411,32.65 +2020-12-16 22:30:00,100.28,138.189,54.411,32.65 +2020-12-16 22:45:00,101.94,130.327,54.411,32.65 +2020-12-16 23:00:00,95.99,124.867,47.878,32.65 +2020-12-16 23:15:00,90.51,122.79,47.878,32.65 +2020-12-16 23:30:00,87.38,123.094,47.878,32.65 +2020-12-16 23:45:00,89.89,122.604,47.878,32.65 +2020-12-17 00:00:00,84.21,116.184,44.513000000000005,32.65 +2020-12-17 00:15:00,85.18,115.789,44.513000000000005,32.65 +2020-12-17 00:30:00,74.91,116.663,44.513000000000005,32.65 +2020-12-17 00:45:00,77.85,117.87100000000001,44.513000000000005,32.65 +2020-12-17 01:00:00,72.93,120.101,43.169,32.65 +2020-12-17 01:15:00,73.81,120.945,43.169,32.65 +2020-12-17 01:30:00,79.24,121.45700000000001,43.169,32.65 +2020-12-17 01:45:00,81.74,121.978,43.169,32.65 +2020-12-17 02:00:00,79.9,123.524,41.763999999999996,32.65 +2020-12-17 02:15:00,75.28,124.90799999999999,41.763999999999996,32.65 +2020-12-17 02:30:00,79.95,124.994,41.763999999999996,32.65 +2020-12-17 02:45:00,81.25,126.792,41.763999999999996,32.65 +2020-12-17 03:00:00,79.07,129.269,41.155,32.65 +2020-12-17 03:15:00,76.1,130.252,41.155,32.65 +2020-12-17 03:30:00,80.42,132.184,41.155,32.65 +2020-12-17 03:45:00,81.44,133.056,41.155,32.65 +2020-12-17 04:00:00,76.27,145.717,41.96,32.65 +2020-12-17 04:15:00,77.05,157.893,41.96,32.65 +2020-12-17 04:30:00,82.44,160.058,41.96,32.65 +2020-12-17 04:45:00,82.35,162.546,41.96,32.65 +2020-12-17 05:00:00,84.79,196.699,45.206,32.65 +2020-12-17 05:15:00,87.77,225.481,45.206,32.65 +2020-12-17 05:30:00,92.75,221.127,45.206,32.65 +2020-12-17 05:45:00,94.22,213.13099999999997,45.206,32.65 +2020-12-17 06:00:00,101.61,209.25799999999998,55.398999999999994,32.65 +2020-12-17 06:15:00,107.73,214.637,55.398999999999994,32.65 +2020-12-17 06:30:00,111.51,216.922,55.398999999999994,32.65 +2020-12-17 06:45:00,116.77,219.614,55.398999999999994,32.65 +2020-12-17 07:00:00,125.56,219.46200000000002,64.627,32.65 +2020-12-17 07:15:00,127.94,224.178,64.627,32.65 +2020-12-17 07:30:00,129.03,226.668,64.627,32.65 +2020-12-17 07:45:00,130.58,227.801,64.627,32.65 +2020-12-17 08:00:00,131.85,226.71900000000002,70.895,32.65 +2020-12-17 08:15:00,132.24,226.554,70.895,32.65 +2020-12-17 08:30:00,132.44,224.88,70.895,32.65 +2020-12-17 08:45:00,131.91,221.704,70.895,32.65 +2020-12-17 09:00:00,135.0,215.017,66.382,32.65 +2020-12-17 09:15:00,133.95,211.517,66.382,32.65 +2020-12-17 09:30:00,134.58,208.97799999999998,66.382,32.65 +2020-12-17 09:45:00,134.76,206.22799999999998,66.382,32.65 +2020-12-17 10:00:00,134.7,201.97299999999998,62.739,32.65 +2020-12-17 10:15:00,134.75,197.532,62.739,32.65 +2020-12-17 10:30:00,131.89,195.32299999999998,62.739,32.65 +2020-12-17 10:45:00,131.47,194.005,62.739,32.65 +2020-12-17 11:00:00,130.35,193.108,60.843,32.65 +2020-12-17 11:15:00,130.67,192.05900000000003,60.843,32.65 +2020-12-17 11:30:00,128.97,191.014,60.843,32.65 +2020-12-17 11:45:00,127.33,189.512,60.843,32.65 +2020-12-17 12:00:00,121.37,183.87400000000002,58.466,32.65 +2020-12-17 12:15:00,126.04,182.985,58.466,32.65 +2020-12-17 12:30:00,128.2,183.188,58.466,32.65 +2020-12-17 12:45:00,130.0,184.072,58.466,32.65 +2020-12-17 13:00:00,126.85,182.94799999999998,56.883,32.65 +2020-12-17 13:15:00,126.09,182.54,56.883,32.65 +2020-12-17 13:30:00,125.29,182.247,56.883,32.65 +2020-12-17 13:45:00,126.95,182.11,56.883,32.65 +2020-12-17 14:00:00,126.35,181.108,56.503,32.65 +2020-12-17 14:15:00,127.72,181.54,56.503,32.65 +2020-12-17 14:30:00,124.54,182.05200000000002,56.503,32.65 +2020-12-17 14:45:00,123.37,182.012,56.503,32.65 +2020-12-17 15:00:00,124.4,182.928,57.803999999999995,32.65 +2020-12-17 15:15:00,125.58,183.40599999999998,57.803999999999995,32.65 +2020-12-17 15:30:00,125.0,185.44799999999998,57.803999999999995,32.65 +2020-12-17 15:45:00,124.76,186.956,57.803999999999995,32.65 +2020-12-17 16:00:00,128.21,189.00099999999998,59.379,32.65 +2020-12-17 16:15:00,129.34,190.415,59.379,32.65 +2020-12-17 16:30:00,129.97,192.825,59.379,32.65 +2020-12-17 16:45:00,130.22,194.135,59.379,32.65 +2020-12-17 17:00:00,132.3,196.93099999999998,64.71600000000001,32.65 +2020-12-17 17:15:00,132.66,197.66,64.71600000000001,32.65 +2020-12-17 17:30:00,133.9,198.40200000000002,64.71600000000001,32.65 +2020-12-17 17:45:00,133.14,198.18400000000003,64.71600000000001,32.65 +2020-12-17 18:00:00,130.47,199.445,68.803,32.65 +2020-12-17 18:15:00,129.63,197.641,68.803,32.65 +2020-12-17 18:30:00,128.9,196.09799999999998,68.803,32.65 +2020-12-17 18:45:00,128.16,195.926,68.803,32.65 +2020-12-17 19:00:00,126.35,197.15900000000002,72.934,32.65 +2020-12-17 19:15:00,123.77,193.59099999999998,72.934,32.65 +2020-12-17 19:30:00,122.92,191.61599999999999,72.934,32.65 +2020-12-17 19:45:00,121.49,187.78799999999998,72.934,32.65 +2020-12-17 20:00:00,114.67,184.33,65.175,32.65 +2020-12-17 20:15:00,109.95,178.645,65.175,32.65 +2020-12-17 20:30:00,106.37,174.94400000000002,65.175,32.65 +2020-12-17 20:45:00,104.71,172.22799999999998,65.175,32.65 +2020-12-17 21:00:00,100.41,170.226,58.55,32.65 +2020-12-17 21:15:00,99.67,168.11,58.55,32.65 +2020-12-17 21:30:00,97.77,165.972,58.55,32.65 +2020-12-17 21:45:00,96.78,164.40099999999998,58.55,32.65 +2020-12-17 22:00:00,90.43,157.846,55.041000000000004,32.65 +2020-12-17 22:15:00,88.71,152.398,55.041000000000004,32.65 +2020-12-17 22:30:00,84.97,138.44899999999998,55.041000000000004,32.65 +2020-12-17 22:45:00,82.32,130.597,55.041000000000004,32.65 +2020-12-17 23:00:00,77.33,125.113,48.258,32.65 +2020-12-17 23:15:00,78.58,123.036,48.258,32.65 +2020-12-17 23:30:00,74.54,123.352,48.258,32.65 +2020-12-17 23:45:00,74.17,122.852,48.258,32.65 +2020-12-18 00:00:00,69.93,115.34700000000001,45.02,32.65 +2020-12-18 00:15:00,68.18,115.12299999999999,45.02,32.65 +2020-12-18 00:30:00,67.68,115.87,45.02,32.65 +2020-12-18 00:45:00,67.22,117.18,45.02,32.65 +2020-12-18 01:00:00,62.8,119.12299999999999,42.695,32.65 +2020-12-18 01:15:00,62.98,120.82700000000001,42.695,32.65 +2020-12-18 01:30:00,62.72,121.15299999999999,42.695,32.65 +2020-12-18 01:45:00,63.95,121.755,42.695,32.65 +2020-12-18 02:00:00,61.06,123.43,41.511,32.65 +2020-12-18 02:15:00,62.5,124.70299999999999,41.511,32.65 +2020-12-18 02:30:00,62.92,125.325,41.511,32.65 +2020-12-18 02:45:00,63.6,127.141,41.511,32.65 +2020-12-18 03:00:00,61.83,128.67600000000002,41.162,32.65 +2020-12-18 03:15:00,62.77,130.548,41.162,32.65 +2020-12-18 03:30:00,63.63,132.457,41.162,32.65 +2020-12-18 03:45:00,64.24,133.673,41.162,32.65 +2020-12-18 04:00:00,64.94,146.534,42.226000000000006,32.65 +2020-12-18 04:15:00,66.41,158.416,42.226000000000006,32.65 +2020-12-18 04:30:00,68.08,160.825,42.226000000000006,32.65 +2020-12-18 04:45:00,70.29,162.19299999999998,42.226000000000006,32.65 +2020-12-18 05:00:00,72.72,195.077,45.597,32.65 +2020-12-18 05:15:00,74.61,225.30200000000002,45.597,32.65 +2020-12-18 05:30:00,78.87,222.00799999999998,45.597,32.65 +2020-12-18 05:45:00,83.71,213.953,45.597,32.65 +2020-12-18 06:00:00,92.43,210.528,56.263999999999996,32.65 +2020-12-18 06:15:00,96.81,214.503,56.263999999999996,32.65 +2020-12-18 06:30:00,100.32,215.99,56.263999999999996,32.65 +2020-12-18 06:45:00,104.38,220.27700000000002,56.263999999999996,32.65 +2020-12-18 07:00:00,110.53,219.352,66.888,32.65 +2020-12-18 07:15:00,113.01,225.084,66.888,32.65 +2020-12-18 07:30:00,116.17,227.328,66.888,32.65 +2020-12-18 07:45:00,119.09,227.59599999999998,66.888,32.65 +2020-12-18 08:00:00,121.73,225.487,73.459,32.65 +2020-12-18 08:15:00,120.52,224.96400000000003,73.459,32.65 +2020-12-18 08:30:00,121.35,224.218,73.459,32.65 +2020-12-18 08:45:00,122.72,219.497,73.459,32.65 +2020-12-18 09:00:00,124.32,213.14,69.087,32.65 +2020-12-18 09:15:00,125.38,210.268,69.087,32.65 +2020-12-18 09:30:00,126.56,207.3,69.087,32.65 +2020-12-18 09:45:00,126.98,204.45,69.087,32.65 +2020-12-18 10:00:00,127.19,199.09799999999998,65.404,32.65 +2020-12-18 10:15:00,126.67,195.303,65.404,32.65 +2020-12-18 10:30:00,127.1,193.03099999999998,65.404,32.65 +2020-12-18 10:45:00,125.45,191.282,65.404,32.65 +2020-12-18 11:00:00,123.87,190.359,63.0,32.65 +2020-12-18 11:15:00,125.98,188.398,63.0,32.65 +2020-12-18 11:30:00,126.3,189.011,63.0,32.65 +2020-12-18 11:45:00,124.8,187.51,63.0,32.65 +2020-12-18 12:00:00,123.25,182.92700000000002,59.083,32.65 +2020-12-18 12:15:00,123.46,180.024,59.083,32.65 +2020-12-18 12:30:00,122.58,180.393,59.083,32.65 +2020-12-18 12:45:00,124.82,181.736,59.083,32.65 +2020-12-18 13:00:00,120.54,181.50900000000001,56.611999999999995,32.65 +2020-12-18 13:15:00,119.45,181.887,56.611999999999995,32.65 +2020-12-18 13:30:00,117.01,181.64,56.611999999999995,32.65 +2020-12-18 13:45:00,115.3,181.44799999999998,56.611999999999995,32.65 +2020-12-18 14:00:00,111.85,179.312,55.161,32.65 +2020-12-18 14:15:00,111.13,179.59599999999998,55.161,32.65 +2020-12-18 14:30:00,110.97,180.68599999999998,55.161,32.65 +2020-12-18 14:45:00,110.48,180.91400000000002,55.161,32.65 +2020-12-18 15:00:00,111.97,181.382,55.583,32.65 +2020-12-18 15:15:00,109.45,181.424,55.583,32.65 +2020-12-18 15:30:00,108.82,182.00400000000002,55.583,32.65 +2020-12-18 15:45:00,110.05,183.68400000000003,55.583,32.65 +2020-12-18 16:00:00,114.02,184.56900000000002,57.611999999999995,32.65 +2020-12-18 16:15:00,116.24,186.3,57.611999999999995,32.65 +2020-12-18 16:30:00,116.88,188.799,57.611999999999995,32.65 +2020-12-18 16:45:00,117.32,189.99,57.611999999999995,32.65 +2020-12-18 17:00:00,119.59,193.02,64.14,32.65 +2020-12-18 17:15:00,117.88,193.373,64.14,32.65 +2020-12-18 17:30:00,118.76,193.835,64.14,32.65 +2020-12-18 17:45:00,117.85,193.396,64.14,32.65 +2020-12-18 18:00:00,116.42,195.33700000000002,68.086,32.65 +2020-12-18 18:15:00,115.7,193.104,68.086,32.65 +2020-12-18 18:30:00,115.31,191.945,68.086,32.65 +2020-12-18 18:45:00,114.72,191.793,68.086,32.65 +2020-12-18 19:00:00,112.86,193.90900000000002,69.915,32.65 +2020-12-18 19:15:00,110.63,191.672,69.915,32.65 +2020-12-18 19:30:00,107.94,189.292,69.915,32.65 +2020-12-18 19:45:00,106.94,184.96900000000002,69.915,32.65 +2020-12-18 20:00:00,101.64,181.555,61.695,32.65 +2020-12-18 20:15:00,99.78,175.899,61.695,32.65 +2020-12-18 20:30:00,94.34,172.12099999999998,61.695,32.65 +2020-12-18 20:45:00,92.41,169.93200000000002,61.695,32.65 +2020-12-18 21:00:00,88.08,168.44799999999998,56.041000000000004,32.65 +2020-12-18 21:15:00,86.79,166.792,56.041000000000004,32.65 +2020-12-18 21:30:00,85.7,164.696,56.041000000000004,32.65 +2020-12-18 21:45:00,83.61,163.665,56.041000000000004,32.65 +2020-12-18 22:00:00,79.01,158.058,51.888999999999996,32.65 +2020-12-18 22:15:00,77.65,152.47799999999998,51.888999999999996,32.65 +2020-12-18 22:30:00,75.96,144.877,51.888999999999996,32.65 +2020-12-18 22:45:00,74.55,140.467,51.888999999999996,32.65 +2020-12-18 23:00:00,68.49,134.586,45.787,32.65 +2020-12-18 23:15:00,67.5,130.575,45.787,32.65 +2020-12-18 23:30:00,65.03,129.435,45.787,32.65 +2020-12-18 23:45:00,63.68,128.28,45.787,32.65 +2020-12-19 00:00:00,58.59,112.90299999999999,41.815,32.468 +2020-12-19 00:15:00,56.86,108.542,41.815,32.468 +2020-12-19 00:30:00,56.32,110.538,41.815,32.468 +2020-12-19 00:45:00,55.88,112.488,41.815,32.468 +2020-12-19 01:00:00,53.45,115.075,38.645,32.468 +2020-12-19 01:15:00,53.4,115.85600000000001,38.645,32.468 +2020-12-19 01:30:00,53.25,115.65799999999999,38.645,32.468 +2020-12-19 01:45:00,53.53,116.088,38.645,32.468 +2020-12-19 02:00:00,51.2,118.405,36.696,32.468 +2020-12-19 02:15:00,51.19,119.29299999999999,36.696,32.468 +2020-12-19 02:30:00,51.56,118.825,36.696,32.468 +2020-12-19 02:45:00,51.39,120.76700000000001,36.696,32.468 +2020-12-19 03:00:00,49.17,122.844,35.42,32.468 +2020-12-19 03:15:00,49.88,123.54899999999999,35.42,32.468 +2020-12-19 03:30:00,50.39,123.95100000000001,35.42,32.468 +2020-12-19 03:45:00,50.38,125.331,35.42,32.468 +2020-12-19 04:00:00,50.5,134.138,35.167,32.468 +2020-12-19 04:15:00,51.79,143.55200000000002,35.167,32.468 +2020-12-19 04:30:00,52.07,143.812,35.167,32.468 +2020-12-19 04:45:00,52.89,144.718,35.167,32.468 +2020-12-19 05:00:00,55.09,161.997,35.311,32.468 +2020-12-19 05:15:00,59.51,173.59400000000002,35.311,32.468 +2020-12-19 05:30:00,56.55,170.71599999999998,35.311,32.468 +2020-12-19 05:45:00,59.21,168.06099999999998,35.311,32.468 +2020-12-19 06:00:00,62.57,183.03900000000002,37.117,32.468 +2020-12-19 06:15:00,65.09,202.952,37.117,32.468 +2020-12-19 06:30:00,65.7,199.22299999999998,37.117,32.468 +2020-12-19 06:45:00,67.91,194.838,37.117,32.468 +2020-12-19 07:00:00,72.04,190.25900000000001,40.948,32.468 +2020-12-19 07:15:00,73.9,194.74,40.948,32.468 +2020-12-19 07:30:00,76.28,199.62900000000002,40.948,32.468 +2020-12-19 07:45:00,80.12,203.72299999999998,40.948,32.468 +2020-12-19 08:00:00,84.67,205.49,44.903,32.468 +2020-12-19 08:15:00,84.63,208.417,44.903,32.468 +2020-12-19 08:30:00,85.9,209.208,44.903,32.468 +2020-12-19 08:45:00,89.47,207.513,44.903,32.468 +2020-12-19 09:00:00,91.2,202.946,46.283,32.468 +2020-12-19 09:15:00,91.66,200.81900000000002,46.283,32.468 +2020-12-19 09:30:00,92.64,198.743,46.283,32.468 +2020-12-19 09:45:00,94.43,196.018,46.283,32.468 +2020-12-19 10:00:00,95.05,190.972,44.103,32.468 +2020-12-19 10:15:00,97.92,187.33599999999998,44.103,32.468 +2020-12-19 10:30:00,97.13,185.18900000000002,44.103,32.468 +2020-12-19 10:45:00,98.68,184.658,44.103,32.468 +2020-12-19 11:00:00,98.05,183.915,42.373999999999995,32.468 +2020-12-19 11:15:00,97.08,181.329,42.373999999999995,32.468 +2020-12-19 11:30:00,99.87,180.894,42.373999999999995,32.468 +2020-12-19 11:45:00,100.27,178.51,42.373999999999995,32.468 +2020-12-19 12:00:00,97.52,173.118,39.937,32.468 +2020-12-19 12:15:00,97.76,170.877,39.937,32.468 +2020-12-19 12:30:00,98.11,171.55,39.937,32.468 +2020-12-19 12:45:00,100.55,172.199,39.937,32.468 +2020-12-19 13:00:00,99.76,171.535,37.138000000000005,32.468 +2020-12-19 13:15:00,98.38,169.935,37.138000000000005,32.468 +2020-12-19 13:30:00,95.17,169.263,37.138000000000005,32.468 +2020-12-19 13:45:00,95.3,169.452,37.138000000000005,32.468 +2020-12-19 14:00:00,93.76,168.46900000000002,36.141999999999996,32.468 +2020-12-19 14:15:00,94.4,168.18900000000002,36.141999999999996,32.468 +2020-12-19 14:30:00,93.73,167.553,36.141999999999996,32.468 +2020-12-19 14:45:00,95.27,168.02,36.141999999999996,32.468 +2020-12-19 15:00:00,95.53,169.136,37.964,32.468 +2020-12-19 15:15:00,95.12,169.968,37.964,32.468 +2020-12-19 15:30:00,94.85,172.035,37.964,32.468 +2020-12-19 15:45:00,95.87,173.68400000000003,37.964,32.468 +2020-12-19 16:00:00,102.71,173.486,40.699,32.468 +2020-12-19 16:15:00,100.85,176.049,40.699,32.468 +2020-12-19 16:30:00,103.71,178.514,40.699,32.468 +2020-12-19 16:45:00,102.26,180.572,40.699,32.468 +2020-12-19 17:00:00,105.22,182.958,46.216,32.468 +2020-12-19 17:15:00,103.82,184.842,46.216,32.468 +2020-12-19 17:30:00,107.42,185.225,46.216,32.468 +2020-12-19 17:45:00,106.86,184.43,46.216,32.468 +2020-12-19 18:00:00,106.39,185.953,51.123999999999995,32.468 +2020-12-19 18:15:00,105.15,185.488,51.123999999999995,32.468 +2020-12-19 18:30:00,104.09,185.662,51.123999999999995,32.468 +2020-12-19 18:45:00,103.35,182.18599999999998,51.123999999999995,32.468 +2020-12-19 19:00:00,101.94,185.102,52.336000000000006,32.468 +2020-12-19 19:15:00,100.64,182.34900000000002,52.336000000000006,32.468 +2020-12-19 19:30:00,101.69,180.71,52.336000000000006,32.468 +2020-12-19 19:45:00,98.12,176.26,52.336000000000006,32.468 +2020-12-19 20:00:00,92.73,174.96900000000002,48.825,32.468 +2020-12-19 20:15:00,89.48,171.326,48.825,32.468 +2020-12-19 20:30:00,86.99,167.16099999999997,48.825,32.468 +2020-12-19 20:45:00,85.88,164.67,48.825,32.468 +2020-12-19 21:00:00,82.34,165.27700000000002,43.729,32.468 +2020-12-19 21:15:00,85.85,164.014,43.729,32.468 +2020-12-19 21:30:00,81.94,163.107,43.729,32.468 +2020-12-19 21:45:00,81.18,161.67600000000002,43.729,32.468 +2020-12-19 22:00:00,76.01,157.365,44.126000000000005,32.468 +2020-12-19 22:15:00,75.43,154.187,44.126000000000005,32.468 +2020-12-19 22:30:00,71.47,152.58100000000002,44.126000000000005,32.468 +2020-12-19 22:45:00,70.06,149.986,44.126000000000005,32.468 +2020-12-19 23:00:00,66.49,146.393,38.169000000000004,32.468 +2020-12-19 23:15:00,66.77,140.828,38.169000000000004,32.468 +2020-12-19 23:30:00,64.05,138.111,38.169000000000004,32.468 +2020-12-19 23:45:00,61.94,134.629,38.169000000000004,32.468 +2020-12-20 00:00:00,57.44,113.59899999999999,35.232,32.468 +2020-12-20 00:15:00,55.83,108.861,35.232,32.468 +2020-12-20 00:30:00,55.22,110.48299999999999,35.232,32.468 +2020-12-20 00:45:00,53.65,113.07600000000001,35.232,32.468 +2020-12-20 01:00:00,50.86,115.56200000000001,31.403000000000002,32.468 +2020-12-20 01:15:00,51.31,117.324,31.403000000000002,32.468 +2020-12-20 01:30:00,50.82,117.62100000000001,31.403000000000002,32.468 +2020-12-20 01:45:00,50.65,117.726,31.403000000000002,32.468 +2020-12-20 02:00:00,48.06,119.34700000000001,30.69,32.468 +2020-12-20 02:15:00,48.95,119.454,30.69,32.468 +2020-12-20 02:30:00,49.69,119.81200000000001,30.69,32.468 +2020-12-20 02:45:00,49.72,122.182,30.69,32.468 +2020-12-20 03:00:00,52.26,124.57700000000001,29.516,32.468 +2020-12-20 03:15:00,50.67,124.82,29.516,32.468 +2020-12-20 03:30:00,50.57,126.522,29.516,32.468 +2020-12-20 03:45:00,50.51,127.795,29.516,32.468 +2020-12-20 04:00:00,50.06,136.341,29.148000000000003,32.468 +2020-12-20 04:15:00,50.44,144.77100000000002,29.148000000000003,32.468 +2020-12-20 04:30:00,51.53,145.145,29.148000000000003,32.468 +2020-12-20 04:45:00,52.44,146.28799999999998,29.148000000000003,32.468 +2020-12-20 05:00:00,53.76,160.211,28.706,32.468 +2020-12-20 05:15:00,54.72,169.44299999999998,28.706,32.468 +2020-12-20 05:30:00,54.25,166.403,28.706,32.468 +2020-12-20 05:45:00,55.03,163.986,28.706,32.468 +2020-12-20 06:00:00,57.82,178.773,28.771,32.468 +2020-12-20 06:15:00,58.08,197.097,28.771,32.468 +2020-12-20 06:30:00,59.03,192.31900000000002,28.771,32.468 +2020-12-20 06:45:00,60.79,186.922,28.771,32.468 +2020-12-20 07:00:00,62.77,184.69099999999997,31.39,32.468 +2020-12-20 07:15:00,63.39,188.282,31.39,32.468 +2020-12-20 07:30:00,65.38,192.062,31.39,32.468 +2020-12-20 07:45:00,67.0,195.424,31.39,32.468 +2020-12-20 08:00:00,72.03,198.976,34.972,32.468 +2020-12-20 08:15:00,73.09,201.861,34.972,32.468 +2020-12-20 08:30:00,75.12,204.26,34.972,32.468 +2020-12-20 08:45:00,78.34,204.452,34.972,32.468 +2020-12-20 09:00:00,79.75,199.475,36.709,32.468 +2020-12-20 09:15:00,81.63,197.854,36.709,32.468 +2020-12-20 09:30:00,81.76,195.66099999999997,36.709,32.468 +2020-12-20 09:45:00,83.87,192.864,36.709,32.468 +2020-12-20 10:00:00,85.18,190.226,35.812,32.468 +2020-12-20 10:15:00,86.79,187.08700000000002,35.812,32.468 +2020-12-20 10:30:00,87.22,185.505,35.812,32.468 +2020-12-20 10:45:00,88.97,183.232,35.812,32.468 +2020-12-20 11:00:00,87.56,183.31900000000002,36.746,32.468 +2020-12-20 11:15:00,90.47,180.827,36.746,32.468 +2020-12-20 11:30:00,91.82,179.59799999999998,36.746,32.468 +2020-12-20 11:45:00,93.1,177.797,36.746,32.468 +2020-12-20 12:00:00,92.2,171.933,35.048,32.468 +2020-12-20 12:15:00,91.24,171.46599999999998,35.048,32.468 +2020-12-20 12:30:00,91.52,170.787,35.048,32.468 +2020-12-20 12:45:00,87.78,170.493,35.048,32.468 +2020-12-20 13:00:00,84.85,169.11700000000002,29.987,32.468 +2020-12-20 13:15:00,83.06,170.32299999999998,29.987,32.468 +2020-12-20 13:30:00,80.86,169.38299999999998,29.987,32.468 +2020-12-20 13:45:00,80.54,169.005,29.987,32.468 +2020-12-20 14:00:00,80.42,168.31099999999998,27.21,32.468 +2020-12-20 14:15:00,80.53,169.185,27.21,32.468 +2020-12-20 14:30:00,83.4,169.662,27.21,32.468 +2020-12-20 14:45:00,81.31,169.695,27.21,32.468 +2020-12-20 15:00:00,85.33,169.394,27.726999999999997,32.468 +2020-12-20 15:15:00,82.97,170.889,27.726999999999997,32.468 +2020-12-20 15:30:00,84.51,173.511,27.726999999999997,32.468 +2020-12-20 15:45:00,86.95,175.827,27.726999999999997,32.468 +2020-12-20 16:00:00,90.83,177.327,32.23,32.468 +2020-12-20 16:15:00,91.19,179.05700000000002,32.23,32.468 +2020-12-20 16:30:00,92.7,181.822,32.23,32.468 +2020-12-20 16:45:00,93.89,184.03099999999998,32.23,32.468 +2020-12-20 17:00:00,96.36,186.42700000000002,42.016999999999996,32.468 +2020-12-20 17:15:00,96.89,188.093,42.016999999999996,32.468 +2020-12-20 17:30:00,101.65,188.80599999999998,42.016999999999996,32.468 +2020-12-20 17:45:00,99.7,190.2,42.016999999999996,32.468 +2020-12-20 18:00:00,99.58,191.257,49.338,32.468 +2020-12-20 18:15:00,98.86,192.024,49.338,32.468 +2020-12-20 18:30:00,99.68,190.203,49.338,32.468 +2020-12-20 18:45:00,98.16,188.51,49.338,32.468 +2020-12-20 19:00:00,96.38,191.18099999999998,52.369,32.468 +2020-12-20 19:15:00,95.5,188.947,52.369,32.468 +2020-12-20 19:30:00,94.26,187.13400000000001,52.369,32.468 +2020-12-20 19:45:00,93.64,184.032,52.369,32.468 +2020-12-20 20:00:00,92.2,182.707,50.405,32.468 +2020-12-20 20:15:00,91.3,179.99400000000003,50.405,32.468 +2020-12-20 20:30:00,89.41,177.025,50.405,32.468 +2020-12-20 20:45:00,88.72,173.33700000000002,50.405,32.468 +2020-12-20 21:00:00,82.11,171.449,46.235,32.468 +2020-12-20 21:15:00,79.31,169.55900000000003,46.235,32.468 +2020-12-20 21:30:00,78.23,168.908,46.235,32.468 +2020-12-20 21:45:00,80.71,167.632,46.235,32.468 +2020-12-20 22:00:00,76.48,162.267,46.861000000000004,32.468 +2020-12-20 22:15:00,75.69,158.30200000000002,46.861000000000004,32.468 +2020-12-20 22:30:00,72.53,153.685,46.861000000000004,32.468 +2020-12-20 22:45:00,71.4,150.233,46.861000000000004,32.468 +2020-12-20 23:00:00,67.24,143.93200000000002,41.302,32.468 +2020-12-20 23:15:00,67.09,140.191,41.302,32.468 +2020-12-20 23:30:00,65.09,138.24200000000002,41.302,32.468 +2020-12-20 23:45:00,63.54,135.611,41.302,32.468 +2020-12-21 00:00:00,59.61,117.899,37.164,32.65 +2020-12-21 00:15:00,58.76,116.00200000000001,37.164,32.65 +2020-12-21 00:30:00,55.48,117.723,37.164,32.65 +2020-12-21 00:45:00,58.12,119.76799999999999,37.164,32.65 +2020-12-21 01:00:00,54.85,122.302,34.994,32.65 +2020-12-21 01:15:00,52.98,123.559,34.994,32.65 +2020-12-21 01:30:00,50.94,123.928,34.994,32.65 +2020-12-21 01:45:00,50.8,124.125,34.994,32.65 +2020-12-21 02:00:00,52.33,125.757,34.571,32.65 +2020-12-21 02:15:00,52.41,127.214,34.571,32.65 +2020-12-21 02:30:00,52.65,127.90100000000001,34.571,32.65 +2020-12-21 02:45:00,53.11,129.673,34.571,32.65 +2020-12-21 03:00:00,51.52,133.284,33.934,32.65 +2020-12-21 03:15:00,52.71,135.15,33.934,32.65 +2020-12-21 03:30:00,52.07,136.624,33.934,32.65 +2020-12-21 03:45:00,49.61,137.35299999999998,33.934,32.65 +2020-12-21 04:00:00,53.48,150.143,34.107,32.65 +2020-12-21 04:15:00,52.93,162.64,34.107,32.65 +2020-12-21 04:30:00,53.67,165.072,34.107,32.65 +2020-12-21 04:45:00,54.53,166.38400000000001,34.107,32.65 +2020-12-21 05:00:00,56.42,195.558,39.575,32.65 +2020-12-21 05:15:00,57.68,224.365,39.575,32.65 +2020-12-21 05:30:00,58.83,221.533,39.575,32.65 +2020-12-21 05:45:00,60.34,213.628,39.575,32.65 +2020-12-21 06:00:00,62.76,210.952,56.156000000000006,32.65 +2020-12-21 06:15:00,66.38,214.78599999999997,56.156000000000006,32.65 +2020-12-21 06:30:00,67.63,217.83,56.156000000000006,32.65 +2020-12-21 06:45:00,70.42,221.06,56.156000000000006,32.65 +2020-12-21 07:00:00,74.09,221.109,67.926,32.65 +2020-12-21 07:15:00,75.56,225.983,67.926,32.65 +2020-12-21 07:30:00,78.04,228.984,67.926,32.65 +2020-12-21 07:45:00,81.33,229.912,67.926,32.65 +2020-12-21 08:00:00,85.45,228.74900000000002,72.58,32.65 +2020-12-21 08:15:00,86.9,229.53400000000002,72.58,32.65 +2020-12-21 08:30:00,89.21,228.0,72.58,32.65 +2020-12-21 08:45:00,91.43,225.005,72.58,32.65 +2020-12-21 09:00:00,94.89,219.02200000000002,66.984,32.65 +2020-12-21 09:15:00,97.25,214.018,66.984,32.65 +2020-12-21 09:30:00,101.27,210.845,66.984,32.65 +2020-12-21 09:45:00,99.72,208.222,66.984,32.65 +2020-12-21 10:00:00,101.1,204.55200000000002,63.158,32.65 +2020-12-21 10:15:00,100.36,201.084,63.158,32.65 +2020-12-21 10:30:00,101.56,198.63400000000001,63.158,32.65 +2020-12-21 10:45:00,102.86,196.997,63.158,32.65 +2020-12-21 11:00:00,100.92,194.606,61.141000000000005,32.65 +2020-12-21 11:15:00,101.74,193.829,61.141000000000005,32.65 +2020-12-21 11:30:00,103.83,193.94,61.141000000000005,32.65 +2020-12-21 11:45:00,102.91,191.77200000000002,61.141000000000005,32.65 +2020-12-21 12:00:00,101.04,187.456,57.961000000000006,32.65 +2020-12-21 12:15:00,101.4,187.00900000000001,57.961000000000006,32.65 +2020-12-21 12:30:00,98.37,186.525,57.961000000000006,32.65 +2020-12-21 12:45:00,97.4,187.678,57.961000000000006,32.65 +2020-12-21 13:00:00,94.93,186.868,56.843,32.65 +2020-12-21 13:15:00,92.49,186.717,56.843,32.65 +2020-12-21 13:30:00,90.19,185.27599999999998,56.843,32.65 +2020-12-21 13:45:00,89.94,184.963,56.843,32.65 +2020-12-21 14:00:00,86.88,183.642,55.992,32.65 +2020-12-21 14:15:00,84.1,183.937,55.992,32.65 +2020-12-21 14:30:00,81.84,183.912,55.992,32.65 +2020-12-21 14:45:00,83.32,184.023,55.992,32.65 +2020-12-21 15:00:00,83.47,185.426,57.523,32.65 +2020-12-21 15:15:00,81.79,185.524,57.523,32.65 +2020-12-21 15:30:00,81.2,187.407,57.523,32.65 +2020-12-21 15:45:00,82.37,189.275,57.523,32.65 +2020-12-21 16:00:00,85.28,191.02,59.471000000000004,32.65 +2020-12-21 16:15:00,84.75,192.044,59.471000000000004,32.65 +2020-12-21 16:30:00,86.17,193.86700000000002,59.471000000000004,32.65 +2020-12-21 16:45:00,86.8,194.97,59.471000000000004,32.65 +2020-12-21 17:00:00,89.11,197.155,65.066,32.65 +2020-12-21 17:15:00,90.27,197.953,65.066,32.65 +2020-12-21 17:30:00,92.59,198.148,65.066,32.65 +2020-12-21 17:45:00,92.01,198.112,65.066,32.65 +2020-12-21 18:00:00,92.03,199.575,69.581,32.65 +2020-12-21 18:15:00,92.38,198.145,69.581,32.65 +2020-12-21 18:30:00,91.75,196.93200000000002,69.581,32.65 +2020-12-21 18:45:00,91.35,196.043,69.581,32.65 +2020-12-21 19:00:00,87.77,197.149,73.771,32.65 +2020-12-21 19:15:00,86.86,193.826,73.771,32.65 +2020-12-21 19:30:00,85.1,192.47299999999998,73.771,32.65 +2020-12-21 19:45:00,84.71,188.53799999999998,73.771,32.65 +2020-12-21 20:00:00,81.21,184.893,65.035,32.65 +2020-12-21 20:15:00,80.86,179.843,65.035,32.65 +2020-12-21 20:30:00,79.94,175.08700000000002,65.035,32.65 +2020-12-21 20:45:00,78.91,172.983,65.035,32.65 +2020-12-21 21:00:00,76.0,171.56799999999998,58.7,32.65 +2020-12-21 21:15:00,76.31,168.549,58.7,32.65 +2020-12-21 21:30:00,74.92,167.11900000000003,58.7,32.65 +2020-12-21 21:45:00,74.61,165.36,58.7,32.65 +2020-12-21 22:00:00,72.26,157.161,53.888000000000005,32.65 +2020-12-21 22:15:00,72.03,152.0,53.888000000000005,32.65 +2020-12-21 22:30:00,70.9,138.101,53.888000000000005,32.65 +2020-12-21 22:45:00,70.7,130.013,53.888000000000005,32.65 +2020-12-21 23:00:00,67.92,124.43299999999999,45.501999999999995,32.65 +2020-12-21 23:15:00,65.32,123.19,45.501999999999995,32.65 +2020-12-21 23:30:00,63.5,123.906,45.501999999999995,32.65 +2020-12-21 23:45:00,63.09,123.789,45.501999999999995,32.65 +2020-12-22 00:00:00,77.11,117.387,43.537,32.65 +2020-12-22 00:15:00,75.55,116.86,43.537,32.65 +2020-12-22 00:30:00,74.5,117.693,43.537,32.65 +2020-12-22 00:45:00,75.85,118.81,43.537,32.65 +2020-12-22 01:00:00,74.35,121.161,41.854,32.65 +2020-12-22 01:15:00,72.04,121.991,41.854,32.65 +2020-12-22 01:30:00,70.74,122.525,41.854,32.65 +2020-12-22 01:45:00,72.79,122.985,41.854,32.65 +2020-12-22 02:00:00,71.78,124.613,40.321,32.65 +2020-12-22 02:15:00,72.48,126.00299999999999,40.321,32.65 +2020-12-22 02:30:00,72.39,126.111,40.321,32.65 +2020-12-22 02:45:00,73.21,127.904,40.321,32.65 +2020-12-22 03:00:00,72.62,130.333,39.632,32.65 +2020-12-22 03:15:00,73.58,131.439,39.632,32.65 +2020-12-22 03:30:00,75.36,133.376,39.632,32.65 +2020-12-22 03:45:00,75.07,134.254,39.632,32.65 +2020-12-22 04:00:00,74.68,146.79,40.183,32.65 +2020-12-22 04:15:00,74.32,158.94799999999998,40.183,32.65 +2020-12-22 04:30:00,75.7,161.071,40.183,32.65 +2020-12-22 04:45:00,77.44,163.56,40.183,32.65 +2020-12-22 05:00:00,80.88,197.551,43.945,32.65 +2020-12-22 05:15:00,83.17,226.143,43.945,32.65 +2020-12-22 05:30:00,88.89,221.862,43.945,32.65 +2020-12-22 05:45:00,94.9,213.957,43.945,32.65 +2020-12-22 06:00:00,103.72,210.203,56.048,32.65 +2020-12-22 06:15:00,108.11,215.607,56.048,32.65 +2020-12-22 06:30:00,112.38,218.05200000000002,56.048,32.65 +2020-12-22 06:45:00,116.06,220.926,56.048,32.65 +2020-12-22 07:00:00,122.27,220.813,65.74,32.65 +2020-12-22 07:15:00,126.37,225.525,65.74,32.65 +2020-12-22 07:30:00,128.12,227.997,65.74,32.65 +2020-12-22 07:45:00,131.48,229.09,65.74,32.65 +2020-12-22 08:00:00,133.47,228.032,72.757,32.65 +2020-12-22 08:15:00,133.0,227.81099999999998,72.757,32.65 +2020-12-22 08:30:00,133.5,226.104,72.757,32.65 +2020-12-22 08:45:00,134.21,222.799,72.757,32.65 +2020-12-22 09:00:00,133.97,216.007,67.692,32.65 +2020-12-22 09:15:00,136.46,212.53900000000002,67.692,32.65 +2020-12-22 09:30:00,137.68,210.049,67.692,32.65 +2020-12-22 09:45:00,137.1,207.25099999999998,67.692,32.65 +2020-12-22 10:00:00,137.74,202.968,63.506,32.65 +2020-12-22 10:15:00,137.9,198.468,63.506,32.65 +2020-12-22 10:30:00,137.65,196.17700000000002,63.506,32.65 +2020-12-22 10:45:00,140.41,194.83900000000003,63.506,32.65 +2020-12-22 11:00:00,142.25,193.857,60.758,32.65 +2020-12-22 11:15:00,143.49,192.766,60.758,32.65 +2020-12-22 11:30:00,143.27,191.726,60.758,32.65 +2020-12-22 11:45:00,138.82,190.21099999999998,60.758,32.65 +2020-12-22 12:00:00,137.52,184.581,57.519,32.65 +2020-12-22 12:15:00,137.54,183.763,57.519,32.65 +2020-12-22 12:30:00,137.08,184.00599999999997,57.519,32.65 +2020-12-22 12:45:00,135.61,184.908,57.519,32.65 +2020-12-22 13:00:00,132.97,183.676,56.46,32.65 +2020-12-22 13:15:00,132.28,183.25400000000002,56.46,32.65 +2020-12-22 13:30:00,130.5,182.924,56.46,32.65 +2020-12-22 13:45:00,130.46,182.74599999999998,56.46,32.65 +2020-12-22 14:00:00,129.11,181.707,56.207,32.65 +2020-12-22 14:15:00,129.2,182.144,56.207,32.65 +2020-12-22 14:30:00,127.97,182.748,56.207,32.65 +2020-12-22 14:45:00,128.67,182.766,56.207,32.65 +2020-12-22 15:00:00,128.91,183.767,57.391999999999996,32.65 +2020-12-22 15:15:00,128.69,184.206,57.391999999999996,32.65 +2020-12-22 15:30:00,128.66,186.304,57.391999999999996,32.65 +2020-12-22 15:45:00,129.67,187.796,57.391999999999996,32.65 +2020-12-22 16:00:00,133.28,189.83700000000002,59.955,32.65 +2020-12-22 16:15:00,135.66,191.335,59.955,32.65 +2020-12-22 16:30:00,137.94,193.77200000000002,59.955,32.65 +2020-12-22 16:45:00,139.17,195.19400000000002,59.955,32.65 +2020-12-22 17:00:00,143.78,197.887,67.063,32.65 +2020-12-22 17:15:00,144.03,198.753,67.063,32.65 +2020-12-22 17:30:00,145.49,199.59599999999998,67.063,32.65 +2020-12-22 17:45:00,142.58,199.43900000000002,67.063,32.65 +2020-12-22 18:00:00,139.78,200.797,71.477,32.65 +2020-12-22 18:15:00,138.93,198.907,71.477,32.65 +2020-12-22 18:30:00,137.96,197.396,71.477,32.65 +2020-12-22 18:45:00,138.52,197.283,71.477,32.65 +2020-12-22 19:00:00,134.79,198.418,74.32,32.65 +2020-12-22 19:15:00,134.11,194.825,74.32,32.65 +2020-12-22 19:30:00,134.57,192.81400000000002,74.32,32.65 +2020-12-22 19:45:00,134.19,188.90900000000002,74.32,32.65 +2020-12-22 20:00:00,126.29,185.421,66.157,32.65 +2020-12-22 20:15:00,120.72,179.71200000000002,66.157,32.65 +2020-12-22 20:30:00,116.25,175.917,66.157,32.65 +2020-12-22 20:45:00,114.97,173.28900000000002,66.157,32.65 +2020-12-22 21:00:00,111.39,171.205,59.806000000000004,32.65 +2020-12-22 21:15:00,112.49,169.00799999999998,59.806000000000004,32.65 +2020-12-22 21:30:00,112.51,166.87900000000002,59.806000000000004,32.65 +2020-12-22 21:45:00,108.75,165.354,59.806000000000004,32.65 +2020-12-22 22:00:00,100.85,158.816,54.785,32.65 +2020-12-22 22:15:00,98.46,153.403,54.785,32.65 +2020-12-22 22:30:00,93.77,139.632,54.785,32.65 +2020-12-22 22:45:00,94.39,131.819,54.785,32.65 +2020-12-22 23:00:00,92.62,126.223,47.176,32.65 +2020-12-22 23:15:00,91.43,124.146,47.176,32.65 +2020-12-22 23:30:00,86.04,124.527,47.176,32.65 +2020-12-22 23:45:00,83.32,123.98200000000001,47.176,32.65 +2020-12-23 00:00:00,83.37,117.60700000000001,43.42,32.65 +2020-12-23 00:15:00,83.59,117.055,43.42,32.65 +2020-12-23 00:30:00,83.23,117.87899999999999,43.42,32.65 +2020-12-23 00:45:00,80.94,118.979,43.42,32.65 +2020-12-23 01:00:00,76.7,121.35,40.869,32.65 +2020-12-23 01:15:00,80.48,122.177,40.869,32.65 +2020-12-23 01:30:00,81.54,122.714,40.869,32.65 +2020-12-23 01:45:00,80.75,123.163,40.869,32.65 +2020-12-23 02:00:00,77.53,124.807,39.541,32.65 +2020-12-23 02:15:00,76.71,126.197,39.541,32.65 +2020-12-23 02:30:00,79.2,126.31,39.541,32.65 +2020-12-23 02:45:00,81.57,128.10299999999998,39.541,32.65 +2020-12-23 03:00:00,79.82,130.524,39.052,32.65 +2020-12-23 03:15:00,77.81,131.65200000000002,39.052,32.65 +2020-12-23 03:30:00,79.49,133.59,39.052,32.65 +2020-12-23 03:45:00,83.44,134.471,39.052,32.65 +2020-12-23 04:00:00,82.03,146.98,40.36,32.65 +2020-12-23 04:15:00,80.22,159.136,40.36,32.65 +2020-12-23 04:30:00,80.62,161.252,40.36,32.65 +2020-12-23 04:45:00,83.03,163.74,40.36,32.65 +2020-12-23 05:00:00,87.93,197.69799999999998,43.133,32.65 +2020-12-23 05:15:00,91.08,226.25599999999997,43.133,32.65 +2020-12-23 05:30:00,94.96,221.985,43.133,32.65 +2020-12-23 05:45:00,98.6,214.09900000000002,43.133,32.65 +2020-12-23 06:00:00,105.55,210.36900000000003,54.953,32.65 +2020-12-23 06:15:00,110.85,215.77700000000002,54.953,32.65 +2020-12-23 06:30:00,115.87,218.25099999999998,54.953,32.65 +2020-12-23 06:45:00,120.4,221.162,54.953,32.65 +2020-12-23 07:00:00,126.78,221.05700000000002,66.566,32.65 +2020-12-23 07:15:00,128.65,225.765,66.566,32.65 +2020-12-23 07:30:00,130.99,228.233,66.566,32.65 +2020-12-23 07:45:00,133.38,229.315,66.566,32.65 +2020-12-23 08:00:00,135.54,228.261,72.902,32.65 +2020-12-23 08:15:00,135.89,228.02700000000002,72.902,32.65 +2020-12-23 08:30:00,136.41,226.31099999999998,72.902,32.65 +2020-12-23 08:45:00,136.61,222.982,72.902,32.65 +2020-12-23 09:00:00,137.52,216.169,68.465,32.65 +2020-12-23 09:15:00,138.65,212.708,68.465,32.65 +2020-12-23 09:30:00,139.53,210.229,68.465,32.65 +2020-12-23 09:45:00,140.58,207.421,68.465,32.65 +2020-12-23 10:00:00,139.15,203.13299999999998,63.625,32.65 +2020-12-23 10:15:00,139.36,198.62400000000002,63.625,32.65 +2020-12-23 10:30:00,139.02,196.31799999999998,63.625,32.65 +2020-12-23 10:45:00,138.24,194.97799999999998,63.625,32.65 +2020-12-23 11:00:00,141.5,193.97799999999998,61.628,32.65 +2020-12-23 11:15:00,140.02,192.88,61.628,32.65 +2020-12-23 11:30:00,142.39,191.84,61.628,32.65 +2020-12-23 11:45:00,137.8,190.325,61.628,32.65 +2020-12-23 12:00:00,134.47,184.69799999999998,58.708999999999996,32.65 +2020-12-23 12:15:00,132.86,183.894,58.708999999999996,32.65 +2020-12-23 12:30:00,132.25,184.143,58.708999999999996,32.65 +2020-12-23 12:45:00,133.37,185.047,58.708999999999996,32.65 +2020-12-23 13:00:00,133.19,183.796,57.373000000000005,32.65 +2020-12-23 13:15:00,133.97,183.37,57.373000000000005,32.65 +2020-12-23 13:30:00,132.3,183.033,57.373000000000005,32.65 +2020-12-23 13:45:00,130.97,182.84799999999998,57.373000000000005,32.65 +2020-12-23 14:00:00,130.64,181.804,57.684,32.65 +2020-12-23 14:15:00,130.79,182.24099999999999,57.684,32.65 +2020-12-23 14:30:00,130.4,182.863,57.684,32.65 +2020-12-23 14:45:00,130.0,182.893,57.684,32.65 +2020-12-23 15:00:00,130.36,183.90900000000002,58.03,32.65 +2020-12-23 15:15:00,130.22,184.33900000000003,58.03,32.65 +2020-12-23 15:30:00,129.82,186.446,58.03,32.65 +2020-12-23 15:45:00,131.13,187.935,58.03,32.65 +2020-12-23 16:00:00,133.7,189.97299999999998,59.97,32.65 +2020-12-23 16:15:00,136.31,191.487,59.97,32.65 +2020-12-23 16:30:00,139.13,193.93,59.97,32.65 +2020-12-23 16:45:00,141.42,195.372,59.97,32.65 +2020-12-23 17:00:00,147.76,198.045,65.661,32.65 +2020-12-23 17:15:00,147.93,198.938,65.661,32.65 +2020-12-23 17:30:00,148.05,199.803,65.661,32.65 +2020-12-23 17:45:00,143.23,199.658,65.661,32.65 +2020-12-23 18:00:00,140.17,201.03599999999997,70.96300000000001,32.65 +2020-12-23 18:15:00,139.83,199.13299999999998,70.96300000000001,32.65 +2020-12-23 18:30:00,138.54,197.62900000000002,70.96300000000001,32.65 +2020-12-23 18:45:00,138.83,197.52900000000002,70.96300000000001,32.65 +2020-12-23 19:00:00,136.66,198.641,74.133,32.65 +2020-12-23 19:15:00,134.75,195.046,74.133,32.65 +2020-12-23 19:30:00,133.88,193.02900000000002,74.133,32.65 +2020-12-23 19:45:00,133.25,189.11,74.133,32.65 +2020-12-23 20:00:00,126.72,185.614,65.613,32.65 +2020-12-23 20:15:00,120.6,179.90200000000002,65.613,32.65 +2020-12-23 20:30:00,117.16,176.08900000000003,65.613,32.65 +2020-12-23 20:45:00,115.94,173.47799999999998,65.613,32.65 +2020-12-23 21:00:00,113.09,171.37900000000002,58.583,32.65 +2020-12-23 21:15:00,114.76,169.165,58.583,32.65 +2020-12-23 21:30:00,112.8,167.037,58.583,32.65 +2020-12-23 21:45:00,109.3,165.523,58.583,32.65 +2020-12-23 22:00:00,103.75,158.988,54.411,32.65 +2020-12-23 22:15:00,102.73,153.583,54.411,32.65 +2020-12-23 22:30:00,99.23,139.843,54.411,32.65 +2020-12-23 22:45:00,99.37,132.03799999999998,54.411,32.65 +2020-12-23 23:00:00,94.42,126.421,47.878,32.65 +2020-12-23 23:15:00,89.7,124.345,47.878,32.65 +2020-12-23 23:30:00,86.46,124.74,47.878,32.65 +2020-12-23 23:45:00,87.03,124.18799999999999,47.878,32.65 +2020-12-24 00:00:00,57.0,117.822,44.513000000000005,32.468 +2020-12-24 00:15:00,57.34,117.244,44.513000000000005,32.468 +2020-12-24 00:30:00,56.42,118.05799999999999,44.513000000000005,32.468 +2020-12-24 00:45:00,55.53,119.14,44.513000000000005,32.468 +2020-12-24 01:00:00,52.63,121.53200000000001,43.169,32.468 +2020-12-24 01:15:00,53.35,122.355,43.169,32.468 +2020-12-24 01:30:00,52.36,122.896,43.169,32.468 +2020-12-24 01:45:00,52.48,123.33200000000001,43.169,32.468 +2020-12-24 02:00:00,51.25,124.992,41.763999999999996,32.468 +2020-12-24 02:15:00,52.09,126.383,41.763999999999996,32.468 +2020-12-24 02:30:00,51.45,126.501,41.763999999999996,32.468 +2020-12-24 02:45:00,51.82,128.293,41.763999999999996,32.468 +2020-12-24 03:00:00,51.68,130.705,41.155,32.468 +2020-12-24 03:15:00,52.17,131.857,41.155,32.468 +2020-12-24 03:30:00,52.33,133.796,41.155,32.468 +2020-12-24 03:45:00,53.49,134.678,41.155,32.468 +2020-12-24 04:00:00,54.37,147.165,41.96,32.468 +2020-12-24 04:15:00,54.98,159.316,41.96,32.468 +2020-12-24 04:30:00,55.93,161.425,41.96,32.468 +2020-12-24 04:45:00,57.9,163.912,41.96,32.468 +2020-12-24 05:00:00,61.23,197.83700000000002,45.206,32.468 +2020-12-24 05:15:00,61.7,226.36,45.206,32.468 +2020-12-24 05:30:00,63.74,222.101,45.206,32.468 +2020-12-24 05:45:00,65.72,214.232,45.206,32.468 +2020-12-24 06:00:00,71.25,210.52599999999998,55.398999999999994,32.468 +2020-12-24 06:15:00,73.82,215.94,55.398999999999994,32.468 +2020-12-24 06:30:00,75.73,218.44099999999997,55.398999999999994,32.468 +2020-12-24 06:45:00,79.76,221.386,55.398999999999994,32.468 +2020-12-24 07:00:00,84.31,221.29,64.627,32.468 +2020-12-24 07:15:00,85.26,225.99599999999998,64.627,32.468 +2020-12-24 07:30:00,86.83,228.458,64.627,32.468 +2020-12-24 07:45:00,89.99,229.53,64.627,32.468 +2020-12-24 08:00:00,92.92,228.477,70.895,32.468 +2020-12-24 08:15:00,93.1,228.232,70.895,32.468 +2020-12-24 08:30:00,94.95,226.505,70.895,32.468 +2020-12-24 08:45:00,95.57,223.15200000000002,70.895,32.468 +2020-12-24 09:00:00,99.59,216.31900000000002,66.382,32.468 +2020-12-24 09:15:00,100.3,212.865,66.382,32.468 +2020-12-24 09:30:00,100.82,210.396,66.382,32.468 +2020-12-24 09:45:00,100.23,207.579,66.382,32.468 +2020-12-24 10:00:00,101.06,203.28799999999998,62.739,32.468 +2020-12-24 10:15:00,101.79,198.771,62.739,32.468 +2020-12-24 10:30:00,102.05,196.449,62.739,32.468 +2020-12-24 10:45:00,102.54,195.107,62.739,32.468 +2020-12-24 11:00:00,100.69,194.09,60.843,32.468 +2020-12-24 11:15:00,101.48,192.986,60.843,32.468 +2020-12-24 11:30:00,100.88,191.946,60.843,32.468 +2020-12-24 11:45:00,99.0,190.43,60.843,32.468 +2020-12-24 12:00:00,94.85,184.805,58.466,32.468 +2020-12-24 12:15:00,95.31,184.016,58.466,32.468 +2020-12-24 12:30:00,93.98,184.271,58.466,32.468 +2020-12-24 12:45:00,96.03,185.178,58.466,32.468 +2020-12-24 13:00:00,94.18,183.908,56.883,32.468 +2020-12-24 13:15:00,90.44,183.48,56.883,32.468 +2020-12-24 13:30:00,90.13,183.132,56.883,32.468 +2020-12-24 13:45:00,90.54,182.94,56.883,32.468 +2020-12-24 14:00:00,89.78,181.893,56.503,32.468 +2020-12-24 14:15:00,92.43,182.33,56.503,32.468 +2020-12-24 14:30:00,90.39,182.968,56.503,32.468 +2020-12-24 14:45:00,92.14,183.01,56.503,32.468 +2020-12-24 15:00:00,92.57,184.044,57.803999999999995,32.468 +2020-12-24 15:15:00,93.64,184.463,57.803999999999995,32.468 +2020-12-24 15:30:00,94.37,186.578,57.803999999999995,32.468 +2020-12-24 15:45:00,97.44,188.062,57.803999999999995,32.468 +2020-12-24 16:00:00,103.78,190.1,59.379,32.468 +2020-12-24 16:15:00,107.92,191.628,59.379,32.468 +2020-12-24 16:30:00,106.71,194.077,59.379,32.468 +2020-12-24 16:45:00,107.58,195.53900000000002,59.379,32.468 +2020-12-24 17:00:00,111.26,198.19299999999998,64.71600000000001,32.468 +2020-12-24 17:15:00,110.82,199.113,64.71600000000001,32.468 +2020-12-24 17:30:00,112.07,200.0,64.71600000000001,32.468 +2020-12-24 17:45:00,111.35,199.86700000000002,64.71600000000001,32.468 +2020-12-24 18:00:00,110.75,201.264,68.803,32.468 +2020-12-24 18:15:00,110.62,199.34900000000002,68.803,32.468 +2020-12-24 18:30:00,110.52,197.851,68.803,32.468 +2020-12-24 18:45:00,110.48,197.765,68.803,32.468 +2020-12-24 19:00:00,111.49,198.856,72.934,32.468 +2020-12-24 19:15:00,106.52,195.25599999999997,72.934,32.468 +2020-12-24 19:30:00,104.08,193.234,72.934,32.468 +2020-12-24 19:45:00,102.68,189.304,72.934,32.468 +2020-12-24 20:00:00,96.72,185.8,65.175,32.468 +2020-12-24 20:15:00,96.77,180.084,65.175,32.468 +2020-12-24 20:30:00,91.18,176.25400000000002,65.175,32.468 +2020-12-24 20:45:00,89.83,173.66,65.175,32.468 +2020-12-24 21:00:00,86.22,171.545,58.55,32.468 +2020-12-24 21:15:00,85.58,169.31400000000002,58.55,32.468 +2020-12-24 21:30:00,83.25,167.188,58.55,32.468 +2020-12-24 21:45:00,85.54,165.68599999999998,58.55,32.468 +2020-12-24 22:00:00,76.93,159.151,55.041000000000004,32.468 +2020-12-24 22:15:00,77.34,153.755,55.041000000000004,32.468 +2020-12-24 22:30:00,73.53,140.047,55.041000000000004,32.468 +2020-12-24 22:45:00,72.69,132.249,55.041000000000004,32.468 +2020-12-24 23:00:00,68.02,126.61,48.258,32.468 +2020-12-24 23:15:00,68.11,124.535,48.258,32.468 +2020-12-24 23:30:00,64.77,124.944,48.258,32.468 +2020-12-24 23:45:00,67.67,124.384,48.258,32.468 +2020-12-25 00:00:00,57.87,114.70100000000001,32.311,32.468 +2020-12-25 00:15:00,58.21,109.835,32.311,32.468 +2020-12-25 00:30:00,57.43,111.413,32.311,32.468 +2020-12-25 00:45:00,57.71,113.917,32.311,32.468 +2020-12-25 01:00:00,54.98,116.509,25.569000000000003,32.468 +2020-12-25 01:15:00,55.19,118.25200000000001,25.569000000000003,32.468 +2020-12-25 01:30:00,54.78,118.568,25.569000000000003,32.468 +2020-12-25 01:45:00,53.55,118.613,25.569000000000003,32.468 +2020-12-25 02:00:00,50.76,120.314,21.038,32.468 +2020-12-25 02:15:00,52.54,120.425,21.038,32.468 +2020-12-25 02:30:00,49.88,120.809,21.038,32.468 +2020-12-25 02:45:00,52.68,123.17399999999999,21.038,32.468 +2020-12-25 03:00:00,51.32,125.52600000000001,19.865,32.468 +2020-12-25 03:15:00,51.45,125.884,19.865,32.468 +2020-12-25 03:30:00,52.01,127.59,19.865,32.468 +2020-12-25 03:45:00,52.84,128.874,19.865,32.468 +2020-12-25 04:00:00,52.17,137.297,19.076,32.468 +2020-12-25 04:15:00,52.24,145.708,19.076,32.468 +2020-12-25 04:30:00,53.63,146.046,19.076,32.468 +2020-12-25 04:45:00,54.09,147.187,19.076,32.468 +2020-12-25 05:00:00,53.22,160.945,20.174,32.468 +2020-12-25 05:15:00,54.53,169.99900000000002,20.174,32.468 +2020-12-25 05:30:00,53.56,167.02200000000002,20.174,32.468 +2020-12-25 05:45:00,57.2,164.69400000000002,20.174,32.468 +2020-12-25 06:00:00,57.45,179.6,19.854,32.468 +2020-12-25 06:15:00,58.55,197.949,19.854,32.468 +2020-12-25 06:30:00,59.92,193.317,19.854,32.468 +2020-12-25 06:45:00,61.05,188.095,19.854,32.468 +2020-12-25 07:00:00,63.95,185.90599999999998,23.096999999999998,32.468 +2020-12-25 07:15:00,65.26,189.485,23.096999999999998,32.468 +2020-12-25 07:30:00,66.28,193.239,23.096999999999998,32.468 +2020-12-25 07:45:00,69.33,196.55,23.096999999999998,32.468 +2020-12-25 08:00:00,71.62,200.118,30.849,32.468 +2020-12-25 08:15:00,74.42,202.94400000000002,30.849,32.468 +2020-12-25 08:30:00,76.82,205.296,30.849,32.468 +2020-12-25 08:45:00,78.68,205.364,30.849,32.468 +2020-12-25 09:00:00,81.32,200.285,30.03,32.468 +2020-12-25 09:15:00,83.82,198.69799999999998,30.03,32.468 +2020-12-25 09:30:00,85.11,196.558,30.03,32.468 +2020-12-25 09:45:00,87.16,193.717,30.03,32.468 +2020-12-25 10:00:00,87.91,191.055,27.625999999999998,32.468 +2020-12-25 10:15:00,89.23,187.868,27.625999999999998,32.468 +2020-12-25 10:30:00,91.55,186.21,27.625999999999998,32.468 +2020-12-25 10:45:00,93.17,183.923,27.625999999999998,32.468 +2020-12-25 11:00:00,94.65,183.926,29.03,32.468 +2020-12-25 11:15:00,98.58,181.39700000000002,29.03,32.468 +2020-12-25 11:30:00,100.6,180.172,29.03,32.468 +2020-12-25 11:45:00,100.05,178.365,29.03,32.468 +2020-12-25 12:00:00,95.72,172.512,25.93,32.468 +2020-12-25 12:15:00,92.54,172.11900000000003,25.93,32.468 +2020-12-25 12:30:00,88.4,171.47,25.93,32.468 +2020-12-25 12:45:00,84.51,171.19299999999998,25.93,32.468 +2020-12-25 13:00:00,81.0,169.72099999999998,16.363,32.468 +2020-12-25 13:15:00,80.85,170.907,16.363,32.468 +2020-12-25 13:30:00,80.27,169.92700000000002,16.363,32.468 +2020-12-25 13:45:00,80.22,169.51,16.363,32.468 +2020-12-25 14:00:00,77.57,168.797,14.370999999999999,32.468 +2020-12-25 14:15:00,78.09,169.671,14.370999999999999,32.468 +2020-12-25 14:30:00,77.16,170.231,14.370999999999999,32.468 +2020-12-25 14:45:00,76.87,170.32299999999998,14.370999999999999,32.468 +2020-12-25 15:00:00,77.34,170.106,19.031,32.468 +2020-12-25 15:15:00,76.88,171.554,19.031,32.468 +2020-12-25 15:30:00,76.93,174.22,19.031,32.468 +2020-12-25 15:45:00,78.2,176.516,19.031,32.468 +2020-12-25 16:00:00,81.38,178.011,24.998,32.468 +2020-12-25 16:15:00,82.28,179.81900000000002,24.998,32.468 +2020-12-25 16:30:00,86.93,182.61,24.998,32.468 +2020-12-25 16:45:00,86.17,184.921,24.998,32.468 +2020-12-25 17:00:00,89.63,187.218,35.976,32.468 +2020-12-25 17:15:00,87.98,189.021,35.976,32.468 +2020-12-25 17:30:00,89.65,189.84099999999998,35.976,32.468 +2020-12-25 17:45:00,90.91,191.3,35.976,32.468 +2020-12-25 18:00:00,91.63,192.454,41.513000000000005,32.468 +2020-12-25 18:15:00,90.25,193.15400000000002,41.513000000000005,32.468 +2020-12-25 18:30:00,91.06,191.364,41.513000000000005,32.468 +2020-12-25 18:45:00,91.59,189.735,41.513000000000005,32.468 +2020-12-25 19:00:00,90.53,192.299,45.607,32.468 +2020-12-25 19:15:00,89.44,190.046,45.607,32.468 +2020-12-25 19:30:00,88.41,188.205,45.607,32.468 +2020-12-25 19:45:00,87.49,185.03900000000002,45.607,32.468 +2020-12-25 20:00:00,85.33,183.676,43.372,32.468 +2020-12-25 20:15:00,84.64,180.94400000000002,43.372,32.468 +2020-12-25 20:30:00,83.34,177.888,43.372,32.468 +2020-12-25 20:45:00,82.68,174.28599999999997,43.372,32.468 +2020-12-25 21:00:00,80.07,172.31599999999997,39.458,32.468 +2020-12-25 21:15:00,79.28,170.345,39.458,32.468 +2020-12-25 21:30:00,78.47,169.702,39.458,32.468 +2020-12-25 21:45:00,77.69,168.477,39.458,32.468 +2020-12-25 22:00:00,74.04,163.124,40.15,32.468 +2020-12-25 22:15:00,75.14,159.202,40.15,32.468 +2020-12-25 22:30:00,71.33,154.743,40.15,32.468 +2020-12-25 22:45:00,71.18,151.332,40.15,32.468 +2020-12-25 23:00:00,67.24,144.92,33.876999999999995,32.468 +2020-12-25 23:15:00,66.92,141.184,33.876999999999995,32.468 +2020-12-25 23:30:00,63.35,139.3,33.876999999999995,32.468 +2020-12-25 23:45:00,62.32,136.632,33.876999999999995,32.468 +2020-12-26 00:00:00,57.9,114.9,32.311,32.468 +2020-12-26 00:15:00,58.19,110.01,32.311,32.468 +2020-12-26 00:30:00,57.69,111.57700000000001,32.311,32.468 +2020-12-26 00:45:00,58.18,114.064,32.311,32.468 +2020-12-26 01:00:00,54.2,116.675,25.569000000000003,32.468 +2020-12-26 01:15:00,53.83,118.415,25.569000000000003,32.468 +2020-12-26 01:30:00,54.04,118.73299999999999,25.569000000000003,32.468 +2020-12-26 01:45:00,53.35,118.766,25.569000000000003,32.468 +2020-12-26 02:00:00,50.27,120.48200000000001,21.038,32.468 +2020-12-26 02:15:00,50.84,120.59299999999999,21.038,32.468 +2020-12-26 02:30:00,50.18,120.984,21.038,32.468 +2020-12-26 02:45:00,49.92,123.348,21.038,32.468 +2020-12-26 03:00:00,50.44,125.69200000000001,19.865,32.468 +2020-12-26 03:15:00,51.13,126.073,19.865,32.468 +2020-12-26 03:30:00,51.68,127.77799999999999,19.865,32.468 +2020-12-26 03:45:00,52.19,129.064,19.865,32.468 +2020-12-26 04:00:00,52.65,137.464,19.076,32.468 +2020-12-26 04:15:00,51.74,145.872,19.076,32.468 +2020-12-26 04:30:00,52.08,146.203,19.076,32.468 +2020-12-26 04:45:00,52.91,147.342,19.076,32.468 +2020-12-26 05:00:00,52.87,161.06799999999998,20.174,32.468 +2020-12-26 05:15:00,54.46,170.08900000000003,20.174,32.468 +2020-12-26 05:30:00,54.44,167.12099999999998,20.174,32.468 +2020-12-26 05:45:00,56.77,164.81099999999998,20.174,32.468 +2020-12-26 06:00:00,56.75,179.74099999999999,19.854,32.468 +2020-12-26 06:15:00,60.38,198.097,19.854,32.468 +2020-12-26 06:30:00,58.83,193.49,19.854,32.468 +2020-12-26 06:45:00,59.86,188.3,19.854,32.468 +2020-12-26 07:00:00,62.28,186.122,23.096999999999998,32.468 +2020-12-26 07:15:00,63.68,189.696,23.096999999999998,32.468 +2020-12-26 07:30:00,64.23,193.442,23.096999999999998,32.468 +2020-12-26 07:45:00,69.19,196.74200000000002,23.096999999999998,32.468 +2020-12-26 08:00:00,69.13,200.31099999999998,28.963,32.468 +2020-12-26 08:15:00,74.4,203.125,28.963,32.468 +2020-12-26 08:30:00,73.12,205.46400000000003,28.963,32.468 +2020-12-26 08:45:00,75.14,205.50900000000001,28.963,32.468 +2020-12-26 09:00:00,78.03,200.41099999999997,28.194000000000003,32.468 +2020-12-26 09:15:00,80.29,198.832,28.194000000000003,32.468 +2020-12-26 09:30:00,83.3,196.702,28.194000000000003,32.468 +2020-12-26 09:45:00,81.36,193.852,28.194000000000003,32.468 +2020-12-26 10:00:00,86.11,191.188,25.936999999999998,32.468 +2020-12-26 10:15:00,85.18,187.99400000000003,25.936999999999998,32.468 +2020-12-26 10:30:00,89.16,186.321,25.936999999999998,32.468 +2020-12-26 10:45:00,88.48,184.033,25.936999999999998,32.468 +2020-12-26 11:00:00,92.1,184.018,27.256,32.468 +2020-12-26 11:15:00,93.15,181.483,27.256,32.468 +2020-12-26 11:30:00,95.06,180.26,27.256,32.468 +2020-12-26 11:45:00,97.74,178.452,27.256,32.468 +2020-12-26 12:00:00,92.69,172.60299999999998,24.345,32.468 +2020-12-26 12:15:00,92.99,172.22400000000002,24.345,32.468 +2020-12-26 12:30:00,85.85,171.58,24.345,32.468 +2020-12-26 12:45:00,84.15,171.30599999999998,24.345,32.468 +2020-12-26 13:00:00,84.26,169.81599999999997,15.363,32.468 +2020-12-26 13:15:00,80.45,170.998,15.363,32.468 +2020-12-26 13:30:00,78.78,170.00900000000001,15.363,32.468 +2020-12-26 13:45:00,79.31,169.584,15.363,32.468 +2020-12-26 14:00:00,76.77,168.87099999999998,13.492,32.468 +2020-12-26 14:15:00,76.41,169.743,13.492,32.468 +2020-12-26 14:30:00,76.48,170.31900000000002,13.492,32.468 +2020-12-26 14:45:00,77.52,170.423,13.492,32.468 +2020-12-26 15:00:00,78.64,170.22299999999998,17.868,32.468 +2020-12-26 15:15:00,80.39,171.66,17.868,32.468 +2020-12-26 15:30:00,77.92,174.332,17.868,32.468 +2020-12-26 15:45:00,79.11,176.623,17.868,32.468 +2020-12-26 16:00:00,86.11,178.118,23.47,32.468 +2020-12-26 16:15:00,83.6,179.93900000000002,23.47,32.468 +2020-12-26 16:30:00,87.85,182.736,23.47,32.468 +2020-12-26 16:45:00,86.53,185.065,23.47,32.468 +2020-12-26 17:00:00,89.38,187.34400000000002,33.777,32.468 +2020-12-26 17:15:00,90.4,189.174,33.777,32.468 +2020-12-26 17:30:00,94.34,190.016,33.777,32.468 +2020-12-26 17:45:00,92.08,191.488,33.777,32.468 +2020-12-26 18:00:00,93.83,192.66099999999997,38.975,32.468 +2020-12-26 18:15:00,92.14,193.35299999999998,38.975,32.468 +2020-12-26 18:30:00,92.33,191.56799999999998,38.975,32.468 +2020-12-26 18:45:00,93.19,189.954,38.975,32.468 +2020-12-26 19:00:00,91.96,192.495,42.818999999999996,32.468 +2020-12-26 19:15:00,91.71,190.238,42.818999999999996,32.468 +2020-12-26 19:30:00,90.43,188.393,42.818999999999996,32.468 +2020-12-26 19:45:00,90.65,185.217,42.818999999999996,32.468 +2020-12-26 20:00:00,86.8,183.845,43.372,32.468 +2020-12-26 20:15:00,86.59,181.109,43.372,32.468 +2020-12-26 20:30:00,84.5,178.03799999999998,43.372,32.468 +2020-12-26 20:45:00,83.84,174.453,43.372,32.468 +2020-12-26 21:00:00,79.9,172.46599999999998,39.458,32.468 +2020-12-26 21:15:00,79.31,170.479,39.458,32.468 +2020-12-26 21:30:00,79.36,169.83700000000002,39.458,32.468 +2020-12-26 21:45:00,81.72,168.62400000000002,39.458,32.468 +2020-12-26 22:00:00,74.52,163.27200000000002,40.15,32.468 +2020-12-26 22:15:00,74.0,159.359,40.15,32.468 +2020-12-26 22:30:00,72.07,154.929,40.15,32.468 +2020-12-26 22:45:00,70.15,151.52700000000002,40.15,32.468 +2020-12-26 23:00:00,66.32,145.092,33.876999999999995,32.468 +2020-12-26 23:15:00,66.62,141.359,33.876999999999995,32.468 +2020-12-26 23:30:00,63.94,139.489,33.876999999999995,32.468 +2020-12-26 23:45:00,64.95,136.814,33.876999999999995,32.468 +2020-12-27 00:00:00,56.66,115.09299999999999,35.232,32.468 +2020-12-27 00:15:00,57.29,110.179,35.232,32.468 +2020-12-27 00:30:00,56.01,111.735,35.232,32.468 +2020-12-27 00:45:00,54.37,114.205,35.232,32.468 +2020-12-27 01:00:00,52.25,116.833,31.403000000000002,32.468 +2020-12-27 01:15:00,53.32,118.569,31.403000000000002,32.468 +2020-12-27 01:30:00,52.46,118.88799999999999,31.403000000000002,32.468 +2020-12-27 01:45:00,51.48,118.911,31.403000000000002,32.468 +2020-12-27 02:00:00,49.93,120.641,30.69,32.468 +2020-12-27 02:15:00,51.57,120.75299999999999,30.69,32.468 +2020-12-27 02:30:00,50.82,121.15,30.69,32.468 +2020-12-27 02:45:00,50.77,123.514,30.69,32.468 +2020-12-27 03:00:00,49.34,125.84899999999999,29.516,32.468 +2020-12-27 03:15:00,50.62,126.25200000000001,29.516,32.468 +2020-12-27 03:30:00,51.39,127.959,29.516,32.468 +2020-12-27 03:45:00,51.75,129.248,29.516,32.468 +2020-12-27 04:00:00,50.79,137.624,29.148000000000003,32.468 +2020-12-27 04:15:00,51.03,146.02700000000002,29.148000000000003,32.468 +2020-12-27 04:30:00,51.16,146.35299999999998,29.148000000000003,32.468 +2020-12-27 04:45:00,52.11,147.49,29.148000000000003,32.468 +2020-12-27 05:00:00,52.51,161.184,28.706,32.468 +2020-12-27 05:15:00,53.68,170.172,28.706,32.468 +2020-12-27 05:30:00,52.91,167.213,28.706,32.468 +2020-12-27 05:45:00,54.55,164.921,28.706,32.468 +2020-12-27 06:00:00,55.49,179.87400000000002,28.771,32.468 +2020-12-27 06:15:00,55.76,198.235,28.771,32.468 +2020-12-27 06:30:00,56.15,193.65400000000002,28.771,32.468 +2020-12-27 06:45:00,57.92,188.49599999999998,28.771,32.468 +2020-12-27 07:00:00,60.11,186.328,31.39,32.468 +2020-12-27 07:15:00,61.04,189.898,31.39,32.468 +2020-12-27 07:30:00,65.58,193.636,31.39,32.468 +2020-12-27 07:45:00,65.21,196.923,31.39,32.468 +2020-12-27 08:00:00,66.03,200.493,34.972,32.468 +2020-12-27 08:15:00,66.67,203.295,34.972,32.468 +2020-12-27 08:30:00,68.78,205.62,34.972,32.468 +2020-12-27 08:45:00,72.45,205.642,34.972,32.468 +2020-12-27 09:00:00,73.81,200.525,36.709,32.468 +2020-12-27 09:15:00,75.71,198.952,36.709,32.468 +2020-12-27 09:30:00,75.67,196.835,36.709,32.468 +2020-12-27 09:45:00,76.59,193.976,36.709,32.468 +2020-12-27 10:00:00,77.56,191.308,35.812,32.468 +2020-12-27 10:15:00,79.49,188.108,35.812,32.468 +2020-12-27 10:30:00,81.12,186.422,35.812,32.468 +2020-12-27 10:45:00,83.85,184.13299999999998,35.812,32.468 +2020-12-27 11:00:00,84.02,184.101,36.746,32.468 +2020-12-27 11:15:00,86.47,181.56,36.746,32.468 +2020-12-27 11:30:00,87.71,180.338,36.746,32.468 +2020-12-27 11:45:00,88.37,178.52900000000002,36.746,32.468 +2020-12-27 12:00:00,87.6,172.685,35.048,32.468 +2020-12-27 12:15:00,85.66,172.321,35.048,32.468 +2020-12-27 12:30:00,83.94,171.68,35.048,32.468 +2020-12-27 12:45:00,84.23,171.40900000000002,35.048,32.468 +2020-12-27 13:00:00,81.23,169.90400000000002,29.987,32.468 +2020-12-27 13:15:00,79.67,171.08,29.987,32.468 +2020-12-27 13:30:00,78.24,170.083,29.987,32.468 +2020-12-27 13:45:00,77.15,169.65,29.987,32.468 +2020-12-27 14:00:00,73.27,168.938,27.21,32.468 +2020-12-27 14:15:00,75.35,169.808,27.21,32.468 +2020-12-27 14:30:00,74.83,170.398,27.21,32.468 +2020-12-27 14:45:00,74.81,170.515,27.21,32.468 +2020-12-27 15:00:00,75.04,170.331,27.726999999999997,32.468 +2020-12-27 15:15:00,76.57,171.757,27.726999999999997,32.468 +2020-12-27 15:30:00,77.1,174.43400000000003,27.726999999999997,32.468 +2020-12-27 15:45:00,79.95,176.72,27.726999999999997,32.468 +2020-12-27 16:00:00,85.35,178.213,32.23,32.468 +2020-12-27 16:15:00,85.01,180.049,32.23,32.468 +2020-12-27 16:30:00,85.83,182.852,32.23,32.468 +2020-12-27 16:45:00,87.36,185.197,32.23,32.468 +2020-12-27 17:00:00,89.42,187.457,42.016999999999996,32.468 +2020-12-27 17:15:00,89.72,189.315,42.016999999999996,32.468 +2020-12-27 17:30:00,91.66,190.179,42.016999999999996,32.468 +2020-12-27 17:45:00,92.42,191.665,42.016999999999996,32.468 +2020-12-27 18:00:00,93.13,192.858,49.338,32.468 +2020-12-27 18:15:00,92.27,193.541,49.338,32.468 +2020-12-27 18:30:00,93.04,191.763,49.338,32.468 +2020-12-27 18:45:00,92.9,190.16299999999998,49.338,32.468 +2020-12-27 19:00:00,91.41,192.68,52.369,32.468 +2020-12-27 19:15:00,90.74,190.42,52.369,32.468 +2020-12-27 19:30:00,89.58,188.57299999999998,52.369,32.468 +2020-12-27 19:45:00,88.24,185.388,52.369,32.468 +2020-12-27 20:00:00,84.87,184.005,50.405,32.468 +2020-12-27 20:15:00,84.15,181.267,50.405,32.468 +2020-12-27 20:30:00,82.18,178.18099999999998,50.405,32.468 +2020-12-27 20:45:00,80.95,174.613,50.405,32.468 +2020-12-27 21:00:00,77.99,172.61,46.235,32.468 +2020-12-27 21:15:00,79.18,170.606,46.235,32.468 +2020-12-27 21:30:00,78.7,169.965,46.235,32.468 +2020-12-27 21:45:00,80.23,168.764,46.235,32.468 +2020-12-27 22:00:00,76.73,163.412,46.861000000000004,32.468 +2020-12-27 22:15:00,73.06,159.51,46.861000000000004,32.468 +2020-12-27 22:30:00,70.7,155.107,46.861000000000004,32.468 +2020-12-27 22:45:00,70.0,151.71200000000002,46.861000000000004,32.468 +2020-12-27 23:00:00,65.04,145.257,41.302,32.468 +2020-12-27 23:15:00,65.95,141.525,41.302,32.468 +2020-12-27 23:30:00,64.23,139.66899999999998,41.302,32.468 +2020-12-27 23:45:00,63.33,136.99,41.302,32.468 +2020-12-28 00:00:00,57.03,119.344,37.164,32.468 +2020-12-28 00:15:00,57.21,117.274,37.164,32.468 +2020-12-28 00:30:00,56.31,118.925,37.164,32.468 +2020-12-28 00:45:00,56.35,120.848,37.164,32.468 +2020-12-28 01:00:00,52.26,123.51899999999999,34.994,32.468 +2020-12-28 01:15:00,53.0,124.74799999999999,34.994,32.468 +2020-12-28 01:30:00,52.53,125.137,34.994,32.468 +2020-12-28 01:45:00,52.32,125.25399999999999,34.994,32.468 +2020-12-28 02:00:00,51.03,126.993,34.571,32.468 +2020-12-28 02:15:00,52.03,128.454,34.571,32.468 +2020-12-28 02:30:00,50.99,129.18200000000002,34.571,32.468 +2020-12-28 02:45:00,50.55,130.94799999999998,34.571,32.468 +2020-12-28 03:00:00,49.6,134.501,33.934,32.468 +2020-12-28 03:15:00,51.33,136.524,33.934,32.468 +2020-12-28 03:30:00,50.91,138.001,33.934,32.468 +2020-12-28 03:45:00,51.45,138.749,33.934,32.468 +2020-12-28 04:00:00,51.82,151.371,34.107,32.468 +2020-12-28 04:15:00,52.15,163.84,34.107,32.468 +2020-12-28 04:30:00,53.03,166.226,34.107,32.468 +2020-12-28 04:45:00,54.7,167.53099999999998,34.107,32.468 +2020-12-28 05:00:00,57.21,196.475,39.575,32.468 +2020-12-28 05:15:00,58.21,225.044,39.575,32.468 +2020-12-28 05:30:00,58.82,222.28799999999998,39.575,32.468 +2020-12-28 05:45:00,60.69,214.507,39.575,32.468 +2020-12-28 06:00:00,63.74,211.99599999999998,56.156000000000006,32.468 +2020-12-28 06:15:00,64.22,215.868,56.156000000000006,32.468 +2020-12-28 06:30:00,66.48,219.101,56.156000000000006,32.468 +2020-12-28 06:45:00,67.97,222.567,56.156000000000006,32.468 +2020-12-28 07:00:00,71.66,222.68099999999998,67.926,32.468 +2020-12-28 07:15:00,73.71,227.53,67.926,32.468 +2020-12-28 07:30:00,73.74,230.484,67.926,32.468 +2020-12-28 07:45:00,76.15,231.332,67.926,32.468 +2020-12-28 08:00:00,78.92,230.185,72.58,32.468 +2020-12-28 08:15:00,77.89,230.88400000000001,72.58,32.468 +2020-12-28 08:30:00,79.51,229.27,72.58,32.468 +2020-12-28 08:45:00,81.98,226.108,72.58,32.468 +2020-12-28 09:00:00,84.91,219.989,66.984,32.468 +2020-12-28 09:15:00,86.68,215.032,66.984,32.468 +2020-12-28 09:30:00,86.35,211.93599999999998,66.984,32.468 +2020-12-28 09:45:00,87.57,209.25400000000002,66.984,32.468 +2020-12-28 10:00:00,88.75,205.554,63.158,32.468 +2020-12-28 10:15:00,89.35,202.032,63.158,32.468 +2020-12-28 10:30:00,90.53,199.482,63.158,32.468 +2020-12-28 10:45:00,92.46,197.829,63.158,32.468 +2020-12-28 11:00:00,92.51,195.31900000000002,61.141000000000005,32.468 +2020-12-28 11:15:00,93.89,194.49599999999998,61.141000000000005,32.468 +2020-12-28 11:30:00,95.76,194.61599999999999,61.141000000000005,32.468 +2020-12-28 11:45:00,98.55,192.44299999999998,61.141000000000005,32.468 +2020-12-28 12:00:00,96.46,188.148,57.961000000000006,32.468 +2020-12-28 12:15:00,96.89,187.805,57.961000000000006,32.468 +2020-12-28 12:30:00,94.61,187.355,57.961000000000006,32.468 +2020-12-28 12:45:00,93.99,188.53,57.961000000000006,32.468 +2020-12-28 13:00:00,91.48,187.59599999999998,56.843,32.468 +2020-12-28 13:15:00,89.62,187.41299999999998,56.843,32.468 +2020-12-28 13:30:00,87.6,185.91299999999998,56.843,32.468 +2020-12-28 13:45:00,89.12,185.547,56.843,32.468 +2020-12-28 14:00:00,86.23,184.21599999999998,55.992,32.468 +2020-12-28 14:15:00,84.24,184.503,55.992,32.468 +2020-12-28 14:30:00,84.27,184.58700000000002,55.992,32.468 +2020-12-28 14:45:00,84.15,184.78400000000002,55.992,32.468 +2020-12-28 15:00:00,83.99,186.304,57.523,32.468 +2020-12-28 15:15:00,84.98,186.33,57.523,32.468 +2020-12-28 15:30:00,82.61,188.26,57.523,32.468 +2020-12-28 15:45:00,84.31,190.09599999999998,57.523,32.468 +2020-12-28 16:00:00,90.69,191.83599999999998,59.471000000000004,32.468 +2020-12-28 16:15:00,89.04,192.96099999999998,59.471000000000004,32.468 +2020-12-28 16:30:00,91.46,194.821,59.471000000000004,32.468 +2020-12-28 16:45:00,92.1,196.054,59.471000000000004,32.468 +2020-12-28 17:00:00,95.79,198.107,65.066,32.468 +2020-12-28 17:15:00,96.36,199.09599999999998,65.066,32.468 +2020-12-28 17:30:00,94.64,199.445,65.066,32.468 +2020-12-28 17:45:00,94.08,199.503,65.066,32.468 +2020-12-28 18:00:00,92.03,201.101,69.581,32.468 +2020-12-28 18:15:00,92.63,199.59799999999998,69.581,32.468 +2020-12-28 18:30:00,92.68,198.426,69.581,32.468 +2020-12-28 18:45:00,95.26,197.632,69.581,32.468 +2020-12-28 19:00:00,91.26,198.58,73.771,32.468 +2020-12-28 19:15:00,88.66,195.235,73.771,32.468 +2020-12-28 19:30:00,89.79,193.85,73.771,32.468 +2020-12-28 19:45:00,85.86,189.838,73.771,32.468 +2020-12-28 20:00:00,81.83,186.13400000000001,65.035,32.468 +2020-12-28 20:15:00,80.18,181.06099999999998,65.035,32.468 +2020-12-28 20:30:00,78.48,176.19099999999997,65.035,32.468 +2020-12-28 20:45:00,77.08,174.206,65.035,32.468 +2020-12-28 21:00:00,73.88,172.676,58.7,32.468 +2020-12-28 21:15:00,73.68,169.542,58.7,32.468 +2020-12-28 21:30:00,72.87,168.12400000000002,58.7,32.468 +2020-12-28 21:45:00,72.61,166.44099999999997,58.7,32.468 +2020-12-28 22:00:00,69.98,158.253,53.888000000000005,32.468 +2020-12-28 22:15:00,69.76,153.156,53.888000000000005,32.468 +2020-12-28 22:30:00,68.69,139.464,53.888000000000005,32.468 +2020-12-28 22:45:00,68.64,131.43200000000002,53.888000000000005,32.468 +2020-12-28 23:00:00,64.36,125.7,45.501999999999995,32.468 +2020-12-28 23:15:00,66.25,124.46799999999999,45.501999999999995,32.468 +2020-12-28 23:30:00,64.12,125.277,45.501999999999995,32.468 +2020-12-28 23:45:00,64.06,125.116,45.501999999999995,32.468 +2020-12-29 00:00:00,57.18,118.78299999999999,43.537,32.468 +2020-12-29 00:15:00,56.17,118.086,43.537,32.468 +2020-12-29 00:30:00,56.18,118.84700000000001,43.537,32.468 +2020-12-29 00:45:00,56.31,119.844,43.537,32.468 +2020-12-29 01:00:00,53.79,122.324,41.854,32.468 +2020-12-29 01:15:00,53.88,123.124,41.854,32.468 +2020-12-29 01:30:00,50.49,123.676,41.854,32.468 +2020-12-29 01:45:00,53.7,124.057,41.854,32.468 +2020-12-29 02:00:00,51.98,125.79,40.321,32.468 +2020-12-29 02:15:00,53.31,127.184,40.321,32.468 +2020-12-29 02:30:00,53.66,127.334,40.321,32.468 +2020-12-29 02:45:00,53.8,129.121,40.321,32.468 +2020-12-29 03:00:00,53.3,131.494,39.632,32.468 +2020-12-29 03:15:00,58.73,132.754,39.632,32.468 +2020-12-29 03:30:00,61.45,134.694,39.632,32.468 +2020-12-29 03:45:00,56.91,135.592,39.632,32.468 +2020-12-29 04:00:00,58.6,147.961,40.183,32.468 +2020-12-29 04:15:00,58.92,160.092,40.183,32.468 +2020-12-29 04:30:00,58.73,162.172,40.183,32.468 +2020-12-29 04:45:00,60.76,164.65200000000002,40.183,32.468 +2020-12-29 05:00:00,65.7,198.412,43.945,32.468 +2020-12-29 05:15:00,68.23,226.771,43.945,32.468 +2020-12-29 05:30:00,72.46,222.56099999999998,43.945,32.468 +2020-12-29 05:45:00,78.0,214.78,43.945,32.468 +2020-12-29 06:00:00,86.13,211.19,56.048,32.468 +2020-12-29 06:15:00,90.88,216.63400000000001,56.048,32.468 +2020-12-29 06:30:00,98.39,219.25900000000001,56.048,32.468 +2020-12-29 06:45:00,101.09,222.36700000000002,56.048,32.468 +2020-12-29 07:00:00,109.02,222.32,65.74,32.468 +2020-12-29 07:15:00,110.78,227.003,65.74,32.468 +2020-12-29 07:30:00,111.89,229.425,65.74,32.468 +2020-12-29 07:45:00,116.47,230.43099999999998,65.74,32.468 +2020-12-29 08:00:00,119.28,229.387,72.757,32.468 +2020-12-29 08:15:00,119.18,229.077,72.757,32.468 +2020-12-29 08:30:00,121.46,227.28400000000002,72.757,32.468 +2020-12-29 08:45:00,123.85,223.81599999999997,72.757,32.468 +2020-12-29 09:00:00,126.31,216.889,67.692,32.468 +2020-12-29 09:15:00,127.18,213.46900000000002,67.692,32.468 +2020-12-29 09:30:00,129.54,211.058,67.692,32.468 +2020-12-29 09:45:00,127.09,208.2,67.692,32.468 +2020-12-29 10:00:00,130.14,203.892,63.506,32.468 +2020-12-29 10:15:00,131.8,199.34099999999998,63.506,32.468 +2020-12-29 10:30:00,131.24,196.954,63.506,32.468 +2020-12-29 10:45:00,133.38,195.604,63.506,32.468 +2020-12-29 11:00:00,132.11,194.502,60.758,32.468 +2020-12-29 11:15:00,132.81,193.368,60.758,32.468 +2020-12-29 11:30:00,132.74,192.33700000000002,60.758,32.468 +2020-12-29 11:45:00,132.51,190.82,60.758,32.468 +2020-12-29 12:00:00,133.01,185.213,57.519,32.468 +2020-12-29 12:15:00,132.94,184.5,57.519,32.468 +2020-12-29 12:30:00,132.61,184.77200000000002,57.519,32.468 +2020-12-29 12:45:00,131.32,185.69400000000002,57.519,32.468 +2020-12-29 13:00:00,129.11,184.345,56.46,32.468 +2020-12-29 13:15:00,129.11,183.888,56.46,32.468 +2020-12-29 13:30:00,128.11,183.49900000000002,56.46,32.468 +2020-12-29 13:45:00,128.44,183.268,56.46,32.468 +2020-12-29 14:00:00,129.73,182.227,56.207,32.468 +2020-12-29 14:15:00,128.01,182.65400000000002,56.207,32.468 +2020-12-29 14:30:00,127.57,183.363,56.207,32.468 +2020-12-29 14:45:00,128.05,183.468,56.207,32.468 +2020-12-29 15:00:00,125.11,184.584,57.391999999999996,32.468 +2020-12-29 15:15:00,124.05,184.94799999999998,57.391999999999996,32.468 +2020-12-29 15:30:00,122.28,187.088,57.391999999999996,32.468 +2020-12-29 15:45:00,122.29,188.547,57.391999999999996,32.468 +2020-12-29 16:00:00,126.09,190.58,59.955,32.468 +2020-12-29 16:15:00,127.33,192.178,59.955,32.468 +2020-12-29 16:30:00,131.25,194.65200000000002,59.955,32.468 +2020-12-29 16:45:00,132.28,196.199,59.955,32.468 +2020-12-29 17:00:00,135.14,198.761,67.063,32.468 +2020-12-29 17:15:00,134.64,199.817,67.063,32.468 +2020-12-29 17:30:00,135.54,200.817,67.063,32.468 +2020-12-29 17:45:00,135.22,200.755,67.063,32.468 +2020-12-29 18:00:00,135.98,202.24900000000002,71.477,32.468 +2020-12-29 18:15:00,133.12,200.295,71.477,32.468 +2020-12-29 18:30:00,132.74,198.825,71.477,32.468 +2020-12-29 18:45:00,132.76,198.80900000000003,71.477,32.468 +2020-12-29 19:00:00,129.1,199.782,74.32,32.468 +2020-12-29 19:15:00,131.66,196.169,74.32,32.468 +2020-12-29 19:30:00,131.57,194.13,74.32,32.468 +2020-12-29 19:45:00,134.28,190.15400000000002,74.32,32.468 +2020-12-29 20:00:00,124.16,186.60299999999998,66.157,32.468 +2020-12-29 20:15:00,118.77,180.87400000000002,66.157,32.468 +2020-12-29 20:30:00,114.09,176.968,66.157,32.468 +2020-12-29 20:45:00,113.21,174.458,66.157,32.468 +2020-12-29 21:00:00,104.2,172.25900000000001,59.806000000000004,32.468 +2020-12-29 21:15:00,110.79,169.947,59.806000000000004,32.468 +2020-12-29 21:30:00,106.78,167.829,59.806000000000004,32.468 +2020-12-29 21:45:00,106.31,166.38299999999998,59.806000000000004,32.468 +2020-12-29 22:00:00,95.44,159.85399999999998,54.785,32.468 +2020-12-29 22:15:00,92.96,154.50799999999998,54.785,32.468 +2020-12-29 22:30:00,85.11,140.935,54.785,32.468 +2020-12-29 22:45:00,86.87,133.178,54.785,32.468 +2020-12-29 23:00:00,82.77,127.431,47.176,32.468 +2020-12-29 23:15:00,84.86,125.367,47.176,32.468 +2020-12-29 23:30:00,77.52,125.84200000000001,47.176,32.468 +2020-12-29 23:45:00,78.36,125.257,47.176,32.468 +2020-12-30 00:00:00,81.02,118.954,43.42,32.468 +2020-12-30 00:15:00,81.82,118.23299999999999,43.42,32.468 +2020-12-30 00:30:00,81.73,118.985,43.42,32.468 +2020-12-30 00:45:00,76.13,119.964,43.42,32.468 +2020-12-30 01:00:00,70.28,122.458,40.869,32.468 +2020-12-30 01:15:00,74.3,123.25299999999999,40.869,32.468 +2020-12-30 01:30:00,77.22,123.807,40.869,32.468 +2020-12-30 01:45:00,76.85,124.179,40.869,32.468 +2020-12-30 02:00:00,73.1,125.92399999999999,39.541,32.468 +2020-12-30 02:15:00,70.43,127.319,39.541,32.468 +2020-12-30 02:30:00,71.98,127.475,39.541,32.468 +2020-12-30 02:45:00,76.52,129.263,39.541,32.468 +2020-12-30 03:00:00,75.41,131.627,39.052,32.468 +2020-12-30 03:15:00,74.83,132.908,39.052,32.468 +2020-12-30 03:30:00,72.89,134.84799999999998,39.052,32.468 +2020-12-30 03:45:00,79.01,135.749,39.052,32.468 +2020-12-30 04:00:00,76.82,148.096,40.36,32.468 +2020-12-30 04:15:00,72.95,160.222,40.36,32.468 +2020-12-30 04:30:00,72.14,162.297,40.36,32.468 +2020-12-30 04:45:00,80.94,164.775,40.36,32.468 +2020-12-30 05:00:00,88.48,198.503,43.133,32.468 +2020-12-30 05:15:00,91.18,226.833,43.133,32.468 +2020-12-30 05:30:00,90.88,222.62900000000002,43.133,32.468 +2020-12-30 05:45:00,93.94,214.865,43.133,32.468 +2020-12-30 06:00:00,101.26,211.298,54.953,32.468 +2020-12-30 06:15:00,106.96,216.748,54.953,32.468 +2020-12-30 06:30:00,112.28,219.395,54.953,32.468 +2020-12-30 06:45:00,115.14,222.53400000000002,54.953,32.468 +2020-12-30 07:00:00,123.05,222.49900000000002,66.566,32.468 +2020-12-30 07:15:00,123.6,227.174,66.566,32.468 +2020-12-30 07:30:00,126.82,229.58599999999998,66.566,32.468 +2020-12-30 07:45:00,128.84,230.578,66.566,32.468 +2020-12-30 08:00:00,131.65,229.533,72.902,32.468 +2020-12-30 08:15:00,130.78,229.21,72.902,32.468 +2020-12-30 08:30:00,132.01,227.4,72.902,32.468 +2020-12-30 08:45:00,132.85,223.91099999999997,72.902,32.468 +2020-12-30 09:00:00,134.02,216.967,68.465,32.468 +2020-12-30 09:15:00,135.31,213.55200000000002,68.465,32.468 +2020-12-30 09:30:00,136.11,211.15400000000002,68.465,32.468 +2020-12-30 09:45:00,137.25,208.28900000000002,68.465,32.468 +2020-12-30 10:00:00,135.85,203.97799999999998,63.625,32.468 +2020-12-30 10:15:00,138.58,199.425,63.625,32.468 +2020-12-30 10:30:00,138.23,197.024,63.625,32.468 +2020-12-30 10:45:00,136.57,195.674,63.625,32.468 +2020-12-30 11:00:00,133.24,194.555,61.628,32.468 +2020-12-30 11:15:00,126.44,193.417,61.628,32.468 +2020-12-30 11:30:00,130.71,192.387,61.628,32.468 +2020-12-30 11:45:00,129.54,190.87099999999998,61.628,32.468 +2020-12-30 12:00:00,125.24,185.268,58.708999999999996,32.468 +2020-12-30 12:15:00,124.69,184.571,58.708999999999996,32.468 +2020-12-30 12:30:00,123.95,184.84400000000002,58.708999999999996,32.468 +2020-12-30 12:45:00,129.68,185.769,58.708999999999996,32.468 +2020-12-30 13:00:00,133.73,184.407,57.373000000000005,32.468 +2020-12-30 13:15:00,136.53,183.94400000000002,57.373000000000005,32.468 +2020-12-30 13:30:00,133.6,183.545,57.373000000000005,32.468 +2020-12-30 13:45:00,133.51,183.30599999999998,57.373000000000005,32.468 +2020-12-30 14:00:00,132.59,182.271,57.684,32.468 +2020-12-30 14:15:00,131.17,182.69400000000002,57.684,32.468 +2020-12-30 14:30:00,128.89,183.417,57.684,32.468 +2020-12-30 14:45:00,127.23,183.533,57.684,32.468 +2020-12-30 15:00:00,128.01,184.666,58.03,32.468 +2020-12-30 15:15:00,127.89,185.017,58.03,32.468 +2020-12-30 15:30:00,127.38,187.16,58.03,32.468 +2020-12-30 15:45:00,128.01,188.612,58.03,32.468 +2020-12-30 16:00:00,131.71,190.645,59.97,32.468 +2020-12-30 16:15:00,132.63,192.255,59.97,32.468 +2020-12-30 16:30:00,136.55,194.735,59.97,32.468 +2020-12-30 16:45:00,136.97,196.297,59.97,32.468 +2020-12-30 17:00:00,140.13,198.84099999999998,65.661,32.468 +2020-12-30 17:15:00,138.72,199.924,65.661,32.468 +2020-12-30 17:30:00,136.98,200.94799999999998,65.661,32.468 +2020-12-30 17:45:00,138.66,200.9,65.661,32.468 +2020-12-30 18:00:00,138.81,202.41299999999998,70.96300000000001,32.468 +2020-12-30 18:15:00,137.74,200.455,70.96300000000001,32.468 +2020-12-30 18:30:00,135.58,198.99099999999999,70.96300000000001,32.468 +2020-12-30 18:45:00,136.58,198.989,70.96300000000001,32.468 +2020-12-30 19:00:00,133.41,199.937,74.133,32.468 +2020-12-30 19:15:00,132.16,196.324,74.133,32.468 +2020-12-30 19:30:00,129.15,194.283,74.133,32.468 +2020-12-30 19:45:00,128.43,190.3,74.133,32.468 +2020-12-30 20:00:00,120.97,186.738,65.613,32.468 +2020-12-30 20:15:00,116.68,181.007,65.613,32.468 +2020-12-30 20:30:00,112.92,177.08900000000003,65.613,32.468 +2020-12-30 20:45:00,113.77,174.59599999999998,65.613,32.468 +2020-12-30 21:00:00,108.79,172.378,58.583,32.468 +2020-12-30 21:15:00,113.74,170.051,58.583,32.468 +2020-12-30 21:30:00,111.85,167.933,58.583,32.468 +2020-12-30 21:45:00,107.84,166.5,58.583,32.468 +2020-12-30 22:00:00,97.67,159.97,54.411,32.468 +2020-12-30 22:15:00,98.11,154.636,54.411,32.468 +2020-12-30 22:30:00,96.26,141.086,54.411,32.468 +2020-12-30 22:45:00,94.93,133.338,54.411,32.468 +2020-12-30 23:00:00,90.4,127.57,47.878,32.468 +2020-12-30 23:15:00,82.73,125.509,47.878,32.468 +2020-12-30 23:30:00,77.9,125.99799999999999,47.878,32.468 +2020-12-30 23:45:00,78.08,125.40899999999999,47.878,32.468 +2020-12-31 00:00:00,72.56,119.118,44.513000000000005,32.468 +2020-12-31 00:15:00,75.51,118.375,44.513000000000005,32.468 +2020-12-31 00:30:00,76.2,119.11399999999999,44.513000000000005,32.468 +2020-12-31 00:45:00,79.94,120.07600000000001,44.513000000000005,32.468 +2020-12-31 01:00:00,76.22,122.584,43.169,32.468 +2020-12-31 01:15:00,75.52,123.374,43.169,32.468 +2020-12-31 01:30:00,69.71,123.929,43.169,32.468 +2020-12-31 01:45:00,72.15,124.29,43.169,32.468 +2020-12-31 02:00:00,68.46,126.051,41.763999999999996,32.468 +2020-12-31 02:15:00,68.93,127.444,41.763999999999996,32.468 +2020-12-31 02:30:00,69.13,127.60799999999999,41.763999999999996,32.468 +2020-12-31 02:45:00,71.05,129.394,41.763999999999996,32.468 +2020-12-31 03:00:00,77.24,131.754,41.155,32.468 +2020-12-31 03:15:00,78.88,133.054,41.155,32.468 +2020-12-31 03:30:00,79.6,134.993,41.155,32.468 +2020-12-31 03:45:00,73.47,135.89700000000002,41.155,32.468 +2020-12-31 04:00:00,71.66,148.222,41.96,32.468 +2020-12-31 04:15:00,72.6,160.344,41.96,32.468 +2020-12-31 04:30:00,74.43,162.416,41.96,32.468 +2020-12-31 04:45:00,76.31,164.89,41.96,32.468 +2020-12-31 05:00:00,80.9,198.58599999999998,45.206,32.468 +2020-12-31 05:15:00,83.91,226.886,45.206,32.468 +2020-12-31 05:30:00,88.55,222.68900000000002,45.206,32.468 +2020-12-31 05:45:00,92.98,214.942,45.206,32.468 +2020-12-31 06:00:00,101.92,211.398,55.398999999999994,32.468 +2020-12-31 06:15:00,106.02,216.854,55.398999999999994,32.468 +2020-12-31 06:30:00,110.67,219.52200000000002,55.398999999999994,32.468 +2020-12-31 06:45:00,115.36,222.69,55.398999999999994,32.468 +2020-12-31 07:00:00,121.21,222.667,64.627,32.468 +2020-12-31 07:15:00,125.44,227.334,64.627,32.468 +2020-12-31 07:30:00,127.04,229.737,64.627,32.468 +2020-12-31 07:45:00,129.3,230.713,64.627,32.468 +2020-12-31 08:00:00,131.83,229.668,70.895,32.468 +2020-12-31 08:15:00,129.92,229.331,70.895,32.468 +2020-12-31 08:30:00,130.03,227.505,70.895,32.468 +2020-12-31 08:45:00,129.65,223.993,70.895,32.468 +2020-12-31 09:00:00,130.4,217.032,66.382,32.468 +2020-12-31 09:15:00,132.18,213.62400000000002,66.382,32.468 +2020-12-31 09:30:00,133.68,211.239,66.382,32.468 +2020-12-31 09:45:00,133.99,208.36700000000002,66.382,32.468 +2020-12-31 10:00:00,133.44,204.053,62.739,32.468 +2020-12-31 10:15:00,135.64,199.49599999999998,62.739,32.468 +2020-12-31 10:30:00,134.92,197.084,62.739,32.468 +2020-12-31 10:45:00,136.65,195.734,62.739,32.468 +2020-12-31 11:00:00,136.01,194.59799999999998,60.843,32.468 +2020-12-31 11:15:00,135.6,193.455,60.843,32.468 +2020-12-31 11:30:00,135.49,192.428,60.843,32.468 +2020-12-31 11:45:00,136.56,190.91299999999998,60.843,32.468 +2020-12-31 12:00:00,136.24,185.315,58.466,32.468 +2020-12-31 12:15:00,135.89,184.63299999999998,58.466,32.468 +2020-12-31 12:30:00,133.92,184.908,58.466,32.468 +2020-12-31 12:45:00,134.1,185.834,58.466,32.468 +2020-12-31 13:00:00,130.94,184.46,56.883,32.468 +2020-12-31 13:15:00,129.98,183.99,56.883,32.468 +2020-12-31 13:30:00,129.31,183.582,56.883,32.468 +2020-12-31 13:45:00,127.59,183.33599999999998,56.883,32.468 +2020-12-31 14:00:00,126.5,182.30700000000002,56.503,32.468 +2020-12-31 14:15:00,129.81,182.727,56.503,32.468 +2020-12-31 14:30:00,128.98,183.46099999999998,56.503,32.468 +2020-12-31 14:45:00,127.66,183.59,56.503,32.468 +2020-12-31 15:00:00,131.69,184.74,57.803999999999995,32.468 +2020-12-31 15:15:00,134.08,185.078,57.803999999999995,32.468 +2020-12-31 15:30:00,130.49,187.222,57.803999999999995,32.468 +2020-12-31 15:45:00,132.71,188.668,57.803999999999995,32.468 +2020-12-31 16:00:00,134.12,190.7,59.379,32.468 +2020-12-31 16:15:00,134.79,192.322,59.379,32.468 +2020-12-31 16:30:00,137.68,194.80599999999998,59.379,32.468 +2020-12-31 16:45:00,138.3,196.382,59.379,32.468 +2020-12-31 17:00:00,141.23,198.90900000000002,64.71600000000001,32.468 +2020-12-31 17:15:00,140.55,200.021,64.71600000000001,32.468 +2020-12-31 17:30:00,141.63,201.067,64.71600000000001,32.468 +2020-12-31 17:45:00,141.55,201.035,64.71600000000001,32.468 +2020-12-31 18:00:00,140.02,202.56799999999998,68.803,32.468 +2020-12-31 18:15:00,138.28,200.606,68.803,32.468 +2020-12-31 18:30:00,137.19,199.14700000000002,68.803,32.468 +2020-12-31 18:45:00,137.6,199.16,68.803,32.468 +2020-12-31 19:00:00,134.26,200.084,72.934,32.468 +2020-12-31 19:15:00,133.22,196.468,72.934,32.468 +2020-12-31 19:30:00,134.64,194.42700000000002,72.934,32.468 +2020-12-31 19:45:00,136.88,190.43900000000002,72.934,32.468 +2020-12-31 20:00:00,130.24,186.864,65.175,32.468 +2020-12-31 20:15:00,119.33,181.132,65.175,32.468 +2020-12-31 20:30:00,118.31,177.201,65.175,32.468 +2020-12-31 20:45:00,112.7,174.72400000000002,65.175,32.468 +2020-12-31 21:00:00,107.44,172.49,58.55,32.468 +2020-12-31 21:15:00,112.86,170.146,58.55,32.468 +2020-12-31 21:30:00,111.93,168.03,58.55,32.468 +2020-12-31 21:45:00,108.29,166.609,58.55,32.468 +2020-12-31 22:00:00,98.52,160.079,55.041000000000004,32.468 +2020-12-31 22:15:00,94.85,154.757,55.041000000000004,32.468 +2020-12-31 22:30:00,98.1,141.22899999999998,55.041000000000004,32.468 +2020-12-31 22:45:00,96.62,133.488,55.041000000000004,32.468 +2020-12-31 23:00:00,92.22,127.7,48.258,32.468 +2020-12-31 23:15:00,84.12,125.64200000000001,48.258,32.468 +2020-12-31 23:30:00,79.31,126.145,48.258,32.468 +2020-12-31 23:45:00,83.54,125.553,48.258,32.468 diff --git a/examples/05_Two-stage-optimization/two_stage_optimization.py b/examples/05_Two-stage-optimization/two_stage_optimization.py new file mode 100644 index 000000000..5e19d54c1 --- /dev/null +++ b/examples/05_Two-stage-optimization/two_stage_optimization.py @@ -0,0 +1,148 @@ +""" +This script demonstrates how to use the different calcualtion types in the flixOPt framework +to model the same energy system. THe Results will be compared to each other. +""" + +import logging +import pathlib +import timeit + +import pandas as pd +import xarray as xr + +import flixopt as fx + +logger = logging.getLogger('flixopt') + +if __name__ == '__main__': + # Data Import + data_import = pd.read_csv(pathlib.Path('Zeitreihen2020.csv'), index_col=0).sort_index() + filtered_data = data_import[:500] + + filtered_data.index = pd.to_datetime(filtered_data.index) + timesteps = filtered_data.index + + # Access specific columns and convert to 1D-numpy array + electricity_demand = filtered_data['P_Netz/MW'].to_numpy() + heat_demand = filtered_data['Q_Netz/MW'].to_numpy() + electricity_price = filtered_data['Strompr.€/MWh'].to_numpy() + gas_price = filtered_data['Gaspr.€/MWh'].to_numpy() + + flow_system = fx.FlowSystem(timesteps) + flow_system.add_elements( + fx.Bus('Strom'), + fx.Bus('Fernwärme'), + fx.Bus('Gas'), + fx.Bus('Kohle'), + fx.Effect('costs', '€', 'Kosten', is_standard=True, is_objective=True), + fx.Effect('CO2', 'kg', 'CO2_e-Emissionen'), + fx.Effect('PE', 'kWh_PE', 'Primärenergie'), + fx.linear_converters.Boiler( + 'Kessel', + eta=0.85, + Q_th=fx.Flow(label='Q_th', bus='Fernwärme'), + Q_fu=fx.Flow( + label='Q_fu', + bus='Gas', + size=fx.InvestParameters(specific_effects={'costs': 1_000}, minimum_size=10, maximum_size=500), + relative_minimum=0.2, + previous_flow_rate=20, + on_off_parameters=fx.OnOffParameters(effects_per_switch_on=300), + ), + ), + fx.linear_converters.CHP( + 'BHKW2', + eta_th=0.58, + eta_el=0.22, + on_off_parameters=fx.OnOffParameters(effects_per_switch_on=1_000, consecutive_on_hours_min=10, consecutive_off_hours_min=10), + P_el=fx.Flow('P_el', bus='Strom'), + Q_th=fx.Flow('Q_th', bus='Fernwärme'), + Q_fu=fx.Flow('Q_fu', bus='Kohle', + size=fx.InvestParameters(specific_effects={'costs':3_000}, minimum_size=10, maximum_size=500), + relative_minimum=0.3, previous_flow_rate=100), + ), + fx.Storage( + 'Speicher', + capacity_in_flow_hours=684, + initial_charge_state=137, + minimal_final_charge_state=137, + maximal_final_charge_state=158, + eta_charge=1, + eta_discharge=1, + relative_loss_per_hour=0.001, + prevent_simultaneous_charge_and_discharge=True, + charging=fx.Flow('Q_th_load', size=137, bus='Fernwärme'), + discharging=fx.Flow('Q_th_unload', size=158, bus='Fernwärme'), + ), + fx.Sink( + 'Wärmelast', sink=fx.Flow('Q_th_Last', bus='Fernwärme', size=1, fixed_relative_profile=heat_demand) + ), + fx.Source( + 'Gastarif', + source=fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': gas_price, 'CO2': 0.3}), + ), + fx.Source( + 'Kohletarif', + source=fx.Flow('Q_Kohle', bus='Kohle', size=1000, effects_per_flow_hour={'costs': 4.6, 'CO2': 0.3}), + ), + fx.Source( + 'Einspeisung', + source=fx.Flow('P_el', bus='Strom', size=1000, effects_per_flow_hour={'costs': electricity_price + 0.5, 'CO2': 0.3}), + ), + fx.Sink( + 'Stromlast', + sink=fx.Flow('P_el_Last', bus='Strom', size=1, fixed_relative_profile=electricity_demand), + ), + fx.Source( + 'Stromtarif', + source=fx.Flow('P_el', bus='Strom', size=1000, effects_per_flow_hour={'costs': electricity_price, 'CO2': 0.3}), + ), + ) + + # Separate optimization of flow sizes and dispatch + start = timeit.default_timer() + calculation_sizing = fx.FullCalculation('Sizing', flow_system.resample('4h')) + calculation_sizing.do_modeling() + calculation_sizing.solve(fx.solvers.HighsSolver(0.1/100, 600)) + timer_sizing = timeit.default_timer() - start + flow_sizes = xr.Dataset({flow.size.name: flow.size for flow in calculation_sizing.results.flows.values()}) + + calculation_dispatch = fx.FullCalculation('Sizing', flow_system) + calculation_dispatch.do_modeling() + for name, da in flow_sizes.data_vars.items(): + if name in calculation_dispatch.model.variables: + con = calculation_dispatch.model.add_constraints( + calculation_dispatch.model[name] == da, + name=f'{name}_fixing', + ) + logger.info(f'Constraint {con.name} added:\n{con}') + + calculation_dispatch.solve(fx.solvers.HighsSolver(0.1 / 100, 600)) + timer_dispatch = timeit.default_timer() - start + + if (calculation_dispatch.results.sizes().round(5) == calculation_sizing.results.sizes().round(5)).all(): + logger.info('Sizes where correctly equalized') + else: + raise RuntimeError('Sizes where not correctly equalized') + + # Optimization of both flow sizes and dispatch together + start = timeit.default_timer() + calculation_combined = fx.FullCalculation('Sizing', flow_system) + calculation_combined.do_modeling() + calculation_combined.solve(fx.solvers.HighsSolver(0.1/100, 600)) + timer_combined = timeit.default_timer() - start + + # Comparison of results + comparison = xr.concat( + [calculation_combined.results.solution, calculation_dispatch.results.solution], dim='mode' + ).assign_coords(mode=['Combined', 'Two-stage']) + comparison['Duration [s]'] = xr.DataArray([timer_combined, timer_sizing + timer_dispatch], dims='mode') + + comparison_main = comparison[['Duration [s]', 'costs|total', 'costs(invest)|total', 'costs(operation)|total', 'BHKW2(Q_fu)|size', 'Kessel(Q_fu)|size']] + comparison_main = xr.concat([ + comparison_main, + ((comparison_main.sel(mode='Two-stage') - comparison_main.sel(mode='Combined')) + / comparison_main.sel(mode='Combined') * 100).assign_coords(mode='Diff [%]') + ], dim='mode') + + print(comparison_main.to_pandas().T.round(2)) From ef0acfc38c9f12868551e6ec14109b576aa95a23 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:24:30 +0200 Subject: [PATCH 178/183] Add example that leverages resampling adn fixing of Investments --- .../two_stage_optimization.py | 45 +++++++++---------- flixopt/calculation.py | 23 ++++++++++ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/examples/05_Two-stage-optimization/two_stage_optimization.py b/examples/05_Two-stage-optimization/two_stage_optimization.py index 5e19d54c1..7f0a412bf 100644 --- a/examples/05_Two-stage-optimization/two_stage_optimization.py +++ b/examples/05_Two-stage-optimization/two_stage_optimization.py @@ -1,6 +1,10 @@ """ -This script demonstrates how to use the different calcualtion types in the flixOPt framework -to model the same energy system. THe Results will be compared to each other. +This script demonstrates how to use downsampling of a FlowSystem to effectively reduce the size of a model. +This can be very useful when working with large models or during developement state, +as it can drastically reduce the computational time. +This leads to faster results and easier debugging. +A common use case is to do optimize the investments of a model with a downsampled version of the original model, and than fix the computed sizes when calculating th actual dispatch. +While the final optimum might differ from the fglobal optimum, the solving will be much faster. """ import logging @@ -38,17 +42,17 @@ fx.Effect('CO2', 'kg', 'CO2_e-Emissionen'), fx.Effect('PE', 'kWh_PE', 'Primärenergie'), fx.linear_converters.Boiler( - 'Kessel', - eta=0.85, - Q_th=fx.Flow(label='Q_th', bus='Fernwärme'), - Q_fu=fx.Flow( - label='Q_fu', - bus='Gas', - size=fx.InvestParameters(specific_effects={'costs': 1_000}, minimum_size=10, maximum_size=500), - relative_minimum=0.2, - previous_flow_rate=20, - on_off_parameters=fx.OnOffParameters(effects_per_switch_on=300), - ), + 'Kessel', + eta=0.85, + Q_th=fx.Flow(label='Q_th', bus='Fernwärme'), + Q_fu=fx.Flow( + label='Q_fu', + bus='Gas', + size=fx.InvestParameters(specific_effects={'costs': 1_000}, minimum_size=10, maximum_size=500), + relative_minimum=0.2, + previous_flow_rate=20, + on_off_parameters=fx.OnOffParameters(effects_per_switch_on=300), + ), ), fx.linear_converters.CHP( 'BHKW2', @@ -63,10 +67,8 @@ ), fx.Storage( 'Speicher', - capacity_in_flow_hours=684, - initial_charge_state=137, - minimal_final_charge_state=137, - maximal_final_charge_state=158, + capacity_in_flow_hours=fx.InvestParameters(minimum_size=10, maximum_size=1000, specific_effects={'costs': 60}), + initial_charge_state='lastValueOfSim', eta_charge=1, eta_discharge=1, relative_loss_per_hour=0.001, @@ -109,14 +111,7 @@ calculation_dispatch = fx.FullCalculation('Sizing', flow_system) calculation_dispatch.do_modeling() - for name, da in flow_sizes.data_vars.items(): - if name in calculation_dispatch.model.variables: - con = calculation_dispatch.model.add_constraints( - calculation_dispatch.model[name] == da, - name=f'{name}_fixing', - ) - logger.info(f'Constraint {con.name} added:\n{con}') - + calculation_dispatch.fix_sizes(calculation_sizing.results.solution) calculation_dispatch.solve(fx.solvers.HighsSolver(0.1 / 100, 600)) timer_dispatch = timeit.default_timer() - start diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 0fb735bef..88e686681 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -180,6 +180,29 @@ def do_modeling(self) -> SystemModel: self.durations['modeling'] = round(timeit.default_timer() - t_start, 2) return self.model + def fix_sizes(self, ds: xr.Dataset, decimal_rounding: Optional[int] = 5): + """Fix the sizes of the calculations to specified values. + + Args: + ds: The dataset that contains the variable names mapped to their sizes. If None, the dataset is loaded from the results. + decimal_rounding: The number of decimal places to round the sizes to. If no rounding is applied, numerical errors might lead to infeasibility. + """ + if decimal_rounding is not None: + ds = ds.round(decimal_rounding) + + for name, da in ds.data_vars.items(): + if '|size' not in name: + continue + if name not in self.model.variables: + logger.debug(f'Variable {name} not found in calculation model. Skipping.') + continue + + con = self.model.add_constraints( + self.model[name] == da, + name=f'{name}-fixed', + ) + logger.debug(f'Fixed "{name}":\n{con}') + def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = True): t_start = timeit.default_timer() From 706c1ec919e28eba6ca454897365334e4e67a3d7 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:36:37 +0200 Subject: [PATCH 179/183] Add flag to Calculation if its modeled --- flixopt/calculation.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index 88e686681..e7ea8d053 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -95,6 +95,8 @@ def __init__( f'Folder {self.folder} and its parent do not exist. Please create them first.' ) from e + self._modeled = False + @property def main_results(self) -> Dict[str, Union[Scalar, Dict]]: from flixopt.features import InvestmentModel @@ -164,6 +166,10 @@ def active_timesteps(self) -> pd.DatetimeIndex: ) return self._active_timesteps + @property + def modeled(self) -> bool: + return True if self.model is not None else False + class FullCalculation(Calculation): """ @@ -187,6 +193,8 @@ def fix_sizes(self, ds: xr.Dataset, decimal_rounding: Optional[int] = 5): ds: The dataset that contains the variable names mapped to their sizes. If None, the dataset is loaded from the results. decimal_rounding: The number of decimal places to round the sizes to. If no rounding is applied, numerical errors might lead to infeasibility. """ + if not self.modeled: + raise RuntimeError('Model was not created. Call do_modeling() first.') if decimal_rounding is not None: ds = ds.round(decimal_rounding) From a4cdb433f48cb50ecce5fe2ccab2f7610d961013 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:37:03 +0200 Subject: [PATCH 180/183] Make flag for connected_and_transformed FLowSystem public --- flixopt/flow_system.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 7001ca9e3..b0dc746bb 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -208,7 +208,7 @@ def to_dataset(self) -> xr.Dataset: Returns: xr.Dataset: Dataset containing all DataArrays with structure in attributes """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: logger.warning('FlowSystem is not connected_and_transformed. Connecting and transforming data now.') self.connect_and_transform() @@ -278,7 +278,7 @@ def to_netcdf(self, path: Union[str, pathlib.Path], compression: int = 0): path: The path to the netCDF file. compression: The compression level to use when saving the file. """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: logger.warning('FlowSystem is not connected. Calling connect_and_transform() now.') self.connect_and_transform() @@ -294,7 +294,7 @@ def get_structure(self, clean: bool = False, stats: bool = False) -> Dict: clean: If True, remove None and empty dicts and lists. stats: If True, replace DataArray references with statistics """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: logger.warning('FlowSystem is not connected. Calling connect_and_transform() now.') self.connect_and_transform() @@ -308,7 +308,7 @@ def to_json(self, path: Union[str, pathlib.Path]): Args: path: The path to the JSON file. """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: logger.warning('FlowSystem needs to be connected and transformed before saving to JSON. Calling connect_and_transform() now.') self.connect_and_transform() @@ -387,7 +387,7 @@ def connect_and_transform(self): logger.warning(f'Scenario weights are not normalized to 1. This is reccomended for a better scaled model. ' f'Sum of weights={self.scenario_weights.sum().item()}') - if not self._connected_and_transformed: + if not self.connected_and_transformed: self._connect_network() for element in list(self.components.values()) + list(self.effects.effects.values()) + list(self.buses.values()): element.transform_data(self) @@ -401,7 +401,7 @@ def add_elements(self, *elements: Element) -> None: *elements: childs of Element like Boiler, HeatPump, Bus,... modeling Elements """ - if self._connected_and_transformed: + if self.connected_and_transformed: warnings.warn( 'You are adding elements to an already connected FlowSystem. This is not recommended (But it works).', stacklevel=2, @@ -420,7 +420,7 @@ def add_elements(self, *elements: Element) -> None: ) def create_model(self) -> SystemModel: - if not self._connected_and_transformed: + if not self.connected_and_transformed: raise RuntimeError('FlowSystem is not connected_and_transformed. Call FlowSystem.connect_and_transform() first.') self.model = SystemModel(self) return self.model @@ -445,7 +445,7 @@ def plot_network( return plotting.plot_network(node_infos, edge_infos, path, controls, show) def network_infos(self) -> Tuple[Dict[str, Dict[str, str]], Dict[str, Dict[str, str]]]: - if not self._connected_and_transformed: + if not self.connected_and_transformed: self.connect_and_transform() nodes = { node.label_full: { @@ -532,7 +532,7 @@ def _connect_network(self): def __repr__(self) -> str: """Compact representation for debugging.""" - status = '✓' if self._connected_and_transformed else '⚠' + status = '✓' if self.connected_and_transformed else '⚠' return ( f'FlowSystem({len(self.timesteps)} timesteps ' f'[{self.timesteps[0].strftime("%Y-%m-%d")} to {self.timesteps[-1].strftime("%Y-%m-%d")}], ' @@ -562,7 +562,7 @@ def format_elements(element_names: list, label: str, alignment: int = 12): format_elements(list(self.components.keys()), 'Components'), format_elements(list(self.buses.keys()), 'Buses'), format_elements(list(self.effects.effects.keys()), 'Effects'), - f'Status: {"Connected & Transformed" if self._connected_and_transformed else "Not connected"}', + f'Status: {"Connected & Transformed" if self.connected_and_transformed else "Not connected"}', ] return '\n'.join(lines) @@ -641,7 +641,7 @@ def sel(self, time: Optional[Union[str, slice, List[str], pd.Timestamp, pd.Datet Returns: FlowSystem: New FlowSystem with selected data """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: self.connect_and_transform() # Build indexers dict from non-None parameters @@ -669,7 +669,7 @@ def isel(self, time: Optional[Union[int, slice, List[int]]] = None, scenario: Op Returns: FlowSystem: New FlowSystem with selected data """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: self.connect_and_transform() # Build indexers dict from non-None parameters @@ -704,7 +704,7 @@ def resample( Returns: FlowSystem: New FlowSystem with resampled data """ - if not self._connected_and_transformed: + if not self.connected_and_transformed: self.connect_and_transform() dataset = self.to_dataset() @@ -737,3 +737,7 @@ def resample( resampled_dataset = resampled_time_data return self.__class__.from_dataset(resampled_dataset) + + @property + def connected_and_transformed(self) -> bool: + return self._connected_and_transformed From 148a8524f45bb2375c16fcd2e0aaebe748c1dae8 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 10:37:27 +0200 Subject: [PATCH 181/183] Make Calcualtion Methods return themselfes to make them chainable --- flixopt/calculation.py | 20 +++++++++++++------- flixopt/flow_system.py | 2 +- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/flixopt/calculation.py b/flixopt/calculation.py index e7ea8d053..764961b78 100644 --- a/flixopt/calculation.py +++ b/flixopt/calculation.py @@ -176,7 +176,7 @@ class FullCalculation(Calculation): class for defined way of solving a flow_system optimization """ - def do_modeling(self) -> SystemModel: + def do_modeling(self) -> 'FullCalculation': t_start = timeit.default_timer() self.flow_system.connect_and_transform() @@ -184,9 +184,9 @@ def do_modeling(self) -> SystemModel: self.model.do_modeling() self.durations['modeling'] = round(timeit.default_timer() - t_start, 2) - return self.model + return self - def fix_sizes(self, ds: xr.Dataset, decimal_rounding: Optional[int] = 5): + def fix_sizes(self, ds: xr.Dataset, decimal_rounding: Optional[int] = 5) -> 'FullCalculation': """Fix the sizes of the calculations to specified values. Args: @@ -211,7 +211,9 @@ def fix_sizes(self, ds: xr.Dataset, decimal_rounding: Optional[int] = 5): ) logger.debug(f'Fixed "{name}":\n{con}') - def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = True): + return self + + def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = True) -> 'FullCalculation': t_start = timeit.default_timer() self.model.solve( @@ -248,6 +250,8 @@ def solve(self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_ma self.results = CalculationResults.from_calculation(self) + return self + class AggregatedCalculation(FullCalculation): """ @@ -288,7 +292,7 @@ def __init__( self.components_to_clusterize = components_to_clusterize self.aggregation = None - def do_modeling(self) -> SystemModel: + def do_modeling(self) -> 'AggregatedCalculation': t_start = timeit.default_timer() self.flow_system.connect_and_transform() self._perform_aggregation() @@ -302,7 +306,7 @@ def do_modeling(self) -> SystemModel: ) self.aggregation.do_modeling() self.durations['modeling'] = round(timeit.default_timer() - t_start, 2) - return self.model + return self def _perform_aggregation(self): from .aggregation import Aggregation @@ -463,7 +467,7 @@ def _create_sub_calculations(self): def do_modeling_and_solve( self, solver: _Solver, log_file: Optional[pathlib.Path] = None, log_main_results: bool = False - ): + ) -> 'SegmentedCalculation': logger.info(f'{"":#^80}') logger.info(f'{" Segmented Solving ":#^80}') self._create_sub_calculations() @@ -505,6 +509,8 @@ def do_modeling_and_solve( self.results = SegmentedCalculationResults.from_calculation(self) + return self + def _transfer_start_values(self, i: int): """ This function gets the last values of the previous solved segment and diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index b0dc746bb..3d43313b3 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -737,7 +737,7 @@ def resample( resampled_dataset = resampled_time_data return self.__class__.from_dataset(resampled_dataset) - + @property def connected_and_transformed(self) -> bool: return self._connected_and_transformed From 61755f9bd68a15f691ba2c2ea3de721adb282d86 Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 13:50:24 +0200 Subject: [PATCH 182/183] Improve example --- examples/05_Two-stage-optimization/two_stage_optimization.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/examples/05_Two-stage-optimization/two_stage_optimization.py b/examples/05_Two-stage-optimization/two_stage_optimization.py index 7f0a412bf..3548726b4 100644 --- a/examples/05_Two-stage-optimization/two_stage_optimization.py +++ b/examples/05_Two-stage-optimization/two_stage_optimization.py @@ -4,7 +4,7 @@ as it can drastically reduce the computational time. This leads to faster results and easier debugging. A common use case is to do optimize the investments of a model with a downsampled version of the original model, and than fix the computed sizes when calculating th actual dispatch. -While the final optimum might differ from the fglobal optimum, the solving will be much faster. +While the final optimum might differ from the global optimum, the solving will be much faster. """ import logging @@ -107,7 +107,6 @@ calculation_sizing.do_modeling() calculation_sizing.solve(fx.solvers.HighsSolver(0.1/100, 600)) timer_sizing = timeit.default_timer() - start - flow_sizes = xr.Dataset({flow.size.name: flow.size for flow in calculation_sizing.results.flows.values()}) calculation_dispatch = fx.FullCalculation('Sizing', flow_system) calculation_dispatch.do_modeling() @@ -133,7 +132,7 @@ ).assign_coords(mode=['Combined', 'Two-stage']) comparison['Duration [s]'] = xr.DataArray([timer_combined, timer_sizing + timer_dispatch], dims='mode') - comparison_main = comparison[['Duration [s]', 'costs|total', 'costs(invest)|total', 'costs(operation)|total', 'BHKW2(Q_fu)|size', 'Kessel(Q_fu)|size']] + comparison_main = comparison[['Duration [s]', 'costs|total', 'costs(invest)|total', 'costs(operation)|total', 'BHKW2(Q_fu)|size', 'Kessel(Q_fu)|size', 'Speicher|size']] comparison_main = xr.concat([ comparison_main, ((comparison_main.sel(mode='Two-stage') - comparison_main.sel(mode='Combined')) From 66f6a8675b421131fcb8f8bedf2a26851783639a Mon Sep 17 00:00:00 2001 From: FBumann <117816358+FBumann@users.noreply.github.com> Date: Wed, 16 Jul 2025 15:54:36 +0200 Subject: [PATCH 183/183] Improve Unreleased CHANGELOG.md --- CHANGELOG.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 860c2e842..f1ee8a916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased - New Model dimensions] ## What's New @@ -35,37 +35,38 @@ This might occur when scenarios represent years or months, while an investment d * Feature 2 - Description -## [Unreleased] +## [Unreleased - Data Management and IO] ### Changed * **BREAKING**: `relative_minimum_charge_state` and `relative_maximum_charge_state` don't have an extra timestep anymore. The final charge state can now be constrained by parameters `relative_minimum_final_charge_state` and `relative_maximum_final_charge_state` instead +* **BREAKING**: Calculation.do_modeling() now returns the Calculation object instead of its linopy.Model * FlowSystems can not be shared across multiple Calculations anymore. A copy of the FlowSystem is created instead, making every Calculation independent * Type system overhaul - added clear separation between temporal and non-temporal data throughout codebase for better clarity * FlowSystem data management simplified - removed `time_series_collection` pattern in favor of direct timestep properties * Enhanced FlowSystem interface with improved `__repr__()` and `__str__()` methods ### Added -* **NEW**: Complete serialization infrastructure through `Interface` base class +* Complete serialization infrastructure through `Interface` base class * IO for all Interfaces and the FlowSystem with round-trip serialization support * Automatic DataArray extraction and restoration * NetCDF export/import capabilities for all Interface objects and FlowSystem * JSON export for documentation purposes * Recursive handling of nested Interface objects -* **NEW**: FlowSystem data manipulation methods +* FlowSystem data manipulation methods * `sel()` and `isel()` methods for temporal data selection * `resample()` method for temporal resampling * `copy()` method to create a copy of a FlowSystem, including all underlying Elements and their data * `__eq__()` method for FlowSystem comparison -* **NEW**: Storage component enhancements +* Storage component enhancements * `relative_minimum_final_charge_state` parameter for final state control * `relative_maximum_final_charge_state` parameter for final state control -* *Internal*: Enhanced data handling methods - * `fit_to_model_coords()` method for data alignment - * `fit_effects_to_model_coords()` method for effect data processing - * `connect_and_transform()` method replacing separate operations -* **NEW**: Core data handling improvements +* Core data handling improvements * `get_dataarray_stats()` function for statistical summaries * Enhanced `DataConverter` class with better TimeSeriesData support +* Internal: Enhanced data handling methods + * `fit_to_model_coords()` method for data alignment + * `fit_effects_to_model_coords()` method for effect data processing + * `connect_and_transform()` method replacing several operations ### Fixed * Enhanced NetCDF I/O with proper attribute preservation for DataArrays @@ -74,7 +75,7 @@ This might occur when scenarios represent years or months, while an investment d ### Know Issues * Plotly >= 6 may raise errors if "nbformat" is not installed. We pinned plotly to <6, but this may be fixed in the future. -* IO for single Interfaces/Elemenets to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arrises from Numeric Data not being stored as xr.DataArray by the user. TO avoid this, always use the `to_dataset()` on Elements inside a FlowSystem thats connected and transformed. +* IO for single Interfaces/Elemenets to Datasets might not work properly if the Interface/Element is not part of a fully transformed and connected FlowSystem. This arrises from Numeric Data not being stored as xr.DataArray by the user. To avoid this, always use the `to_dataset()` on Elements inside a FlowSystem thats connected and transformed. ### Deprecated * The `agg_group` and `agg_weight` parameters of `TimeSeriesData` are deprecated and will be removed in a future version. Use `aggregation_group` and `aggregation_weight` instead.