diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index c05343d70..7a9fb3e66 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -57,7 +57,31 @@ jobs: - name: Install dependencies run: uv pip install --system ".[docs,full]" + - name: Get notebook cache key + id: notebook-cache-key + run: | + # Hash notebooks + flixopt source code (sorted for stable cache keys) + HASH=$(find docs/notebooks -name '*.ipynb' | sort | xargs cat | cat - <(find flixopt -name '*.py' | sort | xargs cat) | sha256sum | cut -d' ' -f1) + echo "hash=$HASH" >> $GITHUB_OUTPUT + + - name: Cache executed notebooks + uses: actions/cache@v4 + id: notebook-cache + with: + path: docs/notebooks/*.ipynb + key: notebooks-${{ steps.notebook-cache-key.outputs.hash }} + + - name: Execute notebooks in parallel + if: steps.notebook-cache.outputs.cache-hit != 'true' + run: | + # Execute all notebooks in parallel (4 at a time) + # Run from notebooks directory so relative imports work + cd docs/notebooks && find . -name '*.ipynb' -print0 | \ + xargs -0 -P 4 -I {} jupyter execute --inplace {} + - name: Build docs + env: + MKDOCS_JUPYTER_EXECUTE: "false" run: mkdocs build --strict - uses: actions/upload-artifact@v4 @@ -95,12 +119,34 @@ jobs: - name: Install dependencies run: uv pip install --system ".[docs,full]" + - name: Get notebook cache key + id: notebook-cache-key + run: | + # Hash notebooks + flixopt source code (sorted for stable cache keys) + HASH=$(find docs/notebooks -name '*.ipynb' | sort | xargs cat | cat - <(find flixopt -name '*.py' | sort | xargs cat) | sha256sum | cut -d' ' -f1) + echo "hash=$HASH" >> $GITHUB_OUTPUT + + - name: Cache executed notebooks + uses: actions/cache@v4 + id: notebook-cache + with: + path: docs/notebooks/*.ipynb + key: notebooks-${{ steps.notebook-cache-key.outputs.hash }} + + - name: Execute notebooks in parallel + if: steps.notebook-cache.outputs.cache-hit != 'true' + run: | + cd docs/notebooks && find . -name '*.ipynb' -print0 | \ + xargs -0 -P 4 -I {} jupyter execute --inplace {} + - name: Configure Git run: | git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - name: Deploy docs + env: + MKDOCS_JUPYTER_EXECUTE: "false" run: | VERSION=${{ inputs.version }} VERSION=${VERSION#v} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f30c2b5cb..18b1eb4be 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,7 +7,7 @@ repos: - id: check-yaml exclude: ^mkdocs\.yml$ # Skip mkdocs.yml - id: check-added-large-files - exclude: .*Zeitreihen2020\.csv$ + exclude: (.*Zeitreihen2020\.csv$|docs/notebooks/data/raw/.*) - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.12.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index b9a30f3a8..bad4e4d52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,6 +51,259 @@ If upgrading from v2.x, see the [v3.0.0 release notes](https://github.com/flixOp Until here --> +## [6.0.0] - Upcoming + +**Summary**: Major release introducing time-series clustering with storage inter-cluster linking, the new `fxplot` accessor for universal xarray plotting, and removal of deprecated v5.0 classes. Includes configurable storage behavior across typical periods and improved weights API. + +!!! warning "Breaking Changes" + This release removes `ClusteredOptimization` and `ClusteringParameters` which were deprecated in v5.0.0. Use `flow_system.transform.cluster()` instead. See [Migration](#migration-from-clusteredoptimization) below. + +### ✨ Added + +**FlowSystem Comparison**: New `Comparison` class for comparing multiple FlowSystems side-by-side: + +```python +# Compare systems (uses FlowSystem.name by default) +comp = fx.Comparison([fs_base, fs_modified]) + +# Or with custom names +comp = fx.Comparison([fs1, fs2, fs3], names=['baseline', 'low_cost', 'high_eff']) + +# Side-by-side plots (auto-facets by 'case' dimension) +comp.statistics.plot.balance('Heat') +comp.statistics.flow_rates.fxplot.line() + +# Access combined data with 'case' dimension +comp.solution # xr.Dataset +comp.statistics.flow_rates # xr.Dataset + +# Compute differences relative to a reference case +comp.diff() # vs first case +comp.diff('baseline') # vs named case +``` + +- Concatenates solutions and statistics from multiple FlowSystems with a `'case'` dimension +- Mirrors all `StatisticsAccessor` properties (`flow_rates`, `flow_hours`, `sizes`, `charge_states`, `temporal_effects`, `periodic_effects`, `total_effects`) +- Mirrors all `StatisticsPlotAccessor` methods (`balance`, `carrier_balance`, `flows`, `sizes`, `duration_curve`, `effects`, `charge_states`, `heatmap`, `storage`) +- Existing plotting infrastructure automatically handles faceting by `'case'` + +**Time-Series Clustering**: Reduce large time series to representative typical periods for faster investment optimization, then expand results back to full resolution. + +```python +# Stage 1: Cluster and optimize (fast sizing) +fs_clustered = flow_system.transform.cluster( + n_clusters=12, # 12 typical days from a year + cluster_duration='1D', # Each cluster represents one day + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], +) +fs_clustered.optimize(solver) + +# Stage 2: Expand back to full resolution +fs_expanded = fs_clustered.transform.expand_solution() +``` + +**Storage Modes for Clustering**: Control how storage behaves across clustered periods via `Storage(cluster_mode=...)`: + +| Mode | Description | Use Case | +|------|-------------|----------| +| `'intercluster_cyclic'` | Links storage across clusters + yearly cyclic (default) | Seasonal storage with yearly optimization | +| `'intercluster'` | Links storage across clusters, free start/end | Multi-year optimization without cyclic constraint | +| `'cyclic'` | Each cluster independent, but cyclic (start = end) | Daily storage only, ignores seasonal patterns | +| `'independent'` | Each cluster fully independent, free start/end | Fastest solve, no long-term storage value | + +**Clustering Parameters**: + +- `n_clusters` (int): Number of representative periods to create +- `cluster_duration` (str): Duration of each cluster period (e.g., `'1D'`, `'24h'`, or integer hours) +- `time_series_for_high_peaks` (list[str]): Ensure clusters containing peak values are captured +- `time_series_for_low_peaks` (list[str]): Ensure clusters containing minimum values are captured + +**Key Features**: + +- **Inter-cluster storage linking**: For `'intercluster'` and `'intercluster_cyclic'` modes, a `SOC_boundary` variable tracks absolute state-of-charge at period boundaries, enabling accurate seasonal storage modeling +- **Self-discharge decay**: Storage losses are correctly applied during solution expansion using the formula: `actual_SOC(t) = SOC_boundary Γ— (1 - loss)^t + Ξ”E(t)` +- **Multi-dimensional support**: Works with periods, scenarios, and clusters dimensions simultaneously +- **Solution expansion**: `transform.expand_solution()` maps clustered results back to original timesteps with proper storage state reconstruction + +**Example: Seasonal Storage with Clustering**: + +```python +# Configure storage for seasonal behavior +storage = fx.Storage( + 'SeasonalPit', + capacity_in_flow_hours=5000, + cluster_mode='intercluster_cyclic', # Enable seasonal storage in clustering + relative_loss_per_hour=0.0001, # Small self-discharge + ... +) + +# Cluster, optimize, and expand +fs_clustered = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D') +fs_clustered.optimize(solver) +fs_expanded = fs_clustered.transform.expand_solution() + +# Full-resolution charge state now available +charge_state = fs_expanded.solution['SeasonalPit|charge_state'] +``` + +!!! tip "Choosing the Right Storage Mode" + Use `'intercluster_cyclic'` (default) for seasonal storage like pit storage or underground thermal storage. + Use `'cyclic'` for short-term storage like batteries or hot water tanks where only daily patterns matter. + Use `'independent'` for quick estimates when storage behavior isn't critical. + +**FXPlot Accessor**: New global xarray accessors for universal plotting with automatic faceting and smart dimension handling. Works on any xarray Dataset, not just flixopt results. + +```python +import flixopt as fx # Registers accessors automatically + +# Plot any xarray Dataset with automatic faceting +dataset.fxplot.bar(x='component') +dataset.fxplot.area(x='time') +dataset.fxplot.heatmap(x='time', y='component') +dataset.fxplot.line(x='time', facet_col='scenario') + +# DataArray support +data_array.fxplot.line() + +# Statistics transformations +dataset.fxstats.to_duration_curve() +``` + +**Available Plot Methods**: + +| Method | Description | +|--------|-------------| +| `.fxplot.bar()` | Grouped bar charts | +| `.fxplot.stacked_bar()` | Stacked bar charts | +| `.fxplot.line()` | Line charts with faceting | +| `.fxplot.area()` | Stacked area charts | +| `.fxplot.heatmap()` | Heatmap visualizations | +| `.fxplot.scatter()` | Scatter plots | +| `.fxplot.pie()` | Pie charts with faceting | +| `.fxstats.to_duration_curve()` | Transform to duration curve format | + +**Key Features**: + +- **Auto-faceting**: Automatically assigns extra dimensions (period, scenario, cluster) to `facet_col`, `facet_row`, or `animation_frame` +- **Smart x-axis**: Intelligently selects x dimension based on priority (time > duration > period > scenario) +- **Universal**: Works on any xarray Dataset/DataArray, not limited to flixopt +- **Configurable**: Customize via `CONFIG.Plotting` (colorscales, facet columns, line shapes) + +### πŸ’₯ Breaking Changes + +- `FlowSystem.scenario_weights` are now always normalized to sum to 1 when set (including after `.sel()` subsetting) + +### ♻️ Changed + +- `FlowSystem.weights` returns `dict[str, xr.DataArray]` (unit weights instead of `1.0` float fallback) +- `FlowSystemDimensions` type now includes `'cluster'` + +### πŸ—‘οΈ Deprecated + +The following items are deprecated and will be removed in **v7.0.0**: + +**Classes** (use FlowSystem methods instead): + +- `Optimization` class β†’ Use `flow_system.optimize(solver)` +- `SegmentedOptimization` class β†’ Use `flow_system.optimize.rolling_horizon()` +- `Results` class β†’ Use `flow_system.solution` and `flow_system.statistics` +- `SegmentedResults` class β†’ Use segment FlowSystems directly + +**FlowSystem methods** (use `transform` or `topology` accessor instead): + +- `flow_system.sel()` β†’ Use `flow_system.transform.sel()` +- `flow_system.isel()` β†’ Use `flow_system.transform.isel()` +- `flow_system.resample()` β†’ Use `flow_system.transform.resample()` +- `flow_system.plot_network()` β†’ Use `flow_system.topology.plot()` +- `flow_system.start_network_app()` β†’ Use `flow_system.topology.start_app()` +- `flow_system.stop_network_app()` β†’ Use `flow_system.topology.stop_app()` +- `flow_system.network_infos()` β†’ Use `flow_system.topology.infos()` + +**Parameters:** + +- `normalize_weights` parameter in `create_model()`, `build_model()`, `optimize()` + +**Topology method name simplifications** (old names still work with deprecation warnings, removal in v7.0.0): + +| Old (v5.x) | New (v6.0.0) | +|------------|--------------| +| `topology.plot_network()` | `topology.plot()` | +| `topology.start_network_app()` | `topology.start_app()` | +| `topology.stop_network_app()` | `topology.stop_app()` | +| `topology.network_infos()` | `topology.infos()` | + +Note: `topology.plot()` now renders a Sankey diagram. The old PyVis visualization is available via `topology.plot_legacy()`. + +### πŸ”₯ Removed + +**Clustering classes removed** (deprecated in v5.0.0): + +- `ClusteredOptimization` class - Use `flow_system.transform.cluster()` then `optimize()` +- `ClusteringParameters` class - Parameters are now passed directly to `transform.cluster()` +- `flixopt/clustering.py` module - Restructured to `flixopt/clustering/` package with new classes + +#### Migration from ClusteredOptimization + +=== "v5.x (Old - No longer works)" + ```python + from flixopt import ClusteredOptimization, ClusteringParameters + + params = ClusteringParameters(hours_per_period=24, nr_of_periods=8) + calc = ClusteredOptimization('model', flow_system, params) + calc.do_modeling_and_solve(solver) + results = calc.results + ``` + +=== "v6.0.0 (New)" + ```python + # Cluster using transform accessor + fs_clustered = flow_system.transform.cluster( + n_clusters=8, # was: nr_of_periods + cluster_duration='1D', # was: hours_per_period=24 + ) + fs_clustered.optimize(solver) + + # Results on the clustered FlowSystem + costs = fs_clustered.solution['costs'].item() + + # Expand back to full resolution if needed + fs_expanded = fs_clustered.transform.expand_solution() + ``` + +### πŸ› Fixed + +- `temporal_weight` and `sum_temporal()` now use consistent implementation + +### πŸ“ Docs + +**New Documentation Pages:** + +- [Time-Series Clustering Guide](https://flixopt.github.io/flixopt/latest/user-guide/optimization/clustering/) - Comprehensive guide to clustering workflows + +**New Jupyter Notebooks:** + +- **08c-clustering.ipynb** - Introduction to time-series clustering +- **08c2-clustering-storage-modes.ipynb** - Comparison of all 4 storage cluster modes +- **08d-clustering-multiperiod.ipynb** - Clustering with periods and scenarios +- **08e-clustering-internals.ipynb** - Understanding clustering internals +- **fxplot_accessor_demo.ipynb** - Demo of the new fxplot accessor + +### πŸ‘· Development + +**New Test Suites for Clustering**: + +- `TestStorageClusterModes`: Tests for all 4 storage `cluster_mode` options +- `TestInterclusterStorageLinking`: Tests for `SOC_boundary` variable and expansion logic +- `TestMultiPeriodClustering`: Tests for clustering with periods and scenarios dimensions +- `TestPeakSelection`: Tests for `time_series_for_high_peaks` and `time_series_for_low_peaks` parameters + +**New Test Suites for Other Features**: + +- `test_clustering_io.py` - Tests for clustering serialization roundtrip +- `test_sel_isel_single_selection.py` - Tests for transform selection methods + +--- + ## [5.0.4] - 2026-01-05 **Summary**: Dependency updates. diff --git a/docs/design/cluster_architecture.md b/docs/design/cluster_architecture.md new file mode 100644 index 000000000..e90aeccff --- /dev/null +++ b/docs/design/cluster_architecture.md @@ -0,0 +1,773 @@ +# Design Document: Cluster Architecture for flixopt + +## Executive Summary + +This document defines the architecture for cluster representation in flixopt using **true `(cluster, time)` dimensions**. + +### Key Decision: True Dimensions (Option B) + +**Chosen Approach:** +```python +# Clustered data structure: +data.dims = ('cluster', 'time', 'period', 'scenario') +data.shape = (9, 24, ...) # 9 clusters Γ— 24 timesteps each +``` + +**Why True Dimensions?** +1. **Temporal constraints just work** - `x[:, 1:] - x[:, :-1]` naturally stays within clusters +2. **No boundary masking** - StorageModel, StatusModel constraints are clean and vectorized +3. **Plotting trivial** - existing `facet_col='cluster'` works automatically + +### Document Scope + +1. Current architecture analysis (Part 1) +2. Architectural options and recommendation (Part 2) +3. Impact on Features - StatusModel, StorageModel, etc. (Part 3) +4. Plotting improvements (Part 4) +5. Future considerations (Part 5) +6. Implementation roadmap (Part 6) + +--- + +## Part 1: Current Architecture Analysis + +### 1.1 Time Dimension Structure + +**Current Implementation:** +``` +time: (n_clusters Γ— timesteps_per_cluster,) # Flat, e.g., (864,) for 9 clusters Γ— 96 timesteps +``` + +**Key Properties:** +- `cluster_weight`: Shape `(time,)` with repeated values per cluster +- `timestep_duration`: Shape `(time,)` or scalar +- `aggregation_weight = timestep_duration Γ— cluster_weight` + +**Cluster Tracking:** +- `cluster_start_positions`: Array of indices where each cluster begins +- `ClusterStructure`: Stores cluster_order, occurrences, n_clusters, timesteps_per_cluster + +### 1.2 Features Affected by Time Structure + +| Feature | Time Usage | Clustering Impact | +|---------|-----------|-------------------| +| **StatusModel** | `aggregation_weight` for active hours, `timestep_duration` for effects | Must sum correctly across clusters | +| **InvestmentModel** | Periodic (no time dim) | Unaffected by time structure | +| **PiecewiseModel** | Per-timestep lambda variables | Must preserve cluster structure | +| **ShareAllocationModel** | Uses `cluster_weight` explicitly | Directly depends on weight structure | +| **StorageModel** | Charge balance across time | Needs cluster boundary handling | +| **InterclusterStorageModel** | SOC_boundary linking | Uses cluster indices extensively | + +### 1.3 Current Plotting Structure + +**StatisticsPlotAccessor Methods:** +- `balance()`: Node flow visualization +- `storage()`: Dual-axis charge/discharge + SOC +- `heatmap()`: 2D time reshaping (days Γ— hours) +- `duration_curve()`: Sorted load profiles +- `effects()`: Cost/emission breakdown + +**Clustering in Plots:** +- `ClusterStructure.plot()`: Shows cluster assignments +- Cluster weight applied when aggregating (`cluster_weight.sum('time')`) +- No visual separation between clusters in time series plots + +--- + +## Part 2: Architectural Options + +### 2.1 Option A: Enhanced Flat with Indexers + +Keep flat `time` dimension, add xarray indexer properties. + +**Pros:** Supports variable-length clusters +**Cons:** Every temporal constraint needs explicit boundary masking + +**NOT RECOMMENDED** - see Option B. + +### 2.2 Option B: True (cluster, time) Dimensions (RECOMMENDED) + +Reshape time to 2D when clustering is active: + +```python +# ═══════════════════════════════════════════════════════════════ +# DIMENSION STRUCTURE +# ═══════════════════════════════════════════════════════════════ + +# Non-clustered: +data.dims = ('time', 'period', 'scenario') +data.shape = (8760, ...) # Full year hourly + +# Clustered: +data.dims = ('cluster', 'time', 'period', 'scenario') +data.shape = (9, 24, ...) # 9 clusters Γ— 24 timesteps each + +# Varying segment durations supported: +timestep_duration.dims = ('cluster', 'time') +timestep_duration.shape = (9, 24) # Different durations per segment per cluster +``` + +**Key Benefits - Temporal Constraints Just Work!** + +```python +# ═══════════════════════════════════════════════════════════════ +# STORAGE: Charge balance naturally within clusters +# ═══════════════════════════════════════════════════════════════ +# charge_state shape: (cluster, time+1, period, scenario) - extra timestep for boundaries +charge_state = ... # (9, 25, ...) + +# Balance constraint - NO MASKING NEEDED! +lhs = charge_state[:, 1:] - charge_state[:, :-1] * (1 - loss) - charge + discharge +# Shape: (cluster, time, period, scenario) = (9, 24, ...) + +# Delta per cluster (for inter-cluster linking): +delta_soc = charge_state[:, -1] - charge_state[:, 0] # Shape: (cluster, ...) + +# ═══════════════════════════════════════════════════════════════ +# STATUS: Uptime/downtime constraints stay within clusters +# ═══════════════════════════════════════════════════════════════ +status = ... # (cluster, time, ...) + +# State transitions - naturally per cluster! +activate = status[:, 1:] - status[:, :-1] # No boundary issues! + +# min_uptime constraint - works correctly, can't span clusters + +# ═══════════════════════════════════════════════════════════════ +# INTER-CLUSTER OPERATIONS +# ═══════════════════════════════════════════════════════════════ +# Select first/last timestep of each cluster: +at_start = data.isel(time=0) # Shape: (cluster, period, scenario) +at_end = data.isel(time=-1) # Shape: (cluster, period, scenario) + +# Compute per-cluster statistics: +mean_per_cluster = data.mean(dim='time') +max_per_cluster = data.max(dim='time') +``` + +**Varying Segment Durations (Future Segmentation):** + +```python +# Same NUMBER of segments per cluster, different DURATIONS: +timestep_duration = xr.DataArray( + [ + [2, 2, 1, 1, 2, 4], # Cluster 0: segments sum to 12h + [1, 3, 2, 2, 2, 2], # Cluster 1: segments sum to 12h + ... + ], + dims=['cluster', 'time'], + coords={'cluster': range(9), 'time': range(6)} +) + +# aggregation_weight still works: +aggregation_weight = timestep_duration * cluster_weight # (cluster, time) * (cluster,) +``` + +**Pros:** +- Temporal constraints naturally stay within clusters - NO MASKING! +- StatusModel uptime/downtime just works +- Storage balance is clean +- Much less code, fewer bugs +- Supports varying segment durations (same count, different lengths) + +**Cons:** +- More upfront refactoring +- All code paths need to handle `(cluster, time)` vs `(time,)` based on `is_clustered` + +### 2.3 Recommendation: Option B (True Dimensions) + +Given: +- Uniform timestep COUNT per cluster (tsam default) +- Variable segment DURATIONS supported via `timestep_duration[cluster, time]` +- Much cleaner constraint handling + +**Option B is the recommended choice.** + +--- + +## Part 3: Impact on Features + +### 3.1 StatusModel Impact - SOLVED BY TRUE DIMENSIONS + +**With `(cluster, time)` dimensions, temporal constraints naturally stay within clusters!** + +```python +status.dims = ('cluster', 'time', 'period', 'scenario') +status.shape = (9, 24, ...) + +# State transitions - per cluster, no boundary issues! +activate = status[:, 1:] - status[:, :-1] + +# min_uptime constraint operates within each cluster's time dimension +# Cannot accidentally span cluster boundaries +``` + +**What works automatically:** +- βœ… `min_uptime`, `min_downtime` - constraints stay within clusters +- βœ… `initial_status` - applies to each cluster's first timestep +- βœ… State transitions - naturally per cluster +- βœ… `active_hours` - uses `aggregation_weight` correctly +- βœ… `effects_per_startup` - counted per cluster, weighted by `cluster_weight` + +**Optional Enhancement - cluster_mode for special cases:** +```python +class StatusParameters: + # NEW: How to handle cluster boundaries (default: independent) + cluster_mode: Literal['independent', 'cyclic'] = 'independent' +``` + +| Mode | Behavior | +|------|----------| +| `independent` | Each cluster starts fresh (default, most common) | +| `cyclic` | `status[:, 0] == status[:, -1]` - status returns to start | + +### 3.2 StorageModel Impact - SIMPLIFIED + +**With `(cluster, time)` dimensions, storage constraints become trivial:** + +```python +charge_state.dims = ('cluster', 'time_extra', 'period', 'scenario') +charge_state.shape = (9, 25, ...) # 24 timesteps + 1 boundary per cluster + +# ═══════════════════════════════════════════════════════════════ +# Charge balance - NO MASKING! +# ═══════════════════════════════════════════════════════════════ +lhs = ( + charge_state[:, 1:] - + charge_state[:, :-1] * (1 - loss_rate) - + charge * eta_charge + + discharge / eta_discharge +) +self.add_constraints(lhs == 0, name='charge_balance') # Clean! + +# ═══════════════════════════════════════════════════════════════ +# Delta SOC per cluster (for inter-cluster linking) +# ═══════════════════════════════════════════════════════════════ +delta_soc = charge_state[:, -1] - charge_state[:, 0] # Shape: (cluster, ...) + +# ═══════════════════════════════════════════════════════════════ +# Cluster start constraint (relative SOC starts at 0) +# ═══════════════════════════════════════════════════════════════ +self.add_constraints(charge_state[:, 0] == 0, name='cluster_start') + +# ═══════════════════════════════════════════════════════════════ +# Cyclic constraint (optional) +# ═══════════════════════════════════════════════════════════════ +self.add_constraints(charge_state[:, 0] == charge_state[:, -1], name='cyclic') +``` + +**InterclusterStorageModel also simplified** - SOC_boundary linking uses clean slicing. + +### 3.3 ShareAllocationModel Impact + +**Current Code (features.py:624):** +```python +self._eq_total.lhs -= (self.total_per_timestep * self._model.cluster_weight).sum(dim='time') +``` + +**With Enhanced Helpers:** +No changes needed - `cluster_weight` structure preserved. + +### 3.4 PiecewiseModel Impact + +**Current Code:** Creates lambda variables per timestep. + +**With Enhanced Helpers:** +No changes needed - operates on flat time dimension. + +### 3.5 Summary: Models with True (cluster, time) Dimensions + +| Model | Cross-Timestep Constraints | With True Dims | Action Needed | +|-------|---------------------------|----------------|---------------| +| **StorageModel** | `cs[t] - cs[t-1]` | βœ… Just works | Simplify code | +| **StatusModel** | min_uptime, min_downtime | βœ… Just works | Optional cluster_mode | +| **consecutive_duration_tracking** | State machine | βœ… Just works | No changes | +| **state_transition_bounds** | `activate[t] - status[t-1]` | βœ… Just works | No changes | +| **PiecewiseModel** | Per-timestep only | βœ… Just works | No changes | +| **ShareAllocationModel** | Sum with cluster_weight | βœ… Just works | No changes | +| **InvestmentModel** | No time dimension | βœ… Just works | No changes | + +**Key Insight:** With true `(cluster, time)` dimensions, `x[:, 1:] - x[:, :-1]` naturally stays within clusters! + +--- + +## Part 4: Plotting Improvements + +### 4.1 Key Benefit of True Dimensions: Minimal Plotting Changes + +With true `(cluster, time)` dimensions, plotting becomes trivial because: +1. Data already has the right shape - no reshaping needed +2. Existing `facet_col='cluster'` parameter just works +3. Only minimal changes needed: auto-add cluster separators in combined views + +### 4.2 Proposed Approach: Leverage Existing Infrastructure + +#### 4.2.1 Use Existing facet_col Parameter + +**No new plot methods needed!** The existing infrastructure handles `cluster` dimension: + +```python +# ═══════════════════════════════════════════════════════════════ +# EXISTING API - works automatically with (cluster, time) dims! +# ═══════════════════════════════════════════════════════════════ +fs.statistics.plot.storage('Battery', facet_col='cluster') # One subplot per cluster +fs.statistics.plot.balance('Heat', facet_col='cluster') # One subplot per cluster +fs.statistics.plot.flows(..., facet_col='cluster') # Same pattern + +# Combine with other dimensions +fs.statistics.plot.balance('Heat', facet_col='cluster', facet_row='scenario') +``` + +#### 4.2.2 Auto-Add Cluster Separators (Small Change) + +For combined views (no faceting), add visual separators: + +```python +def _create_base_plot(self, data, **kwargs): + """Base plot creation - add cluster separators if combined view.""" + fig = ... # existing logic + + # Auto-add cluster separators if clustered and showing combined time + if self._fs.is_clustered and 'cluster' not in kwargs.get('facet_col', ''): + # Add subtle vertical lines between clusters + for cluster_idx in range(1, self._fs.clustering.n_clusters): + x_pos = cluster_idx * self._fs.clustering.timesteps_per_cluster + fig.add_vline(x=x_pos, line_dash='dot', opacity=0.3, line_color='gray') + + return fig +``` + +#### 4.2.3 Per-Cluster Statistics (Natural with True Dims) + +With `(cluster, time)` dimensions, aggregation is trivial: + +```python +# Mean per cluster - just use xarray +mean_per_cluster = data.mean(dim='time') # Shape: (cluster, ...) +max_per_cluster = data.max(dim='time') + +# Can plot directly +fs.statistics.plot.bar(data.mean('time'), x='cluster', title='Mean by Cluster') +``` + +#### 4.2.4 Heatmap (Already Correct Shape) + +With true dimensions, heatmaps work directly: + +```python +# Data already has (cluster, time) shape - heatmap just works! +def cluster_heatmap(self, variable): + data = self._get_variable(variable) + + # With (cluster, time) dims, no reshaping needed! + return self._plot_heatmap( + data, # Already (cluster, time, ...) + x='time', + y='cluster', + colorbar_title=variable + ) +``` + +### 4.3 Summary: Plotting Changes Required + +| Change | Scope | Complexity | +|--------|-------|------------| +| Auto cluster separators in base plot | ~10 lines in `_create_base_plot` | Low | +| Ensure facet_col='cluster' works | Should work already | None | +| Heatmap with cluster dim | Works automatically | None | +| No new plot methods needed | - | - | + +--- + +## Part 5: Future Considerations + +### 5.1 Variable Segment Durations (Out of Scope) + +tsam supports intra-period segmentation with variable segment durations. This could be supported in the future via: +- Integer-based `time` index (0, 1, 2, ...) instead of timestamps +- `timestep_duration[cluster, time]` array for variable durations per segment + +**Not implemented in initial version** - the architecture supports it, but it's not a priority. + +--- + +## Part 6: Implementation Roadmap + +### Phase 1: Core Dimension Refactoring (PRIORITY) + +**Goal:** Introduce true `(cluster, time)` dimensions throughout the codebase. + +**Tasks:** +1. Update `FlowSystem` to support `(cluster, time)` dimension structure when clustered +2. Add `is_clustered` property to `FlowSystem` +3. Update `Clustering` class with: + - `n_clusters: int` property + - `timesteps_per_cluster: int` property + - Coordinate accessors for cluster dimension +4. Update `cluster_weight` to have shape `(cluster,)` instead of `(time,)` +5. Update `timestep_duration` to have shape `(cluster, time)` when clustered +6. Update `aggregation_weight` computation to broadcast correctly + +**Files:** +- `flixopt/flow_system.py` - Core dimension handling +- `flixopt/clustering/base.py` - Updated Clustering class + +**Key Changes:** +```python +# FlowSystem property updates: +@property +def is_clustered(self) -> bool: + return self.clustering is not None + +@property +def cluster_weight(self) -> xr.DataArray: + if not self.is_clustered: + return xr.DataArray(1.0) + # Shape: (cluster,) - one weight per cluster + return xr.DataArray( + self.clustering.cluster_occurrences, + dims=['cluster'], + coords={'cluster': range(self.clustering.n_clusters)} + ) + +@property +def timestep_duration(self) -> xr.DataArray: + if not self.is_clustered: + return self._timestep_duration # Shape: (time,) or scalar + # Shape: (cluster, time) when clustered + return self._timestep_duration # Already 2D from clustering + +@property +def aggregation_weight(self) -> xr.DataArray: + return self.timestep_duration * self.cluster_weight # Broadcasting handles shapes +``` + +### Phase 2: Update Variable/Constraint Creation + +**Goal:** All variables and constraints use `(cluster, time)` dimensions when clustered. + +**Tasks:** +1. Update `create_variable` to use `(cluster, time, period, scenario)` dims when clustered +2. Update constraint generation in all models +3. Verify linopy handles multi-dimensional constraint arrays correctly +4. Add tests for both clustered and non-clustered paths + +**Files:** +- `flixopt/core.py` - Variable creation +- `flixopt/components.py` - StorageModel, other component models +- `flixopt/features.py` - StatusModel, other feature models + +**Key Pattern:** +```python +# Dimension-aware variable creation: +def _get_time_dims(self) -> list[str]: + if self.flow_system.is_clustered: + return ['cluster', 'time'] + return ['time'] + +def _get_time_coords(self) -> dict: + if self.flow_system.is_clustered: + return { + 'cluster': range(self.flow_system.clustering.n_clusters), + 'time': range(self.flow_system.clustering.timesteps_per_cluster) + } + return {'time': self.flow_system.time_coords} +``` + +### Phase 3: Simplify StorageModel and InterclusterStorageModel + +**Goal:** Leverage true dimensions for clean constraint generation. + +**Tasks:** +1. Simplify `StorageModel.charge_balance` - no boundary masking needed +2. Simplify delta SOC calculation: `charge_state[:, -1] - charge_state[:, 0]` +3. Simplify `InterclusterStorageModel` linking constraints +4. Update `intercluster_helpers.py` utilities + +**Files:** +- `flixopt/components.py` - StorageModel, InterclusterStorageModel +- `flixopt/clustering/intercluster_helpers.py` - Simplified helpers + +**Before/After:** +```python +# BEFORE (flat time with masking): +start_positions = clustering.cluster_start_positions +end_positions = start_positions[1:] - 1 +mask = _build_boundary_mask(...) +balance = charge_state.isel(time=slice(1, None)).where(~mask) - ... + +# AFTER (true dimensions): +# charge_state shape: (cluster, time+1, ...) +balance = ( + charge_state[:, 1:] - + charge_state[:, :-1] * (1 - loss_rate) - + charge * eta_charge + + discharge / eta_discharge +) +# No masking needed - constraints naturally stay within clusters! +``` + +### Phase 4: Update transform_accessor.cluster() + +**Goal:** Produce true `(cluster, time)` shaped data. + +**Tasks:** +1. Update `cluster()` to reshape time series to `(cluster, time)` +2. Generate proper coordinates for cluster dimension +3. Update `expand_solution()` to handle reverse transformation +4. Handle SOC_boundary expansion for inter-cluster storage + +**Files:** +- `flixopt/transform_accessor.py` - cluster() and expand_solution() + +**Key Implementation:** +```python +def cluster(self, n_clusters, cluster_duration, ...): + """Create clustered FlowSystem with (cluster, time) dimensions.""" + ... + # Reshape all time series: (flat_time,) β†’ (cluster, time) + for key, ts in time_series.items(): + reshaped = ts.values.reshape(n_clusters, timesteps_per_cluster) + new_ts = xr.DataArray( + reshaped, + dims=['cluster', 'time'], + coords={'cluster': range(n_clusters), 'time': range(timesteps_per_cluster)} + ) + clustered_time_series[key] = new_ts + ... + +def expand_solution(self): + """Expand clustered solution back to original timeline.""" + expanded = {} + for var_name, var_data in self.solution.items(): + if 'cluster' in var_data.dims: + # Expand using cluster_order to map back to original periods + expanded[var_name] = self._expand_clustered_data(var_data) + else: + expanded[var_name] = var_data + return xr.Dataset(expanded) +``` + +### Phase 5: Plotting Integration + +**Goal:** Minimal changes - leverage existing infrastructure. + +**Tasks:** +1. Ensure `facet_col='cluster'` works with existing plot methods +2. Add auto cluster separators in combined time series views +3. Test heatmaps with `(cluster, time)` data + +**Files:** +- `flixopt/statistics_accessor.py` - Minor update to base plot method + +**Implementation:** +```python +# In _create_base_plot or similar: +def _add_cluster_separators(self, fig): + """Add subtle separators between clusters in combined view.""" + if self._fs.is_clustered: + for cluster_idx in range(1, self._fs.clustering.n_clusters): + x_pos = cluster_idx * self._fs.clustering.timesteps_per_cluster + fig.add_vline(x=x_pos, line_dash='dot', opacity=0.3) +``` + +### Phase Summary + +| Phase | Goal | Complexity | StatusModel Fix? | +|-------|------|------------|------------------| +| 1 | Core dimension refactoring | High | N/A (prep work) | +| 2 | Variable/constraint creation | Medium | βœ… Automatic | +| 3 | StorageModel simplification | Medium | N/A | +| 4 | transform_accessor updates | Medium | N/A | +| 5 | Plotting integration | Low | N/A | + +**Key Insight:** With true `(cluster, time)` dimensions, StatusModel and other temporal constraints **just work** without any special handling. The dimension structure naturally prevents constraints from spanning cluster boundaries. + +--- + +## Part 7: Testing Strategy + +### 7.1 Unit Tests + +```python +# Test cluster helpers +def test_cluster_labels_uniform(): + """Verify cluster_labels for uniform cluster lengths.""" + +def test_cluster_slices_variable(): + """Verify cluster_slices for variable cluster lengths.""" + +def test_boundaries_vary_by_period(): + """Verify boundary dispatch for different periods.""" +``` + +### 7.2 Integration Tests + +```python +# Test storage with different cluster modes +def test_storage_intercluster_with_helpers(): + """Verify intercluster storage using new helpers.""" + +def test_storage_variable_boundaries(): + """Verify storage with period-varying boundaries.""" +``` + +### 7.3 Plotting Tests + +```python +# Test new plot methods +def test_storage_by_cluster_facets(): + """Verify faceted cluster view.""" + +def test_cluster_heatmap(): + """Verify cluster heatmap rendering.""" +``` + +--- + +## Part 8: Decisions (Resolved) + +| Question | Decision | +|----------|----------| +| **Naming** | Use `cluster` as the dimension/coordinate name | +| **Indexer return type** | Always return proper multi-dimensional xarray DataArrays | +| **Segmentation** | tsam uniform segments only (sufficient for current needs) | +| **Backwards compatibility** | Not a concern - this is not released yet | + +--- + +## Appendix A: File Reference + +| File | Purpose | +|------|---------| +| `flixopt/clustering/base.py` | ClusterStructure, Clustering classes | +| `flixopt/clustering/intercluster_helpers.py` | SOC boundary utilities | +| `flixopt/flow_system.py` | FlowSystem with is_clustered property | +| `flixopt/transform_accessor.py` | cluster() method, solution expansion | +| `flixopt/components.py` | StorageModel, InterclusterStorageModel | +| `flixopt/features.py` | StatusModel, ShareAllocationModel | +| `flixopt/statistics_accessor.py` | Plotting methods | +| `flixopt/plotting.py` | Plot utilities | + +## Appendix B: Code Examples + +### B.1 Working with True (cluster, time) Dimensions + +```python +# ═══════════════════════════════════════════════════════════════ +# DIMENSION STRUCTURE +# ═══════════════════════════════════════════════════════════════ +# Non-clustered: +flow_rate.dims # ('time', 'period', 'scenario') +flow_rate.shape # (8760, ...) + +# Clustered: +flow_rate.dims # ('cluster', 'time', 'period', 'scenario') +flow_rate.shape # (9, 24, ...) # 9 clusters Γ— 24 timesteps + +# ═══════════════════════════════════════════════════════════════ +# NATURAL CLUSTER BOUNDARY OPERATIONS +# ═══════════════════════════════════════════════════════════════ +# First/last timestep of each cluster - just use isel! +flow_at_start = flow_rate.isel(time=0) # Shape: (cluster, period, scenario) +flow_at_end = flow_rate.isel(time=-1) # Shape: (cluster, period, scenario) + +# Delta per cluster - trivial! +delta_per_cluster = flow_rate.isel(time=-1) - flow_rate.isel(time=0) + +# ═══════════════════════════════════════════════════════════════ +# TEMPORAL CONSTRAINTS - JUST WORK! +# ═══════════════════════════════════════════════════════════════ +# Storage balance - naturally stays within clusters +balance = charge_state[:, 1:] - charge_state[:, :-1] # No masking needed! + +# Status transitions - naturally per cluster +activate = status[:, 1:] - status[:, :-1] # No boundary issues! + +# ═══════════════════════════════════════════════════════════════ +# PER-CLUSTER AGGREGATION - use xarray directly +# ═══════════════════════════════════════════════════════════════ +mean_per_cluster = flow_rate.mean(dim='time') # Shape: (cluster, ...) +max_per_cluster = flow_rate.max(dim='time') +total_per_cluster = (flow_rate * timestep_duration).sum(dim='time') + +# ═══════════════════════════════════════════════════════════════ +# SELECT SPECIFIC WITHIN-CLUSTER TIMESTEP +# ═══════════════════════════════════════════════════════════════ +# Peak hour (hour 18) from each cluster +peak_values = flow_rate.isel(time=18) # Shape: (cluster, ...) + +# Multiple timesteps +morning_values = flow_rate.isel(time=slice(6, 12)) # Hours 6-11 from each cluster +``` + +### B.2 Storage Constraints with True Dimensions + +```python +# ═══════════════════════════════════════════════════════════════ +# charge_state has one extra timestep per cluster for boundaries +# ═══════════════════════════════════════════════════════════════ +# charge_state.dims = ('cluster', 'time_cs', 'period', 'scenario') +# charge_state.shape = (9, 25, ...) # 24 timesteps + 1 boundary + +# Charge balance - vectorized, no loops! +lhs = ( + charge_state[:, 1:] - # SOC at end of timestep + charge_state[:, :-1] * (1 - loss_rate) - # SOC at start, with loss + charge * eta_charge + # Charging adds energy + discharge / eta_discharge # Discharging removes energy +) +model.add_constraints(lhs == 0, name='charge_balance') + +# Delta SOC per cluster (for inter-cluster linking) +delta_soc = charge_state[:, -1] - charge_state[:, 0] # Shape: (cluster, ...) + +# Cluster start constraint (relative SOC starts at 0 within each cluster) +model.add_constraints(charge_state[:, 0] == 0, name='cluster_start') + +# Cyclic constraint (optional) +model.add_constraints( + charge_state[:, 0] == charge_state[:, -1], + name='cyclic' +) +``` + +### B.3 Cluster Plotting (Uses Existing API!) + +```python +# ═══════════════════════════════════════════════════════════════ +# FACET BY CLUSTER - uses existing facet_col parameter +# ═══════════════════════════════════════════════════════════════ +fs.statistics.plot.storage('Battery', facet_col='cluster') +fs.statistics.plot.balance('Heat', facet_col='cluster') +fs.statistics.plot.flows(..., facet_col='cluster') + +# ═══════════════════════════════════════════════════════════════ +# REGULAR PLOTS - auto-add cluster separators when clustered +# ═══════════════════════════════════════════════════════════════ +fs.statistics.plot.storage('Battery') # separators added automatically + +# ═══════════════════════════════════════════════════════════════ +# COMBINE WITH OTHER FACETS +# ═══════════════════════════════════════════════════════════════ +fs.statistics.plot.balance('Heat', facet_col='cluster', facet_row='scenario') +``` + +### B.4 Check Clustering Status and Access Properties + +```python +if flow_system.is_clustered: + clustering = flow_system.clustering + print(f"Clustered: {clustering.n_clusters} clusters Γ— {clustering.timesteps_per_cluster} timesteps") + + # Dimension information + print(f"Data shape: (cluster={clustering.n_clusters}, time={clustering.timesteps_per_cluster})") + + # Cluster weights (how many original periods each cluster represents) + print(f"Cluster weights: {flow_system.cluster_weight.values}") + + # Aggregation weight (cluster_weight Γ— timestep_duration) + print(f"Aggregation weight shape: {flow_system.aggregation_weight.shape}") +else: + print("Not clustered - full time resolution") +``` diff --git a/docs/notebooks/02-heat-system.ipynb b/docs/notebooks/02-heat-system.ipynb index 3ff933ec3..d3514de15 100644 --- a/docs/notebooks/02-heat-system.ipynb +++ b/docs/notebooks/02-heat-system.ipynb @@ -32,9 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", "import pandas as pd\n", - "import plotly.express as px\n", "import xarray as xr\n", "\n", "import flixopt as fx\n", @@ -59,33 +57,12 @@ "metadata": {}, "outputs": [], "source": [ - "# One week, hourly resolution\n", - "timesteps = pd.date_range('2024-01-15', periods=168, freq='h')\n", + "from data.tutorial_data import get_heat_system_data\n", "\n", - "# Create realistic office heat demand pattern\n", - "hours = np.arange(168)\n", - "hour_of_day = hours % 24\n", - "day_of_week = (hours // 24) % 7\n", - "\n", - "# Base demand pattern (kW)\n", - "base_demand = np.where(\n", - " (hour_of_day >= 7) & (hour_of_day <= 18), # Office hours\n", - " 80, # Daytime\n", - " 30, # Night setback\n", - ")\n", - "\n", - "# Reduce on weekends (days 5, 6)\n", - "weekend_factor = np.where(day_of_week >= 5, 0.5, 1.0)\n", - "heat_demand = base_demand * weekend_factor\n", - "\n", - "# Add some random variation\n", - "np.random.seed(42)\n", - "heat_demand = heat_demand + np.random.normal(0, 5, len(heat_demand))\n", - "heat_demand = np.clip(heat_demand, 20, 100)\n", - "\n", - "print(f'Time range: {timesteps[0]} to {timesteps[-1]}')\n", - "print(f'Peak demand: {heat_demand.max():.1f} kW')\n", - "print(f'Total demand: {heat_demand.sum():.0f} kWh')" + "data = get_heat_system_data()\n", + "timesteps = data['timesteps']\n", + "heat_demand = data['heat_demand']\n", + "gas_price = data['gas_price']" ] }, { @@ -95,15 +72,13 @@ "metadata": {}, "outputs": [], "source": [ - "# Visualize the demand pattern with plotly\n", - "demand_series = xr.DataArray(heat_demand, dims=['time'], coords={'time': timesteps}, name='Heat Demand [kW]')\n", - "fig = px.line(\n", - " x=demand_series.time.values,\n", - " y=demand_series.values,\n", - " title='Office Heat Demand Profile',\n", - " labels={'x': 'Time', 'y': 'kW'},\n", + "# Visualize the demand pattern with fxplot\n", + "demand_ds = xr.Dataset(\n", + " {\n", + " 'Heat Demand': xr.DataArray(heat_demand, dims=['time'], coords={'time': timesteps}),\n", + " }\n", ")\n", - "fig" + "demand_ds.fxplot.line(title='Office Heat Demand Profile')" ] }, { @@ -123,15 +98,13 @@ "metadata": {}, "outputs": [], "source": [ - "# Time-of-use gas prices (€/kWh)\n", - "gas_price = np.where(\n", - " (hour_of_day >= 6) & (hour_of_day <= 22),\n", - " 0.08, # Peak: 6am-10pm\n", - " 0.05, # Off-peak: 10pm-6am\n", + "# Visualize time-of-use gas prices with fxplot\n", + "price_ds = xr.Dataset(\n", + " {\n", + " 'Gas Price': xr.DataArray(gas_price, dims=['time'], coords={'time': timesteps}),\n", + " }\n", ")\n", - "\n", - "fig = px.line(x=timesteps, y=gas_price, title='Gas Price [€/kWh]', labels={'x': 'Time', 'y': '€/kWh'})\n", - "fig" + "price_ds.fxplot.line(title='Gas Price [€/kWh]')" ] }, { @@ -309,12 +282,16 @@ "metadata": {}, "outputs": [], "source": [ - "total_costs = flow_system.solution['costs'].item()\n", "total_heat = heat_demand.sum()\n", "\n", - "print(f'Total operating costs: {total_costs:.2f} €')\n", - "print(f'Total heat delivered: {total_heat:.0f} kWh')\n", - "print(f'Average cost: {total_costs / total_heat * 100:.2f} ct/kWh')" + "pd.DataFrame(\n", + " {\n", + " 'Total operating costs [EUR]': flow_system.solution['costs'].item(),\n", + " 'Total heat delivered [kWh]': total_heat,\n", + " 'Average cost [ct/kWh]': flow_system.solution['costs'].item() / total_heat * 100,\n", + " },\n", + " index=['Value'],\n", + ").T" ] }, { @@ -398,17 +375,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.11" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/docs/notebooks/03-investment-optimization.ipynb b/docs/notebooks/03-investment-optimization.ipynb index 349c84ccf..a4ae769c5 100644 --- a/docs/notebooks/03-investment-optimization.ipynb +++ b/docs/notebooks/03-investment-optimization.ipynb @@ -32,9 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", "import pandas as pd\n", - "import plotly.express as px\n", "import xarray as xr\n", "\n", "import flixopt as fx\n", @@ -84,26 +82,15 @@ "metadata": {}, "outputs": [], "source": [ - "# One week in summer, hourly\n", - "timesteps = pd.date_range('2024-07-15', periods=168, freq='h')\n", - "hours = np.arange(168)\n", - "hour_of_day = hours % 24\n", - "\n", - "# Solar radiation profile (kW/mΒ² equivalent, simplified)\n", - "# Peak around noon, zero at night\n", - "solar_profile = np.maximum(0, np.sin((hour_of_day - 6) * np.pi / 12)) * 0.8\n", - "solar_profile = np.where((hour_of_day >= 6) & (hour_of_day <= 20), solar_profile, 0)\n", - "\n", - "# Add some cloud variation\n", - "np.random.seed(42)\n", - "cloud_factor = np.random.uniform(0.6, 1.0, len(timesteps))\n", - "solar_profile = solar_profile * cloud_factor\n", - "\n", - "# Pool operates 8am-10pm, constant demand when open\n", - "pool_demand = np.where((hour_of_day >= 8) & (hour_of_day <= 22), 150, 50) # kW\n", - "\n", - "print(f'Peak solar: {solar_profile.max():.2f} kW/kW_installed')\n", - "print(f'Pool demand: {pool_demand.max():.0f} kW (open), {pool_demand.min():.0f} kW (closed)')" + "from data.tutorial_data import get_investment_data\n", + "\n", + "data = get_investment_data()\n", + "timesteps = data['timesteps']\n", + "solar_profile = data['solar_profile']\n", + "pool_demand = data['pool_demand']\n", + "GAS_PRICE = data['gas_price']\n", + "SOLAR_COST_WEEKLY = data['solar_cost_per_kw_week']\n", + "TANK_COST_WEEKLY = data['tank_cost_per_kwh_week']" ] }, { @@ -113,63 +100,20 @@ "metadata": {}, "outputs": [], "source": [ - "# Visualize profiles with plotly - using xarray and faceting\n", + "# Visualize profiles with fxplot\n", "profiles = xr.Dataset(\n", " {\n", " 'Solar Profile [kW/kW]': xr.DataArray(solar_profile, dims=['time'], coords={'time': timesteps}),\n", " 'Pool Demand [kW]': xr.DataArray(pool_demand, dims=['time'], coords={'time': timesteps}),\n", " }\n", ")\n", - "\n", - "# Convert to long format for faceting\n", - "df = profiles.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')\n", - "fig = px.line(df, x='time', y='value', facet_col='variable', height=300)\n", - "fig.update_yaxes(matches=None, showticklabels=True)\n", - "fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))\n", - "fig" + "profiles.fxplot.line(title='Solar and Pool Profiles', height=300)" ] }, { "cell_type": "markdown", "id": "7", "metadata": {}, - "source": [ - "## Define Costs\n", - "\n", - "Investment costs are **annualized** (€/year) to compare with operating costs:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8", - "metadata": {}, - "outputs": [], - "source": [ - "# Cost parameters\n", - "GAS_PRICE = 0.12 # €/kWh - high gas price makes solar attractive\n", - "\n", - "# Solar collectors: 400 €/kW installed, 20-year lifetime β†’ ~25 €/kW/year annualized\n", - "# (simplified, real calculation would include interest rate)\n", - "SOLAR_COST_PER_KW = 20 # €/kW/year\n", - "\n", - "# Buffer tank: 50 €/kWh capacity, 30-year lifetime β†’ ~2 €/kWh/year\n", - "TANK_COST_PER_KWH = 1.5 # €/kWh/year\n", - "\n", - "# Scale factor: We model 1 week, but costs are annual\n", - "# So we scale investment costs to weekly equivalent\n", - "WEEKS_PER_YEAR = 52\n", - "SOLAR_COST_WEEKLY = SOLAR_COST_PER_KW / WEEKS_PER_YEAR\n", - "TANK_COST_WEEKLY = TANK_COST_PER_KWH / WEEKS_PER_YEAR\n", - "\n", - "print(f'Solar cost: {SOLAR_COST_WEEKLY:.3f} €/kW/week')\n", - "print(f'Tank cost: {TANK_COST_WEEKLY:.4f} €/kWh/week')" - ] - }, - { - "cell_type": "markdown", - "id": "9", - "metadata": {}, "source": [ "## Build the System with Investment Options\n", "\n", @@ -179,7 +123,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -250,7 +194,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "9", "metadata": {}, "source": [ "## Run Optimization" @@ -259,7 +203,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -268,7 +212,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "11", "metadata": {}, "source": [ "## Analyze Investment Decisions\n", @@ -279,21 +223,26 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "12", "metadata": {}, "outputs": [], "source": [ "solar_size = flow_system.statistics.sizes['SolarCollectors(Heat)'].item()\n", "tank_size = flow_system.statistics.sizes['BufferTank'].item()\n", "\n", - "print(\n", - " f'Optimal sizes: Solar {solar_size:.0f} kW, Tank {tank_size:.0f} kWh (ratio: {tank_size / solar_size:.1f} kWh/kW)'\n", - ")" + "pd.DataFrame(\n", + " {\n", + " 'Solar [kW]': solar_size,\n", + " 'Tank [kWh]': tank_size,\n", + " 'Ratio [kWh/kW]': tank_size / solar_size,\n", + " },\n", + " index=['Optimal Size'],\n", + ").T" ] }, { "cell_type": "markdown", - "id": "15", + "id": "13", "metadata": {}, "source": [ "### Visualize Sizes" @@ -302,7 +251,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -311,7 +260,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "15", "metadata": {}, "source": [ "### Cost Breakdown" @@ -320,7 +269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -331,14 +280,19 @@ "tank_invest = tank_size * TANK_COST_WEEKLY\n", "gas_costs = total_costs - solar_invest - tank_invest\n", "\n", - "print(\n", - " f'Weekly costs: Solar {solar_invest:.1f}€ ({solar_invest / total_costs * 100:.0f}%) + Tank {tank_invest:.1f}€ ({tank_invest / total_costs * 100:.0f}%) + Gas {gas_costs:.1f}€ ({gas_costs / total_costs * 100:.0f}%) = {total_costs:.1f}€'\n", + "pd.DataFrame(\n", + " {\n", + " 'Solar Investment': {'EUR': solar_invest, '%': solar_invest / total_costs * 100},\n", + " 'Tank Investment': {'EUR': tank_invest, '%': tank_invest / total_costs * 100},\n", + " 'Gas Costs': {'EUR': gas_costs, '%': gas_costs / total_costs * 100},\n", + " 'Total': {'EUR': total_costs, '%': 100.0},\n", + " }\n", ")" ] }, { "cell_type": "markdown", - "id": "19", + "id": "17", "metadata": {}, "source": [ "### System Operation" @@ -347,7 +301,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -357,7 +311,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -367,7 +321,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "20", "metadata": {}, "outputs": [], "source": [ @@ -376,7 +330,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "21", "metadata": {}, "source": [ "## Compare: What if No Solar?\n", @@ -387,23 +341,30 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "22", "metadata": {}, "outputs": [], "source": [ - "# Gas-only scenario\n", + "# Gas-only scenario for comparison\n", "total_demand = pool_demand.sum()\n", "gas_only_cost = total_demand / 0.92 * GAS_PRICE # All heat from gas boiler\n", - "\n", "savings = gas_only_cost - total_costs\n", - "print(\n", - " f'Solar saves {savings:.1f}€/week ({savings / gas_only_cost * 100:.0f}%) vs gas-only ({gas_only_cost:.1f}€) β†’ {savings * 52:.0f}€/year'\n", - ")" + "\n", + "pd.DataFrame(\n", + " {\n", + " 'Gas-only [EUR/week]': gas_only_cost,\n", + " 'With Solar [EUR/week]': total_costs,\n", + " 'Savings [EUR/week]': savings,\n", + " 'Savings [%]': savings / gas_only_cost * 100,\n", + " 'Savings [EUR/year]': savings * 52,\n", + " },\n", + " index=['Value'],\n", + ").T" ] }, { "cell_type": "markdown", - "id": "25", + "id": "23", "metadata": {}, "source": [ "### Energy Flow Sankey\n", @@ -414,7 +375,7 @@ { "cell_type": "code", "execution_count": null, - "id": "26", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -423,7 +384,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "25", "metadata": {}, "source": [ "## Key Concepts\n", @@ -463,17 +424,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.11" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/docs/notebooks/04-operational-constraints.ipynb b/docs/notebooks/04-operational-constraints.ipynb index fbb611d1c..e55f2aded 100644 --- a/docs/notebooks/04-operational-constraints.ipynb +++ b/docs/notebooks/04-operational-constraints.ipynb @@ -32,9 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", "import pandas as pd\n", - "import plotly.express as px\n", "import xarray as xr\n", "\n", "import flixopt as fx\n", @@ -73,32 +71,11 @@ "metadata": {}, "outputs": [], "source": [ - "# 3 days, hourly resolution\n", - "timesteps = pd.date_range('2024-03-11', periods=72, freq='h')\n", - "hours = np.arange(72)\n", - "hour_of_day = hours % 24\n", - "\n", - "# Factory operates in shifts:\n", - "# - Day shift (6am-2pm): 400 kW\n", - "# - Evening shift (2pm-10pm): 350 kW\n", - "# - Night (10pm-6am): 80 kW (maintenance heating only)\n", - "\n", - "steam_demand = np.select(\n", - " [\n", - " (hour_of_day >= 6) & (hour_of_day < 14), # Day shift\n", - " (hour_of_day >= 14) & (hour_of_day < 22), # Evening shift\n", - " ],\n", - " [400, 350],\n", - " default=80, # Night\n", - ")\n", - "\n", - "# Add some variation\n", - "np.random.seed(123)\n", - "steam_demand = steam_demand + np.random.normal(0, 20, len(steam_demand))\n", - "steam_demand = np.clip(steam_demand, 50, 450).astype(float)\n", + "from data.tutorial_data import get_constraints_data\n", "\n", - "print(f'Peak demand: {steam_demand.max():.0f} kW')\n", - "print(f'Min demand: {steam_demand.min():.0f} kW')" + "data = get_constraints_data()\n", + "timesteps = data['timesteps']\n", + "steam_demand = data['steam_demand']" ] }, { @@ -108,7 +85,13 @@ "metadata": {}, "outputs": [], "source": [ - "px.line(x=timesteps, y=steam_demand, title='Factory Steam Demand', labels={'x': 'Time', 'y': 'kW'})" + "# Visualize the demand with fxplot\n", + "demand_ds = xr.Dataset(\n", + " {\n", + " 'Steam Demand [kW]': xr.DataArray(steam_demand, dims=['time'], coords={'time': timesteps}),\n", + " }\n", + ")\n", + "demand_ds.fxplot.line(title='Factory Steam Demand')" ] }, { @@ -126,7 +109,7 @@ "metadata": {}, "outputs": [], "source": [ - "flow_system = fx.FlowSystem(timesteps)\n", + "flow_system = fx.FlowSystem(timesteps, name='Constrained')\n", "\n", "# Define and register custom carriers\n", "flow_system.add_carriers(\n", @@ -267,11 +250,7 @@ " }\n", ")\n", "\n", - "df = status_ds.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')\n", - "fig = px.line(df, x='time', y='value', facet_col='variable', height=300, title='Main Boiler Operation')\n", - "fig.update_yaxes(matches=None, showticklabels=True)\n", - "fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))\n", - "fig" + "status_ds.fxplot.line(title='Main Boiler Operation', height=300)" ] }, { @@ -294,8 +273,12 @@ "startup_costs = total_startups * 50\n", "gas_costs = total_costs - startup_costs\n", "\n", - "print(\n", - " f'{total_startups} startups Γ— 50€ = {startup_costs:.0f}€ startup + {gas_costs:.0f}€ gas = {total_costs:.0f}€ total'\n", + "pd.DataFrame(\n", + " {\n", + " 'Startups': {'Count': total_startups, 'EUR': startup_costs},\n", + " 'Gas': {'Count': '-', 'EUR': gas_costs},\n", + " 'Total': {'Count': '-', 'EUR': total_costs},\n", + " }\n", ")" ] }, @@ -347,7 +330,7 @@ "outputs": [], "source": [ "# Build unconstrained system\n", - "fs_unconstrained = fx.FlowSystem(timesteps)\n", + "fs_unconstrained = fx.FlowSystem(timesteps, name='Unconstrained')\n", "fs_unconstrained.add_carriers(\n", " fx.Carrier('gas', '#3498db', 'kW'),\n", " fx.Carrier('steam', '#87CEEB', 'kW_th', 'Process steam'),\n", @@ -377,14 +360,43 @@ "fs_unconstrained.optimize(fx.solvers.HighsSolver())\n", "unconstrained_costs = fs_unconstrained.solution['costs'].item()\n", "\n", - "constraint_overhead = (total_costs - unconstrained_costs) / unconstrained_costs * 100\n", - "print(f'Constraints add {constraint_overhead:.1f}% cost: {unconstrained_costs:.0f}€ β†’ {total_costs:.0f}€')" + "pd.DataFrame(\n", + " {\n", + " 'Without Constraints': {'Cost [EUR]': unconstrained_costs},\n", + " 'With Constraints': {'Cost [EUR]': total_costs},\n", + " 'Overhead': {\n", + " 'Cost [EUR]': total_costs - unconstrained_costs,\n", + " '%': (total_costs - unconstrained_costs) / unconstrained_costs * 100,\n", + " },\n", + " }\n", + ")" ] }, { "cell_type": "markdown", "id": "24", "metadata": {}, + "source": [ + "### Side-by-Side Comparison\n", + "\n", + "Use the `Comparison` class to visualize both systems together:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "comp = fx.Comparison([fs_unconstrained, flow_system])\n", + "comp.statistics.plot.effects()" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, "source": [ "### Energy Flow Sankey\n", "\n", @@ -394,7 +406,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -403,7 +415,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "28", "metadata": {}, "source": [ "## Key Concepts\n", @@ -455,7 +467,13 @@ ] } ], - "metadata": {}, + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + } + }, "nbformat": 4, "nbformat_minor": 5 } diff --git a/docs/notebooks/05-multi-carrier-system.ipynb b/docs/notebooks/05-multi-carrier-system.ipynb index a1a9543fa..1feab6e4f 100644 --- a/docs/notebooks/05-multi-carrier-system.ipynb +++ b/docs/notebooks/05-multi-carrier-system.ipynb @@ -32,9 +32,7 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", "import pandas as pd\n", - "import plotly.express as px\n", "import xarray as xr\n", "\n", "import flixopt as fx\n", @@ -85,40 +83,15 @@ "metadata": {}, "outputs": [], "source": [ - "# One week, hourly\n", - "timesteps = pd.date_range('2024-02-05', periods=168, freq='h')\n", - "hours = np.arange(168)\n", - "hour_of_day = hours % 24\n", - "\n", - "# Hospital electricity demand (kW)\n", - "# Base load + daily pattern (higher during day for equipment, lighting)\n", - "elec_base = 150 # 24/7 critical systems\n", - "elec_daily = 100 * np.sin((hour_of_day - 6) * np.pi / 12) # Peak at noon\n", - "elec_daily = np.maximum(0, elec_daily)\n", - "electricity_demand = elec_base + elec_daily\n", - "\n", - "# Hospital heat demand (kW)\n", - "# Higher in morning, drops during day, increases for hot water in evening\n", - "heat_pattern = np.select(\n", - " [\n", - " (hour_of_day >= 5) & (hour_of_day < 9), # Morning warmup\n", - " (hour_of_day >= 9) & (hour_of_day < 17), # Daytime\n", - " (hour_of_day >= 17) & (hour_of_day < 22), # Evening\n", - " ],\n", - " [350, 250, 300],\n", - " default=200, # Night\n", - ")\n", - "heat_demand = heat_pattern.astype(float)\n", - "\n", - "# Add random variation\n", - "np.random.seed(456)\n", - "electricity_demand += np.random.normal(0, 15, len(timesteps))\n", - "heat_demand += np.random.normal(0, 20, len(timesteps))\n", - "electricity_demand = np.clip(electricity_demand, 100, 300)\n", - "heat_demand = np.clip(heat_demand, 150, 400)\n", - "\n", - "print(f'Electricity: {electricity_demand.min():.0f} - {electricity_demand.max():.0f} kW')\n", - "print(f'Heat: {heat_demand.min():.0f} - {heat_demand.max():.0f} kW')" + "from data.tutorial_data import get_multicarrier_data\n", + "\n", + "data = get_multicarrier_data()\n", + "timesteps = data['timesteps']\n", + "electricity_demand = data['electricity_demand']\n", + "heat_demand = data['heat_demand']\n", + "elec_buy_price = data['elec_buy_price']\n", + "elec_sell_price = data['elec_sell_price']\n", + "gas_price = data['gas_price']" ] }, { @@ -128,47 +101,20 @@ "metadata": {}, "outputs": [], "source": [ - "# Electricity prices (€/kWh)\n", - "# Time-of-use: expensive during day, cheaper at night\n", - "elec_buy_price = np.where(\n", - " (hour_of_day >= 7) & (hour_of_day <= 21),\n", - " 0.35, # Peak - high electricity prices make CHP attractive\n", - " 0.20, # Off-peak\n", - ")\n", - "\n", - "# Feed-in tariff (sell price) - allows selling excess CHP electricity\n", - "elec_sell_price = 0.12 # Fixed feed-in rate\n", - "\n", - "# Gas price - relatively low, favoring gas-based generation\n", - "gas_price = 0.05 # €/kWh" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7", - "metadata": {}, - "outputs": [], - "source": [ - "# Visualize demands and prices with plotly - using xarray and faceting\n", + "# Visualize demands and prices with fxplot\n", "profiles = xr.Dataset(\n", " {\n", " 'Electricity Demand [kW]': xr.DataArray(electricity_demand, dims=['time'], coords={'time': timesteps}),\n", " 'Heat Demand [kW]': xr.DataArray(heat_demand, dims=['time'], coords={'time': timesteps}),\n", - " 'Elec. Buy Price [€/kWh]': xr.DataArray(elec_buy_price, dims=['time'], coords={'time': timesteps}),\n", + " 'Elec. Buy Price [EUR/kWh]': xr.DataArray(elec_buy_price, dims=['time'], coords={'time': timesteps}),\n", " }\n", ")\n", - "\n", - "df = profiles.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')\n", - "fig = px.line(df, x='time', y='value', facet_col='variable', height=300)\n", - "fig.update_yaxes(matches=None, showticklabels=True)\n", - "fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))\n", - "fig" + "profiles.fxplot.line(title='Hospital Energy Profiles', height=300)" ] }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "## Build the Multi-Carrier System" @@ -177,11 +123,11 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ - "flow_system = fx.FlowSystem(timesteps)\n", + "flow_system = fx.FlowSystem(timesteps, name='With CHP')\n", "flow_system.add_carriers(\n", " fx.Carrier('gas', '#3498db', 'kW'),\n", " fx.Carrier('electricity', '#f1c40f', 'kW'),\n", @@ -270,7 +216,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "## Run Optimization" @@ -279,7 +225,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -288,7 +234,7 @@ }, { "cell_type": "markdown", - "id": "12", + "id": "11", "metadata": {}, "source": [ "## Analyze Results\n", @@ -299,7 +245,7 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -308,7 +254,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "13", "metadata": {}, "source": [ "### Heat Balance" @@ -317,7 +263,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -326,7 +272,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "15", "metadata": {}, "source": [ "### Gas Balance" @@ -335,7 +281,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -344,7 +290,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "17", "metadata": {}, "source": [ "### CHP Operation Pattern" @@ -353,7 +299,7 @@ { "cell_type": "code", "execution_count": null, - "id": "19", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -362,7 +308,7 @@ }, { "cell_type": "markdown", - "id": "20", + "id": "19", "metadata": {}, "source": [ "### Cost and Emissions Summary" @@ -371,13 +317,10 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "20", "metadata": {}, "outputs": [], "source": [ - "total_costs = flow_system.solution['costs'].item()\n", - "total_co2 = flow_system.solution['CO2'].item()\n", - "\n", "# Energy flows\n", "flow_rates = flow_system.statistics.flow_rates\n", "grid_buy = flow_rates['GridBuy(Electricity)'].sum().item()\n", @@ -389,17 +332,25 @@ "total_elec = electricity_demand.sum()\n", "total_heat = heat_demand.sum()\n", "\n", - "# Display as compact summary\n", - "print(\n", - " f'Electricity: {chp_elec:.0f} kWh CHP ({chp_elec / total_elec * 100:.0f}%) + {grid_buy:.0f} kWh grid, {grid_sell:.0f} kWh sold'\n", - ")\n", - "print(f'Heat: {chp_heat:.0f} kWh CHP ({chp_heat / total_heat * 100:.0f}%) + {boiler_heat:.0f} kWh boiler')\n", - "print(f'Costs: {total_costs:.2f} € | CO2: {total_co2:.0f} kg')" + "pd.DataFrame(\n", + " {\n", + " 'CHP Electricity [kWh]': chp_elec,\n", + " 'CHP Electricity [%]': chp_elec / total_elec * 100,\n", + " 'Grid Buy [kWh]': grid_buy,\n", + " 'Grid Sell [kWh]': grid_sell,\n", + " 'CHP Heat [kWh]': chp_heat,\n", + " 'CHP Heat [%]': chp_heat / total_heat * 100,\n", + " 'Boiler Heat [kWh]': boiler_heat,\n", + " 'Total Costs [EUR]': flow_system.solution['costs'].item(),\n", + " 'Total CO2 [kg]': flow_system.solution['CO2'].item(),\n", + " },\n", + " index=['Value'],\n", + ").T" ] }, { "cell_type": "markdown", - "id": "22", + "id": "21", "metadata": {}, "source": [ "### Compare: What if No CHP?\n", @@ -410,12 +361,12 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "22", "metadata": {}, "outputs": [], "source": [ "# Build system without CHP\n", - "fs_no_chp = fx.FlowSystem(timesteps)\n", + "fs_no_chp = fx.FlowSystem(timesteps, name='No CHP')\n", "fs_no_chp.add_carriers(\n", " fx.Carrier('gas', '#3498db', 'kW'),\n", " fx.Carrier('electricity', '#f1c40f', 'kW'),\n", @@ -454,20 +405,62 @@ "\n", "fs_no_chp.optimize(fx.solvers.HighsSolver())\n", "\n", + "total_costs = flow_system.solution['costs'].item()\n", + "total_co2 = flow_system.solution['CO2'].item()\n", "no_chp_costs = fs_no_chp.solution['costs'].item()\n", "no_chp_co2 = fs_no_chp.solution['CO2'].item()\n", "\n", - "cost_saving = (no_chp_costs - total_costs) / no_chp_costs * 100\n", - "co2_saving = (no_chp_co2 - total_co2) / no_chp_co2 * 100\n", - "print(\n", - " f'CHP saves {cost_saving:.1f}% costs ({no_chp_costs:.0f}β†’{total_costs:.0f} €) and {co2_saving:.1f}% CO2 ({no_chp_co2:.0f}β†’{total_co2:.0f} kg)'\n", + "pd.DataFrame(\n", + " {\n", + " 'Without CHP': {'Cost [EUR]': no_chp_costs, 'CO2 [kg]': no_chp_co2},\n", + " 'With CHP': {'Cost [EUR]': total_costs, 'CO2 [kg]': total_co2},\n", + " 'Savings': {\n", + " 'Cost [EUR]': no_chp_costs - total_costs,\n", + " 'CO2 [kg]': no_chp_co2 - total_co2,\n", + " },\n", + " 'Savings [%]': {\n", + " 'Cost [EUR]': (no_chp_costs - total_costs) / no_chp_costs * 100,\n", + " 'CO2 [kg]': (no_chp_co2 - total_co2) / no_chp_co2 * 100,\n", + " },\n", + " }\n", ")" ] }, { "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "### Side-by-Side Comparison\n", + "\n", + "Use the `Comparison` class to visualize both systems together:" + ] + }, + { + "cell_type": "code", + "execution_count": null, "id": "24", "metadata": {}, + "outputs": [], + "source": [ + "comp = fx.Comparison([fs_no_chp, flow_system])\n", + "comp.statistics.plot.balance('Electricity')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "comp.statistics.plot.balance('Heat')" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, "source": [ "### Energy Flow Sankey\n", "\n", @@ -477,7 +470,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "27", "metadata": {}, "outputs": [], "source": [ @@ -486,7 +479,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "28", "metadata": {}, "source": [ "## Key Concepts\n", diff --git a/docs/notebooks/06a-time-varying-parameters.ipynb b/docs/notebooks/06a-time-varying-parameters.ipynb index 5c833b2ea..5ebca688e 100644 --- a/docs/notebooks/06a-time-varying-parameters.ipynb +++ b/docs/notebooks/06a-time-varying-parameters.ipynb @@ -32,7 +32,6 @@ "outputs": [], "source": [ "import numpy as np\n", - "import pandas as pd\n", "import plotly.express as px\n", "import xarray as xr\n", "\n", @@ -78,20 +77,13 @@ "metadata": {}, "outputs": [], "source": [ - "# One winter week\n", - "timesteps = pd.date_range('2024-01-22', periods=168, freq='h')\n", - "hours = np.arange(168)\n", - "hour_of_day = hours % 24\n", - "\n", - "# Outdoor temperature: daily cycle with cold nights\n", - "temp_base = 2 # Average temp in Β°C\n", - "temp_amplitude = 5 # Daily variation\n", - "outdoor_temp = temp_base + temp_amplitude * np.sin((hour_of_day - 6) * np.pi / 12)\n", - "\n", - "# Add day-to-day variation for realism\n", - "np.random.seed(789)\n", - "daily_offset = np.repeat(np.random.uniform(-3, 3, 7), 24)\n", - "outdoor_temp = outdoor_temp + daily_offset" + "from data.tutorial_data import get_time_varying_data\n", + "\n", + "data = get_time_varying_data()\n", + "timesteps = data['timesteps']\n", + "outdoor_temp = data['outdoor_temp']\n", + "heat_demand = data['heat_demand']\n", + "cop = data['cop']" ] }, { @@ -101,74 +93,41 @@ "metadata": {}, "outputs": [], "source": [ - "# Heat demand: inversely related to outdoor temp (higher demand when colder)\n", - "heat_demand = 200 - 8 * outdoor_temp\n", - "heat_demand = np.clip(heat_demand, 100, 300)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7", - "metadata": {}, - "outputs": [], - "source": [ - "# Visualize input profiles\n", + "# Visualize input profiles with fxplot\n", "profiles = xr.Dataset(\n", " {\n", " 'Outdoor Temp [Β°C]': xr.DataArray(outdoor_temp, dims=['time'], coords={'time': timesteps}),\n", " 'Heat Demand [kW]': xr.DataArray(heat_demand, dims=['time'], coords={'time': timesteps}),\n", " }\n", ")\n", - "\n", - "df = profiles.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')\n", - "fig = px.line(df, x='time', y='value', facet_col='variable', height=300)\n", - "fig.update_yaxes(matches=None, showticklabels=True)\n", - "fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))\n", - "fig" + "profiles.fxplot.line(title='Temperature and Heat Demand Profiles', height=300)" ] }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ - "## Calculate Time-Varying COP\n", + "## Time-Varying COP\n", "\n", - "The COP depends on outdoor temperature. We use a simplified Carnot-based formula:\n", + "The COP is pre-calculated based on outdoor temperature using a simplified Carnot-based formula:\n", "\n", "$$\\text{COP}_{\\text{real}} \\approx 0.45 \\times \\text{COP}_{\\text{Carnot}} = 0.45 \\times \\frac{T_{\\text{supply}}}{T_{\\text{supply}} - T_{\\text{source}}}$$\n", "\n", - "where temperatures are in Kelvin." + "Let's visualize the relationship:" ] }, { "cell_type": "code", "execution_count": null, - "id": "9", - "metadata": {}, - "outputs": [], - "source": [ - "# COP calculation\n", - "T_supply = 45 + 273.15 # Supply temperature 45Β°C in Kelvin\n", - "T_source = outdoor_temp + 273.15 # Outdoor temp in Kelvin\n", - "\n", - "carnot_cop = T_supply / (T_supply - T_source)\n", - "real_cop = 0.45 * carnot_cop\n", - "real_cop = np.clip(real_cop, 2.0, 5.0) # Physical limits" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "10", + "id": "8", "metadata": {}, "outputs": [], "source": [ "# Visualize COP vs temperature relationship\n", "px.scatter(\n", " x=outdoor_temp,\n", - " y=real_cop,\n", + " y=cop,\n", " title='Heat Pump COP vs Outdoor Temperature',\n", " labels={'x': 'Outdoor Temperature [Β°C]', 'y': 'COP'},\n", " opacity=0.5,\n", @@ -177,7 +136,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "9", "metadata": {}, "source": [ "## Build the Model\n", @@ -192,7 +151,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -214,7 +173,7 @@ " 'HeatPump',\n", " inputs=[fx.Flow('Elec', bus='Electricity', size=150)],\n", " outputs=[fx.Flow('Heat', bus='Heat', size=500)],\n", - " conversion_factors=[{'Elec': real_cop, 'Heat': 1}], # <-- Array for time-varying COP\n", + " conversion_factors=[{'Elec': cop, 'Heat': 1}], # <-- Array for time-varying COP\n", " ),\n", " # Heat demand\n", " fx.Sink('Building', inputs=[fx.Flow('Heat', bus='Heat', size=1, fixed_relative_profile=heat_demand)]),\n", @@ -225,7 +184,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "11", "metadata": {}, "source": [ "## Analyze Results" @@ -234,7 +193,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "12", "metadata": {}, "outputs": [], "source": [ @@ -244,7 +203,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -254,7 +213,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -283,7 +242,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "15", "metadata": {}, "source": [ "## Key Concepts\n", @@ -323,7 +282,7 @@ }, { "cell_type": "markdown", - "id": "18", + "id": "16", "metadata": {}, "source": [ "## Summary\n", @@ -349,17 +308,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.10.0" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/docs/notebooks/07-scenarios-and-periods.ipynb b/docs/notebooks/07-scenarios-and-periods.ipynb index db74afefb..0f3cbaef0 100644 --- a/docs/notebooks/07-scenarios-and-periods.ipynb +++ b/docs/notebooks/07-scenarios-and-periods.ipynb @@ -32,9 +32,8 @@ "metadata": {}, "outputs": [], "source": [ - "import numpy as np\n", "import pandas as pd\n", - "import plotly.express as px\n", + "import xarray as xr\n", "\n", "import flixopt as fx\n", "\n", @@ -72,20 +71,16 @@ "metadata": {}, "outputs": [], "source": [ - "# Time horizon: one representative winter week\n", - "timesteps = pd.date_range('2024-01-15', periods=168, freq='h') # 7 days\n", - "\n", - "# Planning periods (years)\n", - "periods = pd.Index([2024, 2025, 2026], name='period')\n", - "\n", - "# Scenarios with probabilities\n", - "scenarios = pd.Index(['Mild Winter', 'Harsh Winter'], name='scenario')\n", - "scenario_weights = np.array([0.6, 0.4]) # 60% mild, 40% harsh\n", - "\n", - "print(f'Time dimension: {len(timesteps)} hours')\n", - "print(f'Periods: {list(periods)}')\n", - "print(f'Scenarios: {list(scenarios)}')\n", - "print(f'Scenario weights: {dict(zip(scenarios, scenario_weights, strict=False))}')" + "from data.tutorial_data import get_scenarios_data\n", + "\n", + "data = get_scenarios_data()\n", + "timesteps = data['timesteps']\n", + "periods = data['periods']\n", + "scenarios = data['scenarios']\n", + "scenario_weights = data['scenario_weights']\n", + "heat_demand = data['heat_demand']\n", + "gas_prices = data['gas_prices']\n", + "elec_prices = data['elec_prices']" ] }, { @@ -93,7 +88,7 @@ "id": "6", "metadata": {}, "source": [ - "## Create Scenario-Dependent Demand Profiles\n", + "## Scenario-Dependent Demand Profiles\n", "\n", "Heat demand differs significantly between mild and harsh winters:" ] @@ -105,97 +100,23 @@ "metadata": {}, "outputs": [], "source": [ - "hours = np.arange(168)\n", - "hour_of_day = hours % 24\n", - "\n", - "# Base daily pattern (kW): higher in morning/evening\n", - "daily_pattern = np.select(\n", - " [\n", - " (hour_of_day >= 6) & (hour_of_day < 9), # Morning peak\n", - " (hour_of_day >= 9) & (hour_of_day < 17), # Daytime\n", - " (hour_of_day >= 17) & (hour_of_day < 22), # Evening peak\n", - " ],\n", - " [180, 120, 160],\n", - " default=100, # Night\n", - ").astype(float)\n", - "\n", - "# Add random variation\n", - "np.random.seed(42)\n", - "noise = np.random.normal(0, 10, len(timesteps))\n", - "\n", - "# Mild winter: lower demand\n", - "mild_demand = daily_pattern * 0.8 + noise\n", - "mild_demand = np.clip(mild_demand, 60, 200)\n", - "\n", - "# Harsh winter: higher demand\n", - "harsh_demand = daily_pattern * 1.3 + noise * 1.5\n", - "harsh_demand = np.clip(harsh_demand, 100, 280)\n", - "\n", - "# Create DataFrame with scenario columns (flixopt uses column names to match scenarios)\n", - "heat_demand = pd.DataFrame(\n", + "# Visualize demand scenarios with fxplot\n", + "demand_ds = xr.Dataset(\n", " {\n", - " 'Mild Winter': mild_demand,\n", - " 'Harsh Winter': harsh_demand,\n", - " },\n", - " index=timesteps,\n", - ")\n", - "\n", - "print(f'Mild winter demand: {mild_demand.min():.0f} - {mild_demand.max():.0f} kW')\n", - "print(f'Harsh winter demand: {harsh_demand.min():.0f} - {harsh_demand.max():.0f} kW')" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "8", - "metadata": {}, - "outputs": [], - "source": [ - "# Visualize demand scenarios with plotly\n", - "fig = px.line(\n", - " heat_demand.iloc[:48],\n", - " title='Heat Demand by Scenario (First 2 Days)',\n", - " labels={'index': 'Time', 'value': 'kW', 'variable': 'Scenario'},\n", + " scenario: xr.DataArray(\n", + " heat_demand[scenario].values,\n", + " dims=['time'],\n", + " coords={'time': timesteps},\n", + " )\n", + " for scenario in scenarios\n", + " }\n", ")\n", - "fig.update_traces(mode='lines')\n", - "fig" + "demand_ds.fxplot.line(title='Heat Demand by Scenario')" ] }, { "cell_type": "markdown", - "id": "9", - "metadata": {}, - "source": [ - "## Create Period-Dependent Prices\n", - "\n", - "Energy prices change across planning years:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "10", - "metadata": {}, - "outputs": [], - "source": [ - "# Gas prices by period (€/kWh) - expected to rise\n", - "gas_prices = np.array([0.06, 0.08, 0.10]) # 2024, 2025, 2026\n", - "\n", - "# Electricity sell prices by period (€/kWh) - CHP revenue\n", - "elec_prices = np.array([0.28, 0.34, 0.43]) # Rising with gas\n", - "\n", - "print('Gas prices by period:')\n", - "for period, price in zip(periods, gas_prices, strict=False):\n", - " print(f' {period}: {price:.2f} €/kWh')\n", - "\n", - "print('\\nElectricity sell prices by period:')\n", - "for period, price in zip(periods, elec_prices, strict=False):\n", - " print(f' {period}: {price:.2f} €/kWh')" - ] - }, - { - "cell_type": "markdown", - "id": "11", + "id": "8", "metadata": {}, "source": [ "## Build the Flow System\n", @@ -206,7 +127,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "9", "metadata": {}, "outputs": [], "source": [ @@ -215,6 +136,7 @@ " periods=periods,\n", " scenarios=scenarios,\n", " scenario_weights=scenario_weights,\n", + " name='Both Scenarios',\n", ")\n", "flow_system.add_carriers(\n", " fx.Carrier('gas', '#3498db', 'kW'),\n", @@ -222,12 +144,12 @@ " fx.Carrier('heat', '#e74c3c', 'kW'),\n", ")\n", "\n", - "print(flow_system)" + "flow_system" ] }, { "cell_type": "markdown", - "id": "13", + "id": "10", "metadata": {}, "source": [ "## Add Components" @@ -236,7 +158,7 @@ { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "11", "metadata": {}, "outputs": [], "source": [ @@ -313,7 +235,7 @@ }, { "cell_type": "markdown", - "id": "15", + "id": "12", "metadata": {}, "source": [ "## Run Optimization" @@ -322,7 +244,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -331,7 +253,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "14", "metadata": {}, "source": [ "## Analyze Results\n", @@ -342,20 +264,25 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "15", "metadata": {}, "outputs": [], "source": [ "chp_size = flow_system.statistics.sizes['CHP(P_el)']\n", - "total_cost = flow_system.solution['costs']\n", "\n", - "print(f'Optimal CHP: {float(chp_size.max()):.0f} kW electrical ({float(chp_size.max()) * 0.50 / 0.35:.0f} kW thermal)')\n", - "print(f'Expected cost: {float(total_cost.sum()):.0f} €')" + "pd.DataFrame(\n", + " {\n", + " 'CHP Electrical [kW]': float(chp_size.max()),\n", + " 'CHP Thermal [kW]': float(chp_size.max()) * 0.50 / 0.35,\n", + " 'Expected Cost [EUR]': float(flow_system.solution['costs'].sum()),\n", + " },\n", + " index=['Optimal'],\n", + ").T" ] }, { "cell_type": "markdown", - "id": "19", + "id": "16", "metadata": {}, "source": [ "### Heat Balance by Scenario\n", @@ -366,7 +293,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "17", "metadata": {}, "outputs": [], "source": [ @@ -375,7 +302,7 @@ }, { "cell_type": "markdown", - "id": "21", + "id": "18", "metadata": {}, "source": [ "### CHP Operation Patterns" @@ -384,7 +311,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -393,7 +320,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "20", "metadata": {}, "source": [ "### Multi-Dimensional Data Access\n", @@ -404,13 +331,11 @@ { "cell_type": "code", "execution_count": null, - "id": "24", + "id": "21", "metadata": {}, "outputs": [], "source": [ - "# View dimensions\n", "flow_rates = flow_system.statistics.flow_rates\n", - "print('Flow rates dimensions:', dict(flow_rates.sizes))\n", "\n", "# Plot flow rates\n", "flow_system.statistics.plot.flows()" @@ -419,22 +344,27 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "22", "metadata": {}, "outputs": [], "source": [ "# CHP operation summary by scenario\n", "chp_heat = flow_rates['CHP(Q_th)']\n", "\n", - "for scenario in scenarios:\n", - " scenario_avg = float(chp_heat.sel(scenario=scenario).mean())\n", - " scenario_max = float(chp_heat.sel(scenario=scenario).max())\n", - " print(f'{scenario}: avg {scenario_avg:.0f} kW, max {scenario_max:.0f} kW')" + "pd.DataFrame(\n", + " {\n", + " scenario: {\n", + " 'Avg [kW]': float(chp_heat.sel(scenario=scenario).mean()),\n", + " 'Max [kW]': float(chp_heat.sel(scenario=scenario).max()),\n", + " }\n", + " for scenario in scenarios\n", + " }\n", + ")" ] }, { "cell_type": "markdown", - "id": "26", + "id": "23", "metadata": {}, "source": [ "## Sensitivity: What if Only Mild Winter?\n", @@ -445,7 +375,7 @@ { "cell_type": "code", "execution_count": null, - "id": "27", + "id": "24", "metadata": {}, "outputs": [], "source": [ @@ -456,14 +386,18 @@ "chp_size_mild = float(fs_mild.statistics.sizes['CHP(P_el)'].max())\n", "chp_size_both = float(chp_size.max())\n", "\n", - "print(\n", - " f'CHP sizing: {chp_size_mild:.0f} kW (mild only) vs {chp_size_both:.0f} kW (both scenarios) β†’ +{chp_size_both - chp_size_mild:.0f} kW for uncertainty'\n", + "pd.DataFrame(\n", + " {\n", + " 'Mild Only': {'CHP Size [kW]': chp_size_mild},\n", + " 'Both Scenarios': {'CHP Size [kW]': chp_size_both},\n", + " 'Uncertainty Buffer': {'CHP Size [kW]': chp_size_both - chp_size_mild},\n", + " }\n", ")" ] }, { "cell_type": "markdown", - "id": "28", + "id": "25", "metadata": {}, "source": [ "### Energy Flow Sankey\n", @@ -474,7 +408,7 @@ { "cell_type": "code", "execution_count": null, - "id": "29", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -483,7 +417,7 @@ }, { "cell_type": "markdown", - "id": "30", + "id": "27", "metadata": {}, "source": [ "## Key Concepts\n", diff --git a/docs/notebooks/08a-aggregation.ipynb b/docs/notebooks/08a-aggregation.ipynb index d7b7576bb..e26c19223 100644 --- a/docs/notebooks/08a-aggregation.ipynb +++ b/docs/notebooks/08a-aggregation.ipynb @@ -12,7 +12,6 @@ "This notebook introduces:\n", "\n", "- **Resampling**: Reduce time resolution (e.g., hourly β†’ 4-hourly)\n", - "- **Clustering**: Identify typical periods (e.g., 8 representative days)\n", "- **Two-stage optimization**: Size with reduced data, dispatch at full resolution\n", "- **Speed vs. accuracy trade-offs**: When to use each technique" ] @@ -35,8 +34,8 @@ "import timeit\n", "\n", "import pandas as pd\n", - "import plotly.express as px\n", - "import xarray as xr\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", "\n", "import flixopt as fx\n", "\n", @@ -48,9 +47,9 @@ "id": "3", "metadata": {}, "source": [ - "## Load Time Series Data\n", + "## Create the FlowSystem\n", "\n", - "We use real-world district heating data at 15-minute resolution (one month):" + "We use a district heating system with real-world time series data (one month at 15-min resolution):" ] }, { @@ -60,22 +59,14 @@ "metadata": {}, "outputs": [], "source": [ - "# Load time series data (15-min resolution)\n", - "data = pd.read_csv('data/Zeitreihen2020.csv', index_col=0, parse_dates=True).sort_index()\n", - "data = data['2020-01-01':'2020-01-31 23:45:00'] # One month\n", - "data.index.name = 'time' # Rename index for consistency\n", - "\n", - "timesteps = data.index\n", - "\n", - "# Extract profiles\n", - "electricity_demand = data['P_Netz/MW'].to_numpy()\n", - "heat_demand = data['Q_Netz/MW'].to_numpy()\n", - "electricity_price = data['Strompr.€/MWh'].to_numpy()\n", - "gas_price = data['Gaspr.€/MWh'].to_numpy()\n", - "\n", - "print(f'Timesteps: {len(timesteps)} ({len(timesteps) / 96:.0f} days at 15-min resolution)')\n", - "print(f'Heat demand: {heat_demand.min():.1f} - {heat_demand.max():.1f} MW')\n", - "print(f'Electricity price: {electricity_price.min():.1f} - {electricity_price.max():.1f} €/MWh')" + "from data.generate_example_systems import create_district_heating_system\n", + "\n", + "flow_system = create_district_heating_system()\n", + "flow_system.connect_and_transform() # Align all data as xarray\n", + "\n", + "timesteps = flow_system.timesteps\n", + "print(f'Loaded FlowSystem: {len(timesteps)} timesteps ({len(timesteps) / 24:.0f} days at hourly resolution)')\n", + "print(f'Components: {list(flow_system.components.keys())}')" ] }, { @@ -85,142 +76,24 @@ "metadata": {}, "outputs": [], "source": [ - "# Visualize first week\n", - "profiles = xr.Dataset(\n", - " {\n", - " 'Heat Demand [MW]': xr.DataArray(heat_demand[:672], dims=['time'], coords={'time': timesteps[:672]}),\n", - " 'Electricity Price [€/MWh]': xr.DataArray(\n", - " electricity_price[:672], dims=['time'], coords={'time': timesteps[:672]}\n", - " ),\n", - " }\n", - ")\n", + "# Visualize first week of data\n", + "heat_demand = flow_system.components['HeatDemand'].inputs[0].fixed_relative_profile\n", + "electricity_price = flow_system.components['GridBuy'].outputs[0].effects_per_flow_hour['costs']\n", "\n", - "df = profiles.to_dataframe().reset_index().melt(id_vars='time', var_name='variable', value_name='value')\n", - "fig = px.line(df, x='time', y='value', facet_col='variable', height=300)\n", - "fig.update_yaxes(matches=None, showticklabels=True)\n", - "fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1]))\n", - "fig" - ] - }, - { - "cell_type": "markdown", - "id": "6", - "metadata": {}, - "source": [ - "## Build the Base FlowSystem\n", + "fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1)\n", "\n", - "A typical district heating system with investment decisions:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "7", - "metadata": {}, - "outputs": [], - "source": [ - "def build_system(timesteps, heat_demand, electricity_demand, electricity_price, gas_price):\n", - " \"\"\"Build a district heating system with CHP, boiler, and storage (with investment options).\"\"\"\n", - " fs = fx.FlowSystem(timesteps)\n", - "\n", - " fs.add_elements(\n", - " # Buses\n", - " fx.Bus('Electricity'),\n", - " fx.Bus('Heat'),\n", - " fx.Bus('Gas'),\n", - " fx.Bus('Coal'),\n", - " # Effects\n", - " fx.Effect('costs', '€', 'Total Costs', is_standard=True, is_objective=True),\n", - " fx.Effect('CO2', 'kg', 'CO2 Emissions'),\n", - " # CHP with investment optimization\n", - " fx.linear_converters.CHP(\n", - " 'CHP',\n", - " thermal_efficiency=0.58,\n", - " electrical_efficiency=0.22,\n", - " electrical_flow=fx.Flow('P_el', bus='Electricity', size=200),\n", - " thermal_flow=fx.Flow(\n", - " 'Q_th',\n", - " bus='Heat',\n", - " size=fx.InvestParameters(\n", - " minimum_size=100,\n", - " maximum_size=300,\n", - " effects_of_investment_per_size={'costs': 10},\n", - " ),\n", - " ),\n", - " fuel_flow=fx.Flow('Q_fu', bus='Coal'),\n", - " ),\n", - " # Gas Boiler with investment optimization\n", - " fx.linear_converters.Boiler(\n", - " 'Boiler',\n", - " thermal_efficiency=0.85,\n", - " thermal_flow=fx.Flow(\n", - " 'Q_th',\n", - " bus='Heat',\n", - " size=fx.InvestParameters(\n", - " minimum_size=0,\n", - " maximum_size=150,\n", - " effects_of_investment_per_size={'costs': 5},\n", - " ),\n", - " ),\n", - " fuel_flow=fx.Flow('Q_fu', bus='Gas'),\n", - " ),\n", - " # Thermal Storage with investment optimization\n", - " fx.Storage(\n", - " 'Storage',\n", - " capacity_in_flow_hours=fx.InvestParameters(\n", - " minimum_size=0,\n", - " maximum_size=1000,\n", - " effects_of_investment_per_size={'costs': 0.5},\n", - " ),\n", - " initial_charge_state=0,\n", - " eta_charge=1,\n", - " eta_discharge=1,\n", - " relative_loss_per_hour=0.001,\n", - " charging=fx.Flow('Charge', size=137, bus='Heat'),\n", - " discharging=fx.Flow('Discharge', size=158, bus='Heat'),\n", - " ),\n", - " # Fuel sources\n", - " fx.Source(\n", - " 'GasGrid',\n", - " outputs=[fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': gas_price, 'CO2': 0.3})],\n", - " ),\n", - " fx.Source(\n", - " 'CoalSupply',\n", - " outputs=[fx.Flow('Q_Coal', bus='Coal', size=1000, effects_per_flow_hour={'costs': 4.6, 'CO2': 0.3})],\n", - " ),\n", - " # Electricity grid connection\n", - " fx.Source(\n", - " 'GridBuy',\n", - " outputs=[\n", - " fx.Flow(\n", - " 'P_el',\n", - " bus='Electricity',\n", - " size=1000,\n", - " effects_per_flow_hour={'costs': electricity_price + 0.5, 'CO2': 0.3},\n", - " )\n", - " ],\n", - " ),\n", - " fx.Sink(\n", - " 'GridSell',\n", - " inputs=[fx.Flow('P_el', bus='Electricity', size=1000, effects_per_flow_hour=-(electricity_price - 0.5))],\n", - " ),\n", - " # Demands\n", - " fx.Sink('HeatDemand', inputs=[fx.Flow('Q_th', bus='Heat', size=1, fixed_relative_profile=heat_demand)]),\n", - " fx.Sink(\n", - " 'ElecDemand', inputs=[fx.Flow('P_el', bus='Electricity', size=1, fixed_relative_profile=electricity_demand)]\n", - " ),\n", - " )\n", - "\n", - " return fs\n", - "\n", - "\n", - "flow_system = build_system(timesteps, heat_demand, electricity_demand, electricity_price, gas_price)\n", - "print(f'System: {len(timesteps)} timesteps')" + "fig.add_trace(go.Scatter(x=timesteps[:168], y=heat_demand.values[:168], name='Heat Demand'), row=1, col=1)\n", + "fig.add_trace(go.Scatter(x=timesteps[:168], y=electricity_price.values[:168], name='Electricity Price'), row=2, col=1)\n", + "\n", + "fig.update_layout(height=400, title='First Week of Data')\n", + "fig.update_yaxes(title_text='Heat Demand [MW]', row=1, col=1)\n", + "fig.update_yaxes(title_text='El. Price [€/MWh]', row=2, col=1)\n", + "fig.show()" ] }, { "cell_type": "markdown", - "id": "8", + "id": "6", "metadata": {}, "source": [ "## Technique 1: Resampling\n", @@ -231,13 +104,13 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "7", "metadata": {}, "outputs": [], "source": [ "solver = fx.solvers.HighsSolver(mip_gap=0.01)\n", "\n", - "# Resample from 15min to 4h resolution\n", + "# Resample from 15-min to 4h resolution\n", "fs_resampled = flow_system.transform.resample('4h')\n", "\n", "reduction = (1 - len(fs_resampled.timesteps) / len(flow_system.timesteps)) * 100\n", @@ -247,7 +120,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -261,7 +134,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "9", "metadata": {}, "source": [ "## Technique 2: Two-Stage Optimization\n", @@ -273,7 +146,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -292,13 +165,14 @@ { "cell_type": "code", "execution_count": null, - "id": "13", + "id": "11", "metadata": {}, "outputs": [], "source": [ "# Stage 2: Dispatch at full resolution with fixed sizes\n", "start = timeit.default_timer()\n", "fs_dispatch = flow_system.transform.fix_sizes(fs_sizing.statistics.sizes)\n", + "fs_dispatch.name = 'Two-Stage'\n", "fs_dispatch.optimize(solver)\n", "time_stage2 = timeit.default_timer() - start\n", "\n", @@ -309,7 +183,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "12", "metadata": {}, "source": [ "## Technique 3: Full Optimization (Baseline)\n", @@ -320,12 +194,13 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "13", "metadata": {}, "outputs": [], "source": [ "start = timeit.default_timer()\n", "fs_full = flow_system.copy()\n", + "fs_full.name = 'Full Optimization'\n", "fs_full.optimize(solver)\n", "time_full = timeit.default_timer() - start\n", "\n", @@ -334,7 +209,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "14", "metadata": {}, "source": [ "## Compare Results" @@ -343,7 +218,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -395,37 +270,29 @@ }, { "cell_type": "markdown", - "id": "18", - "metadata": {}, - "source": [ - "## Visual Comparison: Heat Balance" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "19", + "id": "16", "metadata": {}, - "outputs": [], "source": [ - "# Full optimization heat balance\n", - "fs_full.statistics.plot.balance('Heat')" + "## Visual Comparison: Heat Balance\n", + "\n", + "Compare the full optimization with the two-stage approach side-by-side:" ] }, { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "17", "metadata": {}, "outputs": [], "source": [ - "# Two-stage optimization heat balance\n", - "fs_dispatch.statistics.plot.balance('Heat')" + "# Side-by-side comparison of full optimization vs two-stage\n", + "comp = fx.Comparison([fs_full, fs_dispatch])\n", + "comp.statistics.plot.balance('Heat')" ] }, { "cell_type": "markdown", - "id": "21", + "id": "18", "metadata": {}, "source": [ "### Energy Flow Sankey (Full Optimization)\n", @@ -436,7 +303,7 @@ { "cell_type": "code", "execution_count": null, - "id": "22", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -445,7 +312,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "20", "metadata": {}, "source": [ "## When to Use Each Technique\n", @@ -485,7 +352,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "21", "metadata": {}, "source": [ "## Summary\n", @@ -506,7 +373,8 @@ "\n", "### Next Steps\n", "\n", - "- **[08b-Rolling Horizon](08b-rolling-horizon.ipynb)**: For operational problems without investment decisions, decompose time into sequential segments\n", + "- **[08b-Rolling Horizon](08b-rolling-horizon.ipynb)**: For operational problems, decompose time into sequential segments\n", + "- **[08c-Clustering](08c-clustering.ipynb)**: Use typical periods with the `tsam` package\n", "\n", "### Further Reading\n", "\n", @@ -517,21 +385,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.11" } }, "nbformat": 4, diff --git a/docs/notebooks/08b-rolling-horizon.ipynb b/docs/notebooks/08b-rolling-horizon.ipynb index e9eae6d4f..90da6d2ca 100644 --- a/docs/notebooks/08b-rolling-horizon.ipynb +++ b/docs/notebooks/08b-rolling-horizon.ipynb @@ -51,9 +51,9 @@ "id": "3", "metadata": {}, "source": [ - "## Load Time Series Data\n", + "## Create the FlowSystem\n", "\n", - "We use real-world district heating data at 15-minute resolution (two weeks):" + "We use an operational district heating system with real-world data (two weeks at 15-min resolution):" ] }, { @@ -63,119 +63,19 @@ "metadata": {}, "outputs": [], "source": [ - "# Load time series data (15-min resolution)\n", - "data = pd.read_csv('data/Zeitreihen2020.csv', index_col=0, parse_dates=True).sort_index()\n", - "data = data['2020-01-01':'2020-01-14 23:45:00'] # Two weeks\n", - "data.index.name = 'time' # Rename index for consistency\n", + "from data.generate_example_systems import create_operational_system\n", "\n", - "timesteps = data.index\n", + "flow_system = create_operational_system()\n", + "flow_system.connect_and_transform() # Align all data as xarray\n", "\n", - "# Extract profiles\n", - "electricity_demand = data['P_Netz/MW'].to_numpy()\n", - "heat_demand = data['Q_Netz/MW'].to_numpy()\n", - "electricity_price = data['Strompr.€/MWh'].to_numpy()\n", - "gas_price = data['Gaspr.€/MWh'].to_numpy()\n", - "\n", - "print(\n", - " f'{len(timesteps)} timesteps ({len(timesteps) / 96:.0f} days), heat {heat_demand.min():.0f}-{heat_demand.max():.0f} MW'\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "5", - "metadata": {}, - "outputs": [], - "source": [ - "def build_system(timesteps, heat_demand, electricity_demand, electricity_price, gas_price):\n", - " \"\"\"Build a district heating system with CHP, boiler, and storage.\"\"\"\n", - " fs = fx.FlowSystem(timesteps)\n", - "\n", - " # Effects\n", - "\n", - " # Buses\n", - " fs.add_elements(\n", - " fx.Bus('Electricity'),\n", - " fx.Bus('Heat'),\n", - " fx.Bus('Gas'),\n", - " fx.Bus('Coal'),\n", - " fx.Effect('costs', '€', 'Total Costs', is_standard=True, is_objective=True),\n", - " fx.Effect('CO2', 'kg', 'CO2 Emissions'),\n", - " fx.linear_converters.CHP(\n", - " 'CHP',\n", - " thermal_efficiency=0.58,\n", - " electrical_efficiency=0.22,\n", - " status_parameters=fx.StatusParameters(effects_per_startup=24000),\n", - " electrical_flow=fx.Flow('P_el', bus='Electricity', size=200),\n", - " thermal_flow=fx.Flow('Q_th', bus='Heat', size=200),\n", - " fuel_flow=fx.Flow('Q_fu', bus='Coal', size=288, relative_minimum=87 / 288, previous_flow_rate=100),\n", - " ),\n", - " fx.linear_converters.Boiler(\n", - " 'Boiler',\n", - " thermal_efficiency=0.85,\n", - " thermal_flow=fx.Flow('Q_th', bus='Heat'),\n", - " fuel_flow=fx.Flow(\n", - " 'Q_fu',\n", - " bus='Gas',\n", - " size=95,\n", - " relative_minimum=12 / 95,\n", - " previous_flow_rate=20,\n", - " status_parameters=fx.StatusParameters(effects_per_startup=1000),\n", - " ),\n", - " ),\n", - " fx.Storage(\n", - " 'Storage',\n", - " capacity_in_flow_hours=684,\n", - " initial_charge_state=137,\n", - " minimal_final_charge_state=137,\n", - " maximal_final_charge_state=158,\n", - " eta_charge=1,\n", - " eta_discharge=1,\n", - " relative_loss_per_hour=0.001,\n", - " prevent_simultaneous_charge_and_discharge=True,\n", - " charging=fx.Flow('Charge', size=137, bus='Heat'),\n", - " discharging=fx.Flow('Discharge', size=158, bus='Heat'),\n", - " ),\n", - " fx.Source(\n", - " 'GasGrid',\n", - " outputs=[fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': gas_price, 'CO2': 0.3})],\n", - " ),\n", - " fx.Source(\n", - " 'CoalSupply',\n", - " outputs=[fx.Flow('Q_Coal', bus='Coal', size=1000, effects_per_flow_hour={'costs': 4.6, 'CO2': 0.3})],\n", - " ),\n", - " fx.Source(\n", - " 'GridBuy',\n", - " outputs=[\n", - " fx.Flow(\n", - " 'P_el',\n", - " bus='Electricity',\n", - " size=1000,\n", - " effects_per_flow_hour={'costs': electricity_price + 0.5, 'CO2': 0.3},\n", - " )\n", - " ],\n", - " ),\n", - " fx.Sink(\n", - " 'GridSell',\n", - " inputs=[fx.Flow('P_el', bus='Electricity', size=1000, effects_per_flow_hour=-(electricity_price - 0.5))],\n", - " ),\n", - " fx.Sink('HeatDemand', inputs=[fx.Flow('Q_th', bus='Heat', size=1, fixed_relative_profile=heat_demand)]),\n", - " fx.Sink(\n", - " 'ElecDemand', inputs=[fx.Flow('P_el', bus='Electricity', size=1, fixed_relative_profile=electricity_demand)]\n", - " ),\n", - " )\n", - "\n", - " return fs\n", - "\n", - "\n", - "flow_system = build_system(timesteps, heat_demand, electricity_demand, electricity_price, gas_price)\n", - "print(f'System: {len(timesteps)} timesteps')" + "timesteps = flow_system.timesteps\n", + "print(f'Loaded FlowSystem: {len(timesteps)} timesteps ({len(timesteps) / 24:.0f} days at hourly resolution)')\n", + "print(f'Components: {list(flow_system.components.keys())}')" ] }, { "cell_type": "markdown", - "id": "6", + "id": "5", "metadata": {}, "source": [ "## Full Optimization (Baseline)\n", @@ -186,7 +86,7 @@ { "cell_type": "code", "execution_count": null, - "id": "7", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -194,6 +94,7 @@ "\n", "start = timeit.default_timer()\n", "fs_full = flow_system.copy()\n", + "fs_full.name = 'Full Optimization'\n", "fs_full.optimize(solver)\n", "time_full = timeit.default_timer() - start\n", "\n", @@ -202,7 +103,7 @@ }, { "cell_type": "markdown", - "id": "8", + "id": "7", "metadata": {}, "source": [ "## Rolling Horizon Optimization\n", @@ -227,12 +128,13 @@ { "cell_type": "code", "execution_count": null, - "id": "9", + "id": "8", "metadata": {}, "outputs": [], "source": [ "start = timeit.default_timer()\n", "fs_rolling = flow_system.copy()\n", + "fs_rolling.name = 'Rolling Horizon'\n", "segments = fs_rolling.optimize.rolling_horizon(\n", " solver,\n", " horizon=192, # 2-day segments (192 timesteps at 15-min resolution)\n", @@ -245,7 +147,7 @@ }, { "cell_type": "markdown", - "id": "10", + "id": "9", "metadata": {}, "source": [ "## Compare Results" @@ -254,7 +156,7 @@ { "cell_type": "code", "execution_count": null, - "id": "11", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -276,35 +178,28 @@ }, { "cell_type": "markdown", - "id": "12", - "metadata": {}, - "source": [ - "## Visualize: Heat Balance Comparison" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "13", + "id": "11", "metadata": {}, - "outputs": [], "source": [ - "fs_full.statistics.plot.balance('Heat').figure.update_layout(title='Heat Balance (Full)')" + "markdown## Visualize: Heat Balance Comparison\n", + "\n", + "Use the `Comparison` class to view both methods side-by-side:" ] }, { "cell_type": "code", "execution_count": null, - "id": "14", + "id": "12", "metadata": {}, "outputs": [], "source": [ - "fs_rolling.statistics.plot.balance('Heat').figure.update_layout(title='Heat Balance (Rolling)')" + "comp = fx.Comparison([fs_full, fs_rolling])\n", + "comp.statistics.plot.balance('Heat')" ] }, { "cell_type": "markdown", - "id": "15", + "id": "13", "metadata": {}, "source": [ "## Storage State Continuity\n", @@ -315,7 +210,7 @@ { "cell_type": "code", "execution_count": null, - "id": "16", + "id": "14", "metadata": {}, "outputs": [], "source": [ @@ -339,7 +234,7 @@ }, { "cell_type": "markdown", - "id": "17", + "id": "15", "metadata": {}, "source": [ "## Inspect Individual Segments\n", @@ -350,7 +245,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -363,7 +258,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "17", "metadata": {}, "source": [ "## Visualize Segment Overlaps\n", @@ -374,7 +269,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -391,7 +286,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -403,7 +298,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "20", "metadata": {}, "source": [ "## When to Use Rolling Horizon\n", @@ -424,7 +319,7 @@ }, { "cell_type": "markdown", - "id": "23", + "id": "21", "metadata": {}, "source": [ "## API Reference\n", @@ -448,7 +343,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "22", "metadata": {}, "source": [ "## Summary\n", @@ -472,17 +367,7 @@ ] } ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "name": "python", - "version": "3.11" - } - }, + "metadata": {}, "nbformat": 4, "nbformat_minor": 5 } diff --git a/docs/notebooks/08c-clustering.ipynb b/docs/notebooks/08c-clustering.ipynb new file mode 100644 index 000000000..6d85e60ba --- /dev/null +++ b/docs/notebooks/08c-clustering.ipynb @@ -0,0 +1,608 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Time Series Clustering with `cluster()`\n", + "\n", + "Accelerate investment optimization using typical periods (clustering).\n", + "\n", + "This notebook demonstrates:\n", + "\n", + "- **Typical periods**: Cluster similar time segments (e.g., days) and solve only representative ones\n", + "- **Weighted costs**: Automatically weight operational costs by cluster occurrence\n", + "- **Two-stage workflow**: Fast sizing with clustering, accurate dispatch at full resolution\n", + "\n", + "!!! note \"Requirements\"\n", + " This notebook requires the `tsam` package: `pip install tsam`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import timeit\n", + "\n", + "import pandas as pd\n", + "import xarray as xr\n", + "\n", + "import flixopt as fx\n", + "\n", + "fx.CONFIG.notebook()" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Create the FlowSystem\n", + "\n", + "We use a district heating system with real-world time series data (one month at 15-min resolution):" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from data.generate_example_systems import create_district_heating_system\n", + "\n", + "flow_system = create_district_heating_system()\n", + "flow_system.connect_and_transform()\n", + "\n", + "timesteps = flow_system.timesteps\n", + "\n", + "flow_system" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize input data\n", + "input_ds = xr.Dataset(\n", + " {\n", + " 'Heat Demand': flow_system.components['HeatDemand'].inputs[0].fixed_relative_profile,\n", + " 'Electricity Price': flow_system.components['GridBuy'].outputs[0].effects_per_flow_hour['costs'],\n", + " }\n", + ")\n", + "input_ds.fxplot.line(facet_row='variable', title='One Month of Input Data')" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Method 1: Full Optimization (Baseline)\n", + "\n", + "First, solve the complete problem with all 2976 timesteps:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "solver = fx.solvers.HighsSolver(mip_gap=0.01)\n", + "\n", + "start = timeit.default_timer()\n", + "fs_full = flow_system.copy()\n", + "fs_full.name = 'Full Optimization'\n", + "fs_full.optimize(solver)\n", + "time_full = timeit.default_timer() - start" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "## Method 2: Clustering with `cluster()`\n", + "\n", + "The `cluster()` method:\n", + "\n", + "1. **Clusters similar days** using the TSAM (Time Series Aggregation Module) package\n", + "2. **Reduces timesteps** to only typical periods (e.g., 8 typical days = 768 timesteps)\n", + "3. **Weights costs** by how many original days each typical day represents\n", + "4. **Handles storage** with configurable behavior via `storage_mode`\n", + "\n", + "!!! warning \"Peak Forcing\"\n", + " Always use `time_series_for_high_peaks` to ensure extreme demand days are captured.\n", + " Without this, clustering may miss peak periods, causing undersized components." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "start = timeit.default_timer()\n", + "\n", + "# IMPORTANT: Force inclusion of peak demand periods!\n", + "peak_series = ['HeatDemand(Q_th)|fixed_relative_profile']\n", + "\n", + "# Create reduced FlowSystem with 8 typical days\n", + "fs_clustered = flow_system.transform.cluster(\n", + " n_clusters=8, # 8 typical days\n", + " cluster_duration='1D', # Daily clustering\n", + " time_series_for_high_peaks=peak_series, # Capture peak demand day\n", + ")\n", + "fs_clustered.name = 'Clustered (8 days)'\n", + "\n", + "time_clustering = timeit.default_timer() - start" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "# Optimize the reduced system\n", + "start = timeit.default_timer()\n", + "fs_clustered.optimize(solver)\n", + "time_clustered = timeit.default_timer() - start" + ] + }, + { + "cell_type": "markdown", + "id": "10", + "metadata": {}, + "source": [ + "## Understanding the Clustering\n", + "\n", + "The clustering algorithm groups similar days together. Access all metadata via `fs.clustering`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "# Access clustering metadata directly\n", + "clustering = fs_clustered.clustering\n", + "clustering" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "# Show clustering info using __repr__\n", + "fs_clustered.clustering" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "# Quality metrics - how well do the clusters represent the original data?\n", + "# Lower RMSE/MAE = better representation\n", + "clustering.metrics.to_dataframe().style.format('{:.3f}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "# Visual comparison: original vs clustered time series\n", + "clustering.plot.compare()" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## Advanced Clustering Options\n", + "\n", + "The `cluster()` method exposes many parameters for fine-tuning:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "# Try different clustering algorithms\n", + "fs_kmeans = flow_system.transform.cluster(\n", + " n_clusters=8,\n", + " cluster_duration='1D',\n", + " cluster_method='k_means', # Alternative: 'hierarchical' (default), 'k_medoids', 'averaging'\n", + ")\n", + "\n", + "fs_kmeans.clustering" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare quality metrics between algorithms\n", + "pd.DataFrame(\n", + " {\n", + " 'hierarchical': fs_clustered.clustering.metrics.to_dataframe().iloc[0],\n", + " 'k_means': fs_kmeans.clustering.metrics.to_dataframe().iloc[0],\n", + " }\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize cluster structure with heatmap\n", + "clustering.plot.heatmap()" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "### Manual Cluster Assignment\n", + "\n", + "When comparing design variants or performing sensitivity analysis, you often want to\n", + "use the **same cluster structure** across different FlowSystem configurations.\n", + "Use `predef_cluster_order` to ensure comparable results:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "# Save the cluster order from our optimized system\n", + "cluster_order = fs_clustered.clustering.cluster_order.values\n", + "\n", + "# Now modify the FlowSystem (e.g., increase storage capacity limits)\n", + "flow_system_modified = flow_system.copy()\n", + "flow_system_modified.components['Storage'].capacity_in_flow_hours.maximum_size = 2000 # Larger storage option\n", + "\n", + "# Cluster with the SAME cluster structure for fair comparison\n", + "fs_modified_clustered = flow_system_modified.transform.cluster(\n", + " n_clusters=8,\n", + " cluster_duration='1D',\n", + " predef_cluster_order=cluster_order, # Reuse cluster assignments\n", + ")\n", + "fs_modified_clustered.name = 'Modified (larger storage limit)'\n", + "\n", + "# Optimize the modified system\n", + "fs_modified_clustered.optimize(solver)\n", + "\n", + "# Compare results using Comparison class\n", + "fx.Comparison([fs_clustered, fs_modified_clustered])" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "## Method 3: Two-Stage Workflow (Recommended)\n", + "\n", + "The recommended approach for investment optimization:\n", + "\n", + "1. **Stage 1**: Fast sizing with `cluster()` \n", + "2. **Stage 2**: Fix sizes (with safety margin) and dispatch at full resolution\n", + "\n", + "!!! tip \"Safety Margin\"\n", + " Typical periods aggregate similar days, so individual days may have higher demand \n", + " than the typical day. Adding a 5-10% margin ensures feasibility." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "# Apply safety margin to sizes\n", + "SAFETY_MARGIN = 1.05 # 5% buffer\n", + "sizes_with_margin = {name: float(size.item()) * SAFETY_MARGIN for name, size in fs_clustered.statistics.sizes.items()}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "metadata": {}, + "outputs": [], + "source": [ + "# Stage 2: Fix sizes and optimize at full resolution\n", + "start = timeit.default_timer()\n", + "\n", + "fs_dispatch = flow_system.transform.fix_sizes(sizes_with_margin)\n", + "fs_dispatch.name = 'Two-Stage'\n", + "fs_dispatch.optimize(solver)\n", + "\n", + "time_dispatch = timeit.default_timer() - start\n", + "\n", + "# Total two-stage time\n", + "total_two_stage = time_clustering + time_clustered + time_dispatch" + ] + }, + { + "cell_type": "markdown", + "id": "24", + "metadata": {}, + "source": [ + "## Compare Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "results = {\n", + " 'Full (baseline)': {\n", + " 'Time [s]': time_full,\n", + " 'Cost [€]': fs_full.solution['costs'].item(),\n", + " 'CHP': fs_full.statistics.sizes['CHP(Q_th)'].item(),\n", + " 'Boiler': fs_full.statistics.sizes['Boiler(Q_th)'].item(),\n", + " 'Storage': fs_full.statistics.sizes['Storage'].item(),\n", + " },\n", + " 'Clustered (8 days)': {\n", + " 'Time [s]': time_clustering + time_clustered,\n", + " 'Cost [€]': fs_clustered.solution['costs'].item(),\n", + " 'CHP': fs_clustered.statistics.sizes['CHP(Q_th)'].item(),\n", + " 'Boiler': fs_clustered.statistics.sizes['Boiler(Q_th)'].item(),\n", + " 'Storage': fs_clustered.statistics.sizes['Storage'].item(),\n", + " },\n", + " 'Two-Stage': {\n", + " 'Time [s]': total_two_stage,\n", + " 'Cost [€]': fs_dispatch.solution['costs'].item(),\n", + " 'CHP': sizes_with_margin['CHP(Q_th)'],\n", + " 'Boiler': sizes_with_margin['Boiler(Q_th)'],\n", + " 'Storage': sizes_with_margin['Storage'],\n", + " },\n", + "}\n", + "\n", + "comparison = pd.DataFrame(results).T\n", + "baseline_cost = comparison.loc['Full (baseline)', 'Cost [€]']\n", + "baseline_time = comparison.loc['Full (baseline)', 'Time [s]']\n", + "comparison['Cost Gap [%]'] = ((comparison['Cost [€]'] - baseline_cost) / abs(baseline_cost) * 100).round(2)\n", + "comparison['Speedup'] = (baseline_time / comparison['Time [s]']).round(1)\n", + "\n", + "comparison.style.format(\n", + " {\n", + " 'Time [s]': '{:.1f}',\n", + " 'Cost [€]': '{:,.0f}',\n", + " 'CHP': '{:.1f}',\n", + " 'Boiler': '{:.1f}',\n", + " 'Storage': '{:.0f}',\n", + " 'Cost Gap [%]': '{:.2f}',\n", + " 'Speedup': '{:.1f}x',\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "## Expand Solution to Full Resolution\n", + "\n", + "Use `expand_solution()` to map the clustered solution back to all original timesteps.\n", + "This repeats the typical period values for all days belonging to that cluster:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "# Expand the clustered solution to full resolution\n", + "fs_expanded = fs_clustered.transform.expand_solution()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare heat production: Full vs Expanded\n", + "heat_flows = ['CHP(Q_th)|flow_rate', 'Boiler(Q_th)|flow_rate']\n", + "\n", + "# Create comparison dataset\n", + "comparison_ds = xr.Dataset(\n", + " {\n", + " name.replace('|flow_rate', ''): xr.concat(\n", + " [fs_full.solution[name], fs_expanded.solution[name]], dim=pd.Index(['Full', 'Expanded'], name='method')\n", + " )\n", + " for name in heat_flows\n", + " }\n", + ")\n", + "\n", + "comparison_ds.fxplot.line(facet_col='variable', color='method', title='Heat Production Comparison')" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "## Visualize Clustered Heat Balance" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "fs_clustered.statistics.plot.storage('Storage')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "fs_expanded.statistics.plot.storage('Storage')" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "## API Reference\n", + "\n", + "### `transform.cluster()` Parameters\n", + "\n", + "| Parameter | Type | Default | Description |\n", + "|-----------|------|---------|-------------|\n", + "| `n_clusters` | `int` | - | Number of typical periods (e.g., 8 typical days) |\n", + "| `cluster_duration` | `str \\| float` | - | Duration per cluster ('1D', '24h') or hours |\n", + "| `weights` | `dict[str, float]` | None | Optional weights for time series in clustering |\n", + "| `time_series_for_high_peaks` | `list[str]` | None | **Essential**: Force inclusion of peak periods |\n", + "| `time_series_for_low_peaks` | `list[str]` | None | Force inclusion of minimum periods |\n", + "| `cluster_method` | `str` | 'hierarchical' | Algorithm: 'hierarchical', 'k_means', 'k_medoids', 'k_maxoids', 'averaging' |\n", + "| `representation_method` | `str` | 'medoidRepresentation' | 'medoidRepresentation', 'meanRepresentation', 'distributionAndMinMaxRepresentation' |\n", + "| `extreme_period_method` | `str \\| None` | None | How peaks are integrated: None, 'append', 'new_cluster_center', 'replace_cluster_center' |\n", + "| `rescale_cluster_periods` | `bool` | True | Rescale clusters to match original means |\n", + "| `predef_cluster_order` | `array` | None | Manual cluster assignments |\n", + "| `**tsam_kwargs` | - | - | Additional tsam parameters |\n", + "\n", + "### Clustering Object Properties\n", + "\n", + "After clustering, access metadata via `fs.clustering`:\n", + "\n", + "| Property | Description |\n", + "|----------|-------------|\n", + "| `n_clusters` | Number of clusters |\n", + "| `n_original_clusters` | Number of original time segments (e.g., 365 days) |\n", + "| `timesteps_per_cluster` | Timesteps in each cluster (e.g., 24 for daily) |\n", + "| `cluster_order` | xr.DataArray mapping original segment β†’ cluster ID |\n", + "| `occurrences` | How many original segments each cluster represents |\n", + "| `metrics` | xr.Dataset with RMSE, MAE per time series |\n", + "| `plot.compare()` | Compare original vs clustered time series |\n", + "| `plot.heatmap()` | Visualize cluster structure |\n", + "\n", + "### Storage Behavior\n", + "\n", + "Each `Storage` component has a `cluster_mode` parameter:\n", + "\n", + "| Mode | Description |\n", + "|------|-------------|\n", + "| `'intercluster_cyclic'` | Links storage across clusters + yearly cyclic **(default)** |\n", + "| `'intercluster'` | Links storage across clusters, free start/end |\n", + "| `'cyclic'` | Each cluster is independent but cyclic (start = end) |\n", + "| `'independent'` | Each cluster is independent, free start/end |\n", + "\n", + "For a detailed comparison of storage modes, see [08c2-clustering-storage-modes](08c2-clustering-storage-modes.ipynb).\n", + "\n", + "### Peak Forcing Format\n", + "\n", + "```python\n", + "time_series_for_high_peaks = ['ComponentName(FlowName)|fixed_relative_profile']\n", + "```\n", + "\n", + "### Recommended Workflow\n", + "\n", + "```python\n", + "# Stage 1: Fast sizing\n", + "fs_sizing = flow_system.transform.cluster(\n", + " n_clusters=8,\n", + " cluster_duration='1D',\n", + " time_series_for_high_peaks=['Demand(Flow)|fixed_relative_profile'],\n", + ")\n", + "fs_sizing.optimize(solver)\n", + "\n", + "# Apply safety margin\n", + "sizes = {k: v.item() * 1.05 for k, v in fs_sizing.statistics.sizes.items()}\n", + "\n", + "# Stage 2: Accurate dispatch\n", + "fs_dispatch = flow_system.transform.fix_sizes(sizes)\n", + "fs_dispatch.optimize(solver)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "You learned how to:\n", + "\n", + "- Use **`cluster()`** to reduce time series into typical periods\n", + "- Apply **peak forcing** to capture extreme demand days\n", + "- Use **two-stage optimization** for fast yet accurate investment decisions\n", + "- **Expand solutions** back to full resolution with `expand_solution()`\n", + "- Access **clustering metadata** via `fs.clustering` (metrics, cluster_order, occurrences)\n", + "- Use **advanced options** like different algorithms\n", + "- **Manually assign clusters** using `predef_cluster_order`\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Always use peak forcing** (`time_series_for_high_peaks`) for demand time series\n", + "2. **Add safety margin** (5-10%) when fixing sizes from clustering\n", + "3. **Two-stage is recommended**: clustering for sizing, full resolution for dispatch\n", + "4. **Storage handling** is configurable via `cluster_mode`\n", + "5. **Check metrics** to evaluate clustering quality\n", + "6. **Use `predef_cluster_order`** to reproduce or define custom cluster assignments\n", + "\n", + "### Next Steps\n", + "\n", + "- **[08c2-clustering-storage-modes](08c2-clustering-storage-modes.ipynb)**: Compare storage modes using a seasonal storage system\n", + "- **[08d-clustering-multiperiod](08d-clustering-multiperiod.ipynb)**: Clustering with multiple periods and scenarios" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/08c2-clustering-storage-modes.ipynb b/docs/notebooks/08c2-clustering-storage-modes.ipynb new file mode 100644 index 000000000..3a9ab88ad --- /dev/null +++ b/docs/notebooks/08c2-clustering-storage-modes.ipynb @@ -0,0 +1,443 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Clustering Storage Modes\n", + "\n", + "Compare different storage handling modes when clustering time series.\n", + "\n", + "This notebook demonstrates:\n", + "\n", + "- **Four storage modes**: `independent`, `cyclic`, `intercluster`, `intercluster_cyclic`\n", + "- **Seasonal storage**: Why inter-cluster linking matters for long-term storage\n", + "- **When to use each mode**: Choosing the right mode for your application\n", + "\n", + "!!! note \"Prerequisites\"\n", + " Read [08c-clustering](08c-clustering.ipynb) first for clustering basics." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import timeit\n", + "\n", + "import pandas as pd\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots\n", + "\n", + "import flixopt as fx\n", + "\n", + "fx.CONFIG.notebook()" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Create the Seasonal Storage System\n", + "\n", + "We use a solar thermal + seasonal pit storage system with a full year of data.\n", + "This is ideal for demonstrating storage modes because:\n", + "\n", + "- **Solar peaks in summer** when heat demand is low\n", + "- **Heat demand peaks in winter** when solar is minimal\n", + "- **Seasonal storage** bridges this gap by storing summer heat for winter" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from data.generate_example_systems import create_seasonal_storage_system\n", + "\n", + "flow_system = create_seasonal_storage_system()\n", + "flow_system.connect_and_transform() # Align all data as xarray\n", + "\n", + "timesteps = flow_system.timesteps\n", + "print(f'FlowSystem: {len(timesteps)} timesteps ({len(timesteps) / 24:.0f} days)')\n", + "print(f'Components: {list(flow_system.components.keys())}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize the seasonal patterns\n", + "solar_profile = flow_system.components['SolarThermal'].outputs[0].fixed_relative_profile\n", + "heat_demand = flow_system.components['HeatDemand'].inputs[0].fixed_relative_profile\n", + "\n", + "# Compute daily averages using xarray resample\n", + "solar_daily = solar_profile.resample(time='1D').mean()\n", + "demand_daily = heat_demand.resample(time='1D').mean()\n", + "\n", + "fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.1)\n", + "fig.add_trace(\n", + " go.Scatter(x=solar_daily.time.values, y=solar_daily.values, name='Solar (daily avg)', fill='tozeroy'), row=1, col=1\n", + ")\n", + "fig.add_trace(\n", + " go.Scatter(x=demand_daily.time.values, y=demand_daily.values, name='Heat Demand (daily avg)', fill='tozeroy'),\n", + " row=2,\n", + " col=1,\n", + ")\n", + "fig.update_layout(height=400, title='Seasonal Mismatch: Solar vs Heat Demand')\n", + "fig.update_xaxes(title_text='Day of Year', row=2, col=1)\n", + "fig.update_yaxes(title_text='Solar Profile', row=1, col=1)\n", + "fig.update_yaxes(title_text='Heat Demand [MW]', row=2, col=1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "## Understanding Storage Modes\n", + "\n", + "When clustering reduces a full year to typical periods (e.g., 12 typical days), we need to\n", + "decide how storage behaves across these periods. Each `Storage` component has a \n", + "`cluster_storage_mode` parameter with four options:\n", + "\n", + "| Mode | Description | Use Case |\n", + "|------|-------------|----------|\n", + "| `'intercluster_cyclic'` | Links storage across clusters + yearly cyclic | **Default**. Seasonal storage, yearly optimization |\n", + "| `'intercluster'` | Links storage across clusters, free start/end | Multi-year optimization, flexible boundaries |\n", + "| `'cyclic'` | Each cluster independent, but cyclic (start = end) | Daily storage only, no seasonal effects |\n", + "| `'independent'` | Each cluster independent, free start/end | Fastest solve, ignores long-term storage |\n", + "\n", + "Let's compare them!" + ] + }, + { + "cell_type": "markdown", + "id": "6", + "metadata": {}, + "source": [ + "## Baseline: Full Year Optimization\n", + "\n", + "First, optimize the full system to establish a baseline:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7", + "metadata": {}, + "outputs": [], + "source": [ + "solver = fx.solvers.HighsSolver(mip_gap=0.02)\n", + "\n", + "start = timeit.default_timer()\n", + "fs_full = flow_system.copy()\n", + "fs_full.name = 'Full Optimization'\n", + "fs_full.optimize(solver)\n", + "time_full = timeit.default_timer() - start\n", + "\n", + "print(f'Full optimization: {time_full:.1f} seconds')\n", + "print(f'Total cost: {fs_full.solution[\"costs\"].item():,.0f} EUR')\n", + "print('\\nOptimized sizes:')\n", + "for name, size in fs_full.statistics.sizes.items():\n", + " print(f' {name}: {float(size.item()):.2f}')" + ] + }, + { + "cell_type": "markdown", + "id": "8", + "metadata": {}, + "source": [ + "## Compare Storage Modes\n", + "\n", + "Now let's cluster with each storage mode and compare results.\n", + "We set `cluster_storage_mode` on the Storage component before calling `cluster()`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "# Clustering parameters\n", + "N_CLUSTERS = 24 # 12 typical days for a full year\n", + "CLUSTER_DURATION = '1D'\n", + "PEAK_SERIES = ['HeatDemand(Q_th)|fixed_relative_profile']\n", + "\n", + "# Storage modes to compare\n", + "storage_modes = ['independent', 'cyclic', 'intercluster', 'intercluster_cyclic']\n", + "\n", + "results = {}\n", + "clustered_systems = {}\n", + "\n", + "for mode in storage_modes:\n", + " print(f'\\n--- Mode: {mode} ---')\n", + "\n", + " # Create a copy and set the storage mode\n", + " fs_copy = flow_system.copy()\n", + " fs_copy.storages['SeasonalStorage'].cluster_mode = mode\n", + "\n", + " start = timeit.default_timer()\n", + " fs_clustered = fs_copy.transform.cluster(\n", + " n_clusters=N_CLUSTERS,\n", + " cluster_duration=CLUSTER_DURATION,\n", + " time_series_for_high_peaks=PEAK_SERIES,\n", + " )\n", + " time_cluster = timeit.default_timer() - start\n", + "\n", + " start = timeit.default_timer()\n", + " fs_clustered.optimize(solver)\n", + " time_solve = timeit.default_timer() - start\n", + "\n", + " clustered_systems[mode] = fs_clustered\n", + "\n", + " results[mode] = {\n", + " 'Time [s]': time_cluster + time_solve,\n", + " 'Cost [EUR]': fs_clustered.solution['costs'].item(),\n", + " 'Solar [MW]': fs_clustered.statistics.sizes.get('SolarThermal(Q_th)', 0),\n", + " 'Boiler [MW]': fs_clustered.statistics.sizes.get('GasBoiler(Q_th)', 0),\n", + " 'Storage [MWh]': fs_clustered.statistics.sizes.get('SeasonalStorage', 0),\n", + " }\n", + "\n", + " # Handle xarray types\n", + " for key in ['Solar [MW]', 'Boiler [MW]', 'Storage [MWh]']:\n", + " val = results[mode][key]\n", + " results[mode][key] = float(val.item()) if hasattr(val, 'item') else float(val)\n", + "\n", + " print(f' Time: {results[mode][\"Time [s]\"]:.1f}s')\n", + " print(f' Cost: {results[mode][\"Cost [EUR]\"]:,.0f} EUR')\n", + " print(f' Storage: {results[mode][\"Storage [MWh]\"]:.0f} MWh')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "# Add full optimization result for comparison\n", + "results['Full (baseline)'] = {\n", + " 'Time [s]': time_full,\n", + " 'Cost [EUR]': fs_full.solution['costs'].item(),\n", + " 'Solar [MW]': float(fs_full.statistics.sizes.get('SolarThermal(Q_th)', 0).item()),\n", + " 'Boiler [MW]': float(fs_full.statistics.sizes.get('GasBoiler(Q_th)', 0).item()),\n", + " 'Storage [MWh]': float(fs_full.statistics.sizes.get('SeasonalStorage', 0).item()),\n", + "}\n", + "\n", + "# Create comparison DataFrame\n", + "comparison = pd.DataFrame(results).T\n", + "baseline_cost = comparison.loc['Full (baseline)', 'Cost [EUR]']\n", + "baseline_time = comparison.loc['Full (baseline)', 'Time [s]']\n", + "comparison['Cost Gap [%]'] = (comparison['Cost [EUR]'] - baseline_cost) / abs(baseline_cost) * 100\n", + "comparison['Speedup'] = baseline_time / comparison['Time [s]']\n", + "\n", + "comparison.style.format(\n", + " {\n", + " 'Time [s]': '{:.1f}',\n", + " 'Cost [EUR]': '{:,.0f}',\n", + " 'Solar [MW]': '{:.1f}',\n", + " 'Boiler [MW]': '{:.1f}',\n", + " 'Storage [MWh]': '{:.0f}',\n", + " 'Cost Gap [%]': '{:+.1f}',\n", + " 'Speedup': '{:.1f}x',\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## Visualize Storage Behavior\n", + "\n", + "The key difference between modes is how storage is utilized across the year.\n", + "Let's expand each solution back to full resolution and compare:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "# Expand clustered solutions to full resolution\n", + "expanded_systems = {}\n", + "for mode in storage_modes:\n", + " fs_expanded = clustered_systems[mode].transform.expand_solution()\n", + " fs_expanded.name = f'Mode: {mode}'\n", + " expanded_systems[mode] = fs_expanded" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "# Plot storage charge state for each mode\n", + "fig = make_subplots(\n", + " rows=len(storage_modes) + 1,\n", + " cols=1,\n", + " shared_xaxes=True,\n", + " vertical_spacing=0.05,\n", + " subplot_titles=['Full Optimization'] + [f'Mode: {m}' for m in storage_modes],\n", + ")\n", + "\n", + "# Full optimization\n", + "soc_full = fs_full.solution['SeasonalStorage|charge_state']\n", + "fig.add_trace(go.Scatter(x=fs_full.timesteps, y=soc_full.values, name='Full', line=dict(width=0.8)), row=1, col=1)\n", + "\n", + "# Expanded clustered solutions\n", + "for i, mode in enumerate(storage_modes, start=2):\n", + " fs_exp = expanded_systems[mode]\n", + " soc = fs_exp.solution['SeasonalStorage|charge_state']\n", + " fig.add_trace(go.Scatter(x=fs_exp.timesteps, y=soc.values, name=mode, line=dict(width=0.8)), row=i, col=1)\n", + "\n", + "fig.update_layout(height=800, title='Storage Charge State by Mode', showlegend=False)\n", + "for i in range(1, len(storage_modes) + 2):\n", + " fig.update_yaxes(title_text='SOC [MWh]', row=i, col=1)\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "14", + "metadata": {}, + "source": [ + "### Side-by-Side Comparison\n", + "\n", + "Use the `Comparison` class to compare the full optimization with the recommended mode:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare full optimization with the recommended intercluster_cyclic mode\n", + "comp = fx.Comparison([fs_full, expanded_systems['intercluster_cyclic']])\n", + "comp.statistics.plot.balance('Heat')" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Interpretation\n", + "\n", + "### `'independent'` Mode\n", + "- Each typical period is solved independently\n", + "- Storage starts and ends at arbitrary states within each cluster\n", + "- **No seasonal storage benefit captured** - storage is only used for daily fluctuations\n", + "- Fastest to solve but least accurate for seasonal systems\n", + "\n", + "### `'cyclic'` Mode \n", + "- Each cluster is independent but enforces start = end state\n", + "- Better than independent but still **no cross-season linking**\n", + "- Good for systems where storage only balances within-day variations\n", + "\n", + "### `'intercluster'` Mode\n", + "- Links storage state across the original time series via typical periods\n", + "- **Captures seasonal storage behavior** - summer charging, winter discharging\n", + "- Free start and end states (useful for multi-year optimization)\n", + "\n", + "### `'intercluster_cyclic'` Mode (Default)\n", + "- Inter-cluster linking **plus** yearly cyclic constraint (end = start)\n", + "- **Best for yearly investment optimization** with seasonal storage\n", + "- Ensures the storage cycle is sustainable year after year" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## When to Use Each Mode\n", + "\n", + "| Your System Has... | Recommended Mode |\n", + "|-------------------|------------------|\n", + "| Seasonal storage (pit, underground) | `'intercluster_cyclic'` |\n", + "| Only daily storage (batteries, hot water tanks) | `'cyclic'` |\n", + "| Multi-year optimization with inter-annual storage | `'intercluster'` |\n", + "| Quick sizing estimate, storage not critical | `'independent'` |\n", + "\n", + "### Setting the Mode\n", + "\n", + "```python\n", + "# Option 1: Set when creating the Storage\n", + "storage = fx.Storage(\n", + " 'SeasonalStorage',\n", + " capacity_in_flow_hours=5000,\n", + " cluster_storage_mode='intercluster_cyclic', # default\n", + " ...\n", + ")\n", + "\n", + "# Option 2: Modify before clustering\n", + "flow_system.components['SeasonalStorage'].cluster_storage_mode = 'cyclic'\n", + "fs_clustered = flow_system.transform.cluster(...)\n", + "```\n", + "\n", + "!!! tip \"Rule of Thumb\"\n", + " Use `'intercluster_cyclic'` (default) unless you have a specific reason not to.\n", + " It provides the most accurate representation of storage behavior in clustered systems." + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "You learned how to:\n", + "\n", + "- Use **`cluster_storage_mode`** on Storage components to control behavior in clustering\n", + "- Understand the **difference between modes** and their impact on results\n", + "- Choose the **right mode** for your optimization problem\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Seasonal storage requires inter-cluster linking** to capture charging/discharging across seasons\n", + "2. **`'intercluster_cyclic'`** is the default and best for yearly investment optimization\n", + "3. **`'independent'` and `'cyclic'`** are faster but miss long-term storage value\n", + "4. **Expand solutions** with `expand_solution()` to visualize storage behavior across the year\n", + "\n", + "### Next Steps\n", + "\n", + "- **[08d-clustering-multiperiod](08d-clustering-multiperiod.ipynb)**: Clustering with multiple periods and scenarios" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.12.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/08d-clustering-multiperiod.ipynb b/docs/notebooks/08d-clustering-multiperiod.ipynb new file mode 100644 index 000000000..e599384ac --- /dev/null +++ b/docs/notebooks/08d-clustering-multiperiod.ipynb @@ -0,0 +1,588 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Multi-Period Clustering with `cluster()`\n", + "\n", + "Combine time series clustering with multi-period investment optimization.\n", + "\n", + "This notebook demonstrates:\n", + "\n", + "- **Multi-period modeling**: Optimize investments across multiple planning periods (years)\n", + "- **Scenario analysis**: Handle demand uncertainty with weighted scenarios\n", + "- **Clustering per period**: Apply typical-period clustering independently for each period/scenario\n", + "- **Scalability**: Reduce computational complexity for long-horizon planning\n", + "\n", + "!!! note \"Requirements\"\n", + " This notebook requires the `tsam` package: `pip install tsam`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "import timeit\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import plotly.express as px\n", + "\n", + "import flixopt as fx\n", + "\n", + "fx.CONFIG.notebook()" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Create the Multi-Period System\n", + "\n", + "We use a multi-period heating system with:\n", + "- **3 planning periods** (years 2024, 2025, 2026)\n", + "- **2 scenarios** (high demand 30%, low demand 70%)\n", + "- **2 weeks** at hourly resolution (336 timesteps)\n", + "\n", + "This represents a capacity expansion problem where we optimize component sizes once,\n", + "but operations are simulated across multiple future years and demand scenarios." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "from data.generate_example_systems import create_multiperiod_system\n", + "\n", + "flow_system = create_multiperiod_system()\n", + "\n", + "print(f'Timesteps: {len(flow_system.timesteps)} ({len(flow_system.timesteps) // 24} days)')\n", + "print(f'Periods: {list(flow_system.periods.values)}')\n", + "print(f'Scenarios: {list(flow_system.scenarios.values)}')\n", + "print(f'Scenario weights: {flow_system.scenario_weights.values}')\n", + "print(f'\\nComponents: {list(flow_system.components.keys())}')" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "## Selecting a Subset with `transform.isel()`\n", + "\n", + "For demonstration purposes, we'll use only the first week of data.\n", + "The `isel()` method (index select) lets you slice FlowSystems by time:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "# Select first week only (168 hours)\n", + "flow_system = flow_system.transform.isel(time=slice(0, 168))\n", + "\n", + "print(f'After isel: {len(flow_system.timesteps)} timesteps ({len(flow_system.timesteps) // 24} days)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "# Visualize demand scenarios\n", + "heat_demand = flow_system.components['Building'].inputs[0].fixed_relative_profile\n", + "\n", + "fig = px.line(\n", + " heat_demand.to_dataframe('value').reset_index(), x='time', y='value', facet_col='period', facet_row='scenario'\n", + ")\n", + "\n", + "fig.update_layout(\n", + " height=350,\n", + " title='Heat Demand by Scenario (One Week)',\n", + " xaxis_title='Time',\n", + " yaxis_title='Heat Demand [kW]',\n", + ")\n", + "fig.show()" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "## Full Optimization (Baseline)\n", + "\n", + "First, solve the complete problem with all timesteps across all periods and scenarios:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "solver = fx.solvers.HighsSolver(mip_gap=0.01)\n", + "\n", + "start = timeit.default_timer()\n", + "fs_full = flow_system.copy()\n", + "fs_full.name = 'Full Optimization'\n", + "fs_full.optimize(solver)\n", + "time_full = timeit.default_timer() - start\n", + "\n", + "print(f'Full optimization: {time_full:.2f} seconds')\n", + "print(f'Total cost (objective): {fs_full.solution[\"objective\"].item():,.0f} €')\n", + "print('\\nOptimized sizes:')\n", + "for name, size in fs_full.statistics.sizes.items():\n", + " print(f' {name}: {size.max().item():.1f}')" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## Multi-Period Clustering with `cluster()`\n", + "\n", + "When applied to a multi-period system, `cluster()` clusters **each period/scenario combination independently**.\n", + "This is because demand patterns and optimal operations may differ across:\n", + "\n", + "- **Periods**: Different years may have different characteristics\n", + "- **Scenarios**: High vs low demand scenarios need different representative days\n", + "\n", + "The investment decisions (sizes) remain consistent across all periods and scenarios,\n", + "while the operational patterns are optimized for each cluster." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "start = timeit.default_timer()\n", + "\n", + "# Force inclusion of peak demand periods\n", + "peak_series = ['Building(Heat)|fixed_relative_profile']\n", + "\n", + "# Cluster to 3 typical days (from 7 days)\n", + "fs_clustered = flow_system.transform.cluster(\n", + " n_clusters=3,\n", + " cluster_duration='1D',\n", + " time_series_for_high_peaks=peak_series,\n", + ")\n", + "\n", + "time_clustering = timeit.default_timer() - start\n", + "\n", + "print(f'Clustering time: {time_clustering:.2f} seconds')\n", + "print(f'Reduced: {len(flow_system.timesteps)} β†’ {len(fs_clustered.timesteps)} timesteps per period')\n", + "print('Total problem reduction: 7 days Γ— 3 periods Γ— 2 scenarios β†’ 3 days Γ— 3 Γ— 2')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "# Optimize the reduced system\n", + "start = timeit.default_timer()\n", + "fs_clustered.optimize(solver)\n", + "time_clustered = timeit.default_timer() - start\n", + "\n", + "print(f'Clustered optimization: {time_clustered:.2f} seconds')\n", + "print(f'Total cost (objective): {fs_clustered.solution[\"objective\"].item():,.0f} €')\n", + "print(f'\\nSpeedup vs full: {time_full / (time_clustering + time_clustered):.1f}x')\n", + "print('\\nOptimized sizes:')\n", + "for name, size in fs_clustered.statistics.sizes.items():\n", + " print(f' {name}: {size.max().item():.1f}')" + ] + }, + { + "cell_type": "markdown", + "id": "12", + "metadata": {}, + "source": [ + "## Visualize Clustering Quality\n", + "\n", + "The `.plot` accessor provides built-in visualizations with automatic faceting by period and scenario:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare original vs aggregated data - automatically faceted by period and scenario\n", + "fs_clustered.clustering.plot.compare(variables='Building(Heat)|fixed_relative_profile')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "# Duration curves show how well the distribution is preserved per period/scenario\n", + "fs_clustered.clustering.plot.compare(\n", + " kind='duration_curve',\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap shows cluster assignments - faceted by period and scenario\n", + "fs_clustered.clustering.plot.heatmap()" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Understand the Cluster Structure\n", + "\n", + "Let's inspect how days were grouped into clusters:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "info = fs_clustered.clustering\n", + "cs = info.result.cluster_structure\n", + "\n", + "print('Clustering Configuration:')\n", + "print(f' Typical periods (clusters): {cs.n_clusters}')\n", + "print(f' Timesteps per cluster: {cs.timesteps_per_cluster}')\n", + "\n", + "# The cluster_order shows which cluster each original day belongs to\n", + "cluster_order = cs.cluster_order.values\n", + "day_names = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']\n", + "\n", + "print('\\nCluster assignments per day:')\n", + "for i, cluster_id in enumerate(cluster_order):\n", + " print(f' {day_names[i]}: Cluster {cluster_id}')\n", + "\n", + "# Cluster occurrences (how many original days each cluster represents)\n", + "unique, counts = np.unique(cluster_order, return_counts=True)\n", + "print('\\nCluster weights (days represented):')\n", + "for cluster_id, count in zip(unique, counts, strict=False):\n", + " print(f' Cluster {cluster_id}: {count} days')" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Two-Stage Workflow for Multi-Period\n", + "\n", + "For investment optimization across multiple periods, the recommended workflow is:\n", + "\n", + "1. **Stage 1**: Fast sizing with clustering (reduced timesteps)\n", + "2. **Stage 2**: Fix sizes and run full-resolution dispatch\n", + "\n", + "This gives accurate investment decisions while maintaining computational tractability." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "# Stage 1 already done - apply safety margin\n", + "SAFETY_MARGIN = 1.10 # 10% buffer for multi-period uncertainty\n", + "\n", + "sizes_with_margin = {name: size.max().item() * SAFETY_MARGIN for name, size in fs_clustered.statistics.sizes.items()}\n", + "\n", + "print('Stage 1: Sizing with clustering')\n", + "print(f' Time: {time_clustering + time_clustered:.2f} seconds')\n", + "print(f' Cost estimate: {fs_clustered.solution[\"objective\"].item():,.0f} €')\n", + "print(f'\\nSizes with {(SAFETY_MARGIN - 1) * 100:.0f}% safety margin:')\n", + "for name, size in sizes_with_margin.items():\n", + " original = fs_clustered.statistics.sizes[name].max().item()\n", + " print(f' {name}: {original:.1f} β†’ {size:.1f}')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "# Stage 2: Full resolution dispatch with fixed sizes\n", + "print('Stage 2: Full resolution dispatch')\n", + "start = timeit.default_timer()\n", + "\n", + "fs_dispatch = flow_system.transform.fix_sizes(sizes_with_margin)\n", + "fs_dispatch.name = 'Two-Stage'\n", + "fs_dispatch.optimize(solver)\n", + "\n", + "time_dispatch = timeit.default_timer() - start\n", + "\n", + "print(f' Time: {time_dispatch:.2f} seconds')\n", + "print(f' Actual cost: {fs_dispatch.solution[\"objective\"].item():,.0f} €')\n", + "\n", + "# Total comparison\n", + "total_two_stage = time_clustering + time_clustered + time_dispatch\n", + "print(f'\\nTotal two-stage time: {total_two_stage:.2f} seconds')\n", + "print(f'Speedup vs full: {time_full / total_two_stage:.1f}x')" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "## Compare Results Across Methods" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "results = {\n", + " 'Full (baseline)': {\n", + " 'Time [s]': time_full,\n", + " 'Cost [€]': fs_full.solution['objective'].item(),\n", + " 'Boiler': fs_full.statistics.sizes['Boiler(Heat)'].max().item(),\n", + " 'Storage': fs_full.statistics.sizes['ThermalStorage'].max().item(),\n", + " },\n", + " 'Clustered (3 days)': {\n", + " 'Time [s]': time_clustering + time_clustered,\n", + " 'Cost [€]': fs_clustered.solution['objective'].item(),\n", + " 'Boiler': fs_clustered.statistics.sizes['Boiler(Heat)'].max().item(),\n", + " 'Storage': fs_clustered.statistics.sizes['ThermalStorage'].max().item(),\n", + " },\n", + " 'Two-Stage': {\n", + " 'Time [s]': total_two_stage,\n", + " 'Cost [€]': fs_dispatch.solution['objective'].item(),\n", + " 'Boiler': sizes_with_margin['Boiler(Heat)'],\n", + " 'Storage': sizes_with_margin['ThermalStorage'],\n", + " },\n", + "}\n", + "\n", + "comparison = pd.DataFrame(results).T\n", + "baseline_cost = comparison.loc['Full (baseline)', 'Cost [€]']\n", + "baseline_time = comparison.loc['Full (baseline)', 'Time [s]']\n", + "comparison['Cost Gap [%]'] = ((comparison['Cost [€]'] - baseline_cost) / abs(baseline_cost) * 100).round(2)\n", + "comparison['Speedup'] = (baseline_time / comparison['Time [s]']).round(1)\n", + "\n", + "comparison.style.format(\n", + " {\n", + " 'Time [s]': '{:.2f}',\n", + " 'Cost [€]': '{:,.0f}',\n", + " 'Boiler': '{:.1f}',\n", + " 'Storage': '{:.0f}',\n", + " 'Cost Gap [%]': '{:.2f}',\n", + " 'Speedup': '{:.1f}x',\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "## Visualize Optimization Results\n", + "\n", + "Use the built-in statistics plotting to compare results across periods and scenarios:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "# Plot flow rates with automatic faceting by period and scenario\n", + "fs_full.statistics.plot.flows(component='Boiler')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "# Side-by-side comparison using the Comparison class\n", + "comp = fx.Comparison([fs_full, fs_dispatch])\n", + "comp.statistics.plot.balance('Heat')" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "## Expand Clustered Solution to Full Resolution\n", + "\n", + "Use `expand_solution()` to map the clustered results back to all original timesteps:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "# Expand the clustered solution\n", + "fs_expanded = fs_clustered.transform.expand_solution()\n", + "\n", + "print(f'Expanded: {len(fs_clustered.timesteps)} β†’ {len(fs_expanded.timesteps)} timesteps')\n", + "print(f'Cost (objective): {fs_expanded.solution[\"objective\"].item():,.0f} €')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare expanded solution - shows the repeated cluster patterns\n", + "fs_expanded.statistics.plot.flows(component='Boiler')" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "## Key Considerations for Multi-Period Clustering\n", + "\n", + "### 1. Independent Clustering per Period/Scenario\n", + "\n", + "Each period and scenario combination is clustered independently because:\n", + "- Demand patterns may differ across years (growth, seasonality)\n", + "- Scenarios represent different futures that shouldn't be mixed\n", + "- Investment decisions must be robust across all combinations\n", + "\n", + "### 2. Safety Margins\n", + "\n", + "Multi-period systems often warrant larger safety margins (10-15%) because:\n", + "- More uncertainty across multiple years\n", + "- Investments made once must work for all periods\n", + "- Scenario weights may not perfectly represent actual outcomes\n", + "\n", + "### 3. Computational Benefits\n", + "\n", + "Clustering becomes more valuable as problem size grows:\n", + "\n", + "| Scenario | Full Problem | With Clustering |\n", + "|----------|--------------|----------------|\n", + "| 1 period, 1 scenario, 365 days | 8,760 timesteps | ~730 (10 typical days) |\n", + "| 3 periods, 2 scenarios, 365 days | 52,560 timesteps | ~4,380 |\n", + "| 10 periods, 3 scenarios, 365 days | 262,800 timesteps | ~21,900 |\n", + "\n", + "The speedup factor increases with problem size." + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "You learned how to:\n", + "\n", + "- Load **multi-period systems** with periods and scenarios\n", + "- Use **`transform.isel()`** to select time subsets\n", + "- Apply **`cluster()`** to multi-dimensional FlowSystems\n", + "- **Visualize clustering** with the `.plot` accessor (compare, duration curves, heatmaps)\n", + "- Use the **two-stage workflow** for robust investment optimization\n", + "- **Expand solutions** back to full resolution with `expand_solution()`\n", + "\n", + "### Key Takeaways\n", + "\n", + "1. **Clustering is applied per period/scenario**: Each combination gets independent typical periods\n", + "2. **Investments are shared**: Component sizes are optimized once across all periods/scenarios\n", + "3. **Use larger safety margins**: Multi-period uncertainty warrants 10-15% buffers\n", + "4. **Two-stage is recommended**: Fast sizing with clustering, accurate dispatch at full resolution\n", + "5. **Built-in plotting**: Use `.plot` accessor for automatic faceting by period/scenario\n", + "\n", + "### API Reference\n", + "\n", + "```python\n", + "# Load multi-period system\n", + "fs = fx.FlowSystem.from_netcdf('multiperiod_system.nc4')\n", + "\n", + "# Select time subset (optional)\n", + "fs = fs.transform.isel(time=slice(0, 168)) # First 168 timesteps\n", + "\n", + "# Cluster (applies per period/scenario)\n", + "fs_clustered = fs.transform.cluster(\n", + " n_clusters=10,\n", + " cluster_duration='1D',\n", + " time_series_for_high_peaks=['Demand(Flow)|fixed_relative_profile'],\n", + ")\n", + "\n", + "# Visualize clustering quality\n", + "fs_clustered.clustering.plot.compare(variable='Demand(Flow)|profile')\n", + "fs_clustered.clustering.plot.heatmap()\n", + "\n", + "# Two-stage workflow\n", + "fs_clustered.optimize(solver)\n", + "sizes = {k: v.max().item() * 1.10 for k, v in fs_clustered.statistics.sizes.items()}\n", + "fs_dispatch = fs.transform.fix_sizes(sizes)\n", + "fs_dispatch.optimize(solver)\n", + "\n", + "# Visualize results\n", + "fs_dispatch.statistics.plot.flows(component='Boiler')\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "name": "python", + "version": "3.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/08e-clustering-internals.ipynb b/docs/notebooks/08e-clustering-internals.ipynb new file mode 100644 index 000000000..506a01ed9 --- /dev/null +++ b/docs/notebooks/08e-clustering-internals.ipynb @@ -0,0 +1,293 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Clustering Internals\n", + "\n", + "Understanding the data structures and visualization tools behind time series clustering.\n", + "\n", + "This notebook demonstrates:\n", + "\n", + "- **Data structures**: `Clustering`, `ClusterResult`, and `ClusterStructure`\n", + "- **Plot accessor**: Built-in visualizations via `.plot`\n", + "- **Data expansion**: Using `expand_data()` to map aggregated data back to original timesteps\n", + "\n", + "!!! note \"Prerequisites\"\n", + " This notebook assumes familiarity with [08c-clustering](08c-clustering.ipynb)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1", + "metadata": {}, + "outputs": [], + "source": [ + "from data.generate_example_systems import create_district_heating_system\n", + "\n", + "import flixopt as fx\n", + "\n", + "fx.CONFIG.notebook()\n", + "\n", + "flow_system = create_district_heating_system()\n", + "flow_system.connect_and_transform()" + ] + }, + { + "cell_type": "markdown", + "id": "2", + "metadata": {}, + "source": [ + "## Clustering Metadata\n", + "\n", + "After calling `cluster()`, metadata is stored in `fs.clustering`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3", + "metadata": {}, + "outputs": [], + "source": [ + "fs_clustered = flow_system.transform.cluster(\n", + " n_clusters=8,\n", + " cluster_duration='1D',\n", + " time_series_for_high_peaks=['HeatDemand(Q_th)|fixed_relative_profile'],\n", + ")\n", + "\n", + "fs_clustered.clustering" + ] + }, + { + "cell_type": "markdown", + "id": "4", + "metadata": {}, + "source": [ + "The `Clustering` contains:\n", + "- **`result`**: A `ClusterResult` with timestep mapping and weights\n", + "- **`result.cluster_structure`**: A `ClusterStructure` with cluster assignments" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5", + "metadata": {}, + "outputs": [], + "source": [ + "fs_clustered.clustering.result" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "fs_clustered.clustering.result.cluster_structure" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "## Visualizing Clustering\n", + "\n", + "The `.plot` accessor provides built-in visualizations for understanding clustering results." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare original vs aggregated data as timeseries\n", + "# By default, plots all time-varying variables\n", + "fs_clustered.clustering.plot.compare()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9", + "metadata": {}, + "outputs": [], + "source": [ + "# Compare specific variables only\n", + "fs_clustered.clustering.plot.compare(variables='HeatDemand(Q_th)|fixed_relative_profile')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "# Duration curves show how well the aggregated data preserves the distribution\n", + "fs_clustered.clustering.plot.compare(kind='duration_curve').data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11", + "metadata": {}, + "outputs": [], + "source": [ + "# View typical period profiles for each cluster\n", + "# Each line represents a cluster's representative day\n", + "fs_clustered.clustering.plot.clusters(variables='HeatDemand(Q_th)|fixed_relative_profile')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "# Heatmap shows cluster assignments for each original period\n", + "fs_clustered.clustering.plot.heatmap()" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "## Expanding Aggregated Data\n", + "\n", + "The `ClusterResult.expand_data()` method maps aggregated data back to original timesteps.\n", + "This is useful for comparing clustering results before optimization:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "# Get original and aggregated data\n", + "result = fs_clustered.clustering.result\n", + "original = result.original_data['HeatDemand(Q_th)|fixed_relative_profile']\n", + "aggregated = result.aggregated_data['HeatDemand(Q_th)|fixed_relative_profile']\n", + "\n", + "# Expand aggregated data back to original timesteps\n", + "expanded = result.expand_data(aggregated)\n", + "\n", + "print(f'Original: {len(original.time)} timesteps')\n", + "print(f'Aggregated: {len(aggregated.time)} timesteps')\n", + "print(f'Expanded: {len(expanded.time)} timesteps')" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## Summary\n", + "\n", + "| Class | Purpose |\n", + "|-------|--------|\n", + "| `Clustering` | Stored on `fs.clustering` after `cluster()` |\n", + "| `ClusterResult` | Contains timestep mapping, weights, and `expand_data()` method |\n", + "| `ClusterStructure` | Maps original periods to clusters |\n", + "\n", + "### Plot Accessor Methods\n", + "\n", + "| Method | Description |\n", + "|--------|-------------|\n", + "| `plot.compare()` | Compare original vs aggregated data (timeseries) |\n", + "| `plot.compare(kind='duration_curve')` | Compare as duration curves |\n", + "| `plot.clusters()` | View each cluster's profile |\n", + "| `plot.heatmap()` | Visualize cluster assignments |\n", + "\n", + "### Key Parameters\n", + "\n", + "```python\n", + "# Compare with options\n", + "clustering.plot.compare(\n", + " variables='Demand|profile', # Single variable, list, or None (all)\n", + " kind='timeseries', # 'timeseries' or 'duration_curve'\n", + " select={'scenario': 'Base'}, # xarray-style selection\n", + " colors='viridis', # Colorscale name, list, or dict\n", + " facet_col='period', # Facet by period if present\n", + " facet_row='scenario', # Facet by scenario if present\n", + ")\n", + "\n", + "# Heatmap shows cluster assignments (no variable needed)\n", + "clustering.plot.heatmap()\n", + "\n", + "# Expand aggregated data to original timesteps\n", + "result = clustering.result\n", + "expanded = result.expand_data(aggregated_data)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "id": "16", + "metadata": {}, + "source": [ + "## Cluster Weights\n", + "\n", + "Each representative timestep has a weight equal to the number of original periods it represents.\n", + "This ensures operational costs scale correctly:\n", + "\n", + "$$\\text{Objective} = \\sum_{t \\in \\text{typical}} w_t \\cdot c_t$$\n", + "\n", + "The weights sum to the original timestep count:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "print(f'Sum of weights: {fs_clustered.cluster_weight.sum().item():.0f}')\n", + "print(f'Original timesteps: {len(flow_system.timesteps)}')" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "## Solution Expansion\n", + "\n", + "After optimization, `expand_solution()` maps results back to full resolution:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "solver = fx.solvers.HighsSolver(mip_gap=0.01, log_to_console=False)\n", + "fs_clustered.optimize(solver)\n", + "\n", + "fs_expanded = fs_clustered.transform.expand_solution()\n", + "\n", + "print(f'Clustered: {len(fs_clustered.timesteps)} timesteps')\n", + "print(f'Expanded: {len(fs_expanded.timesteps)} timesteps')" + ] + } + ], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/notebooks/09-plotting-and-data-access.ipynb b/docs/notebooks/09-plotting-and-data-access.ipynb index a4803adf4..39fa788da 100644 --- a/docs/notebooks/09-plotting-and-data-access.ipynb +++ b/docs/notebooks/09-plotting-and-data-access.ipynb @@ -11,7 +11,6 @@ "\n", "This notebook covers:\n", "\n", - "- Loading saved FlowSystems from NetCDF files\n", "- Accessing data (flow rates, sizes, effects, charge states)\n", "- Time series plots (balance, flows, storage)\n", "- Aggregated plots (sizes, effects, duration curves)\n", @@ -36,7 +35,7 @@ "metadata": {}, "outputs": [], "source": [ - "from pathlib import Path\n", + "from data.generate_example_systems import create_complex_system, create_multiperiod_system, create_simple_system\n", "\n", "import flixopt as fx\n", "\n", @@ -48,9 +47,9 @@ "id": "3", "metadata": {}, "source": [ - "## Generate Example Data\n", + "## Generate Example Systems\n", "\n", - "First, run the script that generates three example FlowSystems with solutions:" + "First, create three example FlowSystems with solutions:" ] }, { @@ -60,35 +59,19 @@ "metadata": {}, "outputs": [], "source": [ - "# Run the generation script (only needed once, or to regenerate)\n", - "!python data/generate_example_systems.py > /dev/null 2>&1" - ] - }, - { - "cell_type": "markdown", - "id": "5", - "metadata": {}, - "source": [ - "## 1. Loading Saved FlowSystems\n", + "# Create and optimize the example systems\n", + "solver = fx.solvers.HighsSolver(mip_gap=0.01, log_to_console=False)\n", "\n", - "FlowSystems can be saved to and loaded from NetCDF files, preserving the full structure and solution:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "6", - "metadata": {}, - "outputs": [], - "source": [ - "DATA_DIR = Path('data')\n", + "simple = create_simple_system()\n", + "simple.optimize(solver)\n", + "\n", + "complex_sys = create_complex_system()\n", + "complex_sys.optimize(solver)\n", "\n", - "# Load the three example systems\n", - "simple = fx.FlowSystem.from_netcdf(DATA_DIR / 'simple_system.nc4')\n", - "complex_sys = fx.FlowSystem.from_netcdf(DATA_DIR / 'complex_system.nc4')\n", - "multiperiod = fx.FlowSystem.from_netcdf(DATA_DIR / 'multiperiod_system.nc4')\n", + "multiperiod = create_multiperiod_system()\n", + "multiperiod.optimize(solver)\n", "\n", - "print('Loaded systems:')\n", + "print('Created systems:')\n", "print(f' simple: {len(simple.components)} components, {len(simple.buses)} buses')\n", "print(f' complex_sys: {len(complex_sys.components)} components, {len(complex_sys.buses)} buses')\n", "print(f' multiperiod: {len(multiperiod.components)} components, dims={dict(multiperiod.solution.sizes)}')" @@ -96,7 +79,7 @@ }, { "cell_type": "markdown", - "id": "7", + "id": "5", "metadata": {}, "source": [ "## 2. Quick Overview: Balance Plot\n", @@ -107,7 +90,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8", + "id": "6", "metadata": {}, "outputs": [], "source": [ @@ -117,7 +100,7 @@ }, { "cell_type": "markdown", - "id": "9", + "id": "7", "metadata": {}, "source": [ "### Accessing Plot Data\n", @@ -128,7 +111,7 @@ { "cell_type": "code", "execution_count": null, - "id": "10", + "id": "8", "metadata": {}, "outputs": [], "source": [ @@ -142,7 +125,7 @@ }, { "cell_type": "markdown", - "id": "11", + "id": "9", "metadata": {}, "source": [ "### Energy Totals\n", @@ -153,7 +136,7 @@ { "cell_type": "code", "execution_count": null, - "id": "12", + "id": "10", "metadata": {}, "outputs": [], "source": [ @@ -167,7 +150,7 @@ }, { "cell_type": "markdown", - "id": "13", + "id": "11", "metadata": {}, "source": [ "## 3. Time Series Plots" @@ -175,7 +158,7 @@ }, { "cell_type": "markdown", - "id": "14", + "id": "12", "metadata": {}, "source": [ "### 3.1 Balance Plot\n", @@ -186,7 +169,7 @@ { "cell_type": "code", "execution_count": null, - "id": "15", + "id": "13", "metadata": {}, "outputs": [], "source": [ @@ -196,7 +179,7 @@ }, { "cell_type": "markdown", - "id": "16", + "id": "14", "metadata": {}, "source": [ "### 3.2 Carrier Balance\n", @@ -207,7 +190,7 @@ { "cell_type": "code", "execution_count": null, - "id": "17", + "id": "15", "metadata": {}, "outputs": [], "source": [ @@ -217,7 +200,7 @@ { "cell_type": "code", "execution_count": null, - "id": "18", + "id": "16", "metadata": {}, "outputs": [], "source": [ @@ -226,7 +209,7 @@ }, { "cell_type": "markdown", - "id": "19", + "id": "17", "metadata": {}, "source": [ "### 3.3 Flow Rates\n", @@ -237,7 +220,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20", + "id": "18", "metadata": {}, "outputs": [], "source": [ @@ -248,7 +231,7 @@ { "cell_type": "code", "execution_count": null, - "id": "21", + "id": "19", "metadata": {}, "outputs": [], "source": [ @@ -258,7 +241,7 @@ }, { "cell_type": "markdown", - "id": "22", + "id": "20", "metadata": {}, "source": [ "### 3.4 Storage Plot\n", @@ -269,7 +252,7 @@ { "cell_type": "code", "execution_count": null, - "id": "23", + "id": "21", "metadata": {}, "outputs": [], "source": [ @@ -278,7 +261,7 @@ }, { "cell_type": "markdown", - "id": "24", + "id": "22", "metadata": {}, "source": [ "### 3.5 Charge States Plot\n", @@ -289,7 +272,7 @@ { "cell_type": "code", "execution_count": null, - "id": "25", + "id": "23", "metadata": {}, "outputs": [], "source": [ @@ -298,7 +281,7 @@ }, { "cell_type": "markdown", - "id": "26", + "id": "24", "metadata": {}, "source": [ "## 4. Aggregated Plots" @@ -306,7 +289,7 @@ }, { "cell_type": "markdown", - "id": "27", + "id": "25", "metadata": {}, "source": [ "### 4.1 Sizes Plot\n", @@ -317,7 +300,7 @@ { "cell_type": "code", "execution_count": null, - "id": "28", + "id": "26", "metadata": {}, "outputs": [], "source": [ @@ -326,7 +309,7 @@ }, { "cell_type": "markdown", - "id": "29", + "id": "27", "metadata": {}, "source": [ "### 4.2 Effects Plot\n", @@ -337,7 +320,7 @@ { "cell_type": "code", "execution_count": null, - "id": "30", + "id": "28", "metadata": {}, "outputs": [], "source": [ @@ -347,7 +330,7 @@ { "cell_type": "code", "execution_count": null, - "id": "31", + "id": "29", "metadata": {}, "outputs": [], "source": [ @@ -358,7 +341,7 @@ { "cell_type": "code", "execution_count": null, - "id": "32", + "id": "30", "metadata": {}, "outputs": [], "source": [ @@ -367,7 +350,7 @@ }, { "cell_type": "markdown", - "id": "33", + "id": "31", "metadata": {}, "source": [ "### 4.3 Duration Curve\n", @@ -378,7 +361,7 @@ { "cell_type": "code", "execution_count": null, - "id": "34", + "id": "32", "metadata": {}, "outputs": [], "source": [ @@ -388,7 +371,7 @@ { "cell_type": "code", "execution_count": null, - "id": "35", + "id": "33", "metadata": {}, "outputs": [], "source": [ @@ -398,7 +381,7 @@ }, { "cell_type": "markdown", - "id": "36", + "id": "34", "metadata": {}, "source": [ "## 5. Heatmaps\n", @@ -409,7 +392,7 @@ { "cell_type": "code", "execution_count": null, - "id": "37", + "id": "35", "metadata": {}, "outputs": [], "source": [ @@ -420,7 +403,7 @@ { "cell_type": "code", "execution_count": null, - "id": "38", + "id": "36", "metadata": {}, "outputs": [], "source": [ @@ -431,7 +414,7 @@ { "cell_type": "code", "execution_count": null, - "id": "39", + "id": "37", "metadata": {}, "outputs": [], "source": [ @@ -441,7 +424,7 @@ }, { "cell_type": "markdown", - "id": "40", + "id": "38", "metadata": {}, "source": [ "## 6. Sankey Diagrams\n", @@ -451,7 +434,7 @@ }, { "cell_type": "markdown", - "id": "41", + "id": "39", "metadata": {}, "source": [ "### 6.1 Flow Sankey\n", @@ -462,7 +445,7 @@ { "cell_type": "code", "execution_count": null, - "id": "42", + "id": "40", "metadata": {}, "outputs": [], "source": [ @@ -472,7 +455,7 @@ { "cell_type": "code", "execution_count": null, - "id": "43", + "id": "41", "metadata": {}, "outputs": [], "source": [ @@ -482,7 +465,7 @@ }, { "cell_type": "markdown", - "id": "44", + "id": "42", "metadata": {}, "source": [ "### 6.2 Sizes Sankey\n", @@ -493,7 +476,7 @@ { "cell_type": "code", "execution_count": null, - "id": "45", + "id": "43", "metadata": {}, "outputs": [], "source": [ @@ -502,7 +485,7 @@ }, { "cell_type": "markdown", - "id": "46", + "id": "44", "metadata": {}, "source": [ "### 6.3 Peak Flow Sankey\n", @@ -513,7 +496,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47", + "id": "45", "metadata": {}, "outputs": [], "source": [ @@ -522,7 +505,7 @@ }, { "cell_type": "markdown", - "id": "48", + "id": "46", "metadata": {}, "source": [ "### 6.4 Effects Sankey\n", @@ -533,7 +516,7 @@ { "cell_type": "code", "execution_count": null, - "id": "49", + "id": "47", "metadata": {}, "outputs": [], "source": [ @@ -543,7 +526,7 @@ { "cell_type": "code", "execution_count": null, - "id": "50", + "id": "48", "metadata": {}, "outputs": [], "source": [ @@ -553,7 +536,7 @@ }, { "cell_type": "markdown", - "id": "51", + "id": "49", "metadata": {}, "source": [ "### 6.5 Filtering with `select`\n", @@ -564,7 +547,7 @@ { "cell_type": "code", "execution_count": null, - "id": "52", + "id": "50", "metadata": {}, "outputs": [], "source": [ @@ -574,7 +557,7 @@ }, { "cell_type": "markdown", - "id": "53", + "id": "51", "metadata": {}, "source": [ "## 7. Topology Visualization\n", @@ -584,7 +567,7 @@ }, { "cell_type": "markdown", - "id": "54", + "id": "52", "metadata": {}, "source": [ "### 7.1 Topology Plot\n", @@ -595,7 +578,7 @@ { "cell_type": "code", "execution_count": null, - "id": "55", + "id": "53", "metadata": {}, "outputs": [], "source": [ @@ -605,7 +588,7 @@ { "cell_type": "code", "execution_count": null, - "id": "56", + "id": "54", "metadata": {}, "outputs": [], "source": [ @@ -614,7 +597,7 @@ }, { "cell_type": "markdown", - "id": "57", + "id": "55", "metadata": {}, "source": [ "### 7.2 Topology Info\n", @@ -625,7 +608,7 @@ { "cell_type": "code", "execution_count": null, - "id": "58", + "id": "56", "metadata": {}, "outputs": [], "source": [ @@ -642,7 +625,7 @@ }, { "cell_type": "markdown", - "id": "59", + "id": "57", "metadata": {}, "source": [ "## 8. Multi-Period/Scenario Data\n", @@ -653,7 +636,7 @@ { "cell_type": "code", "execution_count": null, - "id": "60", + "id": "58", "metadata": {}, "outputs": [], "source": [ @@ -666,7 +649,7 @@ { "cell_type": "code", "execution_count": null, - "id": "61", + "id": "59", "metadata": {}, "outputs": [], "source": [ @@ -677,7 +660,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62", + "id": "60", "metadata": {}, "outputs": [], "source": [ @@ -688,7 +671,7 @@ { "cell_type": "code", "execution_count": null, - "id": "63", + "id": "61", "metadata": {}, "outputs": [], "source": [ @@ -698,7 +681,7 @@ }, { "cell_type": "markdown", - "id": "64", + "id": "62", "metadata": {}, "source": [ "## 9. Color Customization\n", @@ -709,7 +692,7 @@ { "cell_type": "code", "execution_count": null, - "id": "65", + "id": "63", "metadata": {}, "outputs": [], "source": [ @@ -720,7 +703,7 @@ { "cell_type": "code", "execution_count": null, - "id": "66", + "id": "64", "metadata": {}, "outputs": [], "source": [ @@ -731,7 +714,7 @@ { "cell_type": "code", "execution_count": null, - "id": "67", + "id": "65", "metadata": {}, "outputs": [], "source": [ @@ -749,7 +732,7 @@ }, { "cell_type": "markdown", - "id": "68", + "id": "66", "metadata": {}, "source": [ "## 10. Exporting Results\n", @@ -760,7 +743,7 @@ { "cell_type": "code", "execution_count": null, - "id": "69", + "id": "67", "metadata": {}, "outputs": [], "source": [ @@ -775,7 +758,7 @@ { "cell_type": "code", "execution_count": null, - "id": "70", + "id": "68", "metadata": {}, "outputs": [], "source": [ @@ -787,7 +770,7 @@ { "cell_type": "code", "execution_count": null, - "id": "71", + "id": "69", "metadata": {}, "outputs": [], "source": [ @@ -800,7 +783,7 @@ }, { "cell_type": "markdown", - "id": "72", + "id": "70", "metadata": {}, "source": [ "## Summary\n", diff --git a/docs/notebooks/data/__init__.py b/docs/notebooks/data/__init__.py new file mode 100644 index 000000000..fd6d62d1d --- /dev/null +++ b/docs/notebooks/data/__init__.py @@ -0,0 +1 @@ +# Data generation utilities for flixopt documentation examples diff --git a/docs/notebooks/data/generate_example_systems.py b/docs/notebooks/data/generate_example_systems.py index 556463302..c53322ef2 100644 --- a/docs/notebooks/data/generate_example_systems.py +++ b/docs/notebooks/data/generate_example_systems.py @@ -1,26 +1,62 @@ -"""Generate example FlowSystem files for the plotting notebook. +"""Generate example FlowSystem files for notebooks. -This script creates three FlowSystems of varying complexity: +This script creates FlowSystems of varying complexity: 1. simple_system - Basic heat system (boiler + storage + sink) 2. complex_system - Multi-carrier with multiple effects and piecewise efficiency 3. multiperiod_system - System with periods and scenarios +4. district_heating_system - Real-world district heating data with investments (1 month) +5. operational_system - Real-world district heating for operational planning (2 weeks, no investments) +6. seasonal_storage_system - Solar thermal + seasonal pit storage (full year, 8760h) Run this script to regenerate the example data files. """ +import sys from pathlib import Path import numpy as np import pandas as pd +# Handle imports in different contexts (direct run, package import, mkdocs-jupyter) +try: + from .generate_realistic_profiles import ( + ElectricityLoadGenerator, + GasPriceGenerator, + ThermalLoadGenerator, + load_electricity_prices, + load_weather, + ) +except ImportError: + # Add data directory to path for mkdocs-jupyter context + try: + _data_dir = Path(__file__).parent + except NameError: + _data_dir = Path('docs/notebooks/data') + if str(_data_dir) not in sys.path: + sys.path.insert(0, str(_data_dir)) + from generate_realistic_profiles import ( + ElectricityLoadGenerator, + GasPriceGenerator, + ThermalLoadGenerator, + load_electricity_prices, + load_weather, + ) + import flixopt as fx # Output directory (same as this script) try: OUTPUT_DIR = Path(__file__).parent + DATA_DIR = Path(__file__).parent # Zeitreihen2020.csv is in the same directory except NameError: # Running in notebook context (e.g., mkdocs-jupyter) OUTPUT_DIR = Path('docs/notebooks/data') + DATA_DIR = Path('docs/notebooks/data') + +# Load shared data +_weather = load_weather() +_elec_prices = load_electricity_prices() +_elec_prices.index = _elec_prices.index.tz_localize(None) # Remove timezone for compatibility def create_simple_system() -> fx.FlowSystem: @@ -29,27 +65,22 @@ def create_simple_system() -> fx.FlowSystem: Components: - Gas boiler (150 kW) - Thermal storage (500 kWh) - - Office heat demand + - Office heat demand (BDEW profile) - One week, hourly resolution. + One week (January 2020), hourly resolution. + Uses realistic BDEW heat demand and seasonal gas prices. """ - # One week, hourly - timesteps = pd.date_range('2024-01-15', periods=168, freq='h') + # One week, hourly (January 2020 for realistic data) + timesteps = pd.date_range('2020-01-15', periods=168, freq='h') + temp = _weather.loc[timesteps, 'temperature_C'].values - # Create demand pattern - hours = np.arange(168) - hour_of_day = hours % 24 - day_of_week = (hours // 24) % 7 + # BDEW office heat demand profile (scaled to fit 150 kW boiler) + thermal_gen = ThermalLoadGenerator() + heat_demand = thermal_gen.generate(timesteps, temp, 'office', annual_demand_kwh=15_000) - base_demand = np.where((hour_of_day >= 7) & (hour_of_day <= 18), 80, 30) - weekend_factor = np.where(day_of_week >= 5, 0.5, 1.0) - - np.random.seed(42) - heat_demand = base_demand * weekend_factor + np.random.normal(0, 5, len(hours)) - heat_demand = np.clip(heat_demand, 20, 100) - - # Time-varying gas price - gas_price = np.where((hour_of_day >= 6) & (hour_of_day <= 22), 0.08, 0.05) + # Seasonal gas price + gas_gen = GasPriceGenerator() + gas_price = gas_gen.generate(timesteps) / 1000 # EUR/kWh fs = fx.FlowSystem(timesteps) fs.add_carriers( @@ -93,30 +124,32 @@ def create_complex_system() -> fx.FlowSystem: - Heat pump - Gas boiler (backup) - Thermal storage - - Heat demand + - Heat demand (BDEW retail profile) + - Electricity demand (BDEW commercial profile) Effects: costs (objective), CO2 - Three days, hourly resolution. + Three days (June 2020), hourly resolution. + Uses realistic BDEW profiles and OPSD electricity prices. """ - timesteps = pd.date_range('2024-06-01', periods=72, freq='h') - hours = np.arange(72) - hour_of_day = hours % 24 + timesteps = pd.date_range('2020-06-01', periods=72, freq='h') + temp = _weather.loc[timesteps, 'temperature_C'].values - # Demand profiles - np.random.seed(123) - heat_demand = 50 + 30 * np.sin(2 * np.pi * hour_of_day / 24 - np.pi / 2) + np.random.normal(0, 5, 72) - heat_demand = np.clip(heat_demand, 20, 100) + # BDEW demand profiles (scaled to fit component sizes) + thermal_gen = ThermalLoadGenerator() + heat_demand = thermal_gen.generate(timesteps, temp, 'retail', annual_demand_kwh=2_000) - electricity_demand = 20 + 15 * np.sin(2 * np.pi * hour_of_day / 24) + np.random.normal(0, 3, 72) - electricity_demand = np.clip(electricity_demand, 10, 50) + elec_gen = ElectricityLoadGenerator() + electricity_demand = elec_gen.generate(timesteps, 'commercial', annual_demand_kwh=50_000) - # Price profiles - electricity_price = np.where((hour_of_day >= 8) & (hour_of_day <= 20), 0.25, 0.12) - gas_price = 0.06 + # Real electricity prices (OPSD) and seasonal gas prices + electricity_price = _elec_prices.reindex(timesteps, method='ffill').values / 1000 # EUR/kWh + gas_gen = GasPriceGenerator() + gas_price = gas_gen.generate(timesteps) / 1000 # EUR/kWh - # CO2 factors (kg/kWh) - electricity_co2 = np.where((hour_of_day >= 8) & (hour_of_day <= 20), 0.4, 0.3) # Higher during peak + # CO2 factors (kg/kWh) - higher during peak hours + hour_of_day = timesteps.hour.values + electricity_co2 = np.where((hour_of_day >= 8) & (hour_of_day <= 20), 0.4, 0.3) gas_co2 = 0.2 fs = fx.FlowSystem(timesteps) @@ -229,6 +262,366 @@ def create_complex_system() -> fx.FlowSystem: return fs +def create_district_heating_system() -> fx.FlowSystem: + """Create a district heating system with BDEW profiles. + + Uses realistic German data: + - One month (January 2020), hourly resolution + - BDEW industrial heat profile + - BDEW commercial electricity profile + - OPSD electricity prices + - Seasonal gas prices + - CHP, boiler, storage, and grid connections + - Investment optimization for sizing + + Used by: 08a-aggregation, 08c-clustering, 08e-clustering-internals notebooks + """ + # One month, hourly + timesteps = pd.date_range('2020-01-01', '2020-01-31 23:00:00', freq='h') + temp = _weather.loc[timesteps, 'temperature_C'].values + + # BDEW profiles (MW scale for district heating) + thermal_gen = ThermalLoadGenerator() + heat_demand = thermal_gen.generate(timesteps, temp, 'industrial', annual_demand_kwh=15_000_000) / 1000 # MW + + elec_gen = ElectricityLoadGenerator() + electricity_demand = elec_gen.generate(timesteps, 'commercial', annual_demand_kwh=5_000_000) / 1000 # MW + + # Prices + electricity_price = _elec_prices.reindex(timesteps, method='ffill').values # EUR/MWh + gas_gen = GasPriceGenerator() + gas_price = gas_gen.generate(timesteps) # EUR/MWh + + fs = fx.FlowSystem(timesteps) + fs.add_elements( + # Buses + fx.Bus('Electricity'), + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Bus('Coal'), + # Effects + fx.Effect('costs', '€', 'Total Costs', is_standard=True, is_objective=True), + fx.Effect('CO2', 'kg', 'CO2 Emissions'), + # CHP unit with investment + fx.linear_converters.CHP( + 'CHP', + thermal_efficiency=0.58, + electrical_efficiency=0.22, + electrical_flow=fx.Flow('P_el', bus='Electricity', size=200), + thermal_flow=fx.Flow( + 'Q_th', + bus='Heat', + size=fx.InvestParameters( + minimum_size=100, + maximum_size=300, + effects_of_investment_per_size={'costs': 10}, + ), + relative_minimum=0.3, + status_parameters=fx.StatusParameters(), + ), + fuel_flow=fx.Flow('Q_fu', bus='Coal'), + ), + # Gas Boiler with investment + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.85, + thermal_flow=fx.Flow( + 'Q_th', + bus='Heat', + size=fx.InvestParameters( + minimum_size=0, + maximum_size=150, + effects_of_investment_per_size={'costs': 5}, + ), + relative_minimum=0.1, + status_parameters=fx.StatusParameters(), + ), + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + ), + # Thermal Storage with investment + fx.Storage( + 'Storage', + capacity_in_flow_hours=fx.InvestParameters( + minimum_size=0, + maximum_size=1000, + effects_of_investment_per_size={'costs': 0.5}, + ), + initial_charge_state=0, + eta_charge=1, + eta_discharge=1, + relative_loss_per_hour=0.001, + charging=fx.Flow('Charge', size=137, bus='Heat'), + discharging=fx.Flow('Discharge', size=158, bus='Heat'), + ), + # Fuel sources + fx.Source( + 'GasGrid', + outputs=[fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': gas_price, 'CO2': 0.3})], + ), + fx.Source( + 'CoalSupply', + outputs=[fx.Flow('Q_Coal', bus='Coal', size=1000, effects_per_flow_hour={'costs': 4.6, 'CO2': 0.3})], + ), + # Electricity grid + fx.Source( + 'GridBuy', + outputs=[ + fx.Flow( + 'P_el', + bus='Electricity', + size=1000, + effects_per_flow_hour={'costs': electricity_price + 0.5, 'CO2': 0.3}, + ) + ], + ), + fx.Sink( + 'GridSell', + inputs=[fx.Flow('P_el', bus='Electricity', size=1000, effects_per_flow_hour=-(electricity_price - 0.5))], + ), + # Demands + fx.Sink('HeatDemand', inputs=[fx.Flow('Q_th', bus='Heat', size=1, fixed_relative_profile=heat_demand)]), + fx.Sink( + 'ElecDemand', inputs=[fx.Flow('P_el', bus='Electricity', size=1, fixed_relative_profile=electricity_demand)] + ), + ) + return fs + + +def create_operational_system() -> fx.FlowSystem: + """Create an operational district heating system (no investments). + + Uses realistic German data (two weeks, January 2020): + - BDEW industrial heat profile + - BDEW commercial electricity profile + - OPSD electricity prices + - Seasonal gas prices + - CHP with startup costs + - Boiler with startup costs + - Storage with fixed capacity + - No investment parameters (for rolling horizon optimization) + + Used by: 08b-rolling-horizon notebook + """ + # Two weeks, hourly + timesteps = pd.date_range('2020-01-01', '2020-01-14 23:00:00', freq='h') + temp = _weather.loc[timesteps, 'temperature_C'].values + + # BDEW profiles (MW scale) + thermal_gen = ThermalLoadGenerator() + heat_demand = thermal_gen.generate(timesteps, temp, 'industrial', annual_demand_kwh=15_000_000) / 1000 # MW + + elec_gen = ElectricityLoadGenerator() + electricity_demand = elec_gen.generate(timesteps, 'commercial', annual_demand_kwh=5_000_000) / 1000 # MW + + # Prices + electricity_price = _elec_prices.reindex(timesteps, method='ffill').values # EUR/MWh + gas_gen = GasPriceGenerator() + gas_price = gas_gen.generate(timesteps) # EUR/MWh + + fs = fx.FlowSystem(timesteps) + fs.add_elements( + fx.Bus('Electricity'), + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Bus('Coal'), + fx.Effect('costs', '€', 'Total Costs', is_standard=True, is_objective=True), + fx.Effect('CO2', 'kg', 'CO2 Emissions'), + # CHP with startup costs + fx.linear_converters.CHP( + 'CHP', + thermal_efficiency=0.58, + electrical_efficiency=0.22, + status_parameters=fx.StatusParameters(effects_per_startup=24000), + electrical_flow=fx.Flow('P_el', bus='Electricity', size=200), + thermal_flow=fx.Flow('Q_th', bus='Heat', size=200), + fuel_flow=fx.Flow('Q_fu', bus='Coal', size=288, relative_minimum=87 / 288, previous_flow_rate=100), + ), + # Boiler with startup costs + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.85, + thermal_flow=fx.Flow('Q_th', bus='Heat'), + fuel_flow=fx.Flow( + 'Q_fu', + bus='Gas', + size=95, + relative_minimum=12 / 95, + previous_flow_rate=20, + status_parameters=fx.StatusParameters(effects_per_startup=1000), + ), + ), + # Storage with fixed capacity + fx.Storage( + 'Storage', + 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('Charge', size=137, bus='Heat'), + discharging=fx.Flow('Discharge', size=158, bus='Heat'), + ), + fx.Source( + 'GasGrid', + outputs=[fx.Flow('Q_Gas', bus='Gas', size=1000, effects_per_flow_hour={'costs': gas_price, 'CO2': 0.3})], + ), + fx.Source( + 'CoalSupply', + outputs=[fx.Flow('Q_Coal', bus='Coal', size=1000, effects_per_flow_hour={'costs': 4.6, 'CO2': 0.3})], + ), + fx.Source( + 'GridBuy', + outputs=[ + fx.Flow( + 'P_el', + bus='Electricity', + size=1000, + effects_per_flow_hour={'costs': electricity_price + 0.5, 'CO2': 0.3}, + ) + ], + ), + fx.Sink( + 'GridSell', + inputs=[fx.Flow('P_el', bus='Electricity', size=1000, effects_per_flow_hour=-(electricity_price - 0.5))], + ), + fx.Sink('HeatDemand', inputs=[fx.Flow('Q_th', bus='Heat', size=1, fixed_relative_profile=heat_demand)]), + fx.Sink( + 'ElecDemand', inputs=[fx.Flow('P_el', bus='Electricity', size=1, fixed_relative_profile=electricity_demand)] + ), + ) + return fs + + +def create_seasonal_storage_system() -> fx.FlowSystem: + """Create a district heating system with solar thermal and seasonal storage. + + Demonstrates seasonal storage value with: + - Full year at hourly resolution (8760 timesteps) + - Solar thermal from PVGIS irradiance data + - Heat demand from BDEW industrial profile + - Large seasonal pit storage (bridges seasons) + - Gas boiler backup + + This system clearly shows the value of inter-cluster storage linking: + - Summer: excess solar heat stored in pit + - Winter: stored heat reduces gas consumption + + Uses realistic PVGIS solar irradiance and BDEW heat profiles. + Used by: 08c-clustering, 08c2-clustering-storage-modes notebooks + """ + # Full year, hourly (use non-leap year to match TMY data which has 8760 hours) + timesteps = pd.date_range('2019-01-01', periods=8760, freq='h') + # Map to 2020 weather data (TMY has 8760 hours, no Feb 29) + temp = _weather['temperature_C'].values + ghi = _weather['ghi_W_m2'].values + + # --- Solar thermal profile from PVGIS irradiance --- + # Normalize GHI to 0-1 range and apply collector efficiency + solar_profile = ghi / 1000 # Normalized (1000 W/mΒ² = 1.0) + solar_profile = np.clip(solar_profile, 0, 1) + + # --- Heat demand from BDEW industrial profile --- + # Scale to MW (district heating scale) + # Use 2019 year for demandlib (non-leap year) + thermal_gen = ThermalLoadGenerator(year=2019) + heat_demand_kw = thermal_gen.generate(timesteps, temp, 'industrial', annual_demand_kwh=20_000_000) + heat_demand = heat_demand_kw / 1000 # Convert to MW + + # --- Gas price with seasonal variation --- + gas_gen = GasPriceGenerator() + gas_price = gas_gen.generate(timesteps) # EUR/MWh + + fs = fx.FlowSystem(timesteps) + fs.add_carriers( + fx.Carrier('gas', '#3498db', 'MW'), + fx.Carrier('heat', '#e74c3c', 'MW'), + ) + fs.add_elements( + # Buses + fx.Bus('Gas', carrier='gas'), + fx.Bus('Heat', carrier='heat'), + # Effects + fx.Effect('costs', '€', 'Total Costs', is_standard=True, is_objective=True), + fx.Effect('CO2', 'kg', 'CO2 Emissions'), + # Solar thermal collector (investment) - profile includes 70% collector efficiency + # Costs annualized for single-year analysis + fx.Source( + 'SolarThermal', + outputs=[ + fx.Flow( + 'Q_th', + bus='Heat', + size=fx.InvestParameters( + minimum_size=0, + maximum_size=20, # MW peak + effects_of_investment_per_size={'costs': 15000}, # €/MW (annualized) + ), + fixed_relative_profile=solar_profile * 0.7, # 70% collector efficiency + ) + ], + ), + # Gas boiler (backup) + fx.linear_converters.Boiler( + 'GasBoiler', + thermal_efficiency=0.90, + thermal_flow=fx.Flow( + 'Q_th', + bus='Heat', + size=fx.InvestParameters( + minimum_size=0, + maximum_size=8, # MW + effects_of_investment_per_size={'costs': 20000}, # €/MW (annualized) + ), + ), + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + ), + # Gas supply (higher price makes solar+storage more attractive) + fx.Source( + 'GasGrid', + outputs=[ + fx.Flow( + 'Q_gas', + bus='Gas', + size=20, + effects_per_flow_hour={'costs': gas_price * 1.5, 'CO2': 0.2}, # €/MWh + ) + ], + ), + # Seasonal pit storage (large capacity for seasonal shifting) + fx.Storage( + 'SeasonalStorage', + capacity_in_flow_hours=fx.InvestParameters( + minimum_size=0, + maximum_size=50000, # MWh - large for seasonal storage + effects_of_investment_per_size={'costs': 20}, # €/MWh (pit storage is cheap) + ), + initial_charge_state='equals_final', # Yearly cyclic + eta_charge=0.95, + eta_discharge=0.95, + relative_loss_per_hour=0.0001, # Very low losses for pit storage + charging=fx.Flow( + 'Charge', + bus='Heat', + size=fx.InvestParameters(maximum_size=10, effects_of_investment_per_size={'costs': 5000}), + ), + discharging=fx.Flow( + 'Discharge', + bus='Heat', + size=fx.InvestParameters(maximum_size=10, effects_of_investment_per_size={'costs': 5000}), + ), + ), + # Heat demand + fx.Sink( + 'HeatDemand', + inputs=[fx.Flow('Q_th', bus='Heat', size=1, fixed_relative_profile=heat_demand)], + ), + ) + return fs + + def create_multiperiod_system() -> fx.FlowSystem: """Create a system with multiple periods and scenarios. @@ -236,10 +629,14 @@ def create_multiperiod_system() -> fx.FlowSystem: - 3 planning periods (years 2024, 2025, 2026) - 2 scenarios (high demand, low demand) - Each period: 48 hours (2 days representative) + Each period: 336 hours (2 weeks) - suitable for clustering demonstrations. + Use transform.sisel() to select subsets if needed. + + Uses BDEW residential heat profile as base, scaled for scenarios. """ - timesteps = pd.date_range('2024-01-01', periods=48, freq='h') - hour_of_day = np.arange(48) % 24 + n_hours = 336 # 2 weeks + timesteps = pd.date_range('2020-01-01', periods=n_hours, freq='h') + temp = _weather.loc[timesteps, 'temperature_C'].values # Period definitions (years) periods = pd.Index([2024, 2025, 2026], name='period') @@ -248,25 +645,27 @@ def create_multiperiod_system() -> fx.FlowSystem: scenarios = pd.Index(['high_demand', 'low_demand'], name='scenario') scenario_weights = np.array([0.3, 0.7]) - # Base demand pattern (hourly) - base_pattern = np.where((hour_of_day >= 7) & (hour_of_day <= 18), 80.0, 35.0) + # BDEW residential heat profile as base (scaled to fit 250 kW boiler with scenarios) + thermal_gen = ThermalLoadGenerator() + base_demand = thermal_gen.generate(timesteps, temp, 'residential', annual_demand_kwh=30_000) # Scenario-specific scaling - np.random.seed(42) - high_demand = base_pattern * 1.2 + np.random.normal(0, 5, 48) - low_demand = base_pattern * 0.85 + np.random.normal(0, 3, 48) + high_demand = base_demand * 1.3 + low_demand = base_demand * 0.7 # Create DataFrame with scenario columns heat_demand = pd.DataFrame( { - 'high_demand': np.clip(high_demand, 20, 120), - 'low_demand': np.clip(low_demand, 15, 90), + 'high_demand': high_demand, + 'low_demand': low_demand, }, index=timesteps, ) - # Gas price varies by period (rising costs) - gas_prices = np.array([0.06, 0.08, 0.10]) # Per period + # Gas price varies by period (rising costs, based on seasonal price) + gas_gen = GasPriceGenerator() + base_gas = gas_gen.generate(timesteps).mean() / 1000 # Average EUR/kWh + gas_prices = np.array([base_gas, base_gas * 1.2, base_gas * 1.5]) # Rising costs per period fs = fx.FlowSystem( timesteps, @@ -316,28 +715,23 @@ def create_multiperiod_system() -> fx.FlowSystem: def main(): """Generate all example systems and save to netCDF.""" - solver = fx.solvers.HighsSolver(log_to_console=False) - systems = [ ('simple_system', create_simple_system), ('complex_system', create_complex_system), ('multiperiod_system', create_multiperiod_system), + ('district_heating_system', create_district_heating_system), + ('operational_system', create_operational_system), + ('seasonal_storage_system', create_seasonal_storage_system), ] for name, create_func in systems: print(f'Creating {name}...') fs = create_func() - print(' Optimizing...') - fs.optimize(solver) - output_path = OUTPUT_DIR / f'{name}.nc4' print(f' Saving to {output_path}...') fs.to_netcdf(output_path, overwrite=True) - print(f' Done. Objective: {fs.solution["objective"].item():.2f}') - print() - print('All systems generated successfully!') diff --git a/docs/notebooks/data/generate_realistic_profiles.py b/docs/notebooks/data/generate_realistic_profiles.py new file mode 100644 index 000000000..5fde58397 --- /dev/null +++ b/docs/notebooks/data/generate_realistic_profiles.py @@ -0,0 +1,262 @@ +"""Generate realistic German energy profiles for flixOpt examples. + +This module provides functions to create realistic time series data for: +- Thermal load profiles (BDEW standard load profiles via demandlib) +- Electricity load profiles (BDEW standard load profiles via demandlib) +- Solar generation profiles (via pvlib) +- Energy prices (bundled OPSD data) +- Weather data (bundled PVGIS TMY data for Dresden) + +Example: + >>> from generate_realistic_profiles import load_weather, ThermalLoadGenerator + >>> weather = load_weather() + >>> thermal = ThermalLoadGenerator() + >>> heat_demand = thermal.generate(weather.index, weather['temperature_C'], 'residential', 50000) +""" + +from __future__ import annotations + +import warnings +from pathlib import Path + +import holidays +import numpy as np +import pandas as pd +import pvlib +from demandlib import bdew + +warnings.resetwarnings() # Reset to default behavior due to weird dependency behaviour + +# Data directory +DATA_DIR = Path(__file__).parent / 'raw' + + +# === Data Loading === + + +def load_weather() -> pd.DataFrame: + """Load PVGIS TMY weather data for Dresden. + + Returns + ------- + pd.DataFrame + Hourly weather data with columns: + - temperature_C: Ambient temperature (Β°C) + - ghi_W_m2: Global horizontal irradiance (W/mΒ²) + - dni_W_m2: Direct normal irradiance (W/mΒ²) + - dhi_W_m2: Diffuse horizontal irradiance (W/mΒ²) + - wind_speed_m_s: Wind speed at 10m (m/s) + """ + return pd.read_csv(DATA_DIR / 'tmy_dresden.csv', parse_dates=['time'], index_col='time') + + +def load_electricity_prices() -> pd.Series: + """Load German day-ahead electricity prices (2020). + + Returns + ------- + pd.Series + Hourly electricity prices in EUR/MWh + """ + df = pd.read_csv(DATA_DIR / 'electricity_prices_de_2020.csv', parse_dates=['time'], index_col='time') + return df['price_eur_mwh'] + + +# === Profile Generators === + + +class ThermalLoadGenerator: + """Generate thermal load profiles using BDEW standard load profiles. + + Uses demandlib to create realistic heat demand profiles based on + German BDEW (Bundesverband der Energie- und Wasserwirtschaft) standards. + """ + + BUILDING_TYPES = { + 'residential': {'shlp_type': 'EFH', 'building_class': 5}, # Single-family house + 'residential_multi': {'shlp_type': 'MFH', 'building_class': 5}, # Multi-family + 'office': {'shlp_type': 'GKO', 'building_class': 0}, # Commercial office + 'retail': {'shlp_type': 'GHA', 'building_class': 0}, # Retail/shops + 'industrial': {'shlp_type': 'GMK', 'building_class': 0}, # Industrial + } + + def __init__(self, year: int = 2020): + self.year = year + self.holidays = holidays.Germany(years=year) + + def generate( + self, + timesteps: pd.DatetimeIndex, + temperature: np.ndarray | pd.Series, + building_type: str = 'residential', + annual_demand_kwh: float = 20000, + ) -> np.ndarray: + """Generate thermal load profile. + + Parameters + ---------- + timesteps + Time index for the profile + temperature + Ambient temperature in Celsius (same length as timesteps) + building_type + One of: 'residential', 'residential_multi', 'office', 'retail', 'industrial' + annual_demand_kwh + Total annual heat demand in kWh + + Returns + ------- + np.ndarray + Heat demand profile in kW + """ + params = self.BUILDING_TYPES[building_type] + temp_series = pd.Series(temperature, index=timesteps) + + profile = bdew.HeatBuilding( + timesteps, + holidays=self.holidays, + temperature=temp_series, + shlp_type=params['shlp_type'], + building_class=params['building_class'], + wind_class=0, + annual_heat_demand=annual_demand_kwh, + name=building_type, + ) + return profile.get_bdew_profile().values + + +class ElectricityLoadGenerator: + """Generate electricity load profiles using BDEW standard load profiles.""" + + CONSUMER_TYPES = { + 'household': 'h0', + 'commercial': 'g0', + 'commercial_office': 'g1', + 'commercial_retail': 'g4', + 'agricultural': 'l0', + } + + def __init__(self, year: int = 2020): + self.year = year + self.holidays = holidays.Germany(years=year) + + def generate( + self, + timesteps: pd.DatetimeIndex, + consumer_type: str = 'household', + annual_demand_kwh: float = 4000, + ) -> np.ndarray: + """Generate electricity load profile. + + Parameters + ---------- + timesteps + Time index for the profile + consumer_type + One of: 'household', 'commercial', 'commercial_office', 'commercial_retail', 'agricultural' + annual_demand_kwh + Total annual electricity demand in kWh + + Returns + ------- + np.ndarray + Electricity demand profile in kW + """ + slp_type = self.CONSUMER_TYPES[consumer_type] + e_slp = bdew.ElecSlp(self.year, holidays=self.holidays) + profile = e_slp.get_scaled_power_profiles({slp_type: annual_demand_kwh}) + # Resample to hourly and align with requested timesteps + profile_hourly = profile[slp_type].resample('h').mean() + return profile_hourly.reindex(timesteps, method='ffill').values + + +class SolarGenerator: + """Generate solar irradiance and PV generation profiles using pvlib. + + Uses Dresden location (51.05Β°N, 13.74Β°E) as default. + """ + + def __init__(self, latitude: float = 51.05, longitude: float = 13.74): + self.location = pvlib.location.Location(latitude, longitude, 'Europe/Berlin', 120, 'Dresden') + + def generate_pv_profile( + self, + timesteps: pd.DatetimeIndex, + weather: pd.DataFrame, + surface_tilt: float = 35, + surface_azimuth: float = 180, # South-facing + capacity_kw: float = 1.0, + ) -> np.ndarray: + """Generate PV power output profile. + + Parameters + ---------- + timesteps + Time index for the profile + weather + Weather data with 'ghi_W_m2', 'dni_W_m2', 'dhi_W_m2', 'temperature_C' + surface_tilt + Panel tilt angle in degrees (0=horizontal, 90=vertical) + surface_azimuth + Panel azimuth in degrees (180=south, 90=east, 270=west) + capacity_kw + Installed PV capacity in kW + + Returns + ------- + np.ndarray + PV power output in kW + """ + # Ensure weather is aligned with timesteps + weather = weather.reindex(timesteps, method='ffill') + + # Get solar position + solar_position = self.location.get_solarposition(timesteps) + + # Calculate plane-of-array irradiance + poa = pvlib.irradiance.get_total_irradiance( + surface_tilt=surface_tilt, + surface_azimuth=surface_azimuth, + solar_zenith=solar_position['apparent_zenith'], + solar_azimuth=solar_position['azimuth'], + dni=weather['dni_W_m2'], + ghi=weather['ghi_W_m2'], + dhi=weather['dhi_W_m2'], + ) + + # Simple efficiency model: ~15% module efficiency, ~85% system efficiency + system_efficiency = 0.15 * 0.85 + pv_output = poa['poa_global'] * system_efficiency * capacity_kw / 1000 + + return np.clip(pv_output.fillna(0).values, 0, capacity_kw) + + +class GasPriceGenerator: + """Generate synthetic gas price profiles with seasonal variation.""" + + def generate( + self, + timesteps: pd.DatetimeIndex, + base_price: float = 35, + winter_premium: float = 10, + ) -> np.ndarray: + """Generate gas price profile. + + Parameters + ---------- + timesteps + Time index for the profile + base_price + Base gas price in EUR/MWh + winter_premium + Additional winter price in EUR/MWh + + Returns + ------- + np.ndarray + Gas prices in EUR/MWh + """ + day_of_year = timesteps.dayofyear.values + # Peak in mid-January (day 15), trough in mid-July + seasonal = winter_premium * np.cos(2 * np.pi * (day_of_year - 15) / 365) + return base_price + seasonal diff --git a/docs/notebooks/data/raw/README.md b/docs/notebooks/data/raw/README.md new file mode 100644 index 000000000..37c83b1e5 --- /dev/null +++ b/docs/notebooks/data/raw/README.md @@ -0,0 +1,31 @@ +# Bundled Data Sources + +## Weather Data (TMY) + +**File:** `tmy_dresden.csv` +**Location:** Dresden, Germany (51.05Β°N, 13.74Β°E) +**Source:** PVGIS - Photovoltaic Geographical Information System +**Provider:** European Commission Joint Research Centre +**License:** Free for any use +**URL:** https://re.jrc.ec.europa.eu/pvg_tools/en/ + +**Columns:** +- `temperature_C`: 2m air temperature (Β°C) +- `ghi_W_m2`: Global horizontal irradiance (W/mΒ²) +- `dni_W_m2`: Direct normal irradiance (W/mΒ²) +- `dhi_W_m2`: Diffuse horizontal irradiance (W/mΒ²) +- `wind_speed_m_s`: Wind speed at 10m (m/s) +- `relative_humidity_percent`: Relative humidity (%) + +## Electricity Prices + +**File:** `electricity_prices_de_2020.csv` +**Coverage:** Germany, Jan-Sep 2020, hourly +**Source:** Open Power System Data +**License:** Open Database License (ODbL) +**URL:** https://data.open-power-system-data.org/time_series/ + +**Attribution required:** "Data from Open Power System Data. https://open-power-system-data.org" + +**Columns:** +- `price_eur_mwh`: Day-ahead electricity price (EUR/MWh) diff --git a/docs/notebooks/data/raw/electricity_prices_de_2020.csv b/docs/notebooks/data/raw/electricity_prices_de_2020.csv new file mode 100644 index 000000000..25a0f2e24 --- /dev/null +++ b/docs/notebooks/data/raw/electricity_prices_de_2020.csv @@ -0,0 +1,6574 @@ +time,price_eur_mwh +2020-01-01 00:00:00+00:00,38.6 +2020-01-01 01:00:00+00:00,36.55 +2020-01-01 02:00:00+00:00,32.32 +2020-01-01 03:00:00+00:00,30.85 +2020-01-01 04:00:00+00:00,30.14 +2020-01-01 05:00:00+00:00,30.17 +2020-01-01 06:00:00+00:00,30.0 +2020-01-01 07:00:00+00:00,30.65 +2020-01-01 08:00:00+00:00,30.65 +2020-01-01 09:00:00+00:00,30.27 +2020-01-01 10:00:00+00:00,30.34 +2020-01-01 11:00:00+00:00,30.99 +2020-01-01 12:00:00+00:00,30.04 +2020-01-01 13:00:00+00:00,30.75 +2020-01-01 14:00:00+00:00,32.11 +2020-01-01 15:00:00+00:00,35.98 +2020-01-01 16:00:00+00:00,40.4 +2020-01-01 17:00:00+00:00,44.05 +2020-01-01 18:00:00+00:00,43.15 +2020-01-01 19:00:00+00:00,43.45 +2020-01-01 20:00:00+00:00,40.68 +2020-01-01 21:00:00+00:00,40.27 +2020-01-01 22:00:00+00:00,34.85 +2020-01-01 23:00:00+00:00,35.4 +2020-01-02 00:00:00+00:00,31.98 +2020-01-02 01:00:00+00:00,30.5 +2020-01-02 02:00:00+00:00,28.79 +2020-01-02 03:00:00+00:00,28.42 +2020-01-02 04:00:00+00:00,28.75 +2020-01-02 05:00:00+00:00,34.16 +2020-01-02 06:00:00+00:00,42.07 +2020-01-02 07:00:00+00:00,44.89 +2020-01-02 08:00:00+00:00,45.26 +2020-01-02 09:00:00+00:00,45.57 +2020-01-02 10:00:00+00:00,45.09 +2020-01-02 11:00:00+00:00,45.16 +2020-01-02 12:00:00+00:00,44.9 +2020-01-02 13:00:00+00:00,44.06 +2020-01-02 14:00:00+00:00,44.84 +2020-01-02 15:00:00+00:00,44.4 +2020-01-02 16:00:00+00:00,46.05 +2020-01-02 17:00:00+00:00,46.72 +2020-01-02 18:00:00+00:00,45.26 +2020-01-02 19:00:00+00:00,39.32 +2020-01-02 20:00:00+00:00,34.06 +2020-01-02 21:00:00+00:00,32.22 +2020-01-02 22:00:00+00:00,24.99 +2020-01-02 23:00:00+00:00,21.47 +2020-01-03 00:00:00+00:00,13.04 +2020-01-03 01:00:00+00:00,1.53 +2020-01-03 02:00:00+00:00,0.14 +2020-01-03 03:00:00+00:00,0.85 +2020-01-03 04:00:00+00:00,9.92 +2020-01-03 05:00:00+00:00,24.48 +2020-01-03 06:00:00+00:00,26.68 +2020-01-03 07:00:00+00:00,28.81 +2020-01-03 08:00:00+00:00,29.28 +2020-01-03 09:00:00+00:00,28.85 +2020-01-03 10:00:00+00:00,31.8 +2020-01-03 11:00:00+00:00,37.94 +2020-01-03 12:00:00+00:00,37.9 +2020-01-03 13:00:00+00:00,38.11 +2020-01-03 14:00:00+00:00,37.91 +2020-01-03 15:00:00+00:00,38.44 +2020-01-03 16:00:00+00:00,40.47 +2020-01-03 17:00:00+00:00,41.35 +2020-01-03 18:00:00+00:00,33.37 +2020-01-03 19:00:00+00:00,28.89 +2020-01-03 20:00:00+00:00,27.7 +2020-01-03 21:00:00+00:00,25.7 +2020-01-03 22:00:00+00:00,22.04 +2020-01-03 23:00:00+00:00,22.9 +2020-01-04 00:00:00+00:00,15.95 +2020-01-04 01:00:00+00:00,16.63 +2020-01-04 02:00:00+00:00,6.45 +2020-01-04 03:00:00+00:00,3.83 +2020-01-04 04:00:00+00:00,0.12 +2020-01-04 05:00:00+00:00,0.07 +2020-01-04 06:00:00+00:00,19.07 +2020-01-04 07:00:00+00:00,17.49 +2020-01-04 08:00:00+00:00,23.98 +2020-01-04 09:00:00+00:00,8.8 +2020-01-04 10:00:00+00:00,17.95 +2020-01-04 11:00:00+00:00,19.5 +2020-01-04 12:00:00+00:00,13.74 +2020-01-04 13:00:00+00:00,17.42 +2020-01-04 14:00:00+00:00,20.38 +2020-01-04 15:00:00+00:00,25.08 +2020-01-04 16:00:00+00:00,28.88 +2020-01-04 17:00:00+00:00,32.02 +2020-01-04 18:00:00+00:00,35.35 +2020-01-04 19:00:00+00:00,29.98 +2020-01-04 20:00:00+00:00,34.46 +2020-01-04 21:00:00+00:00,39.75 +2020-01-04 22:00:00+00:00,37.95 +2020-01-04 23:00:00+00:00,33.1 +2020-01-05 00:00:00+00:00,32.28 +2020-01-05 01:00:00+00:00,31.18 +2020-01-05 02:00:00+00:00,30.1 +2020-01-05 03:00:00+00:00,29.96 +2020-01-05 04:00:00+00:00,29.88 +2020-01-05 05:00:00+00:00,30.38 +2020-01-05 06:00:00+00:00,31.15 +2020-01-05 07:00:00+00:00,32.09 +2020-01-05 08:00:00+00:00,34.27 +2020-01-05 09:00:00+00:00,37.53 +2020-01-05 10:00:00+00:00,38.99 +2020-01-05 11:00:00+00:00,38.15 +2020-01-05 12:00:00+00:00,35.37 +2020-01-05 13:00:00+00:00,34.44 +2020-01-05 14:00:00+00:00,36.1 +2020-01-05 15:00:00+00:00,40.59 +2020-01-05 16:00:00+00:00,44.68 +2020-01-05 17:00:00+00:00,46.16 +2020-01-05 18:00:00+00:00,44.62 +2020-01-05 19:00:00+00:00,39.5 +2020-01-05 20:00:00+00:00,35.76 +2020-01-05 21:00:00+00:00,36.49 +2020-01-05 22:00:00+00:00,30.49 +2020-01-05 23:00:00+00:00,29.16 +2020-01-06 00:00:00+00:00,29.0 +2020-01-06 01:00:00+00:00,29.08 +2020-01-06 02:00:00+00:00,27.72 +2020-01-06 03:00:00+00:00,27.03 +2020-01-06 04:00:00+00:00,28.98 +2020-01-06 05:00:00+00:00,33.18 +2020-01-06 06:00:00+00:00,43.13 +2020-01-06 07:00:00+00:00,44.52 +2020-01-06 08:00:00+00:00,44.96 +2020-01-06 09:00:00+00:00,44.0 +2020-01-06 10:00:00+00:00,42.46 +2020-01-06 11:00:00+00:00,41.3 +2020-01-06 12:00:00+00:00,40.51 +2020-01-06 13:00:00+00:00,41.22 +2020-01-06 14:00:00+00:00,43.28 +2020-01-06 15:00:00+00:00,43.68 +2020-01-06 16:00:00+00:00,47.9 +2020-01-06 17:00:00+00:00,48.91 +2020-01-06 18:00:00+00:00,45.04 +2020-01-06 19:00:00+00:00,40.28 +2020-01-06 20:00:00+00:00,33.89 +2020-01-06 21:00:00+00:00,33.58 +2020-01-06 22:00:00+00:00,32.41 +2020-01-06 23:00:00+00:00,30.75 +2020-01-07 00:00:00+00:00,31.03 +2020-01-07 01:00:00+00:00,29.88 +2020-01-07 02:00:00+00:00,29.0 +2020-01-07 03:00:00+00:00,29.65 +2020-01-07 04:00:00+00:00,31.78 +2020-01-07 05:00:00+00:00,40.87 +2020-01-07 06:00:00+00:00,49.01 +2020-01-07 07:00:00+00:00,51.09 +2020-01-07 08:00:00+00:00,51.12 +2020-01-07 09:00:00+00:00,49.83 +2020-01-07 10:00:00+00:00,49.16 +2020-01-07 11:00:00+00:00,48.43 +2020-01-07 12:00:00+00:00,47.99 +2020-01-07 13:00:00+00:00,47.41 +2020-01-07 14:00:00+00:00,45.87 +2020-01-07 15:00:00+00:00,45.9 +2020-01-07 16:00:00+00:00,47.96 +2020-01-07 17:00:00+00:00,48.11 +2020-01-07 18:00:00+00:00,43.63 +2020-01-07 19:00:00+00:00,33.6 +2020-01-07 20:00:00+00:00,32.93 +2020-01-07 21:00:00+00:00,31.29 +2020-01-07 22:00:00+00:00,26.28 +2020-01-07 23:00:00+00:00,18.95 +2020-01-08 00:00:00+00:00,4.96 +2020-01-08 01:00:00+00:00,0.1 +2020-01-08 02:00:00+00:00,0.11 +2020-01-08 03:00:00+00:00,1.75 +2020-01-08 04:00:00+00:00,20.74 +2020-01-08 05:00:00+00:00,25.57 +2020-01-08 06:00:00+00:00,32.47 +2020-01-08 07:00:00+00:00,33.07 +2020-01-08 08:00:00+00:00,33.05 +2020-01-08 09:00:00+00:00,34.18 +2020-01-08 10:00:00+00:00,39.63 +2020-01-08 11:00:00+00:00,41.35 +2020-01-08 12:00:00+00:00,44.83 +2020-01-08 13:00:00+00:00,46.04 +2020-01-08 14:00:00+00:00,46.33 +2020-01-08 15:00:00+00:00,47.9 +2020-01-08 16:00:00+00:00,51.21 +2020-01-08 17:00:00+00:00,55.92 +2020-01-08 18:00:00+00:00,53.69 +2020-01-08 19:00:00+00:00,48.1 +2020-01-08 20:00:00+00:00,44.92 +2020-01-08 21:00:00+00:00,41.67 +2020-01-08 22:00:00+00:00,39.41 +2020-01-08 23:00:00+00:00,34.08 +2020-01-09 00:00:00+00:00,32.2 +2020-01-09 01:00:00+00:00,32.56 +2020-01-09 02:00:00+00:00,32.35 +2020-01-09 03:00:00+00:00,29.0 +2020-01-09 04:00:00+00:00,30.86 +2020-01-09 05:00:00+00:00,38.95 +2020-01-09 06:00:00+00:00,46.86 +2020-01-09 07:00:00+00:00,47.92 +2020-01-09 08:00:00+00:00,45.68 +2020-01-09 09:00:00+00:00,43.61 +2020-01-09 10:00:00+00:00,40.0 +2020-01-09 11:00:00+00:00,37.06 +2020-01-09 12:00:00+00:00,33.45 +2020-01-09 13:00:00+00:00,32.2 +2020-01-09 14:00:00+00:00,31.86 +2020-01-09 15:00:00+00:00,32.59 +2020-01-09 16:00:00+00:00,42.85 +2020-01-09 17:00:00+00:00,41.73 +2020-01-09 18:00:00+00:00,40.31 +2020-01-09 19:00:00+00:00,32.96 +2020-01-09 20:00:00+00:00,30.72 +2020-01-09 21:00:00+00:00,31.02 +2020-01-09 22:00:00+00:00,29.14 +2020-01-09 23:00:00+00:00,26.94 +2020-01-10 00:00:00+00:00,26.59 +2020-01-10 01:00:00+00:00,25.81 +2020-01-10 02:00:00+00:00,25.89 +2020-01-10 03:00:00+00:00,26.2 +2020-01-10 04:00:00+00:00,26.95 +2020-01-10 05:00:00+00:00,28.95 +2020-01-10 06:00:00+00:00,43.29 +2020-01-10 07:00:00+00:00,47.4 +2020-01-10 08:00:00+00:00,40.63 +2020-01-10 09:00:00+00:00,36.26 +2020-01-10 10:00:00+00:00,32.05 +2020-01-10 11:00:00+00:00,28.64 +2020-01-10 12:00:00+00:00,28.45 +2020-01-10 13:00:00+00:00,28.23 +2020-01-10 14:00:00+00:00,29.56 +2020-01-10 15:00:00+00:00,36.43 +2020-01-10 16:00:00+00:00,45.0 +2020-01-10 17:00:00+00:00,46.73 +2020-01-10 18:00:00+00:00,45.94 +2020-01-10 19:00:00+00:00,45.02 +2020-01-10 20:00:00+00:00,41.29 +2020-01-10 21:00:00+00:00,39.9 +2020-01-10 22:00:00+00:00,33.04 +2020-01-10 23:00:00+00:00,35.01 +2020-01-11 00:00:00+00:00,34.0 +2020-01-11 01:00:00+00:00,31.43 +2020-01-11 02:00:00+00:00,29.14 +2020-01-11 03:00:00+00:00,28.86 +2020-01-11 04:00:00+00:00,28.43 +2020-01-11 05:00:00+00:00,29.22 +2020-01-11 06:00:00+00:00,31.22 +2020-01-11 07:00:00+00:00,35.68 +2020-01-11 08:00:00+00:00,40.0 +2020-01-11 09:00:00+00:00,38.01 +2020-01-11 10:00:00+00:00,37.9 +2020-01-11 11:00:00+00:00,36.08 +2020-01-11 12:00:00+00:00,32.96 +2020-01-11 13:00:00+00:00,31.1 +2020-01-11 14:00:00+00:00,32.25 +2020-01-11 15:00:00+00:00,32.55 +2020-01-11 16:00:00+00:00,40.56 +2020-01-11 17:00:00+00:00,34.46 +2020-01-11 18:00:00+00:00,30.01 +2020-01-11 19:00:00+00:00,25.74 +2020-01-11 20:00:00+00:00,23.73 +2020-01-11 21:00:00+00:00,25.24 +2020-01-11 22:00:00+00:00,20.96 +2020-01-11 23:00:00+00:00,22.82 +2020-01-12 00:00:00+00:00,19.37 +2020-01-12 01:00:00+00:00,18.36 +2020-01-12 02:00:00+00:00,18.34 +2020-01-12 03:00:00+00:00,18.16 +2020-01-12 04:00:00+00:00,18.66 +2020-01-12 05:00:00+00:00,17.39 +2020-01-12 06:00:00+00:00,18.22 +2020-01-12 07:00:00+00:00,22.1 +2020-01-12 08:00:00+00:00,23.93 +2020-01-12 09:00:00+00:00,24.23 +2020-01-12 10:00:00+00:00,24.84 +2020-01-12 11:00:00+00:00,24.45 +2020-01-12 12:00:00+00:00,22.46 +2020-01-12 13:00:00+00:00,20.05 +2020-01-12 14:00:00+00:00,21.48 +2020-01-12 15:00:00+00:00,24.71 +2020-01-12 16:00:00+00:00,26.5 +2020-01-12 17:00:00+00:00,26.68 +2020-01-12 18:00:00+00:00,26.16 +2020-01-12 19:00:00+00:00,25.62 +2020-01-12 20:00:00+00:00,25.53 +2020-01-12 21:00:00+00:00,27.1 +2020-01-12 22:00:00+00:00,26.14 +2020-01-12 23:00:00+00:00,21.82 +2020-01-13 00:00:00+00:00,23.98 +2020-01-13 01:00:00+00:00,25.23 +2020-01-13 02:00:00+00:00,24.85 +2020-01-13 03:00:00+00:00,25.01 +2020-01-13 04:00:00+00:00,27.32 +2020-01-13 05:00:00+00:00,38.4 +2020-01-13 06:00:00+00:00,48.64 +2020-01-13 07:00:00+00:00,52.93 +2020-01-13 08:00:00+00:00,49.89 +2020-01-13 09:00:00+00:00,48.89 +2020-01-13 10:00:00+00:00,47.2 +2020-01-13 11:00:00+00:00,47.0 +2020-01-13 12:00:00+00:00,45.95 +2020-01-13 13:00:00+00:00,45.0 +2020-01-13 14:00:00+00:00,45.96 +2020-01-13 15:00:00+00:00,43.68 +2020-01-13 16:00:00+00:00,46.81 +2020-01-13 17:00:00+00:00,45.27 +2020-01-13 18:00:00+00:00,42.55 +2020-01-13 19:00:00+00:00,31.9 +2020-01-13 20:00:00+00:00,26.68 +2020-01-13 21:00:00+00:00,25.96 +2020-01-13 22:00:00+00:00,23.4 +2020-01-13 23:00:00+00:00,22.59 +2020-01-14 00:00:00+00:00,16.5 +2020-01-14 01:00:00+00:00,8.89 +2020-01-14 02:00:00+00:00,1.52 +2020-01-14 03:00:00+00:00,1.58 +2020-01-14 04:00:00+00:00,14.13 +2020-01-14 05:00:00+00:00,25.23 +2020-01-14 06:00:00+00:00,27.13 +2020-01-14 07:00:00+00:00,28.55 +2020-01-14 08:00:00+00:00,27.68 +2020-01-14 09:00:00+00:00,27.42 +2020-01-14 10:00:00+00:00,27.43 +2020-01-14 11:00:00+00:00,27.68 +2020-01-14 12:00:00+00:00,29.56 +2020-01-14 13:00:00+00:00,32.0 +2020-01-14 14:00:00+00:00,32.74 +2020-01-14 15:00:00+00:00,30.38 +2020-01-14 16:00:00+00:00,38.0 +2020-01-14 17:00:00+00:00,30.32 +2020-01-14 18:00:00+00:00,26.94 +2020-01-14 19:00:00+00:00,25.74 +2020-01-14 20:00:00+00:00,23.82 +2020-01-14 21:00:00+00:00,22.3 +2020-01-14 22:00:00+00:00,12.4 +2020-01-14 23:00:00+00:00,16.14 +2020-01-15 00:00:00+00:00,5.06 +2020-01-15 01:00:00+00:00,0.11 +2020-01-15 02:00:00+00:00,1.77 +2020-01-15 03:00:00+00:00,7.13 +2020-01-15 04:00:00+00:00,17.86 +2020-01-15 05:00:00+00:00,25.18 +2020-01-15 06:00:00+00:00,35.52 +2020-01-15 07:00:00+00:00,36.56 +2020-01-15 08:00:00+00:00,33.33 +2020-01-15 09:00:00+00:00,25.34 +2020-01-15 10:00:00+00:00,24.98 +2020-01-15 11:00:00+00:00,25.05 +2020-01-15 12:00:00+00:00,25.12 +2020-01-15 13:00:00+00:00,25.24 +2020-01-15 14:00:00+00:00,30.17 +2020-01-15 15:00:00+00:00,29.98 +2020-01-15 16:00:00+00:00,36.49 +2020-01-15 17:00:00+00:00,35.46 +2020-01-15 18:00:00+00:00,35.28 +2020-01-15 19:00:00+00:00,33.65 +2020-01-15 20:00:00+00:00,29.57 +2020-01-15 21:00:00+00:00,33.59 +2020-01-15 22:00:00+00:00,30.46 +2020-01-15 23:00:00+00:00,28.38 +2020-01-16 00:00:00+00:00,30.26 +2020-01-16 01:00:00+00:00,29.92 +2020-01-16 02:00:00+00:00,29.39 +2020-01-16 03:00:00+00:00,29.64 +2020-01-16 04:00:00+00:00,31.1 +2020-01-16 05:00:00+00:00,39.04 +2020-01-16 06:00:00+00:00,45.42 +2020-01-16 07:00:00+00:00,52.4 +2020-01-16 08:00:00+00:00,47.0 +2020-01-16 09:00:00+00:00,43.51 +2020-01-16 10:00:00+00:00,42.1 +2020-01-16 11:00:00+00:00,40.36 +2020-01-16 12:00:00+00:00,41.16 +2020-01-16 13:00:00+00:00,42.91 +2020-01-16 14:00:00+00:00,46.0 +2020-01-16 15:00:00+00:00,46.45 +2020-01-16 16:00:00+00:00,44.61 +2020-01-16 17:00:00+00:00,43.15 +2020-01-16 18:00:00+00:00,38.86 +2020-01-16 19:00:00+00:00,33.42 +2020-01-16 20:00:00+00:00,30.8 +2020-01-16 21:00:00+00:00,31.56 +2020-01-16 22:00:00+00:00,28.64 +2020-01-16 23:00:00+00:00,27.36 +2020-01-17 00:00:00+00:00,27.16 +2020-01-17 01:00:00+00:00,26.58 +2020-01-17 02:00:00+00:00,25.71 +2020-01-17 03:00:00+00:00,26.01 +2020-01-17 04:00:00+00:00,27.97 +2020-01-17 05:00:00+00:00,31.09 +2020-01-17 06:00:00+00:00,40.2 +2020-01-17 07:00:00+00:00,43.8 +2020-01-17 08:00:00+00:00,42.94 +2020-01-17 09:00:00+00:00,42.34 +2020-01-17 10:00:00+00:00,40.79 +2020-01-17 11:00:00+00:00,41.04 +2020-01-17 12:00:00+00:00,39.94 +2020-01-17 13:00:00+00:00,39.02 +2020-01-17 14:00:00+00:00,43.01 +2020-01-17 15:00:00+00:00,42.8 +2020-01-17 16:00:00+00:00,45.79 +2020-01-17 17:00:00+00:00,46.0 +2020-01-17 18:00:00+00:00,44.66 +2020-01-17 19:00:00+00:00,40.99 +2020-01-17 20:00:00+00:00,33.9 +2020-01-17 21:00:00+00:00,33.38 +2020-01-17 22:00:00+00:00,33.0 +2020-01-17 23:00:00+00:00,22.15 +2020-01-18 00:00:00+00:00,24.86 +2020-01-18 01:00:00+00:00,22.24 +2020-01-18 02:00:00+00:00,25.36 +2020-01-18 03:00:00+00:00,25.39 +2020-01-18 04:00:00+00:00,25.96 +2020-01-18 05:00:00+00:00,22.99 +2020-01-18 06:00:00+00:00,28.46 +2020-01-18 07:00:00+00:00,28.1 +2020-01-18 08:00:00+00:00,37.73 +2020-01-18 09:00:00+00:00,35.21 +2020-01-18 10:00:00+00:00,33.34 +2020-01-18 11:00:00+00:00,29.2 +2020-01-18 12:00:00+00:00,31.55 +2020-01-18 13:00:00+00:00,34.78 +2020-01-18 14:00:00+00:00,35.09 +2020-01-18 15:00:00+00:00,37.27 +2020-01-18 16:00:00+00:00,41.3 +2020-01-18 17:00:00+00:00,43.04 +2020-01-18 18:00:00+00:00,41.91 +2020-01-18 19:00:00+00:00,38.59 +2020-01-18 20:00:00+00:00,35.13 +2020-01-18 21:00:00+00:00,33.48 +2020-01-18 22:00:00+00:00,31.22 +2020-01-18 23:00:00+00:00,33.1 +2020-01-19 00:00:00+00:00,30.67 +2020-01-19 01:00:00+00:00,29.47 +2020-01-19 02:00:00+00:00,28.79 +2020-01-19 03:00:00+00:00,27.71 +2020-01-19 04:00:00+00:00,27.26 +2020-01-19 05:00:00+00:00,27.82 +2020-01-19 06:00:00+00:00,30.01 +2020-01-19 07:00:00+00:00,31.47 +2020-01-19 08:00:00+00:00,35.2 +2020-01-19 09:00:00+00:00,35.2 +2020-01-19 10:00:00+00:00,34.47 +2020-01-19 11:00:00+00:00,33.36 +2020-01-19 12:00:00+00:00,29.9 +2020-01-19 13:00:00+00:00,29.92 +2020-01-19 14:00:00+00:00,30.86 +2020-01-19 15:00:00+00:00,33.07 +2020-01-19 16:00:00+00:00,42.98 +2020-01-19 17:00:00+00:00,44.24 +2020-01-19 18:00:00+00:00,43.89 +2020-01-19 19:00:00+00:00,41.14 +2020-01-19 20:00:00+00:00,36.74 +2020-01-19 21:00:00+00:00,40.0 +2020-01-19 22:00:00+00:00,36.15 +2020-01-19 23:00:00+00:00,34.24 +2020-01-20 00:00:00+00:00,34.01 +2020-01-20 01:00:00+00:00,33.08 +2020-01-20 02:00:00+00:00,32.1 +2020-01-20 03:00:00+00:00,31.5 +2020-01-20 04:00:00+00:00,34.23 +2020-01-20 05:00:00+00:00,45.63 +2020-01-20 06:00:00+00:00,55.97 +2020-01-20 07:00:00+00:00,60.0 +2020-01-20 08:00:00+00:00,57.01 +2020-01-20 09:00:00+00:00,50.63 +2020-01-20 10:00:00+00:00,48.17 +2020-01-20 11:00:00+00:00,43.96 +2020-01-20 12:00:00+00:00,43.02 +2020-01-20 13:00:00+00:00,43.11 +2020-01-20 14:00:00+00:00,44.95 +2020-01-20 15:00:00+00:00,46.0 +2020-01-20 16:00:00+00:00,55.91 +2020-01-20 17:00:00+00:00,57.05 +2020-01-20 18:00:00+00:00,54.02 +2020-01-20 19:00:00+00:00,48.68 +2020-01-20 20:00:00+00:00,41.51 +2020-01-20 21:00:00+00:00,40.0 +2020-01-20 22:00:00+00:00,34.03 +2020-01-20 23:00:00+00:00,33.17 +2020-01-21 00:00:00+00:00,33.01 +2020-01-21 01:00:00+00:00,32.28 +2020-01-21 02:00:00+00:00,32.42 +2020-01-21 03:00:00+00:00,32.9 +2020-01-21 04:00:00+00:00,33.0 +2020-01-21 05:00:00+00:00,37.98 +2020-01-21 06:00:00+00:00,49.77 +2020-01-21 07:00:00+00:00,52.36 +2020-01-21 08:00:00+00:00,48.07 +2020-01-21 09:00:00+00:00,42.1 +2020-01-21 10:00:00+00:00,39.11 +2020-01-21 11:00:00+00:00,37.29 +2020-01-21 12:00:00+00:00,38.1 +2020-01-21 13:00:00+00:00,42.6 +2020-01-21 14:00:00+00:00,47.71 +2020-01-21 15:00:00+00:00,49.97 +2020-01-21 16:00:00+00:00,55.81 +2020-01-21 17:00:00+00:00,55.29 +2020-01-21 18:00:00+00:00,50.7 +2020-01-21 19:00:00+00:00,43.75 +2020-01-21 20:00:00+00:00,40.42 +2020-01-21 21:00:00+00:00,37.52 +2020-01-21 22:00:00+00:00,33.01 +2020-01-21 23:00:00+00:00,34.7 +2020-01-22 00:00:00+00:00,33.87 +2020-01-22 01:00:00+00:00,32.6 +2020-01-22 02:00:00+00:00,32.42 +2020-01-22 03:00:00+00:00,32.64 +2020-01-22 04:00:00+00:00,33.0 +2020-01-22 05:00:00+00:00,39.12 +2020-01-22 06:00:00+00:00,49.26 +2020-01-22 07:00:00+00:00,56.53 +2020-01-22 08:00:00+00:00,50.5 +2020-01-22 09:00:00+00:00,46.27 +2020-01-22 10:00:00+00:00,45.02 +2020-01-22 11:00:00+00:00,42.82 +2020-01-22 12:00:00+00:00,43.45 +2020-01-22 13:00:00+00:00,49.96 +2020-01-22 14:00:00+00:00,52.4 +2020-01-22 15:00:00+00:00,55.63 +2020-01-22 16:00:00+00:00,62.07 +2020-01-22 17:00:00+00:00,65.09 +2020-01-22 18:00:00+00:00,59.58 +2020-01-22 19:00:00+00:00,52.52 +2020-01-22 20:00:00+00:00,47.01 +2020-01-22 21:00:00+00:00,43.99 +2020-01-22 22:00:00+00:00,41.2 +2020-01-22 23:00:00+00:00,40.1 +2020-01-23 00:00:00+00:00,39.23 +2020-01-23 01:00:00+00:00,38.34 +2020-01-23 02:00:00+00:00,36.32 +2020-01-23 03:00:00+00:00,36.6 +2020-01-23 04:00:00+00:00,41.97 +2020-01-23 05:00:00+00:00,47.0 +2020-01-23 06:00:00+00:00,58.74 +2020-01-23 07:00:00+00:00,66.62 +2020-01-23 08:00:00+00:00,64.86 +2020-01-23 09:00:00+00:00,61.68 +2020-01-23 10:00:00+00:00,57.42 +2020-01-23 11:00:00+00:00,55.36 +2020-01-23 12:00:00+00:00,53.68 +2020-01-23 13:00:00+00:00,51.2 +2020-01-23 14:00:00+00:00,53.54 +2020-01-23 15:00:00+00:00,54.75 +2020-01-23 16:00:00+00:00,62.35 +2020-01-23 17:00:00+00:00,68.64 +2020-01-23 18:00:00+00:00,60.4 +2020-01-23 19:00:00+00:00,55.53 +2020-01-23 20:00:00+00:00,47.16 +2020-01-23 21:00:00+00:00,43.9 +2020-01-23 22:00:00+00:00,41.51 +2020-01-23 23:00:00+00:00,36.23 +2020-01-24 00:00:00+00:00,37.6 +2020-01-24 01:00:00+00:00,36.76 +2020-01-24 02:00:00+00:00,35.1 +2020-01-24 03:00:00+00:00,36.47 +2020-01-24 04:00:00+00:00,36.66 +2020-01-24 05:00:00+00:00,44.54 +2020-01-24 06:00:00+00:00,58.29 +2020-01-24 07:00:00+00:00,66.74 +2020-01-24 08:00:00+00:00,65.0 +2020-01-24 09:00:00+00:00,61.12 +2020-01-24 10:00:00+00:00,58.84 +2020-01-24 11:00:00+00:00,56.61 +2020-01-24 12:00:00+00:00,53.29 +2020-01-24 13:00:00+00:00,52.0 +2020-01-24 14:00:00+00:00,53.06 +2020-01-24 15:00:00+00:00,54.01 +2020-01-24 16:00:00+00:00,60.79 +2020-01-24 17:00:00+00:00,65.1 +2020-01-24 18:00:00+00:00,59.79 +2020-01-24 19:00:00+00:00,49.17 +2020-01-24 20:00:00+00:00,44.32 +2020-01-24 21:00:00+00:00,43.7 +2020-01-24 22:00:00+00:00,40.67 +2020-01-24 23:00:00+00:00,36.19 +2020-01-25 00:00:00+00:00,35.09 +2020-01-25 01:00:00+00:00,37.01 +2020-01-25 02:00:00+00:00,35.14 +2020-01-25 03:00:00+00:00,33.11 +2020-01-25 04:00:00+00:00,33.01 +2020-01-25 05:00:00+00:00,34.12 +2020-01-25 06:00:00+00:00,37.1 +2020-01-25 07:00:00+00:00,42.42 +2020-01-25 08:00:00+00:00,44.97 +2020-01-25 09:00:00+00:00,45.96 +2020-01-25 10:00:00+00:00,44.89 +2020-01-25 11:00:00+00:00,43.67 +2020-01-25 12:00:00+00:00,41.14 +2020-01-25 13:00:00+00:00,39.61 +2020-01-25 14:00:00+00:00,41.51 +2020-01-25 15:00:00+00:00,43.53 +2020-01-25 16:00:00+00:00,46.25 +2020-01-25 17:00:00+00:00,49.99 +2020-01-25 18:00:00+00:00,45.84 +2020-01-25 19:00:00+00:00,43.01 +2020-01-25 20:00:00+00:00,38.25 +2020-01-25 21:00:00+00:00,40.57 +2020-01-25 22:00:00+00:00,36.02 +2020-01-25 23:00:00+00:00,34.39 +2020-01-26 00:00:00+00:00,33.64 +2020-01-26 01:00:00+00:00,32.4 +2020-01-26 02:00:00+00:00,29.84 +2020-01-26 03:00:00+00:00,29.33 +2020-01-26 04:00:00+00:00,29.04 +2020-01-26 05:00:00+00:00,30.02 +2020-01-26 06:00:00+00:00,29.49 +2020-01-26 07:00:00+00:00,31.17 +2020-01-26 08:00:00+00:00,34.61 +2020-01-26 09:00:00+00:00,38.92 +2020-01-26 10:00:00+00:00,41.16 +2020-01-26 11:00:00+00:00,41.86 +2020-01-26 12:00:00+00:00,38.23 +2020-01-26 13:00:00+00:00,36.03 +2020-01-26 14:00:00+00:00,34.56 +2020-01-26 15:00:00+00:00,35.81 +2020-01-26 16:00:00+00:00,39.99 +2020-01-26 17:00:00+00:00,42.0 +2020-01-26 18:00:00+00:00,35.47 +2020-01-26 19:00:00+00:00,31.27 +2020-01-26 20:00:00+00:00,27.53 +2020-01-26 21:00:00+00:00,29.09 +2020-01-26 22:00:00+00:00,27.5 +2020-01-26 23:00:00+00:00,28.09 +2020-01-27 00:00:00+00:00,27.73 +2020-01-27 01:00:00+00:00,26.13 +2020-01-27 02:00:00+00:00,23.4 +2020-01-27 03:00:00+00:00,21.98 +2020-01-27 04:00:00+00:00,26.17 +2020-01-27 05:00:00+00:00,28.83 +2020-01-27 06:00:00+00:00,42.2 +2020-01-27 07:00:00+00:00,41.56 +2020-01-27 08:00:00+00:00,40.29 +2020-01-27 09:00:00+00:00,42.71 +2020-01-27 10:00:00+00:00,39.95 +2020-01-27 11:00:00+00:00,38.02 +2020-01-27 12:00:00+00:00,40.72 +2020-01-27 13:00:00+00:00,43.93 +2020-01-27 14:00:00+00:00,45.22 +2020-01-27 15:00:00+00:00,43.17 +2020-01-27 16:00:00+00:00,47.08 +2020-01-27 17:00:00+00:00,51.21 +2020-01-27 18:00:00+00:00,47.12 +2020-01-27 19:00:00+00:00,43.86 +2020-01-27 20:00:00+00:00,40.6 +2020-01-27 21:00:00+00:00,36.61 +2020-01-27 22:00:00+00:00,33.14 +2020-01-27 23:00:00+00:00,27.54 +2020-01-28 00:00:00+00:00,26.84 +2020-01-28 01:00:00+00:00,25.64 +2020-01-28 02:00:00+00:00,24.99 +2020-01-28 03:00:00+00:00,25.17 +2020-01-28 04:00:00+00:00,26.32 +2020-01-28 05:00:00+00:00,31.76 +2020-01-28 06:00:00+00:00,41.94 +2020-01-28 07:00:00+00:00,44.94 +2020-01-28 08:00:00+00:00,44.33 +2020-01-28 09:00:00+00:00,42.9 +2020-01-28 10:00:00+00:00,39.93 +2020-01-28 11:00:00+00:00,33.98 +2020-01-28 12:00:00+00:00,30.0 +2020-01-28 13:00:00+00:00,28.0 +2020-01-28 14:00:00+00:00,27.91 +2020-01-28 15:00:00+00:00,29.68 +2020-01-28 16:00:00+00:00,39.83 +2020-01-28 17:00:00+00:00,39.98 +2020-01-28 18:00:00+00:00,30.62 +2020-01-28 19:00:00+00:00,25.8 +2020-01-28 20:00:00+00:00,25.42 +2020-01-28 21:00:00+00:00,25.04 +2020-01-28 22:00:00+00:00,20.08 +2020-01-28 23:00:00+00:00,20.44 +2020-01-29 00:00:00+00:00,19.58 +2020-01-29 01:00:00+00:00,19.16 +2020-01-29 02:00:00+00:00,19.61 +2020-01-29 03:00:00+00:00,20.46 +2020-01-29 04:00:00+00:00,25.16 +2020-01-29 05:00:00+00:00,29.03 +2020-01-29 06:00:00+00:00,41.12 +2020-01-29 07:00:00+00:00,44.41 +2020-01-29 08:00:00+00:00,41.85 +2020-01-29 09:00:00+00:00,40.82 +2020-01-29 10:00:00+00:00,36.86 +2020-01-29 11:00:00+00:00,31.34 +2020-01-29 12:00:00+00:00,32.4 +2020-01-29 13:00:00+00:00,34.24 +2020-01-29 14:00:00+00:00,37.92 +2020-01-29 15:00:00+00:00,39.02 +2020-01-29 16:00:00+00:00,42.89 +2020-01-29 17:00:00+00:00,45.01 +2020-01-29 18:00:00+00:00,45.1 +2020-01-29 19:00:00+00:00,40.98 +2020-01-29 20:00:00+00:00,33.02 +2020-01-29 21:00:00+00:00,29.63 +2020-01-29 22:00:00+00:00,27.33 +2020-01-29 23:00:00+00:00,28.97 +2020-01-30 00:00:00+00:00,27.43 +2020-01-30 01:00:00+00:00,25.15 +2020-01-30 02:00:00+00:00,20.24 +2020-01-30 03:00:00+00:00,19.91 +2020-01-30 04:00:00+00:00,24.96 +2020-01-30 05:00:00+00:00,29.7 +2020-01-30 06:00:00+00:00,39.46 +2020-01-30 07:00:00+00:00,40.4 +2020-01-30 08:00:00+00:00,37.28 +2020-01-30 09:00:00+00:00,35.9 +2020-01-30 10:00:00+00:00,35.15 +2020-01-30 11:00:00+00:00,36.69 +2020-01-30 12:00:00+00:00,39.79 +2020-01-30 13:00:00+00:00,37.53 +2020-01-30 14:00:00+00:00,35.51 +2020-01-30 15:00:00+00:00,37.85 +2020-01-30 16:00:00+00:00,44.12 +2020-01-30 17:00:00+00:00,43.04 +2020-01-30 18:00:00+00:00,41.1 +2020-01-30 19:00:00+00:00,31.13 +2020-01-30 20:00:00+00:00,29.04 +2020-01-30 21:00:00+00:00,27.3 +2020-01-30 22:00:00+00:00,20.04 +2020-01-30 23:00:00+00:00,-0.04 +2020-01-31 00:00:00+00:00,0.02 +2020-01-31 01:00:00+00:00,-8.77 +2020-01-31 02:00:00+00:00,-3.89 +2020-01-31 03:00:00+00:00,0.01 +2020-01-31 04:00:00+00:00,13.04 +2020-01-31 05:00:00+00:00,24.01 +2020-01-31 06:00:00+00:00,36.96 +2020-01-31 07:00:00+00:00,39.59 +2020-01-31 08:00:00+00:00,37.39 +2020-01-31 09:00:00+00:00,34.9 +2020-01-31 10:00:00+00:00,35.27 +2020-01-31 11:00:00+00:00,27.97 +2020-01-31 12:00:00+00:00,28.84 +2020-01-31 13:00:00+00:00,29.0 +2020-01-31 14:00:00+00:00,30.07 +2020-01-31 15:00:00+00:00,28.1 +2020-01-31 16:00:00+00:00,33.78 +2020-01-31 17:00:00+00:00,37.05 +2020-01-31 18:00:00+00:00,32.58 +2020-01-31 19:00:00+00:00,25.1 +2020-01-31 20:00:00+00:00,21.24 +2020-01-31 21:00:00+00:00,20.29 +2020-01-31 22:00:00+00:00,17.09 +2020-01-31 23:00:00+00:00,0.07 +2020-02-01 00:00:00+00:00,0.02 +2020-02-01 01:00:00+00:00,-0.7 +2020-02-01 02:00:00+00:00,-1.94 +2020-02-01 03:00:00+00:00,-1.67 +2020-02-01 04:00:00+00:00,-2.4 +2020-02-01 05:00:00+00:00,0.04 +2020-02-01 06:00:00+00:00,8.52 +2020-02-01 07:00:00+00:00,14.1 +2020-02-01 08:00:00+00:00,14.7 +2020-02-01 09:00:00+00:00,14.74 +2020-02-01 10:00:00+00:00,14.47 +2020-02-01 11:00:00+00:00,14.07 +2020-02-01 12:00:00+00:00,14.45 +2020-02-01 13:00:00+00:00,13.18 +2020-02-01 14:00:00+00:00,11.11 +2020-02-01 15:00:00+00:00,13.07 +2020-02-01 16:00:00+00:00,16.79 +2020-02-01 17:00:00+00:00,16.97 +2020-02-01 18:00:00+00:00,15.99 +2020-02-01 19:00:00+00:00,0.42 +2020-02-01 20:00:00+00:00,-0.8 +2020-02-01 21:00:00+00:00,0.0 +2020-02-01 22:00:00+00:00,-11.16 +2020-02-01 23:00:00+00:00,-4.97 +2020-02-02 00:00:00+00:00,-10.1 +2020-02-02 01:00:00+00:00,-16.95 +2020-02-02 02:00:00+00:00,-11.7 +2020-02-02 03:00:00+00:00,-5.98 +2020-02-02 04:00:00+00:00,-5.21 +2020-02-02 05:00:00+00:00,-4.98 +2020-02-02 06:00:00+00:00,0.09 +2020-02-02 07:00:00+00:00,13.06 +2020-02-02 08:00:00+00:00,24.5 +2020-02-02 09:00:00+00:00,27.12 +2020-02-02 10:00:00+00:00,29.43 +2020-02-02 11:00:00+00:00,35.0 +2020-02-02 12:00:00+00:00,33.36 +2020-02-02 13:00:00+00:00,33.75 +2020-02-02 14:00:00+00:00,34.2 +2020-02-02 15:00:00+00:00,28.87 +2020-02-02 16:00:00+00:00,40.27 +2020-02-02 17:00:00+00:00,40.73 +2020-02-02 18:00:00+00:00,36.93 +2020-02-02 19:00:00+00:00,27.05 +2020-02-02 20:00:00+00:00,21.99 +2020-02-02 21:00:00+00:00,23.87 +2020-02-02 22:00:00+00:00,17.38 +2020-02-02 23:00:00+00:00,15.92 +2020-02-03 00:00:00+00:00,15.55 +2020-02-03 01:00:00+00:00,14.38 +2020-02-03 02:00:00+00:00,9.32 +2020-02-03 03:00:00+00:00,13.26 +2020-02-03 04:00:00+00:00,14.03 +2020-02-03 05:00:00+00:00,27.06 +2020-02-03 06:00:00+00:00,36.49 +2020-02-03 07:00:00+00:00,39.97 +2020-02-03 08:00:00+00:00,39.26 +2020-02-03 09:00:00+00:00,35.52 +2020-02-03 10:00:00+00:00,32.97 +2020-02-03 11:00:00+00:00,29.08 +2020-02-03 12:00:00+00:00,28.38 +2020-02-03 13:00:00+00:00,28.73 +2020-02-03 14:00:00+00:00,28.29 +2020-02-03 15:00:00+00:00,33.02 +2020-02-03 16:00:00+00:00,37.95 +2020-02-03 17:00:00+00:00,37.99 +2020-02-03 18:00:00+00:00,36.57 +2020-02-03 19:00:00+00:00,31.0 +2020-02-03 20:00:00+00:00,27.16 +2020-02-03 21:00:00+00:00,27.13 +2020-02-03 22:00:00+00:00,24.76 +2020-02-03 23:00:00+00:00,20.79 +2020-02-04 00:00:00+00:00,17.41 +2020-02-04 01:00:00+00:00,16.24 +2020-02-04 02:00:00+00:00,12.96 +2020-02-04 03:00:00+00:00,13.42 +2020-02-04 04:00:00+00:00,15.88 +2020-02-04 05:00:00+00:00,24.88 +2020-02-04 06:00:00+00:00,29.7 +2020-02-04 07:00:00+00:00,35.01 +2020-02-04 08:00:00+00:00,33.48 +2020-02-04 09:00:00+00:00,29.9 +2020-02-04 10:00:00+00:00,29.03 +2020-02-04 11:00:00+00:00,27.07 +2020-02-04 12:00:00+00:00,26.43 +2020-02-04 13:00:00+00:00,27.02 +2020-02-04 14:00:00+00:00,29.05 +2020-02-04 15:00:00+00:00,31.42 +2020-02-04 16:00:00+00:00,39.92 +2020-02-04 17:00:00+00:00,41.3 +2020-02-04 18:00:00+00:00,40.92 +2020-02-04 19:00:00+00:00,39.75 +2020-02-04 20:00:00+00:00,30.13 +2020-02-04 21:00:00+00:00,30.36 +2020-02-04 22:00:00+00:00,26.94 +2020-02-04 23:00:00+00:00,25.44 +2020-02-05 00:00:00+00:00,25.0 +2020-02-05 01:00:00+00:00,24.43 +2020-02-05 02:00:00+00:00,23.63 +2020-02-05 03:00:00+00:00,24.83 +2020-02-05 04:00:00+00:00,26.62 +2020-02-05 05:00:00+00:00,37.54 +2020-02-05 06:00:00+00:00,44.91 +2020-02-05 07:00:00+00:00,49.16 +2020-02-05 08:00:00+00:00,44.78 +2020-02-05 09:00:00+00:00,41.37 +2020-02-05 10:00:00+00:00,40.0 +2020-02-05 11:00:00+00:00,37.07 +2020-02-05 12:00:00+00:00,35.16 +2020-02-05 13:00:00+00:00,35.17 +2020-02-05 14:00:00+00:00,37.13 +2020-02-05 15:00:00+00:00,40.25 +2020-02-05 16:00:00+00:00,45.45 +2020-02-05 17:00:00+00:00,45.83 +2020-02-05 18:00:00+00:00,45.66 +2020-02-05 19:00:00+00:00,41.74 +2020-02-05 20:00:00+00:00,35.92 +2020-02-05 21:00:00+00:00,31.99 +2020-02-05 22:00:00+00:00,29.7 +2020-02-05 23:00:00+00:00,26.51 +2020-02-06 00:00:00+00:00,25.35 +2020-02-06 01:00:00+00:00,24.47 +2020-02-06 02:00:00+00:00,24.44 +2020-02-06 03:00:00+00:00,25.25 +2020-02-06 04:00:00+00:00,27.97 +2020-02-06 05:00:00+00:00,32.96 +2020-02-06 06:00:00+00:00,43.15 +2020-02-06 07:00:00+00:00,44.88 +2020-02-06 08:00:00+00:00,42.9 +2020-02-06 09:00:00+00:00,42.19 +2020-02-06 10:00:00+00:00,40.81 +2020-02-06 11:00:00+00:00,39.25 +2020-02-06 12:00:00+00:00,38.01 +2020-02-06 13:00:00+00:00,37.94 +2020-02-06 14:00:00+00:00,38.06 +2020-02-06 15:00:00+00:00,39.09 +2020-02-06 16:00:00+00:00,45.95 +2020-02-06 17:00:00+00:00,46.83 +2020-02-06 18:00:00+00:00,47.21 +2020-02-06 19:00:00+00:00,43.63 +2020-02-06 20:00:00+00:00,37.98 +2020-02-06 21:00:00+00:00,37.66 +2020-02-06 22:00:00+00:00,33.58 +2020-02-06 23:00:00+00:00,33.32 +2020-02-07 00:00:00+00:00,32.18 +2020-02-07 01:00:00+00:00,32.03 +2020-02-07 02:00:00+00:00,31.44 +2020-02-07 03:00:00+00:00,32.17 +2020-02-07 04:00:00+00:00,33.71 +2020-02-07 05:00:00+00:00,40.68 +2020-02-07 06:00:00+00:00,47.87 +2020-02-07 07:00:00+00:00,56.96 +2020-02-07 08:00:00+00:00,47.0 +2020-02-07 09:00:00+00:00,41.81 +2020-02-07 10:00:00+00:00,38.52 +2020-02-07 11:00:00+00:00,37.78 +2020-02-07 12:00:00+00:00,34.68 +2020-02-07 13:00:00+00:00,33.09 +2020-02-07 14:00:00+00:00,38.29 +2020-02-07 15:00:00+00:00,39.08 +2020-02-07 16:00:00+00:00,40.96 +2020-02-07 17:00:00+00:00,39.93 +2020-02-07 18:00:00+00:00,35.47 +2020-02-07 19:00:00+00:00,32.23 +2020-02-07 20:00:00+00:00,27.93 +2020-02-07 21:00:00+00:00,27.35 +2020-02-07 22:00:00+00:00,26.0 +2020-02-07 23:00:00+00:00,25.21 +2020-02-08 00:00:00+00:00,24.28 +2020-02-08 01:00:00+00:00,23.98 +2020-02-08 02:00:00+00:00,22.9 +2020-02-08 03:00:00+00:00,21.75 +2020-02-08 04:00:00+00:00,20.12 +2020-02-08 05:00:00+00:00,22.34 +2020-02-08 06:00:00+00:00,25.54 +2020-02-08 07:00:00+00:00,28.05 +2020-02-08 08:00:00+00:00,29.07 +2020-02-08 09:00:00+00:00,26.87 +2020-02-08 10:00:00+00:00,26.74 +2020-02-08 11:00:00+00:00,25.0 +2020-02-08 12:00:00+00:00,25.61 +2020-02-08 13:00:00+00:00,26.8 +2020-02-08 14:00:00+00:00,29.92 +2020-02-08 15:00:00+00:00,37.6 +2020-02-08 16:00:00+00:00,39.5 +2020-02-08 17:00:00+00:00,41.3 +2020-02-08 18:00:00+00:00,38.71 +2020-02-08 19:00:00+00:00,31.29 +2020-02-08 20:00:00+00:00,26.84 +2020-02-08 21:00:00+00:00,27.39 +2020-02-08 22:00:00+00:00,26.4 +2020-02-08 23:00:00+00:00,23.06 +2020-02-09 00:00:00+00:00,14.93 +2020-02-09 01:00:00+00:00,12.8 +2020-02-09 02:00:00+00:00,9.18 +2020-02-09 03:00:00+00:00,7.54 +2020-02-09 04:00:00+00:00,4.24 +2020-02-09 05:00:00+00:00,3.85 +2020-02-09 06:00:00+00:00,5.06 +2020-02-09 07:00:00+00:00,1.72 +2020-02-09 08:00:00+00:00,-0.07 +2020-02-09 09:00:00+00:00,-4.94 +2020-02-09 10:00:00+00:00,-3.81 +2020-02-09 11:00:00+00:00,-8.8 +2020-02-09 12:00:00+00:00,-16.95 +2020-02-09 13:00:00+00:00,-13.64 +2020-02-09 14:00:00+00:00,-2.96 +2020-02-09 15:00:00+00:00,-0.1 +2020-02-09 16:00:00+00:00,0.52 +2020-02-09 17:00:00+00:00,11.97 +2020-02-09 18:00:00+00:00,5.22 +2020-02-09 19:00:00+00:00,-4.01 +2020-02-09 20:00:00+00:00,-3.04 +2020-02-09 21:00:00+00:00,-0.08 +2020-02-09 22:00:00+00:00,-4.1 +2020-02-09 23:00:00+00:00,-4.97 +2020-02-10 00:00:00+00:00,-15.89 +2020-02-10 01:00:00+00:00,-15.11 +2020-02-10 02:00:00+00:00,-16.16 +2020-02-10 03:00:00+00:00,-14.93 +2020-02-10 04:00:00+00:00,-2.0 +2020-02-10 05:00:00+00:00,0.41 +2020-02-10 06:00:00+00:00,14.11 +2020-02-10 07:00:00+00:00,21.38 +2020-02-10 08:00:00+00:00,13.2 +2020-02-10 09:00:00+00:00,12.54 +2020-02-10 10:00:00+00:00,12.57 +2020-02-10 11:00:00+00:00,12.01 +2020-02-10 12:00:00+00:00,11.99 +2020-02-10 13:00:00+00:00,11.98 +2020-02-10 14:00:00+00:00,12.24 +2020-02-10 15:00:00+00:00,12.77 +2020-02-10 16:00:00+00:00,23.14 +2020-02-10 17:00:00+00:00,24.96 +2020-02-10 18:00:00+00:00,25.69 +2020-02-10 19:00:00+00:00,19.93 +2020-02-10 20:00:00+00:00,12.47 +2020-02-10 21:00:00+00:00,11.97 +2020-02-10 22:00:00+00:00,4.86 +2020-02-10 23:00:00+00:00,10.96 +2020-02-11 00:00:00+00:00,4.83 +2020-02-11 01:00:00+00:00,0.11 +2020-02-11 02:00:00+00:00,-0.08 +2020-02-11 03:00:00+00:00,-1.86 +2020-02-11 04:00:00+00:00,1.39 +2020-02-11 05:00:00+00:00,11.68 +2020-02-11 06:00:00+00:00,14.95 +2020-02-11 07:00:00+00:00,14.95 +2020-02-11 08:00:00+00:00,13.24 +2020-02-11 09:00:00+00:00,13.11 +2020-02-11 10:00:00+00:00,13.0 +2020-02-11 11:00:00+00:00,12.85 +2020-02-11 12:00:00+00:00,12.77 +2020-02-11 13:00:00+00:00,13.0 +2020-02-11 14:00:00+00:00,12.94 +2020-02-11 15:00:00+00:00,13.67 +2020-02-11 16:00:00+00:00,23.98 +2020-02-11 17:00:00+00:00,24.55 +2020-02-11 18:00:00+00:00,24.54 +2020-02-11 19:00:00+00:00,12.94 +2020-02-11 20:00:00+00:00,12.22 +2020-02-11 21:00:00+00:00,12.04 +2020-02-11 22:00:00+00:00,7.1 +2020-02-11 23:00:00+00:00,3.12 +2020-02-12 00:00:00+00:00,0.88 +2020-02-12 01:00:00+00:00,0.07 +2020-02-12 02:00:00+00:00,0.08 +2020-02-12 03:00:00+00:00,5.29 +2020-02-12 04:00:00+00:00,12.94 +2020-02-12 05:00:00+00:00,20.93 +2020-02-12 06:00:00+00:00,34.24 +2020-02-12 07:00:00+00:00,32.01 +2020-02-12 08:00:00+00:00,24.17 +2020-02-12 09:00:00+00:00,22.31 +2020-02-12 10:00:00+00:00,17.9 +2020-02-12 11:00:00+00:00,13.69 +2020-02-12 12:00:00+00:00,14.02 +2020-02-12 13:00:00+00:00,13.88 +2020-02-12 14:00:00+00:00,22.98 +2020-02-12 15:00:00+00:00,27.87 +2020-02-12 16:00:00+00:00,31.2 +2020-02-12 17:00:00+00:00,37.45 +2020-02-12 18:00:00+00:00,33.19 +2020-02-12 19:00:00+00:00,31.23 +2020-02-12 20:00:00+00:00,26.53 +2020-02-12 21:00:00+00:00,24.16 +2020-02-12 22:00:00+00:00,16.93 +2020-02-12 23:00:00+00:00,25.53 +2020-02-13 00:00:00+00:00,25.91 +2020-02-13 01:00:00+00:00,24.37 +2020-02-13 02:00:00+00:00,24.31 +2020-02-13 03:00:00+00:00,22.8 +2020-02-13 04:00:00+00:00,26.41 +2020-02-13 05:00:00+00:00,39.37 +2020-02-13 06:00:00+00:00,58.88 +2020-02-13 07:00:00+00:00,53.48 +2020-02-13 08:00:00+00:00,48.17 +2020-02-13 09:00:00+00:00,40.5 +2020-02-13 10:00:00+00:00,38.13 +2020-02-13 11:00:00+00:00,36.94 +2020-02-13 12:00:00+00:00,35.97 +2020-02-13 13:00:00+00:00,35.73 +2020-02-13 14:00:00+00:00,36.02 +2020-02-13 15:00:00+00:00,37.76 +2020-02-13 16:00:00+00:00,43.0 +2020-02-13 17:00:00+00:00,53.03 +2020-02-13 18:00:00+00:00,44.83 +2020-02-13 19:00:00+00:00,41.96 +2020-02-13 20:00:00+00:00,39.53 +2020-02-13 21:00:00+00:00,37.93 +2020-02-13 22:00:00+00:00,31.57 +2020-02-13 23:00:00+00:00,27.36 +2020-02-14 00:00:00+00:00,27.09 +2020-02-14 01:00:00+00:00,27.0 +2020-02-14 02:00:00+00:00,25.03 +2020-02-14 03:00:00+00:00,26.2 +2020-02-14 04:00:00+00:00,27.04 +2020-02-14 05:00:00+00:00,38.3 +2020-02-14 06:00:00+00:00,44.86 +2020-02-14 07:00:00+00:00,56.74 +2020-02-14 08:00:00+00:00,49.39 +2020-02-14 09:00:00+00:00,43.93 +2020-02-14 10:00:00+00:00,41.44 +2020-02-14 11:00:00+00:00,39.28 +2020-02-14 12:00:00+00:00,37.19 +2020-02-14 13:00:00+00:00,36.98 +2020-02-14 14:00:00+00:00,37.88 +2020-02-14 15:00:00+00:00,39.25 +2020-02-14 16:00:00+00:00,42.5 +2020-02-14 17:00:00+00:00,43.61 +2020-02-14 18:00:00+00:00,39.31 +2020-02-14 19:00:00+00:00,33.86 +2020-02-14 20:00:00+00:00,28.35 +2020-02-14 21:00:00+00:00,27.05 +2020-02-14 22:00:00+00:00,25.19 +2020-02-14 23:00:00+00:00,28.2 +2020-02-15 00:00:00+00:00,27.06 +2020-02-15 01:00:00+00:00,26.2 +2020-02-15 02:00:00+00:00,24.31 +2020-02-15 03:00:00+00:00,23.2 +2020-02-15 04:00:00+00:00,23.14 +2020-02-15 05:00:00+00:00,20.99 +2020-02-15 06:00:00+00:00,23.19 +2020-02-15 07:00:00+00:00,25.71 +2020-02-15 08:00:00+00:00,31.49 +2020-02-15 09:00:00+00:00,27.41 +2020-02-15 10:00:00+00:00,22.96 +2020-02-15 11:00:00+00:00,21.29 +2020-02-15 12:00:00+00:00,18.83 +2020-02-15 13:00:00+00:00,15.58 +2020-02-15 14:00:00+00:00,22.92 +2020-02-15 15:00:00+00:00,22.99 +2020-02-15 16:00:00+00:00,24.24 +2020-02-15 17:00:00+00:00,25.12 +2020-02-15 18:00:00+00:00,21.8 +2020-02-15 19:00:00+00:00,8.27 +2020-02-15 20:00:00+00:00,1.58 +2020-02-15 21:00:00+00:00,3.97 +2020-02-15 22:00:00+00:00,0.02 +2020-02-15 23:00:00+00:00,-5.9 +2020-02-16 00:00:00+00:00,-8.65 +2020-02-16 01:00:00+00:00,-4.93 +2020-02-16 02:00:00+00:00,-4.99 +2020-02-16 03:00:00+00:00,-5.76 +2020-02-16 04:00:00+00:00,-6.91 +2020-02-16 05:00:00+00:00,-8.51 +2020-02-16 06:00:00+00:00,-4.96 +2020-02-16 07:00:00+00:00,-0.08 +2020-02-16 08:00:00+00:00,-1.36 +2020-02-16 09:00:00+00:00,-8.46 +2020-02-16 10:00:00+00:00,-6.55 +2020-02-16 11:00:00+00:00,-15.24 +2020-02-16 12:00:00+00:00,-20.88 +2020-02-16 13:00:00+00:00,-21.02 +2020-02-16 14:00:00+00:00,-32.14 +2020-02-16 15:00:00+00:00,-19.26 +2020-02-16 16:00:00+00:00,-8.02 +2020-02-16 17:00:00+00:00,-2.5 +2020-02-16 18:00:00+00:00,-3.72 +2020-02-16 19:00:00+00:00,-4.96 +2020-02-16 20:00:00+00:00,-3.39 +2020-02-16 21:00:00+00:00,0.74 +2020-02-16 22:00:00+00:00,-1.13 +2020-02-16 23:00:00+00:00,-25.04 +2020-02-17 00:00:00+00:00,-27.53 +2020-02-17 01:00:00+00:00,-21.99 +2020-02-17 02:00:00+00:00,-12.05 +2020-02-17 03:00:00+00:00,-4.95 +2020-02-17 04:00:00+00:00,-0.08 +2020-02-17 05:00:00+00:00,22.91 +2020-02-17 06:00:00+00:00,30.06 +2020-02-17 07:00:00+00:00,36.44 +2020-02-17 08:00:00+00:00,33.57 +2020-02-17 09:00:00+00:00,29.8 +2020-02-17 10:00:00+00:00,29.8 +2020-02-17 11:00:00+00:00,29.76 +2020-02-17 12:00:00+00:00,27.35 +2020-02-17 13:00:00+00:00,27.69 +2020-02-17 14:00:00+00:00,32.38 +2020-02-17 15:00:00+00:00,34.01 +2020-02-17 16:00:00+00:00,36.7 +2020-02-17 17:00:00+00:00,36.79 +2020-02-17 18:00:00+00:00,36.95 +2020-02-17 19:00:00+00:00,32.27 +2020-02-17 20:00:00+00:00,24.86 +2020-02-17 21:00:00+00:00,18.44 +2020-02-17 22:00:00+00:00,9.78 +2020-02-17 23:00:00+00:00,20.64 +2020-02-18 00:00:00+00:00,13.11 +2020-02-18 01:00:00+00:00,9.0 +2020-02-18 02:00:00+00:00,8.58 +2020-02-18 03:00:00+00:00,8.94 +2020-02-18 04:00:00+00:00,15.44 +2020-02-18 05:00:00+00:00,26.14 +2020-02-18 06:00:00+00:00,35.93 +2020-02-18 07:00:00+00:00,34.55 +2020-02-18 08:00:00+00:00,26.62 +2020-02-18 09:00:00+00:00,11.73 +2020-02-18 10:00:00+00:00,10.91 +2020-02-18 11:00:00+00:00,10.46 +2020-02-18 12:00:00+00:00,10.47 +2020-02-18 13:00:00+00:00,11.34 +2020-02-18 14:00:00+00:00,24.81 +2020-02-18 15:00:00+00:00,29.14 +2020-02-18 16:00:00+00:00,34.96 +2020-02-18 17:00:00+00:00,38.88 +2020-02-18 18:00:00+00:00,38.51 +2020-02-18 19:00:00+00:00,32.4 +2020-02-18 20:00:00+00:00,25.82 +2020-02-18 21:00:00+00:00,25.21 +2020-02-18 22:00:00+00:00,21.22 +2020-02-18 23:00:00+00:00,23.0 +2020-02-19 00:00:00+00:00,22.6 +2020-02-19 01:00:00+00:00,20.18 +2020-02-19 02:00:00+00:00,19.48 +2020-02-19 03:00:00+00:00,22.08 +2020-02-19 04:00:00+00:00,25.52 +2020-02-19 05:00:00+00:00,29.62 +2020-02-19 06:00:00+00:00,36.53 +2020-02-19 07:00:00+00:00,38.98 +2020-02-19 08:00:00+00:00,38.99 +2020-02-19 09:00:00+00:00,37.0 +2020-02-19 10:00:00+00:00,34.0 +2020-02-19 11:00:00+00:00,27.69 +2020-02-19 12:00:00+00:00,25.68 +2020-02-19 13:00:00+00:00,25.09 +2020-02-19 14:00:00+00:00,26.85 +2020-02-19 15:00:00+00:00,32.75 +2020-02-19 16:00:00+00:00,36.93 +2020-02-19 17:00:00+00:00,44.65 +2020-02-19 18:00:00+00:00,43.38 +2020-02-19 19:00:00+00:00,36.94 +2020-02-19 20:00:00+00:00,33.91 +2020-02-19 21:00:00+00:00,32.1 +2020-02-19 22:00:00+00:00,27.91 +2020-02-19 23:00:00+00:00,26.45 +2020-02-20 00:00:00+00:00,25.01 +2020-02-20 01:00:00+00:00,24.56 +2020-02-20 02:00:00+00:00,24.1 +2020-02-20 03:00:00+00:00,24.2 +2020-02-20 04:00:00+00:00,24.95 +2020-02-20 05:00:00+00:00,34.65 +2020-02-20 06:00:00+00:00,37.79 +2020-02-20 07:00:00+00:00,40.21 +2020-02-20 08:00:00+00:00,36.97 +2020-02-20 09:00:00+00:00,34.41 +2020-02-20 10:00:00+00:00,34.49 +2020-02-20 11:00:00+00:00,32.58 +2020-02-20 12:00:00+00:00,30.11 +2020-02-20 13:00:00+00:00,28.3 +2020-02-20 14:00:00+00:00,25.95 +2020-02-20 15:00:00+00:00,28.22 +2020-02-20 16:00:00+00:00,31.11 +2020-02-20 17:00:00+00:00,34.98 +2020-02-20 18:00:00+00:00,33.33 +2020-02-20 19:00:00+00:00,25.77 +2020-02-20 20:00:00+00:00,23.31 +2020-02-20 21:00:00+00:00,18.77 +2020-02-20 22:00:00+00:00,8.03 +2020-02-20 23:00:00+00:00,8.67 +2020-02-21 00:00:00+00:00,7.6 +2020-02-21 01:00:00+00:00,7.37 +2020-02-21 02:00:00+00:00,7.94 +2020-02-21 03:00:00+00:00,10.78 +2020-02-21 04:00:00+00:00,24.0 +2020-02-21 05:00:00+00:00,29.88 +2020-02-21 06:00:00+00:00,38.08 +2020-02-21 07:00:00+00:00,39.13 +2020-02-21 08:00:00+00:00,33.1 +2020-02-21 09:00:00+00:00,28.6 +2020-02-21 10:00:00+00:00,27.07 +2020-02-21 11:00:00+00:00,24.87 +2020-02-21 12:00:00+00:00,23.06 +2020-02-21 13:00:00+00:00,24.81 +2020-02-21 14:00:00+00:00,25.64 +2020-02-21 15:00:00+00:00,28.68 +2020-02-21 16:00:00+00:00,32.92 +2020-02-21 17:00:00+00:00,36.76 +2020-02-21 18:00:00+00:00,37.71 +2020-02-21 19:00:00+00:00,29.64 +2020-02-21 20:00:00+00:00,25.91 +2020-02-21 21:00:00+00:00,24.57 +2020-02-21 22:00:00+00:00,7.08 +2020-02-21 23:00:00+00:00,8.02 +2020-02-22 00:00:00+00:00,0.12 +2020-02-22 01:00:00+00:00,0.1 +2020-02-22 02:00:00+00:00,0.0 +2020-02-22 03:00:00+00:00,-2.99 +2020-02-22 04:00:00+00:00,-2.46 +2020-02-22 05:00:00+00:00,-7.99 +2020-02-22 06:00:00+00:00,-0.94 +2020-02-22 07:00:00+00:00,-0.02 +2020-02-22 08:00:00+00:00,-0.57 +2020-02-22 09:00:00+00:00,-4.95 +2020-02-22 10:00:00+00:00,-9.83 +2020-02-22 11:00:00+00:00,-5.1 +2020-02-22 12:00:00+00:00,-10.93 +2020-02-22 13:00:00+00:00,-9.94 +2020-02-22 14:00:00+00:00,-4.01 +2020-02-22 15:00:00+00:00,0.52 +2020-02-22 16:00:00+00:00,0.98 +2020-02-22 17:00:00+00:00,8.76 +2020-02-22 18:00:00+00:00,5.01 +2020-02-22 19:00:00+00:00,0.06 +2020-02-22 20:00:00+00:00,-0.09 +2020-02-22 21:00:00+00:00,0.04 +2020-02-22 22:00:00+00:00,0.0 +2020-02-22 23:00:00+00:00,-4.87 +2020-02-23 00:00:00+00:00,-2.99 +2020-02-23 01:00:00+00:00,-2.62 +2020-02-23 02:00:00+00:00,-4.89 +2020-02-23 03:00:00+00:00,-3.76 +2020-02-23 04:00:00+00:00,-0.07 +2020-02-23 05:00:00+00:00,-1.57 +2020-02-23 06:00:00+00:00,-2.44 +2020-02-23 07:00:00+00:00,0.87 +2020-02-23 08:00:00+00:00,14.14 +2020-02-23 09:00:00+00:00,17.1 +2020-02-23 10:00:00+00:00,23.57 +2020-02-23 11:00:00+00:00,22.76 +2020-02-23 12:00:00+00:00,15.03 +2020-02-23 13:00:00+00:00,9.05 +2020-02-23 14:00:00+00:00,8.71 +2020-02-23 15:00:00+00:00,8.51 +2020-02-23 16:00:00+00:00,10.25 +2020-02-23 17:00:00+00:00,15.77 +2020-02-23 18:00:00+00:00,10.25 +2020-02-23 19:00:00+00:00,8.6 +2020-02-23 20:00:00+00:00,8.34 +2020-02-23 21:00:00+00:00,8.94 +2020-02-23 22:00:00+00:00,8.18 +2020-02-23 23:00:00+00:00,-0.08 +2020-02-24 00:00:00+00:00,0.09 +2020-02-24 01:00:00+00:00,1.28 +2020-02-24 02:00:00+00:00,5.38 +2020-02-24 03:00:00+00:00,18.44 +2020-02-24 04:00:00+00:00,26.61 +2020-02-24 05:00:00+00:00,35.91 +2020-02-24 06:00:00+00:00,43.0 +2020-02-24 07:00:00+00:00,47.96 +2020-02-24 08:00:00+00:00,46.42 +2020-02-24 09:00:00+00:00,45.27 +2020-02-24 10:00:00+00:00,45.1 +2020-02-24 11:00:00+00:00,42.28 +2020-02-24 12:00:00+00:00,40.2 +2020-02-24 13:00:00+00:00,38.41 +2020-02-24 14:00:00+00:00,35.98 +2020-02-24 15:00:00+00:00,32.07 +2020-02-24 16:00:00+00:00,31.0 +2020-02-24 17:00:00+00:00,36.1 +2020-02-24 18:00:00+00:00,35.08 +2020-02-24 19:00:00+00:00,28.62 +2020-02-24 20:00:00+00:00,21.68 +2020-02-24 21:00:00+00:00,16.38 +2020-02-24 22:00:00+00:00,10.48 +2020-02-24 23:00:00+00:00,9.92 +2020-02-25 00:00:00+00:00,8.22 +2020-02-25 01:00:00+00:00,7.09 +2020-02-25 02:00:00+00:00,7.03 +2020-02-25 03:00:00+00:00,9.52 +2020-02-25 04:00:00+00:00,11.62 +2020-02-25 05:00:00+00:00,25.84 +2020-02-25 06:00:00+00:00,31.66 +2020-02-25 07:00:00+00:00,32.69 +2020-02-25 08:00:00+00:00,30.69 +2020-02-25 09:00:00+00:00,26.68 +2020-02-25 10:00:00+00:00,27.7 +2020-02-25 11:00:00+00:00,26.3 +2020-02-25 12:00:00+00:00,25.09 +2020-02-25 13:00:00+00:00,23.44 +2020-02-25 14:00:00+00:00,23.56 +2020-02-25 15:00:00+00:00,25.8 +2020-02-25 16:00:00+00:00,28.46 +2020-02-25 17:00:00+00:00,35.97 +2020-02-25 18:00:00+00:00,39.49 +2020-02-25 19:00:00+00:00,35.21 +2020-02-25 20:00:00+00:00,30.1 +2020-02-25 21:00:00+00:00,29.9 +2020-02-25 22:00:00+00:00,26.46 +2020-02-25 23:00:00+00:00,25.56 +2020-02-26 00:00:00+00:00,25.2 +2020-02-26 01:00:00+00:00,24.35 +2020-02-26 02:00:00+00:00,23.53 +2020-02-26 03:00:00+00:00,24.08 +2020-02-26 04:00:00+00:00,25.01 +2020-02-26 05:00:00+00:00,31.24 +2020-02-26 06:00:00+00:00,36.34 +2020-02-26 07:00:00+00:00,37.64 +2020-02-26 08:00:00+00:00,37.01 +2020-02-26 09:00:00+00:00,34.02 +2020-02-26 10:00:00+00:00,33.13 +2020-02-26 11:00:00+00:00,32.1 +2020-02-26 12:00:00+00:00,30.45 +2020-02-26 13:00:00+00:00,28.14 +2020-02-26 14:00:00+00:00,30.17 +2020-02-26 15:00:00+00:00,32.97 +2020-02-26 16:00:00+00:00,37.72 +2020-02-26 17:00:00+00:00,38.46 +2020-02-26 18:00:00+00:00,42.17 +2020-02-26 19:00:00+00:00,36.71 +2020-02-26 20:00:00+00:00,33.83 +2020-02-26 21:00:00+00:00,32.08 +2020-02-26 22:00:00+00:00,28.2 +2020-02-26 23:00:00+00:00,31.57 +2020-02-27 00:00:00+00:00,28.54 +2020-02-27 01:00:00+00:00,27.04 +2020-02-27 02:00:00+00:00,25.17 +2020-02-27 03:00:00+00:00,25.45 +2020-02-27 04:00:00+00:00,28.81 +2020-02-27 05:00:00+00:00,36.8 +2020-02-27 06:00:00+00:00,42.28 +2020-02-27 07:00:00+00:00,44.42 +2020-02-27 08:00:00+00:00,42.41 +2020-02-27 09:00:00+00:00,40.15 +2020-02-27 10:00:00+00:00,40.05 +2020-02-27 11:00:00+00:00,38.82 +2020-02-27 12:00:00+00:00,39.81 +2020-02-27 13:00:00+00:00,43.71 +2020-02-27 14:00:00+00:00,45.19 +2020-02-27 15:00:00+00:00,45.71 +2020-02-27 16:00:00+00:00,51.52 +2020-02-27 17:00:00+00:00,59.98 +2020-02-27 18:00:00+00:00,48.06 +2020-02-27 19:00:00+00:00,39.13 +2020-02-27 20:00:00+00:00,35.68 +2020-02-27 21:00:00+00:00,33.77 +2020-02-27 22:00:00+00:00,28.54 +2020-02-27 23:00:00+00:00,29.54 +2020-02-28 00:00:00+00:00,26.7 +2020-02-28 01:00:00+00:00,25.34 +2020-02-28 02:00:00+00:00,24.87 +2020-02-28 03:00:00+00:00,24.87 +2020-02-28 04:00:00+00:00,27.3 +2020-02-28 05:00:00+00:00,34.97 +2020-02-28 06:00:00+00:00,40.74 +2020-02-28 07:00:00+00:00,43.88 +2020-02-28 08:00:00+00:00,40.12 +2020-02-28 09:00:00+00:00,37.81 +2020-02-28 10:00:00+00:00,32.5 +2020-02-28 11:00:00+00:00,26.55 +2020-02-28 12:00:00+00:00,26.0 +2020-02-28 13:00:00+00:00,26.12 +2020-02-28 14:00:00+00:00,27.1 +2020-02-28 15:00:00+00:00,34.01 +2020-02-28 16:00:00+00:00,37.49 +2020-02-28 17:00:00+00:00,38.95 +2020-02-28 18:00:00+00:00,35.34 +2020-02-28 19:00:00+00:00,27.77 +2020-02-28 20:00:00+00:00,27.06 +2020-02-28 21:00:00+00:00,26.14 +2020-02-28 22:00:00+00:00,23.79 +2020-02-28 23:00:00+00:00,0.4 +2020-02-29 00:00:00+00:00,3.11 +2020-02-29 01:00:00+00:00,9.42 +2020-02-29 02:00:00+00:00,9.49 +2020-02-29 03:00:00+00:00,9.39 +2020-02-29 04:00:00+00:00,8.67 +2020-02-29 05:00:00+00:00,10.18 +2020-02-29 06:00:00+00:00,11.56 +2020-02-29 07:00:00+00:00,12.99 +2020-02-29 08:00:00+00:00,14.01 +2020-02-29 09:00:00+00:00,12.36 +2020-02-29 10:00:00+00:00,9.97 +2020-02-29 11:00:00+00:00,6.71 +2020-02-29 12:00:00+00:00,4.22 +2020-02-29 13:00:00+00:00,4.21 +2020-02-29 14:00:00+00:00,7.63 +2020-02-29 15:00:00+00:00,5.22 +2020-02-29 16:00:00+00:00,12.25 +2020-02-29 17:00:00+00:00,13.92 +2020-02-29 18:00:00+00:00,12.78 +2020-02-29 19:00:00+00:00,8.06 +2020-02-29 20:00:00+00:00,7.38 +2020-02-29 21:00:00+00:00,10.41 +2020-02-29 22:00:00+00:00,9.51 +2020-02-29 23:00:00+00:00,-4.92 +2020-03-01 00:00:00+00:00,-3.88 +2020-03-01 01:00:00+00:00,-6.98 +2020-03-01 02:00:00+00:00,-3.88 +2020-03-01 03:00:00+00:00,-1.02 +2020-03-01 04:00:00+00:00,-1.03 +2020-03-01 05:00:00+00:00,-5.3 +2020-03-01 06:00:00+00:00,-3.89 +2020-03-01 07:00:00+00:00,-0.05 +2020-03-01 08:00:00+00:00,-0.23 +2020-03-01 09:00:00+00:00,-4.03 +2020-03-01 10:00:00+00:00,-2.16 +2020-03-01 11:00:00+00:00,-6.97 +2020-03-01 12:00:00+00:00,-7.85 +2020-03-01 13:00:00+00:00,-5.06 +2020-03-01 14:00:00+00:00,0.03 +2020-03-01 15:00:00+00:00,15.11 +2020-03-01 16:00:00+00:00,33.07 +2020-03-01 17:00:00+00:00,34.7 +2020-03-01 18:00:00+00:00,36.12 +2020-03-01 19:00:00+00:00,31.49 +2020-03-01 20:00:00+00:00,26.1 +2020-03-01 21:00:00+00:00,27.76 +2020-03-01 22:00:00+00:00,24.24 +2020-03-01 23:00:00+00:00,24.31 +2020-03-02 00:00:00+00:00,23.3 +2020-03-02 01:00:00+00:00,21.71 +2020-03-02 02:00:00+00:00,19.2 +2020-03-02 03:00:00+00:00,19.15 +2020-03-02 04:00:00+00:00,23.53 +2020-03-02 05:00:00+00:00,30.91 +2020-03-02 06:00:00+00:00,37.0 +2020-03-02 07:00:00+00:00,39.92 +2020-03-02 08:00:00+00:00,36.94 +2020-03-02 09:00:00+00:00,36.71 +2020-03-02 10:00:00+00:00,37.03 +2020-03-02 11:00:00+00:00,35.13 +2020-03-02 12:00:00+00:00,35.35 +2020-03-02 13:00:00+00:00,34.74 +2020-03-02 14:00:00+00:00,36.79 +2020-03-02 15:00:00+00:00,38.93 +2020-03-02 16:00:00+00:00,41.09 +2020-03-02 17:00:00+00:00,49.5 +2020-03-02 18:00:00+00:00,43.92 +2020-03-02 19:00:00+00:00,37.75 +2020-03-02 20:00:00+00:00,33.54 +2020-03-02 21:00:00+00:00,30.88 +2020-03-02 22:00:00+00:00,28.97 +2020-03-02 23:00:00+00:00,26.04 +2020-03-03 00:00:00+00:00,25.04 +2020-03-03 01:00:00+00:00,24.53 +2020-03-03 02:00:00+00:00,24.1 +2020-03-03 03:00:00+00:00,24.45 +2020-03-03 04:00:00+00:00,28.5 +2020-03-03 05:00:00+00:00,35.68 +2020-03-03 06:00:00+00:00,42.3 +2020-03-03 07:00:00+00:00,45.6 +2020-03-03 08:00:00+00:00,41.68 +2020-03-03 09:00:00+00:00,37.97 +2020-03-03 10:00:00+00:00,34.83 +2020-03-03 11:00:00+00:00,31.82 +2020-03-03 12:00:00+00:00,33.96 +2020-03-03 13:00:00+00:00,35.93 +2020-03-03 14:00:00+00:00,37.66 +2020-03-03 15:00:00+00:00,39.84 +2020-03-03 16:00:00+00:00,41.73 +2020-03-03 17:00:00+00:00,55.98 +2020-03-03 18:00:00+00:00,56.9 +2020-03-03 19:00:00+00:00,40.93 +2020-03-03 20:00:00+00:00,38.19 +2020-03-03 21:00:00+00:00,35.03 +2020-03-03 22:00:00+00:00,31.29 +2020-03-03 23:00:00+00:00,30.1 +2020-03-04 00:00:00+00:00,28.1 +2020-03-04 01:00:00+00:00,27.08 +2020-03-04 02:00:00+00:00,26.8 +2020-03-04 03:00:00+00:00,26.8 +2020-03-04 04:00:00+00:00,30.05 +2020-03-04 05:00:00+00:00,36.51 +2020-03-04 06:00:00+00:00,47.2 +2020-03-04 07:00:00+00:00,54.75 +2020-03-04 08:00:00+00:00,50.32 +2020-03-04 09:00:00+00:00,44.23 +2020-03-04 10:00:00+00:00,41.09 +2020-03-04 11:00:00+00:00,37.9 +2020-03-04 12:00:00+00:00,35.97 +2020-03-04 13:00:00+00:00,35.0 +2020-03-04 14:00:00+00:00,35.88 +2020-03-04 15:00:00+00:00,39.5 +2020-03-04 16:00:00+00:00,48.49 +2020-03-04 17:00:00+00:00,64.7 +2020-03-04 18:00:00+00:00,63.34 +2020-03-04 19:00:00+00:00,43.58 +2020-03-04 20:00:00+00:00,37.53 +2020-03-04 21:00:00+00:00,33.92 +2020-03-04 22:00:00+00:00,31.82 +2020-03-04 23:00:00+00:00,30.24 +2020-03-05 00:00:00+00:00,29.08 +2020-03-05 01:00:00+00:00,28.6 +2020-03-05 02:00:00+00:00,29.01 +2020-03-05 03:00:00+00:00,30.1 +2020-03-05 04:00:00+00:00,31.55 +2020-03-05 05:00:00+00:00,37.91 +2020-03-05 06:00:00+00:00,45.61 +2020-03-05 07:00:00+00:00,54.64 +2020-03-05 08:00:00+00:00,49.78 +2020-03-05 09:00:00+00:00,42.09 +2020-03-05 10:00:00+00:00,40.99 +2020-03-05 11:00:00+00:00,41.97 +2020-03-05 12:00:00+00:00,40.51 +2020-03-05 13:00:00+00:00,37.94 +2020-03-05 14:00:00+00:00,36.75 +2020-03-05 15:00:00+00:00,37.03 +2020-03-05 16:00:00+00:00,38.91 +2020-03-05 17:00:00+00:00,36.9 +2020-03-05 18:00:00+00:00,35.82 +2020-03-05 19:00:00+00:00,30.24 +2020-03-05 20:00:00+00:00,27.85 +2020-03-05 21:00:00+00:00,26.9 +2020-03-05 22:00:00+00:00,25.3 +2020-03-05 23:00:00+00:00,24.55 +2020-03-06 00:00:00+00:00,22.96 +2020-03-06 01:00:00+00:00,21.0 +2020-03-06 02:00:00+00:00,19.99 +2020-03-06 03:00:00+00:00,20.22 +2020-03-06 04:00:00+00:00,24.1 +2020-03-06 05:00:00+00:00,28.8 +2020-03-06 06:00:00+00:00,33.41 +2020-03-06 07:00:00+00:00,35.9 +2020-03-06 08:00:00+00:00,35.1 +2020-03-06 09:00:00+00:00,34.68 +2020-03-06 10:00:00+00:00,36.03 +2020-03-06 11:00:00+00:00,35.0 +2020-03-06 12:00:00+00:00,32.64 +2020-03-06 13:00:00+00:00,31.67 +2020-03-06 14:00:00+00:00,29.06 +2020-03-06 15:00:00+00:00,29.8 +2020-03-06 16:00:00+00:00,35.0 +2020-03-06 17:00:00+00:00,37.3 +2020-03-06 18:00:00+00:00,38.04 +2020-03-06 19:00:00+00:00,35.46 +2020-03-06 20:00:00+00:00,31.71 +2020-03-06 21:00:00+00:00,30.5 +2020-03-06 22:00:00+00:00,28.54 +2020-03-06 23:00:00+00:00,31.01 +2020-03-07 00:00:00+00:00,25.5 +2020-03-07 01:00:00+00:00,24.51 +2020-03-07 02:00:00+00:00,24.03 +2020-03-07 03:00:00+00:00,24.47 +2020-03-07 04:00:00+00:00,24.41 +2020-03-07 05:00:00+00:00,26.25 +2020-03-07 06:00:00+00:00,30.4 +2020-03-07 07:00:00+00:00,33.58 +2020-03-07 08:00:00+00:00,35.0 +2020-03-07 09:00:00+00:00,30.4 +2020-03-07 10:00:00+00:00,28.1 +2020-03-07 11:00:00+00:00,26.08 +2020-03-07 12:00:00+00:00,25.06 +2020-03-07 13:00:00+00:00,24.94 +2020-03-07 14:00:00+00:00,25.0 +2020-03-07 15:00:00+00:00,29.54 +2020-03-07 16:00:00+00:00,35.79 +2020-03-07 17:00:00+00:00,38.8 +2020-03-07 18:00:00+00:00,39.95 +2020-03-07 19:00:00+00:00,35.0 +2020-03-07 20:00:00+00:00,27.17 +2020-03-07 21:00:00+00:00,28.24 +2020-03-07 22:00:00+00:00,24.9 +2020-03-07 23:00:00+00:00,20.8 +2020-03-08 00:00:00+00:00,16.82 +2020-03-08 01:00:00+00:00,12.22 +2020-03-08 02:00:00+00:00,9.24 +2020-03-08 03:00:00+00:00,8.74 +2020-03-08 04:00:00+00:00,8.93 +2020-03-08 05:00:00+00:00,8.67 +2020-03-08 06:00:00+00:00,9.78 +2020-03-08 07:00:00+00:00,9.66 +2020-03-08 08:00:00+00:00,9.38 +2020-03-08 09:00:00+00:00,9.73 +2020-03-08 10:00:00+00:00,9.4 +2020-03-08 11:00:00+00:00,8.93 +2020-03-08 12:00:00+00:00,8.43 +2020-03-08 13:00:00+00:00,8.77 +2020-03-08 14:00:00+00:00,11.01 +2020-03-08 15:00:00+00:00,19.19 +2020-03-08 16:00:00+00:00,25.71 +2020-03-08 17:00:00+00:00,31.97 +2020-03-08 18:00:00+00:00,34.86 +2020-03-08 19:00:00+00:00,34.02 +2020-03-08 20:00:00+00:00,31.9 +2020-03-08 21:00:00+00:00,31.99 +2020-03-08 22:00:00+00:00,30.41 +2020-03-08 23:00:00+00:00,25.02 +2020-03-09 00:00:00+00:00,25.0 +2020-03-09 01:00:00+00:00,25.09 +2020-03-09 02:00:00+00:00,24.77 +2020-03-09 03:00:00+00:00,24.47 +2020-03-09 04:00:00+00:00,25.3 +2020-03-09 05:00:00+00:00,37.1 +2020-03-09 06:00:00+00:00,46.99 +2020-03-09 07:00:00+00:00,50.27 +2020-03-09 08:00:00+00:00,45.65 +2020-03-09 09:00:00+00:00,40.44 +2020-03-09 10:00:00+00:00,40.35 +2020-03-09 11:00:00+00:00,36.88 +2020-03-09 12:00:00+00:00,35.49 +2020-03-09 13:00:00+00:00,33.75 +2020-03-09 14:00:00+00:00,36.75 +2020-03-09 15:00:00+00:00,39.3 +2020-03-09 16:00:00+00:00,46.35 +2020-03-09 17:00:00+00:00,54.57 +2020-03-09 18:00:00+00:00,58.98 +2020-03-09 19:00:00+00:00,40.5 +2020-03-09 20:00:00+00:00,35.98 +2020-03-09 21:00:00+00:00,34.97 +2020-03-09 22:00:00+00:00,26.75 +2020-03-09 23:00:00+00:00,24.06 +2020-03-10 00:00:00+00:00,23.7 +2020-03-10 01:00:00+00:00,21.77 +2020-03-10 02:00:00+00:00,15.91 +2020-03-10 03:00:00+00:00,13.06 +2020-03-10 04:00:00+00:00,15.8 +2020-03-10 05:00:00+00:00,24.23 +2020-03-10 06:00:00+00:00,29.0 +2020-03-10 07:00:00+00:00,30.19 +2020-03-10 08:00:00+00:00,31.08 +2020-03-10 09:00:00+00:00,30.1 +2020-03-10 10:00:00+00:00,31.77 +2020-03-10 11:00:00+00:00,30.55 +2020-03-10 12:00:00+00:00,30.0 +2020-03-10 13:00:00+00:00,29.69 +2020-03-10 14:00:00+00:00,24.84 +2020-03-10 15:00:00+00:00,22.64 +2020-03-10 16:00:00+00:00,24.27 +2020-03-10 17:00:00+00:00,28.17 +2020-03-10 18:00:00+00:00,27.37 +2020-03-10 19:00:00+00:00,24.04 +2020-03-10 20:00:00+00:00,11.79 +2020-03-10 21:00:00+00:00,9.03 +2020-03-10 22:00:00+00:00,4.93 +2020-03-10 23:00:00+00:00,-0.05 +2020-03-11 00:00:00+00:00,0.07 +2020-03-11 01:00:00+00:00,0.1 +2020-03-11 02:00:00+00:00,0.13 +2020-03-11 03:00:00+00:00,5.58 +2020-03-11 04:00:00+00:00,21.13 +2020-03-11 05:00:00+00:00,26.87 +2020-03-11 06:00:00+00:00,33.94 +2020-03-11 07:00:00+00:00,37.17 +2020-03-11 08:00:00+00:00,34.27 +2020-03-11 09:00:00+00:00,33.07 +2020-03-11 10:00:00+00:00,29.2 +2020-03-11 11:00:00+00:00,25.97 +2020-03-11 12:00:00+00:00,24.06 +2020-03-11 13:00:00+00:00,23.73 +2020-03-11 14:00:00+00:00,24.9 +2020-03-11 15:00:00+00:00,26.66 +2020-03-11 16:00:00+00:00,33.8 +2020-03-11 17:00:00+00:00,37.0 +2020-03-11 18:00:00+00:00,37.99 +2020-03-11 19:00:00+00:00,28.1 +2020-03-11 20:00:00+00:00,24.39 +2020-03-11 21:00:00+00:00,23.84 +2020-03-11 22:00:00+00:00,12.58 +2020-03-11 23:00:00+00:00,7.37 +2020-03-12 00:00:00+00:00,1.79 +2020-03-12 01:00:00+00:00,0.06 +2020-03-12 02:00:00+00:00,0.01 +2020-03-12 03:00:00+00:00,-1.58 +2020-03-12 04:00:00+00:00,0.13 +2020-03-12 05:00:00+00:00,10.64 +2020-03-12 06:00:00+00:00,19.98 +2020-03-12 07:00:00+00:00,18.05 +2020-03-12 08:00:00+00:00,9.18 +2020-03-12 09:00:00+00:00,-0.01 +2020-03-12 10:00:00+00:00,-0.05 +2020-03-12 11:00:00+00:00,0.03 +2020-03-12 12:00:00+00:00,0.56 +2020-03-12 13:00:00+00:00,0.08 +2020-03-12 14:00:00+00:00,12.73 +2020-03-12 15:00:00+00:00,23.6 +2020-03-12 16:00:00+00:00,27.45 +2020-03-12 17:00:00+00:00,37.0 +2020-03-12 18:00:00+00:00,39.63 +2020-03-12 19:00:00+00:00,31.76 +2020-03-12 20:00:00+00:00,24.5 +2020-03-12 21:00:00+00:00,23.26 +2020-03-12 22:00:00+00:00,7.5 +2020-03-12 23:00:00+00:00,7.59 +2020-03-13 00:00:00+00:00,6.99 +2020-03-13 01:00:00+00:00,2.48 +2020-03-13 02:00:00+00:00,2.95 +2020-03-13 03:00:00+00:00,2.58 +2020-03-13 04:00:00+00:00,7.96 +2020-03-13 05:00:00+00:00,21.83 +2020-03-13 06:00:00+00:00,24.94 +2020-03-13 07:00:00+00:00,13.99 +2020-03-13 08:00:00+00:00,4.02 +2020-03-13 09:00:00+00:00,0.09 +2020-03-13 10:00:00+00:00,10.69 +2020-03-13 11:00:00+00:00,4.08 +2020-03-13 12:00:00+00:00,0.12 +2020-03-13 13:00:00+00:00,0.01 +2020-03-13 14:00:00+00:00,4.68 +2020-03-13 15:00:00+00:00,21.37 +2020-03-13 16:00:00+00:00,32.65 +2020-03-13 17:00:00+00:00,37.7 +2020-03-13 18:00:00+00:00,42.5 +2020-03-13 19:00:00+00:00,38.41 +2020-03-13 20:00:00+00:00,36.93 +2020-03-13 21:00:00+00:00,36.99 +2020-03-13 22:00:00+00:00,35.35 +2020-03-13 23:00:00+00:00,34.29 +2020-03-14 00:00:00+00:00,32.04 +2020-03-14 01:00:00+00:00,31.43 +2020-03-14 02:00:00+00:00,29.05 +2020-03-14 03:00:00+00:00,29.27 +2020-03-14 04:00:00+00:00,31.15 +2020-03-14 05:00:00+00:00,34.61 +2020-03-14 06:00:00+00:00,34.9 +2020-03-14 07:00:00+00:00,34.83 +2020-03-14 08:00:00+00:00,34.94 +2020-03-14 09:00:00+00:00,29.13 +2020-03-14 10:00:00+00:00,26.96 +2020-03-14 11:00:00+00:00,26.01 +2020-03-14 12:00:00+00:00,23.81 +2020-03-14 13:00:00+00:00,22.62 +2020-03-14 14:00:00+00:00,24.87 +2020-03-14 15:00:00+00:00,25.8 +2020-03-14 16:00:00+00:00,34.82 +2020-03-14 17:00:00+00:00,37.17 +2020-03-14 18:00:00+00:00,38.53 +2020-03-14 19:00:00+00:00,32.79 +2020-03-14 20:00:00+00:00,27.42 +2020-03-14 21:00:00+00:00,26.02 +2020-03-14 22:00:00+00:00,23.95 +2020-03-14 23:00:00+00:00,14.23 +2020-03-15 00:00:00+00:00,7.24 +2020-03-15 01:00:00+00:00,6.69 +2020-03-15 02:00:00+00:00,5.98 +2020-03-15 03:00:00+00:00,5.97 +2020-03-15 04:00:00+00:00,4.34 +2020-03-15 05:00:00+00:00,4.34 +2020-03-15 06:00:00+00:00,0.05 +2020-03-15 07:00:00+00:00,0.08 +2020-03-15 08:00:00+00:00,-0.01 +2020-03-15 09:00:00+00:00,-2.02 +2020-03-15 10:00:00+00:00,-8.79 +2020-03-15 11:00:00+00:00,-33.67 +2020-03-15 12:00:00+00:00,-33.8 +2020-03-15 13:00:00+00:00,-29.05 +2020-03-15 14:00:00+00:00,-4.96 +2020-03-15 15:00:00+00:00,0.07 +2020-03-15 16:00:00+00:00,13.13 +2020-03-15 17:00:00+00:00,26.0 +2020-03-15 18:00:00+00:00,29.96 +2020-03-15 19:00:00+00:00,26.44 +2020-03-15 20:00:00+00:00,25.23 +2020-03-15 21:00:00+00:00,30.18 +2020-03-15 22:00:00+00:00,23.93 +2020-03-15 23:00:00+00:00,20.01 +2020-03-16 00:00:00+00:00,20.13 +2020-03-16 01:00:00+00:00,20.04 +2020-03-16 02:00:00+00:00,20.0 +2020-03-16 03:00:00+00:00,19.92 +2020-03-16 04:00:00+00:00,22.4 +2020-03-16 05:00:00+00:00,33.85 +2020-03-16 06:00:00+00:00,38.34 +2020-03-16 07:00:00+00:00,36.92 +2020-03-16 08:00:00+00:00,32.13 +2020-03-16 09:00:00+00:00,28.0 +2020-03-16 10:00:00+00:00,25.88 +2020-03-16 11:00:00+00:00,24.83 +2020-03-16 12:00:00+00:00,25.0 +2020-03-16 13:00:00+00:00,25.23 +2020-03-16 14:00:00+00:00,27.1 +2020-03-16 15:00:00+00:00,30.77 +2020-03-16 16:00:00+00:00,37.25 +2020-03-16 17:00:00+00:00,54.93 +2020-03-16 18:00:00+00:00,61.96 +2020-03-16 19:00:00+00:00,43.1 +2020-03-16 20:00:00+00:00,35.99 +2020-03-16 21:00:00+00:00,34.56 +2020-03-16 22:00:00+00:00,30.24 +2020-03-16 23:00:00+00:00,25.54 +2020-03-17 00:00:00+00:00,23.49 +2020-03-17 01:00:00+00:00,23.06 +2020-03-17 02:00:00+00:00,22.71 +2020-03-17 03:00:00+00:00,23.0 +2020-03-17 04:00:00+00:00,23.87 +2020-03-17 05:00:00+00:00,30.48 +2020-03-17 06:00:00+00:00,35.17 +2020-03-17 07:00:00+00:00,34.39 +2020-03-17 08:00:00+00:00,31.37 +2020-03-17 09:00:00+00:00,28.89 +2020-03-17 10:00:00+00:00,24.02 +2020-03-17 11:00:00+00:00,25.55 +2020-03-17 12:00:00+00:00,23.9 +2020-03-17 13:00:00+00:00,24.61 +2020-03-17 14:00:00+00:00,24.02 +2020-03-17 15:00:00+00:00,26.09 +2020-03-17 16:00:00+00:00,30.04 +2020-03-17 17:00:00+00:00,35.93 +2020-03-17 18:00:00+00:00,38.23 +2020-03-17 19:00:00+00:00,35.21 +2020-03-17 20:00:00+00:00,31.0 +2020-03-17 21:00:00+00:00,29.85 +2020-03-17 22:00:00+00:00,25.97 +2020-03-17 23:00:00+00:00,22.02 +2020-03-18 00:00:00+00:00,21.73 +2020-03-18 01:00:00+00:00,20.76 +2020-03-18 02:00:00+00:00,20.77 +2020-03-18 03:00:00+00:00,20.73 +2020-03-18 04:00:00+00:00,21.06 +2020-03-18 05:00:00+00:00,24.69 +2020-03-18 06:00:00+00:00,30.59 +2020-03-18 07:00:00+00:00,29.79 +2020-03-18 08:00:00+00:00,22.99 +2020-03-18 09:00:00+00:00,20.4 +2020-03-18 10:00:00+00:00,20.64 +2020-03-18 11:00:00+00:00,20.34 +2020-03-18 12:00:00+00:00,20.74 +2020-03-18 13:00:00+00:00,21.8 +2020-03-18 14:00:00+00:00,24.76 +2020-03-18 15:00:00+00:00,28.62 +2020-03-18 16:00:00+00:00,33.0 +2020-03-18 17:00:00+00:00,37.56 +2020-03-18 18:00:00+00:00,41.07 +2020-03-18 19:00:00+00:00,35.4 +2020-03-18 20:00:00+00:00,33.7 +2020-03-18 21:00:00+00:00,31.83 +2020-03-18 22:00:00+00:00,29.06 +2020-03-18 23:00:00+00:00,26.67 +2020-03-19 00:00:00+00:00,24.0 +2020-03-19 01:00:00+00:00,22.99 +2020-03-19 02:00:00+00:00,22.16 +2020-03-19 03:00:00+00:00,22.26 +2020-03-19 04:00:00+00:00,26.0 +2020-03-19 05:00:00+00:00,30.25 +2020-03-19 06:00:00+00:00,33.98 +2020-03-19 07:00:00+00:00,32.06 +2020-03-19 08:00:00+00:00,28.75 +2020-03-19 09:00:00+00:00,25.04 +2020-03-19 10:00:00+00:00,24.93 +2020-03-19 11:00:00+00:00,22.83 +2020-03-19 12:00:00+00:00,22.99 +2020-03-19 13:00:00+00:00,24.92 +2020-03-19 14:00:00+00:00,27.9 +2020-03-19 15:00:00+00:00,30.57 +2020-03-19 16:00:00+00:00,33.73 +2020-03-19 17:00:00+00:00,40.2 +2020-03-19 18:00:00+00:00,44.42 +2020-03-19 19:00:00+00:00,34.48 +2020-03-19 20:00:00+00:00,31.65 +2020-03-19 21:00:00+00:00,28.7 +2020-03-19 22:00:00+00:00,27.1 +2020-03-19 23:00:00+00:00,25.18 +2020-03-20 00:00:00+00:00,20.0 +2020-03-20 01:00:00+00:00,20.87 +2020-03-20 02:00:00+00:00,20.72 +2020-03-20 03:00:00+00:00,25.0 +2020-03-20 04:00:00+00:00,26.45 +2020-03-20 05:00:00+00:00,31.9 +2020-03-20 06:00:00+00:00,35.0 +2020-03-20 07:00:00+00:00,33.88 +2020-03-20 08:00:00+00:00,28.08 +2020-03-20 09:00:00+00:00,25.99 +2020-03-20 10:00:00+00:00,25.36 +2020-03-20 11:00:00+00:00,22.85 +2020-03-20 12:00:00+00:00,21.59 +2020-03-20 13:00:00+00:00,20.38 +2020-03-20 14:00:00+00:00,22.72 +2020-03-20 15:00:00+00:00,25.03 +2020-03-20 16:00:00+00:00,27.72 +2020-03-20 17:00:00+00:00,29.58 +2020-03-20 18:00:00+00:00,28.92 +2020-03-20 19:00:00+00:00,25.06 +2020-03-20 20:00:00+00:00,20.05 +2020-03-20 21:00:00+00:00,21.02 +2020-03-20 22:00:00+00:00,19.9 +2020-03-20 23:00:00+00:00,18.23 +2020-03-21 00:00:00+00:00,13.97 +2020-03-21 01:00:00+00:00,11.16 +2020-03-21 02:00:00+00:00,9.43 +2020-03-21 03:00:00+00:00,8.94 +2020-03-21 04:00:00+00:00,9.5 +2020-03-21 05:00:00+00:00,7.74 +2020-03-21 06:00:00+00:00,10.8 +2020-03-21 07:00:00+00:00,15.05 +2020-03-21 08:00:00+00:00,8.64 +2020-03-21 09:00:00+00:00,7.51 +2020-03-21 10:00:00+00:00,7.18 +2020-03-21 11:00:00+00:00,7.13 +2020-03-21 12:00:00+00:00,6.17 +2020-03-21 13:00:00+00:00,0.39 +2020-03-21 14:00:00+00:00,5.7 +2020-03-21 15:00:00+00:00,8.3 +2020-03-21 16:00:00+00:00,15.01 +2020-03-21 17:00:00+00:00,20.0 +2020-03-21 18:00:00+00:00,20.12 +2020-03-21 19:00:00+00:00,15.34 +2020-03-21 20:00:00+00:00,9.29 +2020-03-21 21:00:00+00:00,11.29 +2020-03-21 22:00:00+00:00,10.79 +2020-03-21 23:00:00+00:00,7.51 +2020-03-22 00:00:00+00:00,7.51 +2020-03-22 01:00:00+00:00,7.24 +2020-03-22 02:00:00+00:00,6.25 +2020-03-22 03:00:00+00:00,6.09 +2020-03-22 04:00:00+00:00,6.24 +2020-03-22 05:00:00+00:00,6.06 +2020-03-22 06:00:00+00:00,5.68 +2020-03-22 07:00:00+00:00,2.94 +2020-03-22 08:00:00+00:00,-3.63 +2020-03-22 09:00:00+00:00,-20.8 +2020-03-22 10:00:00+00:00,-25.1 +2020-03-22 11:00:00+00:00,-38.48 +2020-03-22 12:00:00+00:00,-55.05 +2020-03-22 13:00:00+00:00,-36.63 +2020-03-22 14:00:00+00:00,-11.98 +2020-03-22 15:00:00+00:00,0.02 +2020-03-22 16:00:00+00:00,11.04 +2020-03-22 17:00:00+00:00,19.78 +2020-03-22 18:00:00+00:00,22.93 +2020-03-22 19:00:00+00:00,19.45 +2020-03-22 20:00:00+00:00,14.36 +2020-03-22 21:00:00+00:00,14.79 +2020-03-22 22:00:00+00:00,11.05 +2020-03-22 23:00:00+00:00,12.07 +2020-03-23 00:00:00+00:00,10.0 +2020-03-23 01:00:00+00:00,10.17 +2020-03-23 02:00:00+00:00,9.28 +2020-03-23 03:00:00+00:00,9.28 +2020-03-23 04:00:00+00:00,12.76 +2020-03-23 05:00:00+00:00,20.0 +2020-03-23 06:00:00+00:00,23.99 +2020-03-23 07:00:00+00:00,20.08 +2020-03-23 08:00:00+00:00,15.84 +2020-03-23 09:00:00+00:00,10.66 +2020-03-23 10:00:00+00:00,10.7 +2020-03-23 11:00:00+00:00,10.62 +2020-03-23 12:00:00+00:00,10.92 +2020-03-23 13:00:00+00:00,12.07 +2020-03-23 14:00:00+00:00,12.01 +2020-03-23 15:00:00+00:00,17.4 +2020-03-23 16:00:00+00:00,25.95 +2020-03-23 17:00:00+00:00,30.67 +2020-03-23 18:00:00+00:00,33.14 +2020-03-23 19:00:00+00:00,27.99 +2020-03-23 20:00:00+00:00,21.97 +2020-03-23 21:00:00+00:00,21.84 +2020-03-23 22:00:00+00:00,18.18 +2020-03-23 23:00:00+00:00,16.63 +2020-03-24 00:00:00+00:00,17.55 +2020-03-24 01:00:00+00:00,16.53 +2020-03-24 02:00:00+00:00,16.81 +2020-03-24 03:00:00+00:00,17.6 +2020-03-24 04:00:00+00:00,19.8 +2020-03-24 05:00:00+00:00,23.63 +2020-03-24 06:00:00+00:00,25.88 +2020-03-24 07:00:00+00:00,20.35 +2020-03-24 08:00:00+00:00,17.5 +2020-03-24 09:00:00+00:00,14.16 +2020-03-24 10:00:00+00:00,15.08 +2020-03-24 11:00:00+00:00,14.3 +2020-03-24 12:00:00+00:00,13.0 +2020-03-24 13:00:00+00:00,14.33 +2020-03-24 14:00:00+00:00,16.65 +2020-03-24 15:00:00+00:00,19.79 +2020-03-24 16:00:00+00:00,27.75 +2020-03-24 17:00:00+00:00,33.8 +2020-03-24 18:00:00+00:00,33.78 +2020-03-24 19:00:00+00:00,29.65 +2020-03-24 20:00:00+00:00,22.98 +2020-03-24 21:00:00+00:00,22.98 +2020-03-24 22:00:00+00:00,20.31 +2020-03-24 23:00:00+00:00,19.29 +2020-03-25 00:00:00+00:00,19.21 +2020-03-25 01:00:00+00:00,19.5 +2020-03-25 02:00:00+00:00,19.33 +2020-03-25 03:00:00+00:00,19.26 +2020-03-25 04:00:00+00:00,21.51 +2020-03-25 05:00:00+00:00,25.84 +2020-03-25 06:00:00+00:00,29.15 +2020-03-25 07:00:00+00:00,27.64 +2020-03-25 08:00:00+00:00,23.62 +2020-03-25 09:00:00+00:00,20.05 +2020-03-25 10:00:00+00:00,19.57 +2020-03-25 11:00:00+00:00,19.97 +2020-03-25 12:00:00+00:00,19.05 +2020-03-25 13:00:00+00:00,18.7 +2020-03-25 14:00:00+00:00,17.57 +2020-03-25 15:00:00+00:00,19.96 +2020-03-25 16:00:00+00:00,27.09 +2020-03-25 17:00:00+00:00,34.88 +2020-03-25 18:00:00+00:00,34.48 +2020-03-25 19:00:00+00:00,28.09 +2020-03-25 20:00:00+00:00,24.51 +2020-03-25 21:00:00+00:00,24.02 +2020-03-25 22:00:00+00:00,21.18 +2020-03-25 23:00:00+00:00,19.68 +2020-03-26 00:00:00+00:00,18.39 +2020-03-26 01:00:00+00:00,19.1 +2020-03-26 02:00:00+00:00,18.39 +2020-03-26 03:00:00+00:00,18.32 +2020-03-26 04:00:00+00:00,19.8 +2020-03-26 05:00:00+00:00,22.2 +2020-03-26 06:00:00+00:00,25.53 +2020-03-26 07:00:00+00:00,25.0 +2020-03-26 08:00:00+00:00,21.32 +2020-03-26 09:00:00+00:00,19.11 +2020-03-26 10:00:00+00:00,18.16 +2020-03-26 11:00:00+00:00,17.67 +2020-03-26 12:00:00+00:00,15.32 +2020-03-26 13:00:00+00:00,16.82 +2020-03-26 14:00:00+00:00,18.19 +2020-03-26 15:00:00+00:00,18.9 +2020-03-26 16:00:00+00:00,27.29 +2020-03-26 17:00:00+00:00,33.87 +2020-03-26 18:00:00+00:00,34.64 +2020-03-26 19:00:00+00:00,30.09 +2020-03-26 20:00:00+00:00,26.27 +2020-03-26 21:00:00+00:00,25.5 +2020-03-26 22:00:00+00:00,23.94 +2020-03-26 23:00:00+00:00,21.26 +2020-03-27 00:00:00+00:00,19.0 +2020-03-27 01:00:00+00:00,18.54 +2020-03-27 02:00:00+00:00,18.25 +2020-03-27 03:00:00+00:00,18.5 +2020-03-27 04:00:00+00:00,20.6 +2020-03-27 05:00:00+00:00,24.65 +2020-03-27 06:00:00+00:00,26.95 +2020-03-27 07:00:00+00:00,25.91 +2020-03-27 08:00:00+00:00,22.67 +2020-03-27 09:00:00+00:00,19.98 +2020-03-27 10:00:00+00:00,18.47 +2020-03-27 11:00:00+00:00,18.5 +2020-03-27 12:00:00+00:00,16.0 +2020-03-27 13:00:00+00:00,15.64 +2020-03-27 14:00:00+00:00,17.44 +2020-03-27 15:00:00+00:00,18.08 +2020-03-27 16:00:00+00:00,21.8 +2020-03-27 17:00:00+00:00,26.72 +2020-03-27 18:00:00+00:00,30.0 +2020-03-27 19:00:00+00:00,24.94 +2020-03-27 20:00:00+00:00,23.19 +2020-03-27 21:00:00+00:00,24.91 +2020-03-27 22:00:00+00:00,25.16 +2020-03-27 23:00:00+00:00,21.93 +2020-03-28 00:00:00+00:00,21.09 +2020-03-28 01:00:00+00:00,21.67 +2020-03-28 02:00:00+00:00,19.38 +2020-03-28 03:00:00+00:00,18.8 +2020-03-28 04:00:00+00:00,18.44 +2020-03-28 05:00:00+00:00,18.96 +2020-03-28 06:00:00+00:00,18.91 +2020-03-28 07:00:00+00:00,17.22 +2020-03-28 08:00:00+00:00,13.08 +2020-03-28 09:00:00+00:00,9.9 +2020-03-28 10:00:00+00:00,6.51 +2020-03-28 11:00:00+00:00,5.12 +2020-03-28 12:00:00+00:00,1.0 +2020-03-28 13:00:00+00:00,-0.72 +2020-03-28 14:00:00+00:00,0.02 +2020-03-28 15:00:00+00:00,5.77 +2020-03-28 16:00:00+00:00,17.14 +2020-03-28 17:00:00+00:00,19.97 +2020-03-28 18:00:00+00:00,21.93 +2020-03-28 19:00:00+00:00,17.02 +2020-03-28 20:00:00+00:00,16.39 +2020-03-28 21:00:00+00:00,16.27 +2020-03-28 22:00:00+00:00,16.0 +2020-03-28 23:00:00+00:00,11.76 +2020-03-29 00:00:00+00:00,11.05 +2020-03-29 01:00:00+00:00,6.6 +2020-03-29 04:00:00+00:00,0.08 +2020-03-29 05:00:00+00:00,0.96 +2020-03-29 06:00:00+00:00,2.59 +2020-03-29 07:00:00+00:00,2.93 +2020-03-29 08:00:00+00:00,-0.03 +2020-03-29 09:00:00+00:00,-0.08 +2020-03-29 10:00:00+00:00,-1.13 +2020-03-29 11:00:00+00:00,-10.87 +2020-03-29 12:00:00+00:00,-15.8 +2020-03-29 13:00:00+00:00,-10.79 +2020-03-29 14:00:00+00:00,-5.98 +2020-03-29 15:00:00+00:00,0.08 +2020-03-29 16:00:00+00:00,7.81 +2020-03-29 17:00:00+00:00,16.99 +2020-03-29 18:00:00+00:00,17.9 +2020-03-29 19:00:00+00:00,17.45 +2020-03-29 20:00:00+00:00,20.14 +2020-03-29 21:00:00+00:00,20.59 +2020-03-29 22:00:00+00:00,18.1 +2020-03-29 23:00:00+00:00,17.81 +2020-03-30 00:00:00+00:00,19.65 +2020-03-30 01:00:00+00:00,17.61 +2020-03-30 02:00:00+00:00,16.99 +2020-03-30 03:00:00+00:00,19.94 +2020-03-30 04:00:00+00:00,24.94 +2020-03-30 05:00:00+00:00,31.44 +2020-03-30 06:00:00+00:00,30.94 +2020-03-30 07:00:00+00:00,26.11 +2020-03-30 08:00:00+00:00,22.4 +2020-03-30 09:00:00+00:00,21.09 +2020-03-30 10:00:00+00:00,21.09 +2020-03-30 11:00:00+00:00,19.12 +2020-03-30 12:00:00+00:00,17.07 +2020-03-30 13:00:00+00:00,15.8 +2020-03-30 14:00:00+00:00,15.5 +2020-03-30 15:00:00+00:00,21.52 +2020-03-30 16:00:00+00:00,27.94 +2020-03-30 17:00:00+00:00,38.93 +2020-03-30 18:00:00+00:00,35.7 +2020-03-30 19:00:00+00:00,28.6 +2020-03-30 20:00:00+00:00,27.01 +2020-03-30 21:00:00+00:00,24.38 +2020-03-30 22:00:00+00:00,23.39 +2020-03-30 23:00:00+00:00,19.69 +2020-03-31 00:00:00+00:00,19.91 +2020-03-31 01:00:00+00:00,19.59 +2020-03-31 02:00:00+00:00,19.01 +2020-03-31 03:00:00+00:00,23.95 +2020-03-31 04:00:00+00:00,30.97 +2020-03-31 05:00:00+00:00,42.3 +2020-03-31 06:00:00+00:00,35.11 +2020-03-31 07:00:00+00:00,25.75 +2020-03-31 08:00:00+00:00,23.94 +2020-03-31 09:00:00+00:00,19.78 +2020-03-31 10:00:00+00:00,20.0 +2020-03-31 11:00:00+00:00,18.39 +2020-03-31 12:00:00+00:00,17.5 +2020-03-31 13:00:00+00:00,17.6 +2020-03-31 14:00:00+00:00,17.86 +2020-03-31 15:00:00+00:00,24.03 +2020-03-31 16:00:00+00:00,31.44 +2020-03-31 17:00:00+00:00,42.66 +2020-03-31 18:00:00+00:00,33.53 +2020-03-31 19:00:00+00:00,26.97 +2020-03-31 20:00:00+00:00,26.61 +2020-03-31 21:00:00+00:00,24.1 +2020-03-31 22:00:00+00:00,22.26 +2020-03-31 23:00:00+00:00,20.22 +2020-04-01 00:00:00+00:00,19.67 +2020-04-01 01:00:00+00:00,19.25 +2020-04-01 02:00:00+00:00,19.31 +2020-04-01 03:00:00+00:00,20.76 +2020-04-01 04:00:00+00:00,25.43 +2020-04-01 05:00:00+00:00,27.21 +2020-04-01 06:00:00+00:00,27.96 +2020-04-01 07:00:00+00:00,25.5 +2020-04-01 08:00:00+00:00,21.97 +2020-04-01 09:00:00+00:00,20.27 +2020-04-01 10:00:00+00:00,19.35 +2020-04-01 11:00:00+00:00,18.25 +2020-04-01 12:00:00+00:00,17.3 +2020-04-01 13:00:00+00:00,18.09 +2020-04-01 14:00:00+00:00,19.11 +2020-04-01 15:00:00+00:00,22.01 +2020-04-01 16:00:00+00:00,26.9 +2020-04-01 17:00:00+00:00,31.27 +2020-04-01 18:00:00+00:00,34.97 +2020-04-01 19:00:00+00:00,28.94 +2020-04-01 20:00:00+00:00,28.31 +2020-04-01 21:00:00+00:00,26.35 +2020-04-01 22:00:00+00:00,24.18 +2020-04-01 23:00:00+00:00,22.93 +2020-04-02 00:00:00+00:00,21.8 +2020-04-02 01:00:00+00:00,20.96 +2020-04-02 02:00:00+00:00,20.8 +2020-04-02 03:00:00+00:00,22.56 +2020-04-02 04:00:00+00:00,25.31 +2020-04-02 05:00:00+00:00,27.31 +2020-04-02 06:00:00+00:00,27.8 +2020-04-02 07:00:00+00:00,25.0 +2020-04-02 08:00:00+00:00,21.1 +2020-04-02 09:00:00+00:00,18.65 +2020-04-02 10:00:00+00:00,12.42 +2020-04-02 11:00:00+00:00,9.06 +2020-04-02 12:00:00+00:00,4.64 +2020-04-02 13:00:00+00:00,4.62 +2020-04-02 14:00:00+00:00,6.65 +2020-04-02 15:00:00+00:00,17.2 +2020-04-02 16:00:00+00:00,22.71 +2020-04-02 17:00:00+00:00,25.82 +2020-04-02 18:00:00+00:00,27.3 +2020-04-02 19:00:00+00:00,24.47 +2020-04-02 20:00:00+00:00,25.0 +2020-04-02 21:00:00+00:00,20.55 +2020-04-02 22:00:00+00:00,21.69 +2020-04-02 23:00:00+00:00,18.93 +2020-04-03 00:00:00+00:00,18.15 +2020-04-03 01:00:00+00:00,18.19 +2020-04-03 02:00:00+00:00,18.47 +2020-04-03 03:00:00+00:00,18.45 +2020-04-03 04:00:00+00:00,23.76 +2020-04-03 05:00:00+00:00,26.1 +2020-04-03 06:00:00+00:00,26.45 +2020-04-03 07:00:00+00:00,25.5 +2020-04-03 08:00:00+00:00,23.03 +2020-04-03 09:00:00+00:00,21.04 +2020-04-03 10:00:00+00:00,20.24 +2020-04-03 11:00:00+00:00,17.7 +2020-04-03 12:00:00+00:00,17.08 +2020-04-03 13:00:00+00:00,17.03 +2020-04-03 14:00:00+00:00,17.07 +2020-04-03 15:00:00+00:00,19.98 +2020-04-03 16:00:00+00:00,25.57 +2020-04-03 17:00:00+00:00,28.44 +2020-04-03 18:00:00+00:00,33.54 +2020-04-03 19:00:00+00:00,27.23 +2020-04-03 20:00:00+00:00,29.15 +2020-04-03 21:00:00+00:00,28.0 +2020-04-03 22:00:00+00:00,25.1 +2020-04-03 23:00:00+00:00,22.99 +2020-04-04 00:00:00+00:00,21.1 +2020-04-04 01:00:00+00:00,20.1 +2020-04-04 02:00:00+00:00,19.8 +2020-04-04 03:00:00+00:00,19.6 +2020-04-04 04:00:00+00:00,21.79 +2020-04-04 05:00:00+00:00,22.97 +2020-04-04 06:00:00+00:00,21.97 +2020-04-04 07:00:00+00:00,19.0 +2020-04-04 08:00:00+00:00,14.98 +2020-04-04 09:00:00+00:00,13.06 +2020-04-04 10:00:00+00:00,14.28 +2020-04-04 11:00:00+00:00,10.82 +2020-04-04 12:00:00+00:00,10.0 +2020-04-04 13:00:00+00:00,9.88 +2020-04-04 14:00:00+00:00,15.74 +2020-04-04 15:00:00+00:00,21.51 +2020-04-04 16:00:00+00:00,26.84 +2020-04-04 17:00:00+00:00,36.75 +2020-04-04 18:00:00+00:00,32.67 +2020-04-04 19:00:00+00:00,23.93 +2020-04-04 20:00:00+00:00,21.54 +2020-04-04 21:00:00+00:00,17.07 +2020-04-04 22:00:00+00:00,17.99 +2020-04-04 23:00:00+00:00,11.4 +2020-04-05 00:00:00+00:00,9.82 +2020-04-05 01:00:00+00:00,9.9 +2020-04-05 02:00:00+00:00,8.17 +2020-04-05 03:00:00+00:00,5.26 +2020-04-05 04:00:00+00:00,5.53 +2020-04-05 05:00:00+00:00,5.5 +2020-04-05 06:00:00+00:00,8.05 +2020-04-05 07:00:00+00:00,5.77 +2020-04-05 08:00:00+00:00,4.55 +2020-04-05 09:00:00+00:00,1.36 +2020-04-05 10:00:00+00:00,-1.85 +2020-04-05 11:00:00+00:00,-31.95 +2020-04-05 12:00:00+00:00,-50.26 +2020-04-05 13:00:00+00:00,-30.29 +2020-04-05 14:00:00+00:00,-4.95 +2020-04-05 15:00:00+00:00,4.48 +2020-04-05 16:00:00+00:00,16.51 +2020-04-05 17:00:00+00:00,19.9 +2020-04-05 18:00:00+00:00,18.48 +2020-04-05 19:00:00+00:00,10.39 +2020-04-05 20:00:00+00:00,11.95 +2020-04-05 21:00:00+00:00,8.65 +2020-04-05 22:00:00+00:00,6.66 +2020-04-05 23:00:00+00:00,4.28 +2020-04-06 00:00:00+00:00,6.34 +2020-04-06 01:00:00+00:00,3.7 +2020-04-06 02:00:00+00:00,3.66 +2020-04-06 03:00:00+00:00,5.09 +2020-04-06 04:00:00+00:00,11.89 +2020-04-06 05:00:00+00:00,21.81 +2020-04-06 06:00:00+00:00,21.63 +2020-04-06 07:00:00+00:00,18.73 +2020-04-06 08:00:00+00:00,14.54 +2020-04-06 09:00:00+00:00,11.19 +2020-04-06 10:00:00+00:00,14.02 +2020-04-06 11:00:00+00:00,10.55 +2020-04-06 12:00:00+00:00,10.0 +2020-04-06 13:00:00+00:00,11.83 +2020-04-06 14:00:00+00:00,14.83 +2020-04-06 15:00:00+00:00,21.68 +2020-04-06 16:00:00+00:00,31.01 +2020-04-06 17:00:00+00:00,43.33 +2020-04-06 18:00:00+00:00,45.33 +2020-04-06 19:00:00+00:00,30.86 +2020-04-06 20:00:00+00:00,28.92 +2020-04-06 21:00:00+00:00,24.17 +2020-04-06 22:00:00+00:00,20.05 +2020-04-06 23:00:00+00:00,19.97 +2020-04-07 00:00:00+00:00,20.61 +2020-04-07 01:00:00+00:00,20.48 +2020-04-07 02:00:00+00:00,21.65 +2020-04-07 03:00:00+00:00,23.86 +2020-04-07 04:00:00+00:00,31.98 +2020-04-07 05:00:00+00:00,34.91 +2020-04-07 06:00:00+00:00,31.32 +2020-04-07 07:00:00+00:00,23.66 +2020-04-07 08:00:00+00:00,19.19 +2020-04-07 09:00:00+00:00,18.05 +2020-04-07 10:00:00+00:00,17.55 +2020-04-07 11:00:00+00:00,13.52 +2020-04-07 12:00:00+00:00,12.89 +2020-04-07 13:00:00+00:00,15.48 +2020-04-07 14:00:00+00:00,18.0 +2020-04-07 15:00:00+00:00,22.66 +2020-04-07 16:00:00+00:00,31.42 +2020-04-07 17:00:00+00:00,40.1 +2020-04-07 18:00:00+00:00,37.31 +2020-04-07 19:00:00+00:00,27.4 +2020-04-07 20:00:00+00:00,23.84 +2020-04-07 21:00:00+00:00,19.05 +2020-04-07 22:00:00+00:00,21.17 +2020-04-07 23:00:00+00:00,20.53 +2020-04-08 00:00:00+00:00,19.72 +2020-04-08 01:00:00+00:00,19.87 +2020-04-08 02:00:00+00:00,20.09 +2020-04-08 03:00:00+00:00,23.15 +2020-04-08 04:00:00+00:00,29.33 +2020-04-08 05:00:00+00:00,29.7 +2020-04-08 06:00:00+00:00,29.08 +2020-04-08 07:00:00+00:00,25.91 +2020-04-08 08:00:00+00:00,21.95 +2020-04-08 09:00:00+00:00,21.69 +2020-04-08 10:00:00+00:00,19.08 +2020-04-08 11:00:00+00:00,17.92 +2020-04-08 12:00:00+00:00,17.57 +2020-04-08 13:00:00+00:00,19.54 +2020-04-08 14:00:00+00:00,21.55 +2020-04-08 15:00:00+00:00,25.9 +2020-04-08 16:00:00+00:00,35.31 +2020-04-08 17:00:00+00:00,49.12 +2020-04-08 18:00:00+00:00,50.33 +2020-04-08 19:00:00+00:00,39.03 +2020-04-08 20:00:00+00:00,31.41 +2020-04-08 21:00:00+00:00,27.34 +2020-04-08 22:00:00+00:00,22.66 +2020-04-08 23:00:00+00:00,21.3 +2020-04-09 00:00:00+00:00,21.08 +2020-04-09 01:00:00+00:00,20.9 +2020-04-09 02:00:00+00:00,20.51 +2020-04-09 03:00:00+00:00,22.68 +2020-04-09 04:00:00+00:00,27.66 +2020-04-09 05:00:00+00:00,29.7 +2020-04-09 06:00:00+00:00,29.53 +2020-04-09 07:00:00+00:00,25.21 +2020-04-09 08:00:00+00:00,22.43 +2020-04-09 09:00:00+00:00,21.26 +2020-04-09 10:00:00+00:00,19.42 +2020-04-09 11:00:00+00:00,17.4 +2020-04-09 12:00:00+00:00,13.19 +2020-04-09 13:00:00+00:00,16.41 +2020-04-09 14:00:00+00:00,17.88 +2020-04-09 15:00:00+00:00,23.18 +2020-04-09 16:00:00+00:00,26.58 +2020-04-09 17:00:00+00:00,30.92 +2020-04-09 18:00:00+00:00,31.43 +2020-04-09 19:00:00+00:00,30.0 +2020-04-09 20:00:00+00:00,27.92 +2020-04-09 21:00:00+00:00,25.0 +2020-04-09 22:00:00+00:00,25.11 +2020-04-09 23:00:00+00:00,25.02 +2020-04-10 00:00:00+00:00,22.0 +2020-04-10 01:00:00+00:00,24.59 +2020-04-10 02:00:00+00:00,26.37 +2020-04-10 03:00:00+00:00,26.86 +2020-04-10 04:00:00+00:00,27.06 +2020-04-10 05:00:00+00:00,28.57 +2020-04-10 06:00:00+00:00,28.0 +2020-04-10 07:00:00+00:00,24.58 +2020-04-10 08:00:00+00:00,18.04 +2020-04-10 09:00:00+00:00,17.06 +2020-04-10 10:00:00+00:00,13.97 +2020-04-10 11:00:00+00:00,10.55 +2020-04-10 12:00:00+00:00,9.16 +2020-04-10 13:00:00+00:00,10.03 +2020-04-10 14:00:00+00:00,14.0 +2020-04-10 15:00:00+00:00,24.54 +2020-04-10 16:00:00+00:00,27.0 +2020-04-10 17:00:00+00:00,31.43 +2020-04-10 18:00:00+00:00,34.83 +2020-04-10 19:00:00+00:00,31.57 +2020-04-10 20:00:00+00:00,29.81 +2020-04-10 21:00:00+00:00,25.18 +2020-04-10 22:00:00+00:00,24.19 +2020-04-10 23:00:00+00:00,21.84 +2020-04-11 00:00:00+00:00,20.1 +2020-04-11 01:00:00+00:00,21.11 +2020-04-11 02:00:00+00:00,23.01 +2020-04-11 03:00:00+00:00,24.72 +2020-04-11 04:00:00+00:00,24.92 +2020-04-11 05:00:00+00:00,25.16 +2020-04-11 06:00:00+00:00,25.04 +2020-04-11 07:00:00+00:00,20.82 +2020-04-11 08:00:00+00:00,14.3 +2020-04-11 09:00:00+00:00,11.59 +2020-04-11 10:00:00+00:00,12.33 +2020-04-11 11:00:00+00:00,8.6 +2020-04-11 12:00:00+00:00,5.1 +2020-04-11 13:00:00+00:00,7.28 +2020-04-11 14:00:00+00:00,14.01 +2020-04-11 15:00:00+00:00,20.0 +2020-04-11 16:00:00+00:00,28.97 +2020-04-11 17:00:00+00:00,35.11 +2020-04-11 18:00:00+00:00,35.95 +2020-04-11 19:00:00+00:00,29.66 +2020-04-11 20:00:00+00:00,24.91 +2020-04-11 21:00:00+00:00,21.82 +2020-04-11 22:00:00+00:00,21.8 +2020-04-11 23:00:00+00:00,19.01 +2020-04-12 00:00:00+00:00,16.71 +2020-04-12 01:00:00+00:00,14.38 +2020-04-12 02:00:00+00:00,14.06 +2020-04-12 03:00:00+00:00,15.02 +2020-04-12 04:00:00+00:00,15.74 +2020-04-12 05:00:00+00:00,16.0 +2020-04-12 06:00:00+00:00,14.06 +2020-04-12 07:00:00+00:00,9.99 +2020-04-12 08:00:00+00:00,5.18 +2020-04-12 09:00:00+00:00,4.93 +2020-04-12 10:00:00+00:00,4.98 +2020-04-12 11:00:00+00:00,1.84 +2020-04-12 12:00:00+00:00,0.0 +2020-04-12 13:00:00+00:00,0.17 +2020-04-12 14:00:00+00:00,3.0 +2020-04-12 15:00:00+00:00,4.14 +2020-04-12 16:00:00+00:00,18.13 +2020-04-12 17:00:00+00:00,25.17 +2020-04-12 18:00:00+00:00,24.82 +2020-04-12 19:00:00+00:00,21.97 +2020-04-12 20:00:00+00:00,19.5 +2020-04-12 21:00:00+00:00,8.2 +2020-04-12 22:00:00+00:00,10.34 +2020-04-12 23:00:00+00:00,0.07 +2020-04-13 00:00:00+00:00,1.72 +2020-04-13 01:00:00+00:00,0.77 +2020-04-13 02:00:00+00:00,0.02 +2020-04-13 03:00:00+00:00,-0.09 +2020-04-13 04:00:00+00:00,-2.35 +2020-04-13 05:00:00+00:00,-5.0 +2020-04-13 06:00:00+00:00,-5.91 +2020-04-13 07:00:00+00:00,-4.94 +2020-04-13 08:00:00+00:00,-5.09 +2020-04-13 09:00:00+00:00,-19.91 +2020-04-13 10:00:00+00:00,-55.62 +2020-04-13 11:00:00+00:00,-70.1 +2020-04-13 12:00:00+00:00,-78.0 +2020-04-13 13:00:00+00:00,-78.15 +2020-04-13 14:00:00+00:00,-74.97 +2020-04-13 15:00:00+00:00,-39.94 +2020-04-13 16:00:00+00:00,-1.77 +2020-04-13 17:00:00+00:00,11.21 +2020-04-13 18:00:00+00:00,9.76 +2020-04-13 19:00:00+00:00,6.45 +2020-04-13 20:00:00+00:00,9.29 +2020-04-13 21:00:00+00:00,9.02 +2020-04-13 22:00:00+00:00,3.79 +2020-04-13 23:00:00+00:00,4.4 +2020-04-14 00:00:00+00:00,3.9 +2020-04-14 01:00:00+00:00,3.96 +2020-04-14 02:00:00+00:00,4.6 +2020-04-14 03:00:00+00:00,15.88 +2020-04-14 04:00:00+00:00,25.24 +2020-04-14 05:00:00+00:00,32.74 +2020-04-14 06:00:00+00:00,31.14 +2020-04-14 07:00:00+00:00,23.68 +2020-04-14 08:00:00+00:00,21.86 +2020-04-14 09:00:00+00:00,19.73 +2020-04-14 10:00:00+00:00,17.13 +2020-04-14 11:00:00+00:00,16.12 +2020-04-14 12:00:00+00:00,13.54 +2020-04-14 13:00:00+00:00,15.03 +2020-04-14 14:00:00+00:00,19.1 +2020-04-14 15:00:00+00:00,23.15 +2020-04-14 16:00:00+00:00,26.82 +2020-04-14 17:00:00+00:00,33.38 +2020-04-14 18:00:00+00:00,39.91 +2020-04-14 19:00:00+00:00,31.45 +2020-04-14 20:00:00+00:00,26.77 +2020-04-14 21:00:00+00:00,24.28 +2020-04-14 22:00:00+00:00,19.87 +2020-04-14 23:00:00+00:00,17.08 +2020-04-15 00:00:00+00:00,17.0 +2020-04-15 01:00:00+00:00,16.37 +2020-04-15 02:00:00+00:00,16.57 +2020-04-15 03:00:00+00:00,19.1 +2020-04-15 04:00:00+00:00,25.15 +2020-04-15 05:00:00+00:00,26.09 +2020-04-15 06:00:00+00:00,24.99 +2020-04-15 07:00:00+00:00,21.2 +2020-04-15 08:00:00+00:00,14.29 +2020-04-15 09:00:00+00:00,14.16 +2020-04-15 10:00:00+00:00,8.07 +2020-04-15 11:00:00+00:00,8.0 +2020-04-15 12:00:00+00:00,4.46 +2020-04-15 13:00:00+00:00,8.05 +2020-04-15 14:00:00+00:00,12.13 +2020-04-15 15:00:00+00:00,21.85 +2020-04-15 16:00:00+00:00,27.34 +2020-04-15 17:00:00+00:00,37.84 +2020-04-15 18:00:00+00:00,41.91 +2020-04-15 19:00:00+00:00,31.34 +2020-04-15 20:00:00+00:00,26.25 +2020-04-15 21:00:00+00:00,24.99 +2020-04-15 22:00:00+00:00,20.81 +2020-04-15 23:00:00+00:00,20.61 +2020-04-16 00:00:00+00:00,21.02 +2020-04-16 01:00:00+00:00,21.1 +2020-04-16 02:00:00+00:00,21.1 +2020-04-16 03:00:00+00:00,23.73 +2020-04-16 04:00:00+00:00,26.96 +2020-04-16 05:00:00+00:00,35.01 +2020-04-16 06:00:00+00:00,30.98 +2020-04-16 07:00:00+00:00,25.0 +2020-04-16 08:00:00+00:00,21.5 +2020-04-16 09:00:00+00:00,19.49 +2020-04-16 10:00:00+00:00,16.23 +2020-04-16 11:00:00+00:00,15.29 +2020-04-16 12:00:00+00:00,15.1 +2020-04-16 13:00:00+00:00,16.29 +2020-04-16 14:00:00+00:00,19.32 +2020-04-16 15:00:00+00:00,24.99 +2020-04-16 16:00:00+00:00,29.34 +2020-04-16 17:00:00+00:00,46.39 +2020-04-16 18:00:00+00:00,53.25 +2020-04-16 19:00:00+00:00,33.37 +2020-04-16 20:00:00+00:00,27.01 +2020-04-16 21:00:00+00:00,23.71 +2020-04-16 22:00:00+00:00,22.08 +2020-04-16 23:00:00+00:00,21.58 +2020-04-17 00:00:00+00:00,21.06 +2020-04-17 01:00:00+00:00,21.99 +2020-04-17 02:00:00+00:00,22.44 +2020-04-17 03:00:00+00:00,24.41 +2020-04-17 04:00:00+00:00,32.93 +2020-04-17 05:00:00+00:00,47.84 +2020-04-17 06:00:00+00:00,44.81 +2020-04-17 07:00:00+00:00,26.41 +2020-04-17 08:00:00+00:00,24.9 +2020-04-17 09:00:00+00:00,23.87 +2020-04-17 10:00:00+00:00,20.44 +2020-04-17 11:00:00+00:00,18.07 +2020-04-17 12:00:00+00:00,18.54 +2020-04-17 13:00:00+00:00,21.87 +2020-04-17 14:00:00+00:00,21.75 +2020-04-17 15:00:00+00:00,25.15 +2020-04-17 16:00:00+00:00,36.76 +2020-04-17 17:00:00+00:00,48.75 +2020-04-17 18:00:00+00:00,42.77 +2020-04-17 19:00:00+00:00,32.38 +2020-04-17 20:00:00+00:00,24.92 +2020-04-17 21:00:00+00:00,20.76 +2020-04-17 22:00:00+00:00,22.1 +2020-04-17 23:00:00+00:00,20.13 +2020-04-18 00:00:00+00:00,18.78 +2020-04-18 01:00:00+00:00,18.1 +2020-04-18 02:00:00+00:00,18.55 +2020-04-18 03:00:00+00:00,19.07 +2020-04-18 04:00:00+00:00,21.62 +2020-04-18 05:00:00+00:00,23.82 +2020-04-18 06:00:00+00:00,23.87 +2020-04-18 07:00:00+00:00,24.33 +2020-04-18 08:00:00+00:00,17.3 +2020-04-18 09:00:00+00:00,16.63 +2020-04-18 10:00:00+00:00,16.8 +2020-04-18 11:00:00+00:00,12.84 +2020-04-18 12:00:00+00:00,12.32 +2020-04-18 13:00:00+00:00,12.77 +2020-04-18 14:00:00+00:00,15.05 +2020-04-18 15:00:00+00:00,24.13 +2020-04-18 16:00:00+00:00,25.99 +2020-04-18 17:00:00+00:00,29.42 +2020-04-18 18:00:00+00:00,28.66 +2020-04-18 19:00:00+00:00,23.81 +2020-04-18 20:00:00+00:00,21.16 +2020-04-18 21:00:00+00:00,18.38 +2020-04-18 22:00:00+00:00,16.51 +2020-04-18 23:00:00+00:00,13.71 +2020-04-19 00:00:00+00:00,12.12 +2020-04-19 01:00:00+00:00,9.3 +2020-04-19 02:00:00+00:00,10.98 +2020-04-19 03:00:00+00:00,11.31 +2020-04-19 04:00:00+00:00,11.66 +2020-04-19 05:00:00+00:00,11.15 +2020-04-19 06:00:00+00:00,14.01 +2020-04-19 07:00:00+00:00,12.47 +2020-04-19 08:00:00+00:00,10.88 +2020-04-19 09:00:00+00:00,8.39 +2020-04-19 10:00:00+00:00,9.21 +2020-04-19 11:00:00+00:00,0.03 +2020-04-19 12:00:00+00:00,-18.5 +2020-04-19 13:00:00+00:00,-26.0 +2020-04-19 14:00:00+00:00,-11.84 +2020-04-19 15:00:00+00:00,4.99 +2020-04-19 16:00:00+00:00,11.2 +2020-04-19 17:00:00+00:00,15.42 +2020-04-19 18:00:00+00:00,15.59 +2020-04-19 19:00:00+00:00,12.73 +2020-04-19 20:00:00+00:00,12.34 +2020-04-19 21:00:00+00:00,10.19 +2020-04-19 22:00:00+00:00,4.43 +2020-04-19 23:00:00+00:00,4.38 +2020-04-20 00:00:00+00:00,3.69 +2020-04-20 01:00:00+00:00,0.66 +2020-04-20 02:00:00+00:00,0.03 +2020-04-20 03:00:00+00:00,4.78 +2020-04-20 04:00:00+00:00,18.0 +2020-04-20 05:00:00+00:00,22.82 +2020-04-20 06:00:00+00:00,22.93 +2020-04-20 07:00:00+00:00,16.07 +2020-04-20 08:00:00+00:00,11.72 +2020-04-20 09:00:00+00:00,5.46 +2020-04-20 10:00:00+00:00,-4.98 +2020-04-20 11:00:00+00:00,-29.7 +2020-04-20 12:00:00+00:00,-44.25 +2020-04-20 13:00:00+00:00,-39.49 +2020-04-20 14:00:00+00:00,-23.71 +2020-04-20 15:00:00+00:00,4.01 +2020-04-20 16:00:00+00:00,16.38 +2020-04-20 17:00:00+00:00,17.81 +2020-04-20 18:00:00+00:00,18.38 +2020-04-20 19:00:00+00:00,14.05 +2020-04-20 20:00:00+00:00,10.28 +2020-04-20 21:00:00+00:00,4.85 +2020-04-20 22:00:00+00:00,4.65 +2020-04-20 23:00:00+00:00,4.31 +2020-04-21 00:00:00+00:00,3.69 +2020-04-21 01:00:00+00:00,-0.59 +2020-04-21 02:00:00+00:00,3.72 +2020-04-21 03:00:00+00:00,4.73 +2020-04-21 04:00:00+00:00,10.0 +2020-04-21 05:00:00+00:00,16.13 +2020-04-21 06:00:00+00:00,16.52 +2020-04-21 07:00:00+00:00,7.92 +2020-04-21 08:00:00+00:00,-19.21 +2020-04-21 09:00:00+00:00,-69.05 +2020-04-21 10:00:00+00:00,-79.74 +2020-04-21 11:00:00+00:00,-80.09 +2020-04-21 12:00:00+00:00,-83.94 +2020-04-21 13:00:00+00:00,-80.02 +2020-04-21 14:00:00+00:00,-78.09 +2020-04-21 15:00:00+00:00,-23.12 +2020-04-21 16:00:00+00:00,5.53 +2020-04-21 17:00:00+00:00,10.99 +2020-04-21 18:00:00+00:00,10.99 +2020-04-21 19:00:00+00:00,11.17 +2020-04-21 20:00:00+00:00,7.6 +2020-04-21 21:00:00+00:00,8.31 +2020-04-21 22:00:00+00:00,4.39 +2020-04-21 23:00:00+00:00,4.12 +2020-04-22 00:00:00+00:00,3.5 +2020-04-22 01:00:00+00:00,4.14 +2020-04-22 02:00:00+00:00,4.57 +2020-04-22 03:00:00+00:00,6.0 +2020-04-22 04:00:00+00:00,14.92 +2020-04-22 05:00:00+00:00,23.39 +2020-04-22 06:00:00+00:00,19.81 +2020-04-22 07:00:00+00:00,10.57 +2020-04-22 08:00:00+00:00,6.77 +2020-04-22 09:00:00+00:00,6.1 +2020-04-22 10:00:00+00:00,-0.56 +2020-04-22 11:00:00+00:00,-24.97 +2020-04-22 12:00:00+00:00,-29.98 +2020-04-22 13:00:00+00:00,-5.76 +2020-04-22 14:00:00+00:00,4.07 +2020-04-22 15:00:00+00:00,8.59 +2020-04-22 16:00:00+00:00,18.05 +2020-04-22 17:00:00+00:00,24.57 +2020-04-22 18:00:00+00:00,25.54 +2020-04-22 19:00:00+00:00,22.19 +2020-04-22 20:00:00+00:00,20.01 +2020-04-22 21:00:00+00:00,19.0 +2020-04-22 22:00:00+00:00,20.1 +2020-04-22 23:00:00+00:00,19.68 +2020-04-23 00:00:00+00:00,18.38 +2020-04-23 01:00:00+00:00,20.07 +2020-04-23 02:00:00+00:00,20.6 +2020-04-23 03:00:00+00:00,22.34 +2020-04-23 04:00:00+00:00,33.7 +2020-04-23 05:00:00+00:00,42.03 +2020-04-23 06:00:00+00:00,38.48 +2020-04-23 07:00:00+00:00,23.72 +2020-04-23 08:00:00+00:00,18.18 +2020-04-23 09:00:00+00:00,17.4 +2020-04-23 10:00:00+00:00,16.95 +2020-04-23 11:00:00+00:00,16.63 +2020-04-23 12:00:00+00:00,14.95 +2020-04-23 13:00:00+00:00,14.97 +2020-04-23 14:00:00+00:00,18.11 +2020-04-23 15:00:00+00:00,25.07 +2020-04-23 16:00:00+00:00,34.51 +2020-04-23 17:00:00+00:00,58.95 +2020-04-23 18:00:00+00:00,69.68 +2020-04-23 19:00:00+00:00,38.62 +2020-04-23 20:00:00+00:00,34.43 +2020-04-23 21:00:00+00:00,28.6 +2020-04-23 22:00:00+00:00,23.87 +2020-04-23 23:00:00+00:00,21.63 +2020-04-24 00:00:00+00:00,21.54 +2020-04-24 01:00:00+00:00,21.54 +2020-04-24 02:00:00+00:00,20.93 +2020-04-24 03:00:00+00:00,22.0 +2020-04-24 04:00:00+00:00,26.69 +2020-04-24 05:00:00+00:00,35.85 +2020-04-24 06:00:00+00:00,31.0 +2020-04-24 07:00:00+00:00,21.84 +2020-04-24 08:00:00+00:00,20.0 +2020-04-24 09:00:00+00:00,18.45 +2020-04-24 10:00:00+00:00,16.15 +2020-04-24 11:00:00+00:00,14.55 +2020-04-24 12:00:00+00:00,12.02 +2020-04-24 13:00:00+00:00,10.0 +2020-04-24 14:00:00+00:00,11.81 +2020-04-24 15:00:00+00:00,15.62 +2020-04-24 16:00:00+00:00,19.97 +2020-04-24 17:00:00+00:00,22.27 +2020-04-24 18:00:00+00:00,22.5 +2020-04-24 19:00:00+00:00,21.98 +2020-04-24 20:00:00+00:00,23.0 +2020-04-24 21:00:00+00:00,20.4 +2020-04-24 22:00:00+00:00,12.9 +2020-04-24 23:00:00+00:00,9.07 +2020-04-25 00:00:00+00:00,12.61 +2020-04-25 01:00:00+00:00,14.42 +2020-04-25 02:00:00+00:00,14.48 +2020-04-25 03:00:00+00:00,14.03 +2020-04-25 04:00:00+00:00,14.88 +2020-04-25 05:00:00+00:00,15.09 +2020-04-25 06:00:00+00:00,18.7 +2020-04-25 07:00:00+00:00,16.63 +2020-04-25 08:00:00+00:00,16.03 +2020-04-25 09:00:00+00:00,16.3 +2020-04-25 10:00:00+00:00,15.08 +2020-04-25 11:00:00+00:00,12.25 +2020-04-25 12:00:00+00:00,7.76 +2020-04-25 13:00:00+00:00,7.99 +2020-04-25 14:00:00+00:00,10.07 +2020-04-25 15:00:00+00:00,16.62 +2020-04-25 16:00:00+00:00,23.51 +2020-04-25 17:00:00+00:00,26.7 +2020-04-25 18:00:00+00:00,32.0 +2020-04-25 19:00:00+00:00,29.77 +2020-04-25 20:00:00+00:00,25.69 +2020-04-25 21:00:00+00:00,22.11 +2020-04-25 22:00:00+00:00,22.3 +2020-04-25 23:00:00+00:00,19.79 +2020-04-26 00:00:00+00:00,20.76 +2020-04-26 01:00:00+00:00,21.06 +2020-04-26 02:00:00+00:00,22.23 +2020-04-26 03:00:00+00:00,21.95 +2020-04-26 04:00:00+00:00,18.81 +2020-04-26 05:00:00+00:00,18.09 +2020-04-26 06:00:00+00:00,16.06 +2020-04-26 07:00:00+00:00,15.01 +2020-04-26 08:00:00+00:00,13.0 +2020-04-26 09:00:00+00:00,14.38 +2020-04-26 10:00:00+00:00,15.0 +2020-04-26 11:00:00+00:00,11.9 +2020-04-26 12:00:00+00:00,5.56 +2020-04-26 13:00:00+00:00,6.93 +2020-04-26 14:00:00+00:00,12.42 +2020-04-26 15:00:00+00:00,16.15 +2020-04-26 16:00:00+00:00,24.82 +2020-04-26 17:00:00+00:00,27.97 +2020-04-26 18:00:00+00:00,31.84 +2020-04-26 19:00:00+00:00,27.49 +2020-04-26 20:00:00+00:00,26.06 +2020-04-26 21:00:00+00:00,23.76 +2020-04-26 22:00:00+00:00,20.84 +2020-04-26 23:00:00+00:00,21.11 +2020-04-27 00:00:00+00:00,19.08 +2020-04-27 01:00:00+00:00,19.34 +2020-04-27 02:00:00+00:00,20.23 +2020-04-27 03:00:00+00:00,21.61 +2020-04-27 04:00:00+00:00,25.95 +2020-04-27 05:00:00+00:00,28.0 +2020-04-27 06:00:00+00:00,25.9 +2020-04-27 07:00:00+00:00,21.99 +2020-04-27 08:00:00+00:00,21.17 +2020-04-27 09:00:00+00:00,20.64 +2020-04-27 10:00:00+00:00,21.92 +2020-04-27 11:00:00+00:00,20.39 +2020-04-27 12:00:00+00:00,19.85 +2020-04-27 13:00:00+00:00,18.42 +2020-04-27 14:00:00+00:00,18.4 +2020-04-27 15:00:00+00:00,24.92 +2020-04-27 16:00:00+00:00,32.96 +2020-04-27 17:00:00+00:00,48.31 +2020-04-27 18:00:00+00:00,45.56 +2020-04-27 19:00:00+00:00,38.56 +2020-04-27 20:00:00+00:00,32.1 +2020-04-27 21:00:00+00:00,24.74 +2020-04-27 22:00:00+00:00,22.21 +2020-04-27 23:00:00+00:00,20.27 +2020-04-28 00:00:00+00:00,20.42 +2020-04-28 01:00:00+00:00,20.16 +2020-04-28 02:00:00+00:00,20.61 +2020-04-28 03:00:00+00:00,21.94 +2020-04-28 04:00:00+00:00,25.94 +2020-04-28 05:00:00+00:00,27.94 +2020-04-28 06:00:00+00:00,28.72 +2020-04-28 07:00:00+00:00,27.95 +2020-04-28 08:00:00+00:00,26.16 +2020-04-28 09:00:00+00:00,25.83 +2020-04-28 10:00:00+00:00,24.93 +2020-04-28 11:00:00+00:00,24.06 +2020-04-28 12:00:00+00:00,24.89 +2020-04-28 13:00:00+00:00,24.85 +2020-04-28 14:00:00+00:00,24.81 +2020-04-28 15:00:00+00:00,27.18 +2020-04-28 16:00:00+00:00,28.74 +2020-04-28 17:00:00+00:00,31.28 +2020-04-28 18:00:00+00:00,29.99 +2020-04-28 19:00:00+00:00,27.44 +2020-04-28 20:00:00+00:00,24.09 +2020-04-28 21:00:00+00:00,21.03 +2020-04-28 22:00:00+00:00,19.04 +2020-04-28 23:00:00+00:00,17.58 +2020-04-29 00:00:00+00:00,17.09 +2020-04-29 01:00:00+00:00,17.11 +2020-04-29 02:00:00+00:00,18.04 +2020-04-29 03:00:00+00:00,20.91 +2020-04-29 04:00:00+00:00,24.28 +2020-04-29 05:00:00+00:00,27.09 +2020-04-29 06:00:00+00:00,30.57 +2020-04-29 07:00:00+00:00,30.24 +2020-04-29 08:00:00+00:00,27.98 +2020-04-29 09:00:00+00:00,26.0 +2020-04-29 10:00:00+00:00,23.49 +2020-04-29 11:00:00+00:00,19.08 +2020-04-29 12:00:00+00:00,18.0 +2020-04-29 13:00:00+00:00,17.7 +2020-04-29 14:00:00+00:00,19.01 +2020-04-29 15:00:00+00:00,21.59 +2020-04-29 16:00:00+00:00,26.46 +2020-04-29 17:00:00+00:00,28.7 +2020-04-29 18:00:00+00:00,30.85 +2020-04-29 19:00:00+00:00,27.81 +2020-04-29 20:00:00+00:00,23.69 +2020-04-29 21:00:00+00:00,20.96 +2020-04-29 22:00:00+00:00,17.55 +2020-04-29 23:00:00+00:00,15.57 +2020-04-30 00:00:00+00:00,14.05 +2020-04-30 01:00:00+00:00,13.64 +2020-04-30 02:00:00+00:00,13.84 +2020-04-30 03:00:00+00:00,17.4 +2020-04-30 04:00:00+00:00,21.35 +2020-04-30 05:00:00+00:00,24.97 +2020-04-30 06:00:00+00:00,25.11 +2020-04-30 07:00:00+00:00,23.46 +2020-04-30 08:00:00+00:00,21.89 +2020-04-30 09:00:00+00:00,21.02 +2020-04-30 10:00:00+00:00,14.81 +2020-04-30 11:00:00+00:00,9.25 +2020-04-30 12:00:00+00:00,10.22 +2020-04-30 13:00:00+00:00,13.45 +2020-04-30 14:00:00+00:00,17.75 +2020-04-30 15:00:00+00:00,23.41 +2020-04-30 16:00:00+00:00,27.13 +2020-04-30 17:00:00+00:00,34.74 +2020-04-30 18:00:00+00:00,34.66 +2020-04-30 19:00:00+00:00,29.02 +2020-04-30 20:00:00+00:00,22.04 +2020-04-30 21:00:00+00:00,13.77 +2020-04-30 22:00:00+00:00,5.5 +2020-04-30 23:00:00+00:00,5.35 +2020-05-01 00:00:00+00:00,3.82 +2020-05-01 01:00:00+00:00,2.63 +2020-05-01 02:00:00+00:00,1.56 +2020-05-01 03:00:00+00:00,2.46 +2020-05-01 04:00:00+00:00,2.54 +2020-05-01 05:00:00+00:00,1.5 +2020-05-01 06:00:00+00:00,-1.57 +2020-05-01 07:00:00+00:00,-2.43 +2020-05-01 08:00:00+00:00,-2.89 +2020-05-01 09:00:00+00:00,-2.47 +2020-05-01 10:00:00+00:00,0.35 +2020-05-01 11:00:00+00:00,-2.04 +2020-05-01 12:00:00+00:00,-2.06 +2020-05-01 13:00:00+00:00,-0.04 +2020-05-01 14:00:00+00:00,1.95 +2020-05-01 15:00:00+00:00,7.88 +2020-05-01 16:00:00+00:00,18.99 +2020-05-01 17:00:00+00:00,23.5 +2020-05-01 18:00:00+00:00,28.43 +2020-05-01 19:00:00+00:00,26.88 +2020-05-01 20:00:00+00:00,20.91 +2020-05-01 21:00:00+00:00,16.0 +2020-05-01 22:00:00+00:00,12.2 +2020-05-01 23:00:00+00:00,10.0 +2020-05-02 00:00:00+00:00,10.0 +2020-05-02 01:00:00+00:00,8.0 +2020-05-02 02:00:00+00:00,8.0 +2020-05-02 03:00:00+00:00,8.0 +2020-05-02 04:00:00+00:00,7.2 +2020-05-02 05:00:00+00:00,8.0 +2020-05-02 06:00:00+00:00,10.3 +2020-05-02 07:00:00+00:00,10.55 +2020-05-02 08:00:00+00:00,10.7 +2020-05-02 09:00:00+00:00,11.28 +2020-05-02 10:00:00+00:00,10.01 +2020-05-02 11:00:00+00:00,7.35 +2020-05-02 12:00:00+00:00,5.65 +2020-05-02 13:00:00+00:00,5.25 +2020-05-02 14:00:00+00:00,5.66 +2020-05-02 15:00:00+00:00,8.5 +2020-05-02 16:00:00+00:00,17.93 +2020-05-02 17:00:00+00:00,25.51 +2020-05-02 18:00:00+00:00,28.04 +2020-05-02 19:00:00+00:00,26.37 +2020-05-02 20:00:00+00:00,24.38 +2020-05-02 21:00:00+00:00,20.49 +2020-05-02 22:00:00+00:00,18.0 +2020-05-02 23:00:00+00:00,13.76 +2020-05-03 00:00:00+00:00,11.44 +2020-05-03 01:00:00+00:00,11.15 +2020-05-03 02:00:00+00:00,11.82 +2020-05-03 03:00:00+00:00,13.37 +2020-05-03 04:00:00+00:00,11.56 +2020-05-03 05:00:00+00:00,10.94 +2020-05-03 06:00:00+00:00,10.0 +2020-05-03 07:00:00+00:00,9.4 +2020-05-03 08:00:00+00:00,9.7 +2020-05-03 09:00:00+00:00,10.0 +2020-05-03 10:00:00+00:00,10.0 +2020-05-03 11:00:00+00:00,7.06 +2020-05-03 12:00:00+00:00,5.84 +2020-05-03 13:00:00+00:00,5.57 +2020-05-03 14:00:00+00:00,7.14 +2020-05-03 15:00:00+00:00,10.21 +2020-05-03 16:00:00+00:00,20.05 +2020-05-03 17:00:00+00:00,24.97 +2020-05-03 18:00:00+00:00,28.69 +2020-05-03 19:00:00+00:00,28.96 +2020-05-03 20:00:00+00:00,25.37 +2020-05-03 21:00:00+00:00,22.33 +2020-05-03 22:00:00+00:00,22.2 +2020-05-03 23:00:00+00:00,20.46 +2020-05-04 00:00:00+00:00,21.86 +2020-05-04 01:00:00+00:00,20.7 +2020-05-04 02:00:00+00:00,20.14 +2020-05-04 03:00:00+00:00,22.99 +2020-05-04 04:00:00+00:00,34.05 +2020-05-04 05:00:00+00:00,48.53 +2020-05-04 06:00:00+00:00,55.13 +2020-05-04 07:00:00+00:00,40.04 +2020-05-04 08:00:00+00:00,27.86 +2020-05-04 09:00:00+00:00,25.07 +2020-05-04 10:00:00+00:00,22.96 +2020-05-04 11:00:00+00:00,20.74 +2020-05-04 12:00:00+00:00,19.02 +2020-05-04 13:00:00+00:00,19.58 +2020-05-04 14:00:00+00:00,19.81 +2020-05-04 15:00:00+00:00,22.39 +2020-05-04 16:00:00+00:00,24.24 +2020-05-04 17:00:00+00:00,27.92 +2020-05-04 18:00:00+00:00,28.97 +2020-05-04 19:00:00+00:00,25.99 +2020-05-04 20:00:00+00:00,22.51 +2020-05-04 21:00:00+00:00,19.89 +2020-05-04 22:00:00+00:00,18.14 +2020-05-04 23:00:00+00:00,17.99 +2020-05-05 00:00:00+00:00,17.2 +2020-05-05 01:00:00+00:00,18.05 +2020-05-05 02:00:00+00:00,18.07 +2020-05-05 03:00:00+00:00,20.08 +2020-05-05 04:00:00+00:00,23.2 +2020-05-05 05:00:00+00:00,27.61 +2020-05-05 06:00:00+00:00,26.06 +2020-05-05 07:00:00+00:00,22.35 +2020-05-05 08:00:00+00:00,19.92 +2020-05-05 09:00:00+00:00,21.02 +2020-05-05 10:00:00+00:00,20.27 +2020-05-05 11:00:00+00:00,19.1 +2020-05-05 12:00:00+00:00,17.16 +2020-05-05 13:00:00+00:00,16.12 +2020-05-05 14:00:00+00:00,17.31 +2020-05-05 15:00:00+00:00,20.42 +2020-05-05 16:00:00+00:00,24.1 +2020-05-05 17:00:00+00:00,28.75 +2020-05-05 18:00:00+00:00,32.41 +2020-05-05 19:00:00+00:00,27.34 +2020-05-05 20:00:00+00:00,25.34 +2020-05-05 21:00:00+00:00,23.24 +2020-05-05 22:00:00+00:00,20.99 +2020-05-05 23:00:00+00:00,20.06 +2020-05-06 00:00:00+00:00,20.0 +2020-05-06 01:00:00+00:00,19.04 +2020-05-06 02:00:00+00:00,17.04 +2020-05-06 03:00:00+00:00,20.1 +2020-05-06 04:00:00+00:00,24.95 +2020-05-06 05:00:00+00:00,29.2 +2020-05-06 06:00:00+00:00,26.96 +2020-05-06 07:00:00+00:00,24.22 +2020-05-06 08:00:00+00:00,21.16 +2020-05-06 09:00:00+00:00,20.34 +2020-05-06 10:00:00+00:00,20.28 +2020-05-06 11:00:00+00:00,18.07 +2020-05-06 12:00:00+00:00,16.41 +2020-05-06 13:00:00+00:00,16.27 +2020-05-06 14:00:00+00:00,16.69 +2020-05-06 15:00:00+00:00,18.79 +2020-05-06 16:00:00+00:00,22.94 +2020-05-06 17:00:00+00:00,24.96 +2020-05-06 18:00:00+00:00,25.42 +2020-05-06 19:00:00+00:00,24.97 +2020-05-06 20:00:00+00:00,24.68 +2020-05-06 21:00:00+00:00,22.14 +2020-05-06 22:00:00+00:00,20.08 +2020-05-06 23:00:00+00:00,19.03 +2020-05-07 00:00:00+00:00,18.57 +2020-05-07 01:00:00+00:00,18.5 +2020-05-07 02:00:00+00:00,18.48 +2020-05-07 03:00:00+00:00,19.07 +2020-05-07 04:00:00+00:00,23.01 +2020-05-07 05:00:00+00:00,25.03 +2020-05-07 06:00:00+00:00,24.98 +2020-05-07 07:00:00+00:00,21.54 +2020-05-07 08:00:00+00:00,19.7 +2020-05-07 09:00:00+00:00,19.06 +2020-05-07 10:00:00+00:00,17.06 +2020-05-07 11:00:00+00:00,16.75 +2020-05-07 12:00:00+00:00,17.34 +2020-05-07 13:00:00+00:00,18.32 +2020-05-07 14:00:00+00:00,19.0 +2020-05-07 15:00:00+00:00,20.39 +2020-05-07 16:00:00+00:00,26.91 +2020-05-07 17:00:00+00:00,48.95 +2020-05-07 18:00:00+00:00,50.98 +2020-05-07 19:00:00+00:00,35.45 +2020-05-07 20:00:00+00:00,27.93 +2020-05-07 21:00:00+00:00,25.8 +2020-05-07 22:00:00+00:00,23.18 +2020-05-07 23:00:00+00:00,20.44 +2020-05-08 00:00:00+00:00,20.04 +2020-05-08 01:00:00+00:00,20.09 +2020-05-08 02:00:00+00:00,20.83 +2020-05-08 03:00:00+00:00,22.61 +2020-05-08 04:00:00+00:00,26.95 +2020-05-08 05:00:00+00:00,30.09 +2020-05-08 06:00:00+00:00,26.8 +2020-05-08 07:00:00+00:00,22.99 +2020-05-08 08:00:00+00:00,20.35 +2020-05-08 09:00:00+00:00,19.01 +2020-05-08 10:00:00+00:00,17.89 +2020-05-08 11:00:00+00:00,16.0 +2020-05-08 12:00:00+00:00,15.04 +2020-05-08 13:00:00+00:00,16.26 +2020-05-08 14:00:00+00:00,19.3 +2020-05-08 15:00:00+00:00,22.47 +2020-05-08 16:00:00+00:00,26.96 +2020-05-08 17:00:00+00:00,36.93 +2020-05-08 18:00:00+00:00,41.62 +2020-05-08 19:00:00+00:00,30.71 +2020-05-08 20:00:00+00:00,29.33 +2020-05-08 21:00:00+00:00,24.9 +2020-05-08 22:00:00+00:00,21.51 +2020-05-08 23:00:00+00:00,20.72 +2020-05-09 00:00:00+00:00,20.89 +2020-05-09 01:00:00+00:00,21.01 +2020-05-09 02:00:00+00:00,22.0 +2020-05-09 03:00:00+00:00,21.73 +2020-05-09 04:00:00+00:00,21.11 +2020-05-09 05:00:00+00:00,21.11 +2020-05-09 06:00:00+00:00,21.06 +2020-05-09 07:00:00+00:00,20.24 +2020-05-09 08:00:00+00:00,18.79 +2020-05-09 09:00:00+00:00,19.01 +2020-05-09 10:00:00+00:00,18.38 +2020-05-09 11:00:00+00:00,16.93 +2020-05-09 12:00:00+00:00,16.02 +2020-05-09 13:00:00+00:00,14.75 +2020-05-09 14:00:00+00:00,16.05 +2020-05-09 15:00:00+00:00,21.11 +2020-05-09 16:00:00+00:00,26.97 +2020-05-09 17:00:00+00:00,36.61 +2020-05-09 18:00:00+00:00,33.99 +2020-05-09 19:00:00+00:00,28.28 +2020-05-09 20:00:00+00:00,26.41 +2020-05-09 21:00:00+00:00,22.93 +2020-05-09 22:00:00+00:00,20.7 +2020-05-09 23:00:00+00:00,19.41 +2020-05-10 00:00:00+00:00,19.1 +2020-05-10 01:00:00+00:00,18.29 +2020-05-10 02:00:00+00:00,16.43 +2020-05-10 03:00:00+00:00,16.0 +2020-05-10 04:00:00+00:00,15.0 +2020-05-10 05:00:00+00:00,12.76 +2020-05-10 06:00:00+00:00,14.01 +2020-05-10 07:00:00+00:00,13.77 +2020-05-10 08:00:00+00:00,12.64 +2020-05-10 09:00:00+00:00,12.46 +2020-05-10 10:00:00+00:00,11.72 +2020-05-10 11:00:00+00:00,6.0 +2020-05-10 12:00:00+00:00,2.36 +2020-05-10 13:00:00+00:00,0.93 +2020-05-10 14:00:00+00:00,2.97 +2020-05-10 15:00:00+00:00,6.23 +2020-05-10 16:00:00+00:00,12.1 +2020-05-10 17:00:00+00:00,14.51 +2020-05-10 18:00:00+00:00,14.73 +2020-05-10 19:00:00+00:00,14.99 +2020-05-10 20:00:00+00:00,14.7 +2020-05-10 21:00:00+00:00,10.1 +2020-05-10 22:00:00+00:00,5.79 +2020-05-10 23:00:00+00:00,2.02 +2020-05-11 00:00:00+00:00,0.13 +2020-05-11 01:00:00+00:00,-0.01 +2020-05-11 02:00:00+00:00,0.02 +2020-05-11 03:00:00+00:00,3.74 +2020-05-11 04:00:00+00:00,18.96 +2020-05-11 05:00:00+00:00,22.66 +2020-05-11 06:00:00+00:00,24.95 +2020-05-11 07:00:00+00:00,22.5 +2020-05-11 08:00:00+00:00,20.14 +2020-05-11 09:00:00+00:00,19.07 +2020-05-11 10:00:00+00:00,16.46 +2020-05-11 11:00:00+00:00,14.15 +2020-05-11 12:00:00+00:00,8.71 +2020-05-11 13:00:00+00:00,8.01 +2020-05-11 14:00:00+00:00,8.07 +2020-05-11 15:00:00+00:00,13.89 +2020-05-11 16:00:00+00:00,18.33 +2020-05-11 17:00:00+00:00,22.0 +2020-05-11 18:00:00+00:00,22.24 +2020-05-11 19:00:00+00:00,23.97 +2020-05-11 20:00:00+00:00,21.85 +2020-05-11 21:00:00+00:00,20.01 +2020-05-11 22:00:00+00:00,21.4 +2020-05-11 23:00:00+00:00,18.93 +2020-05-12 00:00:00+00:00,18.06 +2020-05-12 01:00:00+00:00,17.05 +2020-05-12 02:00:00+00:00,16.84 +2020-05-12 03:00:00+00:00,19.3 +2020-05-12 04:00:00+00:00,26.53 +2020-05-12 05:00:00+00:00,33.36 +2020-05-12 06:00:00+00:00,26.68 +2020-05-12 07:00:00+00:00,24.0 +2020-05-12 08:00:00+00:00,22.54 +2020-05-12 09:00:00+00:00,22.0 +2020-05-12 10:00:00+00:00,20.0 +2020-05-12 11:00:00+00:00,18.03 +2020-05-12 12:00:00+00:00,17.03 +2020-05-12 13:00:00+00:00,17.05 +2020-05-12 14:00:00+00:00,17.1 +2020-05-12 15:00:00+00:00,21.53 +2020-05-12 16:00:00+00:00,25.28 +2020-05-12 17:00:00+00:00,27.83 +2020-05-12 18:00:00+00:00,28.76 +2020-05-12 19:00:00+00:00,26.52 +2020-05-12 20:00:00+00:00,25.15 +2020-05-12 21:00:00+00:00,23.6 +2020-05-12 22:00:00+00:00,23.92 +2020-05-12 23:00:00+00:00,22.05 +2020-05-13 00:00:00+00:00,20.46 +2020-05-13 01:00:00+00:00,19.97 +2020-05-13 02:00:00+00:00,20.16 +2020-05-13 03:00:00+00:00,22.58 +2020-05-13 04:00:00+00:00,32.09 +2020-05-13 05:00:00+00:00,43.68 +2020-05-13 06:00:00+00:00,43.09 +2020-05-13 07:00:00+00:00,35.8 +2020-05-13 08:00:00+00:00,35.79 +2020-05-13 09:00:00+00:00,34.0 +2020-05-13 10:00:00+00:00,32.44 +2020-05-13 11:00:00+00:00,30.49 +2020-05-13 12:00:00+00:00,28.8 +2020-05-13 13:00:00+00:00,28.72 +2020-05-13 14:00:00+00:00,28.33 +2020-05-13 15:00:00+00:00,34.88 +2020-05-13 16:00:00+00:00,34.73 +2020-05-13 17:00:00+00:00,34.88 +2020-05-13 18:00:00+00:00,33.5 +2020-05-13 19:00:00+00:00,29.87 +2020-05-13 20:00:00+00:00,23.91 +2020-05-13 21:00:00+00:00,20.06 +2020-05-13 22:00:00+00:00,18.15 +2020-05-13 23:00:00+00:00,17.91 +2020-05-14 00:00:00+00:00,16.75 +2020-05-14 01:00:00+00:00,16.96 +2020-05-14 02:00:00+00:00,18.17 +2020-05-14 03:00:00+00:00,19.03 +2020-05-14 04:00:00+00:00,23.33 +2020-05-14 05:00:00+00:00,29.25 +2020-05-14 06:00:00+00:00,29.48 +2020-05-14 07:00:00+00:00,27.44 +2020-05-14 08:00:00+00:00,24.28 +2020-05-14 09:00:00+00:00,23.0 +2020-05-14 10:00:00+00:00,21.32 +2020-05-14 11:00:00+00:00,19.5 +2020-05-14 12:00:00+00:00,19.62 +2020-05-14 13:00:00+00:00,19.3 +2020-05-14 14:00:00+00:00,19.84 +2020-05-14 15:00:00+00:00,23.42 +2020-05-14 16:00:00+00:00,26.9 +2020-05-14 17:00:00+00:00,28.99 +2020-05-14 18:00:00+00:00,30.08 +2020-05-14 19:00:00+00:00,27.06 +2020-05-14 20:00:00+00:00,25.2 +2020-05-14 21:00:00+00:00,21.89 +2020-05-14 22:00:00+00:00,19.05 +2020-05-14 23:00:00+00:00,17.53 +2020-05-15 00:00:00+00:00,17.03 +2020-05-15 01:00:00+00:00,17.07 +2020-05-15 02:00:00+00:00,17.07 +2020-05-15 03:00:00+00:00,19.71 +2020-05-15 04:00:00+00:00,24.08 +2020-05-15 05:00:00+00:00,27.39 +2020-05-15 06:00:00+00:00,27.97 +2020-05-15 07:00:00+00:00,23.0 +2020-05-15 08:00:00+00:00,22.65 +2020-05-15 09:00:00+00:00,21.6 +2020-05-15 10:00:00+00:00,19.96 +2020-05-15 11:00:00+00:00,18.63 +2020-05-15 12:00:00+00:00,18.45 +2020-05-15 13:00:00+00:00,18.0 +2020-05-15 14:00:00+00:00,18.0 +2020-05-15 15:00:00+00:00,19.99 +2020-05-15 16:00:00+00:00,22.05 +2020-05-15 17:00:00+00:00,24.92 +2020-05-15 18:00:00+00:00,25.9 +2020-05-15 19:00:00+00:00,25.25 +2020-05-15 20:00:00+00:00,21.85 +2020-05-15 21:00:00+00:00,18.86 +2020-05-15 22:00:00+00:00,20.0 +2020-05-15 23:00:00+00:00,18.14 +2020-05-16 00:00:00+00:00,17.08 +2020-05-16 01:00:00+00:00,16.43 +2020-05-16 02:00:00+00:00,14.95 +2020-05-16 03:00:00+00:00,14.54 +2020-05-16 04:00:00+00:00,14.94 +2020-05-16 05:00:00+00:00,16.0 +2020-05-16 06:00:00+00:00,15.71 +2020-05-16 07:00:00+00:00,13.39 +2020-05-16 08:00:00+00:00,10.08 +2020-05-16 09:00:00+00:00,10.85 +2020-05-16 10:00:00+00:00,13.27 +2020-05-16 11:00:00+00:00,11.86 +2020-05-16 12:00:00+00:00,8.87 +2020-05-16 13:00:00+00:00,11.24 +2020-05-16 14:00:00+00:00,11.03 +2020-05-16 15:00:00+00:00,14.5 +2020-05-16 16:00:00+00:00,20.39 +2020-05-16 17:00:00+00:00,26.09 +2020-05-16 18:00:00+00:00,28.58 +2020-05-16 19:00:00+00:00,29.42 +2020-05-16 20:00:00+00:00,26.5 +2020-05-16 21:00:00+00:00,23.08 +2020-05-16 22:00:00+00:00,18.93 +2020-05-16 23:00:00+00:00,17.79 +2020-05-17 00:00:00+00:00,15.15 +2020-05-17 01:00:00+00:00,13.66 +2020-05-17 02:00:00+00:00,11.89 +2020-05-17 03:00:00+00:00,12.62 +2020-05-17 04:00:00+00:00,8.52 +2020-05-17 05:00:00+00:00,11.9 +2020-05-17 06:00:00+00:00,11.61 +2020-05-17 07:00:00+00:00,8.08 +2020-05-17 08:00:00+00:00,1.97 +2020-05-17 09:00:00+00:00,0.08 +2020-05-17 10:00:00+00:00,0.96 +2020-05-17 11:00:00+00:00,-5.19 +2020-05-17 12:00:00+00:00,-16.76 +2020-05-17 13:00:00+00:00,-14.9 +2020-05-17 14:00:00+00:00,-2.06 +2020-05-17 15:00:00+00:00,6.41 +2020-05-17 16:00:00+00:00,16.05 +2020-05-17 17:00:00+00:00,20.51 +2020-05-17 18:00:00+00:00,22.18 +2020-05-17 19:00:00+00:00,23.95 +2020-05-17 20:00:00+00:00,23.22 +2020-05-17 21:00:00+00:00,20.54 +2020-05-17 22:00:00+00:00,18.0 +2020-05-17 23:00:00+00:00,15.54 +2020-05-18 00:00:00+00:00,14.01 +2020-05-18 01:00:00+00:00,12.95 +2020-05-18 02:00:00+00:00,13.97 +2020-05-18 03:00:00+00:00,17.98 +2020-05-18 04:00:00+00:00,24.96 +2020-05-18 05:00:00+00:00,27.94 +2020-05-18 06:00:00+00:00,29.91 +2020-05-18 07:00:00+00:00,22.96 +2020-05-18 08:00:00+00:00,18.91 +2020-05-18 09:00:00+00:00,16.9 +2020-05-18 10:00:00+00:00,15.36 +2020-05-18 11:00:00+00:00,14.04 +2020-05-18 12:00:00+00:00,13.26 +2020-05-18 13:00:00+00:00,12.85 +2020-05-18 14:00:00+00:00,13.44 +2020-05-18 15:00:00+00:00,19.07 +2020-05-18 16:00:00+00:00,22.81 +2020-05-18 17:00:00+00:00,25.95 +2020-05-18 18:00:00+00:00,31.9 +2020-05-18 19:00:00+00:00,26.08 +2020-05-18 20:00:00+00:00,23.99 +2020-05-18 21:00:00+00:00,21.04 +2020-05-18 22:00:00+00:00,17.05 +2020-05-18 23:00:00+00:00,16.13 +2020-05-19 00:00:00+00:00,16.16 +2020-05-19 01:00:00+00:00,15.74 +2020-05-19 02:00:00+00:00,17.01 +2020-05-19 03:00:00+00:00,18.53 +2020-05-19 04:00:00+00:00,22.26 +2020-05-19 05:00:00+00:00,26.04 +2020-05-19 06:00:00+00:00,28.18 +2020-05-19 07:00:00+00:00,22.96 +2020-05-19 08:00:00+00:00,20.07 +2020-05-19 09:00:00+00:00,20.13 +2020-05-19 10:00:00+00:00,19.31 +2020-05-19 11:00:00+00:00,18.19 +2020-05-19 12:00:00+00:00,17.71 +2020-05-19 13:00:00+00:00,17.82 +2020-05-19 14:00:00+00:00,19.03 +2020-05-19 15:00:00+00:00,22.14 +2020-05-19 16:00:00+00:00,28.67 +2020-05-19 17:00:00+00:00,42.65 +2020-05-19 18:00:00+00:00,49.98 +2020-05-19 19:00:00+00:00,40.48 +2020-05-19 20:00:00+00:00,29.07 +2020-05-19 21:00:00+00:00,23.18 +2020-05-19 22:00:00+00:00,21.24 +2020-05-19 23:00:00+00:00,22.35 +2020-05-20 00:00:00+00:00,21.58 +2020-05-20 01:00:00+00:00,21.56 +2020-05-20 02:00:00+00:00,20.67 +2020-05-20 03:00:00+00:00,23.25 +2020-05-20 04:00:00+00:00,44.83 +2020-05-20 05:00:00+00:00,56.69 +2020-05-20 06:00:00+00:00,57.0 +2020-05-20 07:00:00+00:00,34.31 +2020-05-20 08:00:00+00:00,28.72 +2020-05-20 09:00:00+00:00,30.55 +2020-05-20 10:00:00+00:00,28.09 +2020-05-20 11:00:00+00:00,23.43 +2020-05-20 12:00:00+00:00,22.93 +2020-05-20 13:00:00+00:00,23.06 +2020-05-20 14:00:00+00:00,23.24 +2020-05-20 15:00:00+00:00,25.63 +2020-05-20 16:00:00+00:00,33.97 +2020-05-20 17:00:00+00:00,44.58 +2020-05-20 18:00:00+00:00,56.3 +2020-05-20 19:00:00+00:00,46.9 +2020-05-20 20:00:00+00:00,35.42 +2020-05-20 21:00:00+00:00,22.94 +2020-05-20 22:00:00+00:00,21.37 +2020-05-20 23:00:00+00:00,20.01 +2020-05-21 00:00:00+00:00,17.29 +2020-05-21 01:00:00+00:00,16.74 +2020-05-21 02:00:00+00:00,16.84 +2020-05-21 03:00:00+00:00,16.18 +2020-05-21 04:00:00+00:00,17.85 +2020-05-21 05:00:00+00:00,19.77 +2020-05-21 06:00:00+00:00,19.56 +2020-05-21 07:00:00+00:00,16.12 +2020-05-21 08:00:00+00:00,14.82 +2020-05-21 09:00:00+00:00,14.92 +2020-05-21 10:00:00+00:00,14.81 +2020-05-21 11:00:00+00:00,13.29 +2020-05-21 12:00:00+00:00,12.14 +2020-05-21 13:00:00+00:00,13.9 +2020-05-21 14:00:00+00:00,15.47 +2020-05-21 15:00:00+00:00,18.0 +2020-05-21 16:00:00+00:00,22.09 +2020-05-21 17:00:00+00:00,29.66 +2020-05-21 18:00:00+00:00,42.66 +2020-05-21 19:00:00+00:00,29.52 +2020-05-21 20:00:00+00:00,24.12 +2020-05-21 21:00:00+00:00,22.78 +2020-05-21 22:00:00+00:00,20.0 +2020-05-21 23:00:00+00:00,17.03 +2020-05-22 00:00:00+00:00,15.5 +2020-05-22 01:00:00+00:00,14.02 +2020-05-22 02:00:00+00:00,14.15 +2020-05-22 03:00:00+00:00,15.75 +2020-05-22 04:00:00+00:00,20.83 +2020-05-22 05:00:00+00:00,21.94 +2020-05-22 06:00:00+00:00,21.92 +2020-05-22 07:00:00+00:00,21.2 +2020-05-22 08:00:00+00:00,16.49 +2020-05-22 09:00:00+00:00,14.59 +2020-05-22 10:00:00+00:00,14.69 +2020-05-22 11:00:00+00:00,14.57 +2020-05-22 12:00:00+00:00,16.09 +2020-05-22 13:00:00+00:00,17.27 +2020-05-22 14:00:00+00:00,18.35 +2020-05-22 15:00:00+00:00,22.07 +2020-05-22 16:00:00+00:00,22.98 +2020-05-22 17:00:00+00:00,23.71 +2020-05-22 18:00:00+00:00,23.35 +2020-05-22 19:00:00+00:00,22.2 +2020-05-22 20:00:00+00:00,22.81 +2020-05-22 21:00:00+00:00,20.47 +2020-05-22 22:00:00+00:00,14.01 +2020-05-22 23:00:00+00:00,11.48 +2020-05-23 00:00:00+00:00,10.87 +2020-05-23 01:00:00+00:00,8.72 +2020-05-23 02:00:00+00:00,9.37 +2020-05-23 03:00:00+00:00,9.5 +2020-05-23 04:00:00+00:00,12.37 +2020-05-23 05:00:00+00:00,15.07 +2020-05-23 06:00:00+00:00,16.57 +2020-05-23 07:00:00+00:00,16.72 +2020-05-23 08:00:00+00:00,15.53 +2020-05-23 09:00:00+00:00,12.63 +2020-05-23 10:00:00+00:00,12.7 +2020-05-23 11:00:00+00:00,4.64 +2020-05-23 12:00:00+00:00,0.01 +2020-05-23 13:00:00+00:00,-0.94 +2020-05-23 14:00:00+00:00,-0.38 +2020-05-23 15:00:00+00:00,0.75 +2020-05-23 16:00:00+00:00,8.01 +2020-05-23 17:00:00+00:00,12.2 +2020-05-23 18:00:00+00:00,14.04 +2020-05-23 19:00:00+00:00,13.94 +2020-05-23 20:00:00+00:00,14.56 +2020-05-23 21:00:00+00:00,13.99 +2020-05-23 22:00:00+00:00,0.03 +2020-05-23 23:00:00+00:00,-2.44 +2020-05-24 00:00:00+00:00,-8.77 +2020-05-24 01:00:00+00:00,-20.01 +2020-05-24 02:00:00+00:00,-20.37 +2020-05-24 03:00:00+00:00,-24.65 +2020-05-24 04:00:00+00:00,-30.98 +2020-05-24 05:00:00+00:00,-25.0 +2020-05-24 06:00:00+00:00,-26.97 +2020-05-24 07:00:00+00:00,-39.46 +2020-05-24 08:00:00+00:00,-63.04 +2020-05-24 09:00:00+00:00,-63.06 +2020-05-24 10:00:00+00:00,-70.04 +2020-05-24 11:00:00+00:00,-74.97 +2020-05-24 12:00:00+00:00,-74.97 +2020-05-24 13:00:00+00:00,-69.99 +2020-05-24 14:00:00+00:00,-57.74 +2020-05-24 15:00:00+00:00,-16.98 +2020-05-24 16:00:00+00:00,1.54 +2020-05-24 17:00:00+00:00,8.03 +2020-05-24 18:00:00+00:00,14.0 +2020-05-24 19:00:00+00:00,14.39 +2020-05-24 20:00:00+00:00,16.31 +2020-05-24 21:00:00+00:00,8.0 +2020-05-24 22:00:00+00:00,13.01 +2020-05-24 23:00:00+00:00,12.03 +2020-05-25 00:00:00+00:00,8.03 +2020-05-25 01:00:00+00:00,7.59 +2020-05-25 02:00:00+00:00,7.73 +2020-05-25 03:00:00+00:00,9.99 +2020-05-25 04:00:00+00:00,16.02 +2020-05-25 05:00:00+00:00,21.09 +2020-05-25 06:00:00+00:00,22.06 +2020-05-25 07:00:00+00:00,20.92 +2020-05-25 08:00:00+00:00,20.0 +2020-05-25 09:00:00+00:00,20.94 +2020-05-25 10:00:00+00:00,17.5 +2020-05-25 11:00:00+00:00,15.47 +2020-05-25 12:00:00+00:00,14.38 +2020-05-25 13:00:00+00:00,14.0 +2020-05-25 14:00:00+00:00,15.75 +2020-05-25 15:00:00+00:00,18.9 +2020-05-25 16:00:00+00:00,23.53 +2020-05-25 17:00:00+00:00,26.08 +2020-05-25 18:00:00+00:00,38.32 +2020-05-25 19:00:00+00:00,34.35 +2020-05-25 20:00:00+00:00,26.25 +2020-05-25 21:00:00+00:00,23.19 +2020-05-25 22:00:00+00:00,21.1 +2020-05-25 23:00:00+00:00,22.63 +2020-05-26 00:00:00+00:00,21.41 +2020-05-26 01:00:00+00:00,21.49 +2020-05-26 02:00:00+00:00,22.65 +2020-05-26 03:00:00+00:00,22.72 +2020-05-26 04:00:00+00:00,34.94 +2020-05-26 05:00:00+00:00,40.05 +2020-05-26 06:00:00+00:00,34.65 +2020-05-26 07:00:00+00:00,24.21 +2020-05-26 08:00:00+00:00,22.59 +2020-05-26 09:00:00+00:00,21.97 +2020-05-26 10:00:00+00:00,21.0 +2020-05-26 11:00:00+00:00,19.75 +2020-05-26 12:00:00+00:00,19.98 +2020-05-26 13:00:00+00:00,20.91 +2020-05-26 14:00:00+00:00,21.0 +2020-05-26 15:00:00+00:00,24.2 +2020-05-26 16:00:00+00:00,31.29 +2020-05-26 17:00:00+00:00,47.9 +2020-05-26 18:00:00+00:00,50.15 +2020-05-26 19:00:00+00:00,35.95 +2020-05-26 20:00:00+00:00,31.51 +2020-05-26 21:00:00+00:00,24.15 +2020-05-26 22:00:00+00:00,23.15 +2020-05-26 23:00:00+00:00,21.68 +2020-05-27 00:00:00+00:00,21.4 +2020-05-27 01:00:00+00:00,21.92 +2020-05-27 02:00:00+00:00,21.99 +2020-05-27 03:00:00+00:00,22.54 +2020-05-27 04:00:00+00:00,26.05 +2020-05-27 05:00:00+00:00,31.94 +2020-05-27 06:00:00+00:00,26.85 +2020-05-27 07:00:00+00:00,24.1 +2020-05-27 08:00:00+00:00,21.16 +2020-05-27 09:00:00+00:00,23.08 +2020-05-27 10:00:00+00:00,22.04 +2020-05-27 11:00:00+00:00,20.71 +2020-05-27 12:00:00+00:00,20.0 +2020-05-27 13:00:00+00:00,19.99 +2020-05-27 14:00:00+00:00,19.65 +2020-05-27 15:00:00+00:00,21.4 +2020-05-27 16:00:00+00:00,22.76 +2020-05-27 17:00:00+00:00,24.92 +2020-05-27 18:00:00+00:00,25.54 +2020-05-27 19:00:00+00:00,23.99 +2020-05-27 20:00:00+00:00,22.65 +2020-05-27 21:00:00+00:00,20.02 +2020-05-27 22:00:00+00:00,18.09 +2020-05-27 23:00:00+00:00,16.31 +2020-05-28 00:00:00+00:00,16.2 +2020-05-28 01:00:00+00:00,15.92 +2020-05-28 02:00:00+00:00,16.16 +2020-05-28 03:00:00+00:00,18.03 +2020-05-28 04:00:00+00:00,22.94 +2020-05-28 05:00:00+00:00,25.55 +2020-05-28 06:00:00+00:00,25.16 +2020-05-28 07:00:00+00:00,22.99 +2020-05-28 08:00:00+00:00,20.15 +2020-05-28 09:00:00+00:00,18.95 +2020-05-28 10:00:00+00:00,18.0 +2020-05-28 11:00:00+00:00,17.03 +2020-05-28 12:00:00+00:00,16.9 +2020-05-28 13:00:00+00:00,16.19 +2020-05-28 14:00:00+00:00,15.48 +2020-05-28 15:00:00+00:00,19.99 +2020-05-28 16:00:00+00:00,22.99 +2020-05-28 17:00:00+00:00,26.15 +2020-05-28 18:00:00+00:00,26.11 +2020-05-28 19:00:00+00:00,27.16 +2020-05-28 20:00:00+00:00,25.05 +2020-05-28 21:00:00+00:00,22.39 +2020-05-28 22:00:00+00:00,19.42 +2020-05-28 23:00:00+00:00,18.88 +2020-05-29 00:00:00+00:00,18.22 +2020-05-29 01:00:00+00:00,19.07 +2020-05-29 02:00:00+00:00,19.43 +2020-05-29 03:00:00+00:00,22.36 +2020-05-29 04:00:00+00:00,27.92 +2020-05-29 05:00:00+00:00,37.2 +2020-05-29 06:00:00+00:00,30.92 +2020-05-29 07:00:00+00:00,24.4 +2020-05-29 08:00:00+00:00,22.35 +2020-05-29 09:00:00+00:00,20.68 +2020-05-29 10:00:00+00:00,19.91 +2020-05-29 11:00:00+00:00,19.04 +2020-05-29 12:00:00+00:00,16.15 +2020-05-29 13:00:00+00:00,17.3 +2020-05-29 14:00:00+00:00,18.1 +2020-05-29 15:00:00+00:00,22.85 +2020-05-29 16:00:00+00:00,25.68 +2020-05-29 17:00:00+00:00,26.49 +2020-05-29 18:00:00+00:00,25.79 +2020-05-29 19:00:00+00:00,25.97 +2020-05-29 20:00:00+00:00,24.16 +2020-05-29 21:00:00+00:00,22.32 +2020-05-29 22:00:00+00:00,18.27 +2020-05-29 23:00:00+00:00,15.55 +2020-05-30 00:00:00+00:00,15.06 +2020-05-30 01:00:00+00:00,15.0 +2020-05-30 02:00:00+00:00,14.55 +2020-05-30 03:00:00+00:00,15.0 +2020-05-30 04:00:00+00:00,14.4 +2020-05-30 05:00:00+00:00,15.04 +2020-05-30 06:00:00+00:00,13.0 +2020-05-30 07:00:00+00:00,12.0 +2020-05-30 08:00:00+00:00,9.33 +2020-05-30 09:00:00+00:00,7.17 +2020-05-30 10:00:00+00:00,9.44 +2020-05-30 11:00:00+00:00,4.8 +2020-05-30 12:00:00+00:00,2.61 +2020-05-30 13:00:00+00:00,2.17 +2020-05-30 14:00:00+00:00,4.61 +2020-05-30 15:00:00+00:00,9.06 +2020-05-30 16:00:00+00:00,15.33 +2020-05-30 17:00:00+00:00,17.58 +2020-05-30 18:00:00+00:00,17.46 +2020-05-30 19:00:00+00:00,18.28 +2020-05-30 20:00:00+00:00,17.91 +2020-05-30 21:00:00+00:00,17.26 +2020-05-30 22:00:00+00:00,11.88 +2020-05-30 23:00:00+00:00,11.6 +2020-05-31 00:00:00+00:00,11.76 +2020-05-31 01:00:00+00:00,11.09 +2020-05-31 02:00:00+00:00,8.83 +2020-05-31 03:00:00+00:00,7.15 +2020-05-31 04:00:00+00:00,5.56 +2020-05-31 05:00:00+00:00,6.12 +2020-05-31 06:00:00+00:00,2.58 +2020-05-31 07:00:00+00:00,2.7 +2020-05-31 08:00:00+00:00,0.1 +2020-05-31 09:00:00+00:00,1.05 +2020-05-31 10:00:00+00:00,0.85 +2020-05-31 11:00:00+00:00,-7.68 +2020-05-31 12:00:00+00:00,-35.51 +2020-05-31 13:00:00+00:00,-45.05 +2020-05-31 14:00:00+00:00,-20.22 +2020-05-31 15:00:00+00:00,-0.1 +2020-05-31 16:00:00+00:00,4.98 +2020-05-31 17:00:00+00:00,12.54 +2020-05-31 18:00:00+00:00,12.57 +2020-05-31 19:00:00+00:00,13.38 +2020-05-31 20:00:00+00:00,12.59 +2020-05-31 21:00:00+00:00,11.1 +2020-05-31 22:00:00+00:00,8.15 +2020-05-31 23:00:00+00:00,8.93 +2020-06-01 00:00:00+00:00,9.8 +2020-06-01 01:00:00+00:00,6.07 +2020-06-01 02:00:00+00:00,4.07 +2020-06-01 03:00:00+00:00,5.77 +2020-06-01 04:00:00+00:00,7.31 +2020-06-01 05:00:00+00:00,10.05 +2020-06-01 06:00:00+00:00,10.96 +2020-06-01 07:00:00+00:00,9.17 +2020-06-01 08:00:00+00:00,4.01 +2020-06-01 09:00:00+00:00,1.71 +2020-06-01 10:00:00+00:00,3.04 +2020-06-01 11:00:00+00:00,-20.51 +2020-06-01 12:00:00+00:00,-48.17 +2020-06-01 13:00:00+00:00,-15.47 +2020-06-01 14:00:00+00:00,0.47 +2020-06-01 15:00:00+00:00,11.53 +2020-06-01 16:00:00+00:00,16.57 +2020-06-01 17:00:00+00:00,22.0 +2020-06-01 18:00:00+00:00,23.05 +2020-06-01 19:00:00+00:00,22.91 +2020-06-01 20:00:00+00:00,23.37 +2020-06-01 21:00:00+00:00,20.9 +2020-06-01 22:00:00+00:00,18.08 +2020-06-01 23:00:00+00:00,16.33 +2020-06-02 00:00:00+00:00,14.99 +2020-06-02 01:00:00+00:00,14.65 +2020-06-02 02:00:00+00:00,15.92 +2020-06-02 03:00:00+00:00,19.96 +2020-06-02 04:00:00+00:00,29.37 +2020-06-02 05:00:00+00:00,47.66 +2020-06-02 06:00:00+00:00,42.29 +2020-06-02 07:00:00+00:00,24.1 +2020-06-02 08:00:00+00:00,21.86 +2020-06-02 09:00:00+00:00,21.45 +2020-06-02 10:00:00+00:00,20.91 +2020-06-02 11:00:00+00:00,19.54 +2020-06-02 12:00:00+00:00,19.02 +2020-06-02 13:00:00+00:00,18.06 +2020-06-02 14:00:00+00:00,20.09 +2020-06-02 15:00:00+00:00,26.02 +2020-06-02 16:00:00+00:00,41.7 +2020-06-02 17:00:00+00:00,53.65 +2020-06-02 18:00:00+00:00,60.75 +2020-06-02 19:00:00+00:00,49.95 +2020-06-02 20:00:00+00:00,37.92 +2020-06-02 21:00:00+00:00,25.92 +2020-06-02 22:00:00+00:00,25.54 +2020-06-02 23:00:00+00:00,24.0 +2020-06-03 00:00:00+00:00,22.72 +2020-06-03 01:00:00+00:00,20.87 +2020-06-03 02:00:00+00:00,20.24 +2020-06-03 03:00:00+00:00,21.17 +2020-06-03 04:00:00+00:00,31.82 +2020-06-03 05:00:00+00:00,51.41 +2020-06-03 06:00:00+00:00,36.06 +2020-06-03 07:00:00+00:00,31.07 +2020-06-03 08:00:00+00:00,28.72 +2020-06-03 09:00:00+00:00,28.0 +2020-06-03 10:00:00+00:00,27.23 +2020-06-03 11:00:00+00:00,25.27 +2020-06-03 12:00:00+00:00,25.58 +2020-06-03 13:00:00+00:00,26.04 +2020-06-03 14:00:00+00:00,25.78 +2020-06-03 15:00:00+00:00,26.18 +2020-06-03 16:00:00+00:00,29.68 +2020-06-03 17:00:00+00:00,40.17 +2020-06-03 18:00:00+00:00,40.1 +2020-06-03 19:00:00+00:00,32.6 +2020-06-03 20:00:00+00:00,27.91 +2020-06-03 21:00:00+00:00,23.99 +2020-06-03 22:00:00+00:00,22.17 +2020-06-03 23:00:00+00:00,20.98 +2020-06-04 00:00:00+00:00,19.61 +2020-06-04 01:00:00+00:00,20.15 +2020-06-04 02:00:00+00:00,20.36 +2020-06-04 03:00:00+00:00,23.83 +2020-06-04 04:00:00+00:00,28.13 +2020-06-04 05:00:00+00:00,33.64 +2020-06-04 06:00:00+00:00,43.97 +2020-06-04 07:00:00+00:00,33.25 +2020-06-04 08:00:00+00:00,31.82 +2020-06-04 09:00:00+00:00,31.77 +2020-06-04 10:00:00+00:00,30.5 +2020-06-04 11:00:00+00:00,28.93 +2020-06-04 12:00:00+00:00,26.13 +2020-06-04 13:00:00+00:00,26.0 +2020-06-04 14:00:00+00:00,25.7 +2020-06-04 15:00:00+00:00,26.38 +2020-06-04 16:00:00+00:00,28.2 +2020-06-04 17:00:00+00:00,26.07 +2020-06-04 18:00:00+00:00,25.0 +2020-06-04 19:00:00+00:00,23.94 +2020-06-04 20:00:00+00:00,22.69 +2020-06-04 21:00:00+00:00,18.04 +2020-06-04 22:00:00+00:00,17.25 +2020-06-04 23:00:00+00:00,16.8 +2020-06-05 00:00:00+00:00,17.06 +2020-06-05 01:00:00+00:00,15.99 +2020-06-05 02:00:00+00:00,15.61 +2020-06-05 03:00:00+00:00,17.09 +2020-06-05 04:00:00+00:00,24.41 +2020-06-05 05:00:00+00:00,28.96 +2020-06-05 06:00:00+00:00,37.07 +2020-06-05 07:00:00+00:00,33.07 +2020-06-05 08:00:00+00:00,28.4 +2020-06-05 09:00:00+00:00,31.42 +2020-06-05 10:00:00+00:00,26.43 +2020-06-05 11:00:00+00:00,24.0 +2020-06-05 12:00:00+00:00,22.5 +2020-06-05 13:00:00+00:00,23.02 +2020-06-05 14:00:00+00:00,23.6 +2020-06-05 15:00:00+00:00,25.65 +2020-06-05 16:00:00+00:00,27.91 +2020-06-05 17:00:00+00:00,27.99 +2020-06-05 18:00:00+00:00,27.03 +2020-06-05 19:00:00+00:00,25.89 +2020-06-05 20:00:00+00:00,25.89 +2020-06-05 21:00:00+00:00,20.16 +2020-06-05 22:00:00+00:00,7.69 +2020-06-05 23:00:00+00:00,1.46 +2020-06-06 00:00:00+00:00,-0.09 +2020-06-06 01:00:00+00:00,0.03 +2020-06-06 02:00:00+00:00,1.44 +2020-06-06 03:00:00+00:00,1.35 +2020-06-06 04:00:00+00:00,0.08 +2020-06-06 05:00:00+00:00,2.81 +2020-06-06 06:00:00+00:00,5.79 +2020-06-06 07:00:00+00:00,5.65 +2020-06-06 08:00:00+00:00,4.22 +2020-06-06 09:00:00+00:00,2.42 +2020-06-06 10:00:00+00:00,0.06 +2020-06-06 11:00:00+00:00,-4.9 +2020-06-06 12:00:00+00:00,-3.46 +2020-06-06 13:00:00+00:00,-1.71 +2020-06-06 14:00:00+00:00,0.05 +2020-06-06 15:00:00+00:00,9.78 +2020-06-06 16:00:00+00:00,16.42 +2020-06-06 17:00:00+00:00,21.23 +2020-06-06 18:00:00+00:00,23.27 +2020-06-06 19:00:00+00:00,22.98 +2020-06-06 20:00:00+00:00,23.91 +2020-06-06 21:00:00+00:00,19.96 +2020-06-06 22:00:00+00:00,21.04 +2020-06-06 23:00:00+00:00,17.21 +2020-06-07 00:00:00+00:00,14.93 +2020-06-07 01:00:00+00:00,12.63 +2020-06-07 02:00:00+00:00,10.92 +2020-06-07 03:00:00+00:00,10.96 +2020-06-07 04:00:00+00:00,10.88 +2020-06-07 05:00:00+00:00,14.04 +2020-06-07 06:00:00+00:00,15.36 +2020-06-07 07:00:00+00:00,16.0 +2020-06-07 08:00:00+00:00,15.7 +2020-06-07 09:00:00+00:00,16.06 +2020-06-07 10:00:00+00:00,16.94 +2020-06-07 11:00:00+00:00,13.37 +2020-06-07 12:00:00+00:00,10.76 +2020-06-07 13:00:00+00:00,10.64 +2020-06-07 14:00:00+00:00,13.64 +2020-06-07 15:00:00+00:00,17.0 +2020-06-07 16:00:00+00:00,21.06 +2020-06-07 17:00:00+00:00,25.36 +2020-06-07 18:00:00+00:00,28.56 +2020-06-07 19:00:00+00:00,29.14 +2020-06-07 20:00:00+00:00,32.33 +2020-06-07 21:00:00+00:00,28.91 +2020-06-07 22:00:00+00:00,26.0 +2020-06-07 23:00:00+00:00,22.7 +2020-06-08 00:00:00+00:00,22.01 +2020-06-08 01:00:00+00:00,21.36 +2020-06-08 02:00:00+00:00,21.25 +2020-06-08 03:00:00+00:00,23.03 +2020-06-08 04:00:00+00:00,33.45 +2020-06-08 05:00:00+00:00,41.92 +2020-06-08 06:00:00+00:00,40.25 +2020-06-08 07:00:00+00:00,34.76 +2020-06-08 08:00:00+00:00,33.95 +2020-06-08 09:00:00+00:00,35.0 +2020-06-08 10:00:00+00:00,33.0 +2020-06-08 11:00:00+00:00,31.93 +2020-06-08 12:00:00+00:00,30.2 +2020-06-08 13:00:00+00:00,29.28 +2020-06-08 14:00:00+00:00,29.08 +2020-06-08 15:00:00+00:00,33.31 +2020-06-08 16:00:00+00:00,38.85 +2020-06-08 17:00:00+00:00,38.65 +2020-06-08 18:00:00+00:00,35.23 +2020-06-08 19:00:00+00:00,34.6 +2020-06-08 20:00:00+00:00,33.05 +2020-06-08 21:00:00+00:00,30.0 +2020-06-08 22:00:00+00:00,28.39 +2020-06-08 23:00:00+00:00,25.79 +2020-06-09 00:00:00+00:00,24.08 +2020-06-09 01:00:00+00:00,23.67 +2020-06-09 02:00:00+00:00,23.48 +2020-06-09 03:00:00+00:00,25.27 +2020-06-09 04:00:00+00:00,33.43 +2020-06-09 05:00:00+00:00,43.85 +2020-06-09 06:00:00+00:00,52.31 +2020-06-09 07:00:00+00:00,42.46 +2020-06-09 08:00:00+00:00,46.62 +2020-06-09 09:00:00+00:00,46.68 +2020-06-09 10:00:00+00:00,44.12 +2020-06-09 11:00:00+00:00,39.11 +2020-06-09 12:00:00+00:00,36.39 +2020-06-09 13:00:00+00:00,33.49 +2020-06-09 14:00:00+00:00,33.76 +2020-06-09 15:00:00+00:00,41.61 +2020-06-09 16:00:00+00:00,45.21 +2020-06-09 17:00:00+00:00,48.07 +2020-06-09 18:00:00+00:00,43.09 +2020-06-09 19:00:00+00:00,37.47 +2020-06-09 20:00:00+00:00,32.83 +2020-06-09 21:00:00+00:00,29.06 +2020-06-09 22:00:00+00:00,26.09 +2020-06-09 23:00:00+00:00,24.2 +2020-06-10 00:00:00+00:00,22.61 +2020-06-10 01:00:00+00:00,21.49 +2020-06-10 02:00:00+00:00,22.58 +2020-06-10 03:00:00+00:00,24.15 +2020-06-10 04:00:00+00:00,29.41 +2020-06-10 05:00:00+00:00,35.5 +2020-06-10 06:00:00+00:00,42.15 +2020-06-10 07:00:00+00:00,41.06 +2020-06-10 08:00:00+00:00,40.4 +2020-06-10 09:00:00+00:00,35.81 +2020-06-10 10:00:00+00:00,34.9 +2020-06-10 11:00:00+00:00,34.14 +2020-06-10 12:00:00+00:00,32.4 +2020-06-10 13:00:00+00:00,31.3 +2020-06-10 14:00:00+00:00,30.24 +2020-06-10 15:00:00+00:00,31.87 +2020-06-10 16:00:00+00:00,33.77 +2020-06-10 17:00:00+00:00,34.91 +2020-06-10 18:00:00+00:00,33.05 +2020-06-10 19:00:00+00:00,31.5 +2020-06-10 20:00:00+00:00,31.45 +2020-06-10 21:00:00+00:00,27.77 +2020-06-10 22:00:00+00:00,26.06 +2020-06-10 23:00:00+00:00,22.75 +2020-06-11 00:00:00+00:00,21.07 +2020-06-11 01:00:00+00:00,19.43 +2020-06-11 02:00:00+00:00,18.96 +2020-06-11 03:00:00+00:00,18.96 +2020-06-11 04:00:00+00:00,21.56 +2020-06-11 05:00:00+00:00,26.06 +2020-06-11 06:00:00+00:00,30.0 +2020-06-11 07:00:00+00:00,28.96 +2020-06-11 08:00:00+00:00,26.06 +2020-06-11 09:00:00+00:00,25.51 +2020-06-11 10:00:00+00:00,23.89 +2020-06-11 11:00:00+00:00,21.99 +2020-06-11 12:00:00+00:00,20.5 +2020-06-11 13:00:00+00:00,19.1 +2020-06-11 14:00:00+00:00,19.28 +2020-06-11 15:00:00+00:00,23.07 +2020-06-11 16:00:00+00:00,26.08 +2020-06-11 17:00:00+00:00,30.65 +2020-06-11 18:00:00+00:00,30.17 +2020-06-11 19:00:00+00:00,27.09 +2020-06-11 20:00:00+00:00,26.51 +2020-06-11 21:00:00+00:00,23.81 +2020-06-11 22:00:00+00:00,20.72 +2020-06-11 23:00:00+00:00,17.96 +2020-06-12 00:00:00+00:00,17.09 +2020-06-12 01:00:00+00:00,16.01 +2020-06-12 02:00:00+00:00,16.0 +2020-06-12 03:00:00+00:00,18.08 +2020-06-12 04:00:00+00:00,22.92 +2020-06-12 05:00:00+00:00,25.1 +2020-06-12 06:00:00+00:00,26.01 +2020-06-12 07:00:00+00:00,23.23 +2020-06-12 08:00:00+00:00,21.07 +2020-06-12 09:00:00+00:00,20.5 +2020-06-12 10:00:00+00:00,19.45 +2020-06-12 11:00:00+00:00,15.2 +2020-06-12 12:00:00+00:00,16.19 +2020-06-12 13:00:00+00:00,16.14 +2020-06-12 14:00:00+00:00,17.4 +2020-06-12 15:00:00+00:00,20.06 +2020-06-12 16:00:00+00:00,23.53 +2020-06-12 17:00:00+00:00,27.51 +2020-06-12 18:00:00+00:00,26.22 +2020-06-12 19:00:00+00:00,24.7 +2020-06-12 20:00:00+00:00,25.46 +2020-06-12 21:00:00+00:00,20.78 +2020-06-12 22:00:00+00:00,20.98 +2020-06-12 23:00:00+00:00,19.44 +2020-06-13 00:00:00+00:00,18.7 +2020-06-13 01:00:00+00:00,18.8 +2020-06-13 02:00:00+00:00,19.0 +2020-06-13 03:00:00+00:00,18.9 +2020-06-13 04:00:00+00:00,19.33 +2020-06-13 05:00:00+00:00,19.86 +2020-06-13 06:00:00+00:00,20.0 +2020-06-13 07:00:00+00:00,19.1 +2020-06-13 08:00:00+00:00,18.5 +2020-06-13 09:00:00+00:00,18.11 +2020-06-13 10:00:00+00:00,16.47 +2020-06-13 11:00:00+00:00,16.25 +2020-06-13 12:00:00+00:00,15.89 +2020-06-13 13:00:00+00:00,15.84 +2020-06-13 14:00:00+00:00,17.5 +2020-06-13 15:00:00+00:00,18.5 +2020-06-13 16:00:00+00:00,21.93 +2020-06-13 17:00:00+00:00,24.87 +2020-06-13 18:00:00+00:00,25.96 +2020-06-13 19:00:00+00:00,24.42 +2020-06-13 20:00:00+00:00,24.85 +2020-06-13 21:00:00+00:00,23.06 +2020-06-13 22:00:00+00:00,18.53 +2020-06-13 23:00:00+00:00,16.8 +2020-06-14 00:00:00+00:00,14.91 +2020-06-14 01:00:00+00:00,13.82 +2020-06-14 02:00:00+00:00,14.84 +2020-06-14 03:00:00+00:00,14.04 +2020-06-14 04:00:00+00:00,14.94 +2020-06-14 05:00:00+00:00,15.3 +2020-06-14 06:00:00+00:00,15.0 +2020-06-14 07:00:00+00:00,18.54 +2020-06-14 08:00:00+00:00,19.07 +2020-06-14 09:00:00+00:00,20.74 +2020-06-14 10:00:00+00:00,19.73 +2020-06-14 11:00:00+00:00,17.97 +2020-06-14 12:00:00+00:00,15.14 +2020-06-14 13:00:00+00:00,14.49 +2020-06-14 14:00:00+00:00,14.8 +2020-06-14 15:00:00+00:00,17.7 +2020-06-14 16:00:00+00:00,21.08 +2020-06-14 17:00:00+00:00,23.8 +2020-06-14 18:00:00+00:00,25.97 +2020-06-14 19:00:00+00:00,25.87 +2020-06-14 20:00:00+00:00,27.91 +2020-06-14 21:00:00+00:00,23.17 +2020-06-14 22:00:00+00:00,23.07 +2020-06-14 23:00:00+00:00,20.65 +2020-06-15 00:00:00+00:00,19.06 +2020-06-15 01:00:00+00:00,18.02 +2020-06-15 02:00:00+00:00,17.4 +2020-06-15 03:00:00+00:00,21.36 +2020-06-15 04:00:00+00:00,29.93 +2020-06-15 05:00:00+00:00,35.28 +2020-06-15 06:00:00+00:00,36.05 +2020-06-15 07:00:00+00:00,35.89 +2020-06-15 08:00:00+00:00,34.09 +2020-06-15 09:00:00+00:00,34.0 +2020-06-15 10:00:00+00:00,34.1 +2020-06-15 11:00:00+00:00,33.15 +2020-06-15 12:00:00+00:00,31.9 +2020-06-15 13:00:00+00:00,31.0 +2020-06-15 14:00:00+00:00,30.52 +2020-06-15 15:00:00+00:00,33.0 +2020-06-15 16:00:00+00:00,38.0 +2020-06-15 17:00:00+00:00,43.52 +2020-06-15 18:00:00+00:00,41.07 +2020-06-15 19:00:00+00:00,35.91 +2020-06-15 20:00:00+00:00,36.39 +2020-06-15 21:00:00+00:00,31.92 +2020-06-15 22:00:00+00:00,29.86 +2020-06-15 23:00:00+00:00,26.59 +2020-06-16 00:00:00+00:00,25.17 +2020-06-16 01:00:00+00:00,24.79 +2020-06-16 02:00:00+00:00,24.34 +2020-06-16 03:00:00+00:00,25.1 +2020-06-16 04:00:00+00:00,31.13 +2020-06-16 05:00:00+00:00,38.39 +2020-06-16 06:00:00+00:00,45.9 +2020-06-16 07:00:00+00:00,41.39 +2020-06-16 08:00:00+00:00,39.32 +2020-06-16 09:00:00+00:00,40.31 +2020-06-16 10:00:00+00:00,38.75 +2020-06-16 11:00:00+00:00,34.97 +2020-06-16 12:00:00+00:00,32.21 +2020-06-16 13:00:00+00:00,30.86 +2020-06-16 14:00:00+00:00,30.66 +2020-06-16 15:00:00+00:00,33.73 +2020-06-16 16:00:00+00:00,38.32 +2020-06-16 17:00:00+00:00,42.91 +2020-06-16 18:00:00+00:00,40.66 +2020-06-16 19:00:00+00:00,37.66 +2020-06-16 20:00:00+00:00,34.92 +2020-06-16 21:00:00+00:00,30.94 +2020-06-16 22:00:00+00:00,32.05 +2020-06-16 23:00:00+00:00,30.38 +2020-06-17 00:00:00+00:00,28.1 +2020-06-17 01:00:00+00:00,26.92 +2020-06-17 02:00:00+00:00,26.53 +2020-06-17 03:00:00+00:00,28.76 +2020-06-17 04:00:00+00:00,35.96 +2020-06-17 05:00:00+00:00,51.93 +2020-06-17 06:00:00+00:00,56.52 +2020-06-17 07:00:00+00:00,42.14 +2020-06-17 08:00:00+00:00,38.99 +2020-06-17 09:00:00+00:00,38.39 +2020-06-17 10:00:00+00:00,36.03 +2020-06-17 11:00:00+00:00,36.01 +2020-06-17 12:00:00+00:00,35.05 +2020-06-17 13:00:00+00:00,33.59 +2020-06-17 14:00:00+00:00,33.0 +2020-06-17 15:00:00+00:00,40.06 +2020-06-17 16:00:00+00:00,45.58 +2020-06-17 17:00:00+00:00,47.33 +2020-06-17 18:00:00+00:00,42.32 +2020-06-17 19:00:00+00:00,39.71 +2020-06-17 20:00:00+00:00,36.59 +2020-06-17 21:00:00+00:00,31.7 +2020-06-17 22:00:00+00:00,27.74 +2020-06-17 23:00:00+00:00,25.26 +2020-06-18 00:00:00+00:00,23.51 +2020-06-18 01:00:00+00:00,22.82 +2020-06-18 02:00:00+00:00,22.92 +2020-06-18 03:00:00+00:00,24.31 +2020-06-18 04:00:00+00:00,28.86 +2020-06-18 05:00:00+00:00,34.59 +2020-06-18 06:00:00+00:00,36.59 +2020-06-18 07:00:00+00:00,36.93 +2020-06-18 08:00:00+00:00,36.73 +2020-06-18 09:00:00+00:00,36.87 +2020-06-18 10:00:00+00:00,36.21 +2020-06-18 11:00:00+00:00,34.0 +2020-06-18 12:00:00+00:00,32.17 +2020-06-18 13:00:00+00:00,31.81 +2020-06-18 14:00:00+00:00,30.72 +2020-06-18 15:00:00+00:00,33.1 +2020-06-18 16:00:00+00:00,35.0 +2020-06-18 17:00:00+00:00,36.99 +2020-06-18 18:00:00+00:00,36.3 +2020-06-18 19:00:00+00:00,35.57 +2020-06-18 20:00:00+00:00,34.3 +2020-06-18 21:00:00+00:00,29.98 +2020-06-18 22:00:00+00:00,28.1 +2020-06-18 23:00:00+00:00,24.45 +2020-06-19 00:00:00+00:00,23.25 +2020-06-19 01:00:00+00:00,22.28 +2020-06-19 02:00:00+00:00,22.2 +2020-06-19 03:00:00+00:00,23.07 +2020-06-19 04:00:00+00:00,30.06 +2020-06-19 05:00:00+00:00,35.33 +2020-06-19 06:00:00+00:00,37.8 +2020-06-19 07:00:00+00:00,36.08 +2020-06-19 08:00:00+00:00,34.56 +2020-06-19 09:00:00+00:00,33.33 +2020-06-19 10:00:00+00:00,31.22 +2020-06-19 11:00:00+00:00,28.4 +2020-06-19 12:00:00+00:00,26.89 +2020-06-19 13:00:00+00:00,25.29 +2020-06-19 14:00:00+00:00,24.07 +2020-06-19 15:00:00+00:00,28.64 +2020-06-19 16:00:00+00:00,32.61 +2020-06-19 17:00:00+00:00,34.0 +2020-06-19 18:00:00+00:00,34.22 +2020-06-19 19:00:00+00:00,34.13 +2020-06-19 20:00:00+00:00,35.0 +2020-06-19 21:00:00+00:00,31.5 +2020-06-19 22:00:00+00:00,26.7 +2020-06-19 23:00:00+00:00,24.73 +2020-06-20 00:00:00+00:00,22.8 +2020-06-20 01:00:00+00:00,21.63 +2020-06-20 02:00:00+00:00,20.48 +2020-06-20 03:00:00+00:00,20.04 +2020-06-20 04:00:00+00:00,21.85 +2020-06-20 05:00:00+00:00,22.94 +2020-06-20 06:00:00+00:00,24.17 +2020-06-20 07:00:00+00:00,23.67 +2020-06-20 08:00:00+00:00,22.06 +2020-06-20 09:00:00+00:00,21.5 +2020-06-20 10:00:00+00:00,20.5 +2020-06-20 11:00:00+00:00,17.9 +2020-06-20 12:00:00+00:00,15.27 +2020-06-20 13:00:00+00:00,15.68 +2020-06-20 14:00:00+00:00,18.98 +2020-06-20 15:00:00+00:00,23.67 +2020-06-20 16:00:00+00:00,28.3 +2020-06-20 17:00:00+00:00,30.87 +2020-06-20 18:00:00+00:00,33.0 +2020-06-20 19:00:00+00:00,34.77 +2020-06-20 20:00:00+00:00,34.94 +2020-06-20 21:00:00+00:00,31.58 +2020-06-20 22:00:00+00:00,30.11 +2020-06-20 23:00:00+00:00,25.07 +2020-06-21 00:00:00+00:00,22.15 +2020-06-21 01:00:00+00:00,20.26 +2020-06-21 02:00:00+00:00,16.2 +2020-06-21 03:00:00+00:00,12.53 +2020-06-21 04:00:00+00:00,10.98 +2020-06-21 05:00:00+00:00,11.25 +2020-06-21 06:00:00+00:00,14.0 +2020-06-21 07:00:00+00:00,13.68 +2020-06-21 08:00:00+00:00,15.6 +2020-06-21 09:00:00+00:00,17.0 +2020-06-21 10:00:00+00:00,18.62 +2020-06-21 11:00:00+00:00,13.08 +2020-06-21 12:00:00+00:00,8.26 +2020-06-21 13:00:00+00:00,8.4 +2020-06-21 14:00:00+00:00,11.65 +2020-06-21 15:00:00+00:00,20.84 +2020-06-21 16:00:00+00:00,24.83 +2020-06-21 17:00:00+00:00,29.61 +2020-06-21 18:00:00+00:00,30.2 +2020-06-21 19:00:00+00:00,30.84 +2020-06-21 20:00:00+00:00,32.93 +2020-06-21 21:00:00+00:00,29.9 +2020-06-21 22:00:00+00:00,23.57 +2020-06-21 23:00:00+00:00,21.88 +2020-06-22 00:00:00+00:00,20.61 +2020-06-22 01:00:00+00:00,21.04 +2020-06-22 02:00:00+00:00,21.5 +2020-06-22 03:00:00+00:00,23.04 +2020-06-22 04:00:00+00:00,30.69 +2020-06-22 05:00:00+00:00,35.41 +2020-06-22 06:00:00+00:00,38.58 +2020-06-22 07:00:00+00:00,34.5 +2020-06-22 08:00:00+00:00,33.08 +2020-06-22 09:00:00+00:00,31.66 +2020-06-22 10:00:00+00:00,30.04 +2020-06-22 11:00:00+00:00,24.67 +2020-06-22 12:00:00+00:00,24.49 +2020-06-22 13:00:00+00:00,23.62 +2020-06-22 14:00:00+00:00,24.59 +2020-06-22 15:00:00+00:00,31.41 +2020-06-22 16:00:00+00:00,34.39 +2020-06-22 17:00:00+00:00,36.74 +2020-06-22 18:00:00+00:00,37.63 +2020-06-22 19:00:00+00:00,37.39 +2020-06-22 20:00:00+00:00,36.38 +2020-06-22 21:00:00+00:00,33.01 +2020-06-22 22:00:00+00:00,30.0 +2020-06-22 23:00:00+00:00,27.12 +2020-06-23 00:00:00+00:00,25.88 +2020-06-23 01:00:00+00:00,24.4 +2020-06-23 02:00:00+00:00,24.43 +2020-06-23 03:00:00+00:00,27.04 +2020-06-23 04:00:00+00:00,35.03 +2020-06-23 05:00:00+00:00,37.44 +2020-06-23 06:00:00+00:00,37.16 +2020-06-23 07:00:00+00:00,34.9 +2020-06-23 08:00:00+00:00,31.92 +2020-06-23 09:00:00+00:00,30.24 +2020-06-23 10:00:00+00:00,28.79 +2020-06-23 11:00:00+00:00,28.23 +2020-06-23 12:00:00+00:00,27.82 +2020-06-23 13:00:00+00:00,28.65 +2020-06-23 14:00:00+00:00,30.0 +2020-06-23 15:00:00+00:00,33.0 +2020-06-23 16:00:00+00:00,36.59 +2020-06-23 17:00:00+00:00,44.51 +2020-06-23 18:00:00+00:00,48.65 +2020-06-23 19:00:00+00:00,41.29 +2020-06-23 20:00:00+00:00,38.41 +2020-06-23 21:00:00+00:00,34.3 +2020-06-23 22:00:00+00:00,33.02 +2020-06-23 23:00:00+00:00,31.37 +2020-06-24 00:00:00+00:00,27.83 +2020-06-24 01:00:00+00:00,26.76 +2020-06-24 02:00:00+00:00,26.86 +2020-06-24 03:00:00+00:00,28.53 +2020-06-24 04:00:00+00:00,37.57 +2020-06-24 05:00:00+00:00,41.71 +2020-06-24 06:00:00+00:00,39.03 +2020-06-24 07:00:00+00:00,37.66 +2020-06-24 08:00:00+00:00,36.12 +2020-06-24 09:00:00+00:00,35.21 +2020-06-24 10:00:00+00:00,34.84 +2020-06-24 11:00:00+00:00,33.69 +2020-06-24 12:00:00+00:00,32.77 +2020-06-24 13:00:00+00:00,32.72 +2020-06-24 14:00:00+00:00,33.98 +2020-06-24 15:00:00+00:00,37.16 +2020-06-24 16:00:00+00:00,40.93 +2020-06-24 17:00:00+00:00,47.11 +2020-06-24 18:00:00+00:00,45.65 +2020-06-24 19:00:00+00:00,40.24 +2020-06-24 20:00:00+00:00,38.95 +2020-06-24 21:00:00+00:00,33.98 +2020-06-24 22:00:00+00:00,30.32 +2020-06-24 23:00:00+00:00,27.54 +2020-06-25 00:00:00+00:00,26.51 +2020-06-25 01:00:00+00:00,25.75 +2020-06-25 02:00:00+00:00,25.43 +2020-06-25 03:00:00+00:00,27.1 +2020-06-25 04:00:00+00:00,34.49 +2020-06-25 05:00:00+00:00,40.73 +2020-06-25 06:00:00+00:00,42.09 +2020-06-25 07:00:00+00:00,40.98 +2020-06-25 08:00:00+00:00,38.83 +2020-06-25 09:00:00+00:00,36.5 +2020-06-25 10:00:00+00:00,33.31 +2020-06-25 11:00:00+00:00,31.38 +2020-06-25 12:00:00+00:00,29.94 +2020-06-25 13:00:00+00:00,29.87 +2020-06-25 14:00:00+00:00,32.4 +2020-06-25 15:00:00+00:00,37.06 +2020-06-25 16:00:00+00:00,39.55 +2020-06-25 17:00:00+00:00,44.13 +2020-06-25 18:00:00+00:00,41.23 +2020-06-25 19:00:00+00:00,40.24 +2020-06-25 20:00:00+00:00,38.0 +2020-06-25 21:00:00+00:00,33.43 +2020-06-25 22:00:00+00:00,32.47 +2020-06-25 23:00:00+00:00,28.12 +2020-06-26 00:00:00+00:00,25.96 +2020-06-26 01:00:00+00:00,25.59 +2020-06-26 02:00:00+00:00,25.63 +2020-06-26 03:00:00+00:00,26.8 +2020-06-26 04:00:00+00:00,34.05 +2020-06-26 05:00:00+00:00,38.94 +2020-06-26 06:00:00+00:00,40.13 +2020-06-26 07:00:00+00:00,39.19 +2020-06-26 08:00:00+00:00,36.93 +2020-06-26 09:00:00+00:00,34.64 +2020-06-26 10:00:00+00:00,33.12 +2020-06-26 11:00:00+00:00,32.79 +2020-06-26 12:00:00+00:00,32.42 +2020-06-26 13:00:00+00:00,32.18 +2020-06-26 14:00:00+00:00,33.28 +2020-06-26 15:00:00+00:00,36.45 +2020-06-26 16:00:00+00:00,40.43 +2020-06-26 17:00:00+00:00,46.8 +2020-06-26 18:00:00+00:00,41.96 +2020-06-26 19:00:00+00:00,39.9 +2020-06-26 20:00:00+00:00,40.1 +2020-06-26 21:00:00+00:00,37.4 +2020-06-26 22:00:00+00:00,35.92 +2020-06-26 23:00:00+00:00,30.68 +2020-06-27 00:00:00+00:00,28.3 +2020-06-27 01:00:00+00:00,27.48 +2020-06-27 02:00:00+00:00,27.14 +2020-06-27 03:00:00+00:00,26.82 +2020-06-27 04:00:00+00:00,25.34 +2020-06-27 05:00:00+00:00,27.72 +2020-06-27 06:00:00+00:00,28.44 +2020-06-27 07:00:00+00:00,28.22 +2020-06-27 08:00:00+00:00,27.01 +2020-06-27 09:00:00+00:00,25.0 +2020-06-27 10:00:00+00:00,23.09 +2020-06-27 11:00:00+00:00,21.56 +2020-06-27 12:00:00+00:00,18.29 +2020-06-27 13:00:00+00:00,17.52 +2020-06-27 14:00:00+00:00,18.52 +2020-06-27 15:00:00+00:00,23.05 +2020-06-27 16:00:00+00:00,27.08 +2020-06-27 17:00:00+00:00,30.52 +2020-06-27 18:00:00+00:00,32.28 +2020-06-27 19:00:00+00:00,32.96 +2020-06-27 20:00:00+00:00,32.91 +2020-06-27 21:00:00+00:00,28.09 +2020-06-27 22:00:00+00:00,23.93 +2020-06-27 23:00:00+00:00,21.03 +2020-06-28 00:00:00+00:00,16.9 +2020-06-28 01:00:00+00:00,14.26 +2020-06-28 02:00:00+00:00,15.48 +2020-06-28 03:00:00+00:00,13.02 +2020-06-28 04:00:00+00:00,12.45 +2020-06-28 05:00:00+00:00,12.6 +2020-06-28 06:00:00+00:00,13.36 +2020-06-28 07:00:00+00:00,12.48 +2020-06-28 08:00:00+00:00,14.17 +2020-06-28 09:00:00+00:00,15.9 +2020-06-28 10:00:00+00:00,13.71 +2020-06-28 11:00:00+00:00,1.42 +2020-06-28 12:00:00+00:00,0.08 +2020-06-28 13:00:00+00:00,0.54 +2020-06-28 14:00:00+00:00,2.22 +2020-06-28 15:00:00+00:00,16.9 +2020-06-28 16:00:00+00:00,25.04 +2020-06-28 17:00:00+00:00,29.83 +2020-06-28 18:00:00+00:00,33.04 +2020-06-28 19:00:00+00:00,35.07 +2020-06-28 20:00:00+00:00,36.0 +2020-06-28 21:00:00+00:00,31.14 +2020-06-28 22:00:00+00:00,24.02 +2020-06-28 23:00:00+00:00,19.77 +2020-06-29 00:00:00+00:00,17.76 +2020-06-29 01:00:00+00:00,17.4 +2020-06-29 02:00:00+00:00,17.6 +2020-06-29 03:00:00+00:00,19.88 +2020-06-29 04:00:00+00:00,26.52 +2020-06-29 05:00:00+00:00,35.54 +2020-06-29 06:00:00+00:00,40.45 +2020-06-29 07:00:00+00:00,38.81 +2020-06-29 08:00:00+00:00,35.4 +2020-06-29 09:00:00+00:00,34.03 +2020-06-29 10:00:00+00:00,29.35 +2020-06-29 11:00:00+00:00,22.63 +2020-06-29 12:00:00+00:00,21.98 +2020-06-29 13:00:00+00:00,21.02 +2020-06-29 14:00:00+00:00,19.85 +2020-06-29 15:00:00+00:00,22.84 +2020-06-29 16:00:00+00:00,32.96 +2020-06-29 17:00:00+00:00,34.95 +2020-06-29 18:00:00+00:00,34.9 +2020-06-29 19:00:00+00:00,34.63 +2020-06-29 20:00:00+00:00,33.97 +2020-06-29 21:00:00+00:00,23.28 +2020-06-29 22:00:00+00:00,22.49 +2020-06-29 23:00:00+00:00,21.02 +2020-06-30 00:00:00+00:00,17.53 +2020-06-30 01:00:00+00:00,15.82 +2020-06-30 02:00:00+00:00,16.44 +2020-06-30 03:00:00+00:00,19.1 +2020-06-30 04:00:00+00:00,25.79 +2020-06-30 05:00:00+00:00,31.95 +2020-06-30 06:00:00+00:00,26.14 +2020-06-30 07:00:00+00:00,17.1 +2020-06-30 08:00:00+00:00,2.58 +2020-06-30 09:00:00+00:00,0.39 +2020-06-30 10:00:00+00:00,0.92 +2020-06-30 11:00:00+00:00,-0.08 +2020-06-30 12:00:00+00:00,0.06 +2020-06-30 13:00:00+00:00,1.32 +2020-06-30 14:00:00+00:00,1.44 +2020-06-30 15:00:00+00:00,21.2 +2020-06-30 16:00:00+00:00,27.73 +2020-06-30 17:00:00+00:00,33.38 +2020-06-30 18:00:00+00:00,36.1 +2020-06-30 19:00:00+00:00,35.54 +2020-06-30 20:00:00+00:00,34.94 +2020-06-30 21:00:00+00:00,31.6 +2020-06-30 22:00:00+00:00,23.92 +2020-06-30 23:00:00+00:00,25.04 +2020-07-01 00:00:00+00:00,25.59 +2020-07-01 01:00:00+00:00,25.03 +2020-07-01 02:00:00+00:00,24.78 +2020-07-01 03:00:00+00:00,25.89 +2020-07-01 04:00:00+00:00,33.75 +2020-07-01 05:00:00+00:00,37.97 +2020-07-01 06:00:00+00:00,39.99 +2020-07-01 07:00:00+00:00,38.29 +2020-07-01 08:00:00+00:00,37.06 +2020-07-01 09:00:00+00:00,36.14 +2020-07-01 10:00:00+00:00,28.4 +2020-07-01 11:00:00+00:00,24.16 +2020-07-01 12:00:00+00:00,25.0 +2020-07-01 13:00:00+00:00,26.41 +2020-07-01 14:00:00+00:00,27.17 +2020-07-01 15:00:00+00:00,32.91 +2020-07-01 16:00:00+00:00,37.37 +2020-07-01 17:00:00+00:00,43.29 +2020-07-01 18:00:00+00:00,40.8 +2020-07-01 19:00:00+00:00,40.12 +2020-07-01 20:00:00+00:00,40.25 +2020-07-01 21:00:00+00:00,37.91 +2020-07-01 22:00:00+00:00,34.53 +2020-07-01 23:00:00+00:00,30.91 +2020-07-02 00:00:00+00:00,28.38 +2020-07-02 01:00:00+00:00,27.2 +2020-07-02 02:00:00+00:00,27.9 +2020-07-02 03:00:00+00:00,30.19 +2020-07-02 04:00:00+00:00,38.96 +2020-07-02 05:00:00+00:00,42.84 +2020-07-02 06:00:00+00:00,47.3 +2020-07-02 07:00:00+00:00,41.69 +2020-07-02 08:00:00+00:00,42.14 +2020-07-02 09:00:00+00:00,41.59 +2020-07-02 10:00:00+00:00,39.47 +2020-07-02 11:00:00+00:00,38.33 +2020-07-02 12:00:00+00:00,35.94 +2020-07-02 13:00:00+00:00,35.06 +2020-07-02 14:00:00+00:00,33.92 +2020-07-02 15:00:00+00:00,38.87 +2020-07-02 16:00:00+00:00,46.0 +2020-07-02 17:00:00+00:00,52.65 +2020-07-02 18:00:00+00:00,47.91 +2020-07-02 19:00:00+00:00,45.33 +2020-07-02 20:00:00+00:00,46.0 +2020-07-02 21:00:00+00:00,40.82 +2020-07-02 22:00:00+00:00,40.46 +2020-07-02 23:00:00+00:00,35.0 +2020-07-03 00:00:00+00:00,32.57 +2020-07-03 01:00:00+00:00,31.27 +2020-07-03 02:00:00+00:00,29.9 +2020-07-03 03:00:00+00:00,30.87 +2020-07-03 04:00:00+00:00,39.99 +2020-07-03 05:00:00+00:00,47.1 +2020-07-03 06:00:00+00:00,49.95 +2020-07-03 07:00:00+00:00,43.44 +2020-07-03 08:00:00+00:00,38.95 +2020-07-03 09:00:00+00:00,37.32 +2020-07-03 10:00:00+00:00,34.05 +2020-07-03 11:00:00+00:00,28.88 +2020-07-03 12:00:00+00:00,25.73 +2020-07-03 13:00:00+00:00,24.95 +2020-07-03 14:00:00+00:00,24.22 +2020-07-03 15:00:00+00:00,31.22 +2020-07-03 16:00:00+00:00,37.24 +2020-07-03 17:00:00+00:00,38.82 +2020-07-03 18:00:00+00:00,36.97 +2020-07-03 19:00:00+00:00,35.25 +2020-07-03 20:00:00+00:00,35.02 +2020-07-03 21:00:00+00:00,29.18 +2020-07-03 22:00:00+00:00,27.19 +2020-07-03 23:00:00+00:00,24.28 +2020-07-04 00:00:00+00:00,21.07 +2020-07-04 01:00:00+00:00,14.86 +2020-07-04 02:00:00+00:00,16.15 +2020-07-04 03:00:00+00:00,15.52 +2020-07-04 04:00:00+00:00,14.46 +2020-07-04 05:00:00+00:00,18.56 +2020-07-04 06:00:00+00:00,19.2 +2020-07-04 07:00:00+00:00,2.41 +2020-07-04 08:00:00+00:00,1.47 +2020-07-04 09:00:00+00:00,0.38 +2020-07-04 10:00:00+00:00,0.02 +2020-07-04 11:00:00+00:00,-4.71 +2020-07-04 12:00:00+00:00,0.91 +2020-07-04 13:00:00+00:00,0.29 +2020-07-04 14:00:00+00:00,13.61 +2020-07-04 15:00:00+00:00,21.95 +2020-07-04 16:00:00+00:00,26.81 +2020-07-04 17:00:00+00:00,28.03 +2020-07-04 18:00:00+00:00,27.58 +2020-07-04 19:00:00+00:00,26.73 +2020-07-04 20:00:00+00:00,28.0 +2020-07-04 21:00:00+00:00,24.91 +2020-07-04 22:00:00+00:00,11.21 +2020-07-04 23:00:00+00:00,2.19 +2020-07-05 00:00:00+00:00,0.08 +2020-07-05 01:00:00+00:00,-0.05 +2020-07-05 02:00:00+00:00,-3.82 +2020-07-05 03:00:00+00:00,-13.5 +2020-07-05 04:00:00+00:00,-14.91 +2020-07-05 05:00:00+00:00,-13.45 +2020-07-05 06:00:00+00:00,-13.87 +2020-07-05 07:00:00+00:00,-14.54 +2020-07-05 08:00:00+00:00,-17.01 +2020-07-05 09:00:00+00:00,-26.93 +2020-07-05 10:00:00+00:00,-63.02 +2020-07-05 11:00:00+00:00,-64.55 +2020-07-05 12:00:00+00:00,-64.99 +2020-07-05 13:00:00+00:00,-64.96 +2020-07-05 14:00:00+00:00,-64.59 +2020-07-05 15:00:00+00:00,-36.97 +2020-07-05 16:00:00+00:00,-4.44 +2020-07-05 17:00:00+00:00,1.49 +2020-07-05 18:00:00+00:00,19.17 +2020-07-05 19:00:00+00:00,25.94 +2020-07-05 20:00:00+00:00,30.24 +2020-07-05 21:00:00+00:00,23.19 +2020-07-05 22:00:00+00:00,19.9 +2020-07-05 23:00:00+00:00,7.71 +2020-07-06 00:00:00+00:00,6.45 +2020-07-06 01:00:00+00:00,3.37 +2020-07-06 02:00:00+00:00,3.23 +2020-07-06 03:00:00+00:00,5.61 +2020-07-06 04:00:00+00:00,20.42 +2020-07-06 05:00:00+00:00,28.27 +2020-07-06 06:00:00+00:00,27.97 +2020-07-06 07:00:00+00:00,21.89 +2020-07-06 08:00:00+00:00,1.49 +2020-07-06 09:00:00+00:00,1.1 +2020-07-06 10:00:00+00:00,1.25 +2020-07-06 11:00:00+00:00,0.05 +2020-07-06 12:00:00+00:00,-3.05 +2020-07-06 13:00:00+00:00,-2.97 +2020-07-06 14:00:00+00:00,-0.02 +2020-07-06 15:00:00+00:00,1.23 +2020-07-06 16:00:00+00:00,27.96 +2020-07-06 17:00:00+00:00,34.22 +2020-07-06 18:00:00+00:00,34.51 +2020-07-06 19:00:00+00:00,35.31 +2020-07-06 20:00:00+00:00,35.94 +2020-07-06 21:00:00+00:00,30.94 +2020-07-06 22:00:00+00:00,29.56 +2020-07-06 23:00:00+00:00,28.54 +2020-07-07 00:00:00+00:00,27.96 +2020-07-07 01:00:00+00:00,27.3 +2020-07-07 02:00:00+00:00,27.15 +2020-07-07 03:00:00+00:00,27.9 +2020-07-07 04:00:00+00:00,33.83 +2020-07-07 05:00:00+00:00,37.65 +2020-07-07 06:00:00+00:00,37.2 +2020-07-07 07:00:00+00:00,34.87 +2020-07-07 08:00:00+00:00,30.82 +2020-07-07 09:00:00+00:00,27.99 +2020-07-07 10:00:00+00:00,27.73 +2020-07-07 11:00:00+00:00,24.98 +2020-07-07 12:00:00+00:00,24.36 +2020-07-07 13:00:00+00:00,24.8 +2020-07-07 14:00:00+00:00,25.18 +2020-07-07 15:00:00+00:00,30.48 +2020-07-07 16:00:00+00:00,38.48 +2020-07-07 17:00:00+00:00,40.78 +2020-07-07 18:00:00+00:00,40.84 +2020-07-07 19:00:00+00:00,40.1 +2020-07-07 20:00:00+00:00,40.15 +2020-07-07 21:00:00+00:00,37.0 +2020-07-07 22:00:00+00:00,35.93 +2020-07-07 23:00:00+00:00,31.05 +2020-07-08 00:00:00+00:00,30.41 +2020-07-08 01:00:00+00:00,29.92 +2020-07-08 02:00:00+00:00,29.55 +2020-07-08 03:00:00+00:00,30.3 +2020-07-08 04:00:00+00:00,36.98 +2020-07-08 05:00:00+00:00,43.0 +2020-07-08 06:00:00+00:00,47.0 +2020-07-08 07:00:00+00:00,46.2 +2020-07-08 08:00:00+00:00,44.44 +2020-07-08 09:00:00+00:00,44.71 +2020-07-08 10:00:00+00:00,42.02 +2020-07-08 11:00:00+00:00,41.09 +2020-07-08 12:00:00+00:00,40.2 +2020-07-08 13:00:00+00:00,39.99 +2020-07-08 14:00:00+00:00,40.02 +2020-07-08 15:00:00+00:00,45.05 +2020-07-08 16:00:00+00:00,52.13 +2020-07-08 17:00:00+00:00,55.0 +2020-07-08 18:00:00+00:00,52.92 +2020-07-08 19:00:00+00:00,45.93 +2020-07-08 20:00:00+00:00,45.49 +2020-07-08 21:00:00+00:00,39.8 +2020-07-08 22:00:00+00:00,39.94 +2020-07-08 23:00:00+00:00,35.41 +2020-07-09 00:00:00+00:00,32.66 +2020-07-09 01:00:00+00:00,30.21 +2020-07-09 02:00:00+00:00,30.99 +2020-07-09 03:00:00+00:00,34.37 +2020-07-09 04:00:00+00:00,42.9 +2020-07-09 05:00:00+00:00,51.96 +2020-07-09 06:00:00+00:00,51.18 +2020-07-09 07:00:00+00:00,47.94 +2020-07-09 08:00:00+00:00,47.08 +2020-07-09 09:00:00+00:00,47.56 +2020-07-09 10:00:00+00:00,43.23 +2020-07-09 11:00:00+00:00,42.53 +2020-07-09 12:00:00+00:00,40.11 +2020-07-09 13:00:00+00:00,39.84 +2020-07-09 14:00:00+00:00,39.9 +2020-07-09 15:00:00+00:00,41.78 +2020-07-09 16:00:00+00:00,46.35 +2020-07-09 17:00:00+00:00,51.7 +2020-07-09 18:00:00+00:00,47.39 +2020-07-09 19:00:00+00:00,46.22 +2020-07-09 20:00:00+00:00,45.0 +2020-07-09 21:00:00+00:00,41.91 +2020-07-09 22:00:00+00:00,39.0 +2020-07-09 23:00:00+00:00,34.62 +2020-07-10 00:00:00+00:00,32.94 +2020-07-10 01:00:00+00:00,31.1 +2020-07-10 02:00:00+00:00,30.52 +2020-07-10 03:00:00+00:00,32.05 +2020-07-10 04:00:00+00:00,38.06 +2020-07-10 05:00:00+00:00,41.01 +2020-07-10 06:00:00+00:00,42.94 +2020-07-10 07:00:00+00:00,42.54 +2020-07-10 08:00:00+00:00,41.76 +2020-07-10 09:00:00+00:00,39.95 +2020-07-10 10:00:00+00:00,38.43 +2020-07-10 11:00:00+00:00,35.41 +2020-07-10 12:00:00+00:00,29.54 +2020-07-10 13:00:00+00:00,29.17 +2020-07-10 14:00:00+00:00,30.5 +2020-07-10 15:00:00+00:00,35.34 +2020-07-10 16:00:00+00:00,38.98 +2020-07-10 17:00:00+00:00,40.79 +2020-07-10 18:00:00+00:00,39.0 +2020-07-10 19:00:00+00:00,39.0 +2020-07-10 20:00:00+00:00,39.4 +2020-07-10 21:00:00+00:00,36.8 +2020-07-10 22:00:00+00:00,34.57 +2020-07-10 23:00:00+00:00,29.9 +2020-07-11 00:00:00+00:00,28.37 +2020-07-11 01:00:00+00:00,27.13 +2020-07-11 02:00:00+00:00,26.1 +2020-07-11 03:00:00+00:00,26.1 +2020-07-11 04:00:00+00:00,26.09 +2020-07-11 05:00:00+00:00,27.58 +2020-07-11 06:00:00+00:00,29.09 +2020-07-11 07:00:00+00:00,28.34 +2020-07-11 08:00:00+00:00,26.53 +2020-07-11 09:00:00+00:00,25.62 +2020-07-11 10:00:00+00:00,23.29 +2020-07-11 11:00:00+00:00,22.86 +2020-07-11 12:00:00+00:00,22.42 +2020-07-11 13:00:00+00:00,22.45 +2020-07-11 14:00:00+00:00,24.36 +2020-07-11 15:00:00+00:00,26.5 +2020-07-11 16:00:00+00:00,30.42 +2020-07-11 17:00:00+00:00,34.0 +2020-07-11 18:00:00+00:00,36.15 +2020-07-11 19:00:00+00:00,35.91 +2020-07-11 20:00:00+00:00,37.73 +2020-07-11 21:00:00+00:00,34.92 +2020-07-11 22:00:00+00:00,32.97 +2020-07-11 23:00:00+00:00,28.82 +2020-07-12 00:00:00+00:00,26.19 +2020-07-12 01:00:00+00:00,25.03 +2020-07-12 02:00:00+00:00,24.7 +2020-07-12 03:00:00+00:00,24.1 +2020-07-12 04:00:00+00:00,21.68 +2020-07-12 05:00:00+00:00,22.69 +2020-07-12 06:00:00+00:00,18.41 +2020-07-12 07:00:00+00:00,18.04 +2020-07-12 08:00:00+00:00,16.97 +2020-07-12 09:00:00+00:00,16.07 +2020-07-12 10:00:00+00:00,18.62 +2020-07-12 11:00:00+00:00,16.04 +2020-07-12 12:00:00+00:00,14.76 +2020-07-12 13:00:00+00:00,15.8 +2020-07-12 14:00:00+00:00,18.11 +2020-07-12 15:00:00+00:00,23.97 +2020-07-12 16:00:00+00:00,29.05 +2020-07-12 17:00:00+00:00,35.12 +2020-07-12 18:00:00+00:00,37.33 +2020-07-12 19:00:00+00:00,37.74 +2020-07-12 20:00:00+00:00,37.08 +2020-07-12 21:00:00+00:00,35.23 +2020-07-12 22:00:00+00:00,29.48 +2020-07-12 23:00:00+00:00,26.03 +2020-07-13 00:00:00+00:00,25.07 +2020-07-13 01:00:00+00:00,25.07 +2020-07-13 02:00:00+00:00,25.1 +2020-07-13 03:00:00+00:00,26.51 +2020-07-13 04:00:00+00:00,34.64 +2020-07-13 05:00:00+00:00,39.52 +2020-07-13 06:00:00+00:00,38.12 +2020-07-13 07:00:00+00:00,37.68 +2020-07-13 08:00:00+00:00,34.05 +2020-07-13 09:00:00+00:00,32.6 +2020-07-13 10:00:00+00:00,31.13 +2020-07-13 11:00:00+00:00,31.18 +2020-07-13 12:00:00+00:00,32.08 +2020-07-13 13:00:00+00:00,31.96 +2020-07-13 14:00:00+00:00,34.0 +2020-07-13 15:00:00+00:00,37.41 +2020-07-13 16:00:00+00:00,40.8 +2020-07-13 17:00:00+00:00,45.56 +2020-07-13 18:00:00+00:00,44.76 +2020-07-13 19:00:00+00:00,41.01 +2020-07-13 20:00:00+00:00,40.05 +2020-07-13 21:00:00+00:00,36.4 +2020-07-13 22:00:00+00:00,34.47 +2020-07-13 23:00:00+00:00,29.79 +2020-07-14 00:00:00+00:00,28.16 +2020-07-14 01:00:00+00:00,27.74 +2020-07-14 02:00:00+00:00,27.45 +2020-07-14 03:00:00+00:00,28.52 +2020-07-14 04:00:00+00:00,35.85 +2020-07-14 05:00:00+00:00,42.91 +2020-07-14 06:00:00+00:00,38.93 +2020-07-14 07:00:00+00:00,36.63 +2020-07-14 08:00:00+00:00,36.15 +2020-07-14 09:00:00+00:00,36.52 +2020-07-14 10:00:00+00:00,36.98 +2020-07-14 11:00:00+00:00,36.97 +2020-07-14 12:00:00+00:00,36.57 +2020-07-14 13:00:00+00:00,36.57 +2020-07-14 14:00:00+00:00,36.2 +2020-07-14 15:00:00+00:00,38.69 +2020-07-14 16:00:00+00:00,44.74 +2020-07-14 17:00:00+00:00,45.97 +2020-07-14 18:00:00+00:00,46.08 +2020-07-14 19:00:00+00:00,41.98 +2020-07-14 20:00:00+00:00,41.43 +2020-07-14 21:00:00+00:00,37.02 +2020-07-14 22:00:00+00:00,35.55 +2020-07-14 23:00:00+00:00,30.22 +2020-07-15 00:00:00+00:00,30.0 +2020-07-15 01:00:00+00:00,28.73 +2020-07-15 02:00:00+00:00,29.84 +2020-07-15 03:00:00+00:00,34.29 +2020-07-15 04:00:00+00:00,41.05 +2020-07-15 05:00:00+00:00,48.91 +2020-07-15 06:00:00+00:00,57.38 +2020-07-15 07:00:00+00:00,47.9 +2020-07-15 08:00:00+00:00,46.07 +2020-07-15 09:00:00+00:00,50.95 +2020-07-15 10:00:00+00:00,46.57 +2020-07-15 11:00:00+00:00,43.78 +2020-07-15 12:00:00+00:00,41.86 +2020-07-15 13:00:00+00:00,42.1 +2020-07-15 14:00:00+00:00,39.4 +2020-07-15 15:00:00+00:00,41.71 +2020-07-15 16:00:00+00:00,44.52 +2020-07-15 17:00:00+00:00,47.3 +2020-07-15 18:00:00+00:00,44.93 +2020-07-15 19:00:00+00:00,47.91 +2020-07-15 20:00:00+00:00,42.09 +2020-07-15 21:00:00+00:00,37.97 +2020-07-15 22:00:00+00:00,36.19 +2020-07-15 23:00:00+00:00,32.5 +2020-07-16 00:00:00+00:00,29.84 +2020-07-16 01:00:00+00:00,28.97 +2020-07-16 02:00:00+00:00,29.35 +2020-07-16 03:00:00+00:00,32.29 +2020-07-16 04:00:00+00:00,43.11 +2020-07-16 05:00:00+00:00,50.9 +2020-07-16 06:00:00+00:00,56.18 +2020-07-16 07:00:00+00:00,58.04 +2020-07-16 08:00:00+00:00,58.7 +2020-07-16 09:00:00+00:00,53.87 +2020-07-16 10:00:00+00:00,52.62 +2020-07-16 11:00:00+00:00,44.41 +2020-07-16 12:00:00+00:00,43.93 +2020-07-16 13:00:00+00:00,44.03 +2020-07-16 14:00:00+00:00,44.01 +2020-07-16 15:00:00+00:00,48.92 +2020-07-16 16:00:00+00:00,45.38 +2020-07-16 17:00:00+00:00,48.39 +2020-07-16 18:00:00+00:00,54.56 +2020-07-16 19:00:00+00:00,48.43 +2020-07-16 20:00:00+00:00,43.1 +2020-07-16 21:00:00+00:00,38.98 +2020-07-16 22:00:00+00:00,36.48 +2020-07-16 23:00:00+00:00,31.85 +2020-07-17 00:00:00+00:00,29.51 +2020-07-17 01:00:00+00:00,28.68 +2020-07-17 02:00:00+00:00,28.76 +2020-07-17 03:00:00+00:00,32.55 +2020-07-17 04:00:00+00:00,40.32 +2020-07-17 05:00:00+00:00,42.74 +2020-07-17 06:00:00+00:00,44.86 +2020-07-17 07:00:00+00:00,42.72 +2020-07-17 08:00:00+00:00,41.25 +2020-07-17 09:00:00+00:00,41.38 +2020-07-17 10:00:00+00:00,39.01 +2020-07-17 11:00:00+00:00,37.0 +2020-07-17 12:00:00+00:00,35.74 +2020-07-17 13:00:00+00:00,34.55 +2020-07-17 14:00:00+00:00,34.95 +2020-07-17 15:00:00+00:00,38.0 +2020-07-17 16:00:00+00:00,41.01 +2020-07-17 17:00:00+00:00,42.01 +2020-07-17 18:00:00+00:00,41.24 +2020-07-17 19:00:00+00:00,40.31 +2020-07-17 20:00:00+00:00,40.93 +2020-07-17 21:00:00+00:00,36.0 +2020-07-17 22:00:00+00:00,34.57 +2020-07-17 23:00:00+00:00,30.54 +2020-07-18 00:00:00+00:00,27.62 +2020-07-18 01:00:00+00:00,26.63 +2020-07-18 02:00:00+00:00,26.0 +2020-07-18 03:00:00+00:00,25.75 +2020-07-18 04:00:00+00:00,25.8 +2020-07-18 05:00:00+00:00,26.5 +2020-07-18 06:00:00+00:00,26.87 +2020-07-18 07:00:00+00:00,26.73 +2020-07-18 08:00:00+00:00,24.35 +2020-07-18 09:00:00+00:00,23.99 +2020-07-18 10:00:00+00:00,23.09 +2020-07-18 11:00:00+00:00,23.15 +2020-07-18 12:00:00+00:00,23.12 +2020-07-18 13:00:00+00:00,23.47 +2020-07-18 14:00:00+00:00,24.15 +2020-07-18 15:00:00+00:00,28.0 +2020-07-18 16:00:00+00:00,34.65 +2020-07-18 17:00:00+00:00,37.95 +2020-07-18 18:00:00+00:00,38.41 +2020-07-18 19:00:00+00:00,38.0 +2020-07-18 20:00:00+00:00,39.45 +2020-07-18 21:00:00+00:00,37.52 +2020-07-18 22:00:00+00:00,34.0 +2020-07-18 23:00:00+00:00,28.36 +2020-07-19 00:00:00+00:00,27.0 +2020-07-19 01:00:00+00:00,25.4 +2020-07-19 02:00:00+00:00,25.02 +2020-07-19 03:00:00+00:00,25.06 +2020-07-19 04:00:00+00:00,24.94 +2020-07-19 05:00:00+00:00,25.4 +2020-07-19 06:00:00+00:00,24.91 +2020-07-19 07:00:00+00:00,24.93 +2020-07-19 08:00:00+00:00,24.72 +2020-07-19 09:00:00+00:00,23.49 +2020-07-19 10:00:00+00:00,23.19 +2020-07-19 11:00:00+00:00,21.13 +2020-07-19 12:00:00+00:00,19.46 +2020-07-19 13:00:00+00:00,20.89 +2020-07-19 14:00:00+00:00,24.28 +2020-07-19 15:00:00+00:00,26.0 +2020-07-19 16:00:00+00:00,29.8 +2020-07-19 17:00:00+00:00,36.15 +2020-07-19 18:00:00+00:00,37.52 +2020-07-19 19:00:00+00:00,37.95 +2020-07-19 20:00:00+00:00,40.42 +2020-07-19 21:00:00+00:00,36.69 +2020-07-19 22:00:00+00:00,32.02 +2020-07-19 23:00:00+00:00,27.57 +2020-07-20 00:00:00+00:00,26.4 +2020-07-20 01:00:00+00:00,26.01 +2020-07-20 02:00:00+00:00,26.0 +2020-07-20 03:00:00+00:00,27.09 +2020-07-20 04:00:00+00:00,35.24 +2020-07-20 05:00:00+00:00,38.29 +2020-07-20 06:00:00+00:00,39.0 +2020-07-20 07:00:00+00:00,38.22 +2020-07-20 08:00:00+00:00,36.35 +2020-07-20 09:00:00+00:00,30.95 +2020-07-20 10:00:00+00:00,26.34 +2020-07-20 11:00:00+00:00,25.15 +2020-07-20 12:00:00+00:00,25.36 +2020-07-20 13:00:00+00:00,25.11 +2020-07-20 14:00:00+00:00,25.93 +2020-07-20 15:00:00+00:00,35.19 +2020-07-20 16:00:00+00:00,39.12 +2020-07-20 17:00:00+00:00,42.54 +2020-07-20 18:00:00+00:00,43.9 +2020-07-20 19:00:00+00:00,41.0 +2020-07-20 20:00:00+00:00,40.8 +2020-07-20 21:00:00+00:00,37.52 +2020-07-20 22:00:00+00:00,31.38 +2020-07-20 23:00:00+00:00,27.85 +2020-07-21 00:00:00+00:00,27.08 +2020-07-21 01:00:00+00:00,26.95 +2020-07-21 02:00:00+00:00,26.98 +2020-07-21 03:00:00+00:00,28.05 +2020-07-21 04:00:00+00:00,35.84 +2020-07-21 05:00:00+00:00,39.07 +2020-07-21 06:00:00+00:00,38.28 +2020-07-21 07:00:00+00:00,35.25 +2020-07-21 08:00:00+00:00,27.79 +2020-07-21 09:00:00+00:00,26.17 +2020-07-21 10:00:00+00:00,26.31 +2020-07-21 11:00:00+00:00,24.83 +2020-07-21 12:00:00+00:00,22.46 +2020-07-21 13:00:00+00:00,24.01 +2020-07-21 14:00:00+00:00,24.75 +2020-07-21 15:00:00+00:00,28.1 +2020-07-21 16:00:00+00:00,38.07 +2020-07-21 17:00:00+00:00,42.74 +2020-07-21 18:00:00+00:00,41.82 +2020-07-21 19:00:00+00:00,40.48 +2020-07-21 20:00:00+00:00,39.73 +2020-07-21 21:00:00+00:00,37.13 +2020-07-21 22:00:00+00:00,33.31 +2020-07-21 23:00:00+00:00,28.39 +2020-07-22 00:00:00+00:00,27.17 +2020-07-22 01:00:00+00:00,26.07 +2020-07-22 02:00:00+00:00,26.05 +2020-07-22 03:00:00+00:00,28.78 +2020-07-22 04:00:00+00:00,35.18 +2020-07-22 05:00:00+00:00,39.4 +2020-07-22 06:00:00+00:00,38.9 +2020-07-22 07:00:00+00:00,37.67 +2020-07-22 08:00:00+00:00,34.44 +2020-07-22 09:00:00+00:00,32.31 +2020-07-22 10:00:00+00:00,29.5 +2020-07-22 11:00:00+00:00,28.19 +2020-07-22 12:00:00+00:00,26.97 +2020-07-22 13:00:00+00:00,29.0 +2020-07-22 14:00:00+00:00,31.69 +2020-07-22 15:00:00+00:00,38.83 +2020-07-22 16:00:00+00:00,42.19 +2020-07-22 17:00:00+00:00,45.74 +2020-07-22 18:00:00+00:00,44.34 +2020-07-22 19:00:00+00:00,41.96 +2020-07-22 20:00:00+00:00,41.44 +2020-07-22 21:00:00+00:00,36.92 +2020-07-22 22:00:00+00:00,35.96 +2020-07-22 23:00:00+00:00,32.98 +2020-07-23 00:00:00+00:00,31.0 +2020-07-23 01:00:00+00:00,29.86 +2020-07-23 02:00:00+00:00,28.99 +2020-07-23 03:00:00+00:00,31.43 +2020-07-23 04:00:00+00:00,40.33 +2020-07-23 05:00:00+00:00,45.89 +2020-07-23 06:00:00+00:00,39.36 +2020-07-23 07:00:00+00:00,37.67 +2020-07-23 08:00:00+00:00,33.92 +2020-07-23 09:00:00+00:00,30.97 +2020-07-23 10:00:00+00:00,30.76 +2020-07-23 11:00:00+00:00,29.25 +2020-07-23 12:00:00+00:00,27.9 +2020-07-23 13:00:00+00:00,29.69 +2020-07-23 14:00:00+00:00,30.93 +2020-07-23 15:00:00+00:00,38.19 +2020-07-23 16:00:00+00:00,42.86 +2020-07-23 17:00:00+00:00,45.23 +2020-07-23 18:00:00+00:00,43.64 +2020-07-23 19:00:00+00:00,42.09 +2020-07-23 20:00:00+00:00,41.69 +2020-07-23 21:00:00+00:00,36.0 +2020-07-23 22:00:00+00:00,31.72 +2020-07-23 23:00:00+00:00,25.63 +2020-07-24 00:00:00+00:00,23.94 +2020-07-24 01:00:00+00:00,22.94 +2020-07-24 02:00:00+00:00,23.71 +2020-07-24 03:00:00+00:00,25.22 +2020-07-24 04:00:00+00:00,30.09 +2020-07-24 05:00:00+00:00,36.14 +2020-07-24 06:00:00+00:00,39.95 +2020-07-24 07:00:00+00:00,39.83 +2020-07-24 08:00:00+00:00,40.02 +2020-07-24 09:00:00+00:00,36.04 +2020-07-24 10:00:00+00:00,29.57 +2020-07-24 11:00:00+00:00,26.04 +2020-07-24 12:00:00+00:00,24.2 +2020-07-24 13:00:00+00:00,24.01 +2020-07-24 14:00:00+00:00,23.85 +2020-07-24 15:00:00+00:00,26.75 +2020-07-24 16:00:00+00:00,34.97 +2020-07-24 17:00:00+00:00,36.85 +2020-07-24 18:00:00+00:00,37.39 +2020-07-24 19:00:00+00:00,37.04 +2020-07-24 20:00:00+00:00,37.17 +2020-07-24 21:00:00+00:00,34.65 +2020-07-24 22:00:00+00:00,34.63 +2020-07-24 23:00:00+00:00,30.56 +2020-07-25 00:00:00+00:00,27.93 +2020-07-25 01:00:00+00:00,26.03 +2020-07-25 02:00:00+00:00,25.0 +2020-07-25 03:00:00+00:00,25.04 +2020-07-25 04:00:00+00:00,25.18 +2020-07-25 05:00:00+00:00,26.3 +2020-07-25 06:00:00+00:00,26.29 +2020-07-25 07:00:00+00:00,23.95 +2020-07-25 08:00:00+00:00,20.82 +2020-07-25 09:00:00+00:00,20.18 +2020-07-25 10:00:00+00:00,20.26 +2020-07-25 11:00:00+00:00,17.87 +2020-07-25 12:00:00+00:00,18.15 +2020-07-25 13:00:00+00:00,18.45 +2020-07-25 14:00:00+00:00,19.78 +2020-07-25 15:00:00+00:00,25.01 +2020-07-25 16:00:00+00:00,28.9 +2020-07-25 17:00:00+00:00,31.58 +2020-07-25 18:00:00+00:00,32.22 +2020-07-25 19:00:00+00:00,31.41 +2020-07-25 20:00:00+00:00,32.15 +2020-07-25 21:00:00+00:00,24.79 +2020-07-25 22:00:00+00:00,23.78 +2020-07-25 23:00:00+00:00,21.6 +2020-07-26 00:00:00+00:00,20.37 +2020-07-26 01:00:00+00:00,17.0 +2020-07-26 02:00:00+00:00,17.1 +2020-07-26 03:00:00+00:00,16.5 +2020-07-26 04:00:00+00:00,10.7 +2020-07-26 05:00:00+00:00,10.82 +2020-07-26 06:00:00+00:00,15.88 +2020-07-26 07:00:00+00:00,17.83 +2020-07-26 08:00:00+00:00,9.9 +2020-07-26 09:00:00+00:00,2.0 +2020-07-26 10:00:00+00:00,0.84 +2020-07-26 11:00:00+00:00,-5.82 +2020-07-26 12:00:00+00:00,-44.97 +2020-07-26 13:00:00+00:00,-21.35 +2020-07-26 14:00:00+00:00,-2.07 +2020-07-26 15:00:00+00:00,6.77 +2020-07-26 16:00:00+00:00,22.64 +2020-07-26 17:00:00+00:00,25.64 +2020-07-26 18:00:00+00:00,32.7 +2020-07-26 19:00:00+00:00,35.69 +2020-07-26 20:00:00+00:00,36.97 +2020-07-26 21:00:00+00:00,32.04 +2020-07-26 22:00:00+00:00,32.43 +2020-07-26 23:00:00+00:00,27.11 +2020-07-27 00:00:00+00:00,25.8 +2020-07-27 01:00:00+00:00,24.15 +2020-07-27 02:00:00+00:00,24.02 +2020-07-27 03:00:00+00:00,27.18 +2020-07-27 04:00:00+00:00,35.86 +2020-07-27 05:00:00+00:00,37.95 +2020-07-27 06:00:00+00:00,38.59 +2020-07-27 07:00:00+00:00,38.72 +2020-07-27 08:00:00+00:00,36.87 +2020-07-27 09:00:00+00:00,35.45 +2020-07-27 10:00:00+00:00,32.18 +2020-07-27 11:00:00+00:00,30.54 +2020-07-27 12:00:00+00:00,30.46 +2020-07-27 13:00:00+00:00,31.4 +2020-07-27 14:00:00+00:00,34.44 +2020-07-27 15:00:00+00:00,36.44 +2020-07-27 16:00:00+00:00,38.5 +2020-07-27 17:00:00+00:00,39.36 +2020-07-27 18:00:00+00:00,37.98 +2020-07-27 19:00:00+00:00,33.72 +2020-07-27 20:00:00+00:00,29.0 +2020-07-27 21:00:00+00:00,22.62 +2020-07-27 22:00:00+00:00,18.71 +2020-07-27 23:00:00+00:00,19.16 +2020-07-28 00:00:00+00:00,16.82 +2020-07-28 01:00:00+00:00,17.2 +2020-07-28 02:00:00+00:00,17.11 +2020-07-28 03:00:00+00:00,21.56 +2020-07-28 04:00:00+00:00,26.21 +2020-07-28 05:00:00+00:00,32.17 +2020-07-28 06:00:00+00:00,33.32 +2020-07-28 07:00:00+00:00,28.62 +2020-07-28 08:00:00+00:00,23.58 +2020-07-28 09:00:00+00:00,21.04 +2020-07-28 10:00:00+00:00,14.93 +2020-07-28 11:00:00+00:00,0.04 +2020-07-28 12:00:00+00:00,1.48 +2020-07-28 13:00:00+00:00,8.13 +2020-07-28 14:00:00+00:00,21.03 +2020-07-28 15:00:00+00:00,27.09 +2020-07-28 16:00:00+00:00,30.96 +2020-07-28 17:00:00+00:00,35.78 +2020-07-28 18:00:00+00:00,36.94 +2020-07-28 19:00:00+00:00,36.78 +2020-07-28 20:00:00+00:00,34.21 +2020-07-28 21:00:00+00:00,28.31 +2020-07-28 22:00:00+00:00,26.16 +2020-07-28 23:00:00+00:00,23.5 +2020-07-29 00:00:00+00:00,23.53 +2020-07-29 01:00:00+00:00,22.38 +2020-07-29 02:00:00+00:00,22.79 +2020-07-29 03:00:00+00:00,23.65 +2020-07-29 04:00:00+00:00,29.78 +2020-07-29 05:00:00+00:00,32.85 +2020-07-29 06:00:00+00:00,30.59 +2020-07-29 07:00:00+00:00,22.73 +2020-07-29 08:00:00+00:00,10.65 +2020-07-29 09:00:00+00:00,3.56 +2020-07-29 10:00:00+00:00,0.13 +2020-07-29 11:00:00+00:00,0.07 +2020-07-29 12:00:00+00:00,0.07 +2020-07-29 13:00:00+00:00,0.08 +2020-07-29 14:00:00+00:00,3.77 +2020-07-29 15:00:00+00:00,14.74 +2020-07-29 16:00:00+00:00,26.74 +2020-07-29 17:00:00+00:00,34.02 +2020-07-29 18:00:00+00:00,35.61 +2020-07-29 19:00:00+00:00,39.38 +2020-07-29 20:00:00+00:00,37.0 +2020-07-29 21:00:00+00:00,34.2 +2020-07-29 22:00:00+00:00,35.97 +2020-07-29 23:00:00+00:00,31.78 +2020-07-30 00:00:00+00:00,29.1 +2020-07-30 01:00:00+00:00,27.17 +2020-07-30 02:00:00+00:00,26.99 +2020-07-30 03:00:00+00:00,29.2 +2020-07-30 04:00:00+00:00,36.91 +2020-07-30 05:00:00+00:00,39.64 +2020-07-30 06:00:00+00:00,39.53 +2020-07-30 07:00:00+00:00,34.88 +2020-07-30 08:00:00+00:00,28.14 +2020-07-30 09:00:00+00:00,27.49 +2020-07-30 10:00:00+00:00,26.73 +2020-07-30 11:00:00+00:00,25.55 +2020-07-30 12:00:00+00:00,26.2 +2020-07-30 13:00:00+00:00,27.59 +2020-07-30 14:00:00+00:00,30.25 +2020-07-30 15:00:00+00:00,38.08 +2020-07-30 16:00:00+00:00,44.94 +2020-07-30 17:00:00+00:00,60.02 +2020-07-30 18:00:00+00:00,56.8 +2020-07-30 19:00:00+00:00,49.83 +2020-07-30 20:00:00+00:00,44.54 +2020-07-30 21:00:00+00:00,37.53 +2020-07-30 22:00:00+00:00,38.8 +2020-07-30 23:00:00+00:00,33.9 +2020-07-31 00:00:00+00:00,31.99 +2020-07-31 01:00:00+00:00,31.58 +2020-07-31 02:00:00+00:00,31.74 +2020-07-31 03:00:00+00:00,34.28 +2020-07-31 04:00:00+00:00,40.56 +2020-07-31 05:00:00+00:00,43.69 +2020-07-31 06:00:00+00:00,44.91 +2020-07-31 07:00:00+00:00,41.65 +2020-07-31 08:00:00+00:00,38.25 +2020-07-31 09:00:00+00:00,32.7 +2020-07-31 10:00:00+00:00,30.49 +2020-07-31 11:00:00+00:00,29.49 +2020-07-31 12:00:00+00:00,30.09 +2020-07-31 13:00:00+00:00,31.95 +2020-07-31 14:00:00+00:00,33.49 +2020-07-31 15:00:00+00:00,42.66 +2020-07-31 16:00:00+00:00,49.08 +2020-07-31 17:00:00+00:00,54.4 +2020-07-31 18:00:00+00:00,49.12 +2020-07-31 19:00:00+00:00,46.15 +2020-07-31 20:00:00+00:00,44.97 +2020-07-31 21:00:00+00:00,38.27 +2020-07-31 22:00:00+00:00,33.35 +2020-07-31 23:00:00+00:00,24.6 +2020-08-01 00:00:00+00:00,24.28 +2020-08-01 01:00:00+00:00,23.99 +2020-08-01 02:00:00+00:00,23.67 +2020-08-01 03:00:00+00:00,24.0 +2020-08-01 04:00:00+00:00,25.0 +2020-08-01 05:00:00+00:00,27.59 +2020-08-01 06:00:00+00:00,25.93 +2020-08-01 07:00:00+00:00,24.4 +2020-08-01 08:00:00+00:00,23.22 +2020-08-01 09:00:00+00:00,23.37 +2020-08-01 10:00:00+00:00,24.31 +2020-08-01 11:00:00+00:00,23.74 +2020-08-01 12:00:00+00:00,23.11 +2020-08-01 13:00:00+00:00,23.16 +2020-08-01 14:00:00+00:00,25.54 +2020-08-01 15:00:00+00:00,32.62 +2020-08-01 16:00:00+00:00,38.43 +2020-08-01 17:00:00+00:00,38.78 +2020-08-01 18:00:00+00:00,38.37 +2020-08-01 19:00:00+00:00,37.78 +2020-08-01 20:00:00+00:00,37.12 +2020-08-01 21:00:00+00:00,33.09 +2020-08-01 22:00:00+00:00,30.67 +2020-08-01 23:00:00+00:00,27.82 +2020-08-02 00:00:00+00:00,26.97 +2020-08-02 01:00:00+00:00,25.25 +2020-08-02 02:00:00+00:00,25.23 +2020-08-02 03:00:00+00:00,25.09 +2020-08-02 04:00:00+00:00,25.46 +2020-08-02 05:00:00+00:00,26.09 +2020-08-02 06:00:00+00:00,27.4 +2020-08-02 07:00:00+00:00,27.62 +2020-08-02 08:00:00+00:00,27.63 +2020-08-02 09:00:00+00:00,28.01 +2020-08-02 10:00:00+00:00,26.42 +2020-08-02 11:00:00+00:00,24.76 +2020-08-02 12:00:00+00:00,24.53 +2020-08-02 13:00:00+00:00,24.16 +2020-08-02 14:00:00+00:00,23.86 +2020-08-02 15:00:00+00:00,25.56 +2020-08-02 16:00:00+00:00,28.08 +2020-08-02 17:00:00+00:00,33.0 +2020-08-02 18:00:00+00:00,34.14 +2020-08-02 19:00:00+00:00,35.49 +2020-08-02 20:00:00+00:00,38.43 +2020-08-02 21:00:00+00:00,34.89 +2020-08-02 22:00:00+00:00,27.93 +2020-08-02 23:00:00+00:00,26.19 +2020-08-03 00:00:00+00:00,26.05 +2020-08-03 01:00:00+00:00,25.06 +2020-08-03 02:00:00+00:00,26.03 +2020-08-03 03:00:00+00:00,27.93 +2020-08-03 04:00:00+00:00,34.12 +2020-08-03 05:00:00+00:00,39.97 +2020-08-03 06:00:00+00:00,41.0 +2020-08-03 07:00:00+00:00,42.65 +2020-08-03 08:00:00+00:00,41.2 +2020-08-03 09:00:00+00:00,41.16 +2020-08-03 10:00:00+00:00,40.97 +2020-08-03 11:00:00+00:00,39.9 +2020-08-03 12:00:00+00:00,38.24 +2020-08-03 13:00:00+00:00,37.18 +2020-08-03 14:00:00+00:00,37.75 +2020-08-03 15:00:00+00:00,39.67 +2020-08-03 16:00:00+00:00,42.1 +2020-08-03 17:00:00+00:00,45.45 +2020-08-03 18:00:00+00:00,43.06 +2020-08-03 19:00:00+00:00,41.7 +2020-08-03 20:00:00+00:00,40.9 +2020-08-03 21:00:00+00:00,35.05 +2020-08-03 22:00:00+00:00,31.6 +2020-08-03 23:00:00+00:00,28.02 +2020-08-04 00:00:00+00:00,26.14 +2020-08-04 01:00:00+00:00,23.66 +2020-08-04 02:00:00+00:00,23.67 +2020-08-04 03:00:00+00:00,28.35 +2020-08-04 04:00:00+00:00,33.0 +2020-08-04 05:00:00+00:00,46.97 +2020-08-04 06:00:00+00:00,49.67 +2020-08-04 07:00:00+00:00,46.45 +2020-08-04 08:00:00+00:00,41.35 +2020-08-04 09:00:00+00:00,40.33 +2020-08-04 10:00:00+00:00,39.38 +2020-08-04 11:00:00+00:00,36.98 +2020-08-04 12:00:00+00:00,35.22 +2020-08-04 13:00:00+00:00,34.81 +2020-08-04 14:00:00+00:00,33.96 +2020-08-04 15:00:00+00:00,37.77 +2020-08-04 16:00:00+00:00,40.41 +2020-08-04 17:00:00+00:00,41.32 +2020-08-04 18:00:00+00:00,41.2 +2020-08-04 19:00:00+00:00,41.16 +2020-08-04 20:00:00+00:00,39.83 +2020-08-04 21:00:00+00:00,32.2 +2020-08-04 22:00:00+00:00,32.27 +2020-08-04 23:00:00+00:00,26.29 +2020-08-05 00:00:00+00:00,23.51 +2020-08-05 01:00:00+00:00,19.64 +2020-08-05 02:00:00+00:00,19.5 +2020-08-05 03:00:00+00:00,22.85 +2020-08-05 04:00:00+00:00,28.77 +2020-08-05 05:00:00+00:00,33.08 +2020-08-05 06:00:00+00:00,32.67 +2020-08-05 07:00:00+00:00,26.18 +2020-08-05 08:00:00+00:00,24.58 +2020-08-05 09:00:00+00:00,23.91 +2020-08-05 10:00:00+00:00,23.72 +2020-08-05 11:00:00+00:00,23.52 +2020-08-05 12:00:00+00:00,23.79 +2020-08-05 13:00:00+00:00,23.32 +2020-08-05 14:00:00+00:00,24.83 +2020-08-05 15:00:00+00:00,31.22 +2020-08-05 16:00:00+00:00,39.6 +2020-08-05 17:00:00+00:00,43.04 +2020-08-05 18:00:00+00:00,43.4 +2020-08-05 19:00:00+00:00,42.43 +2020-08-05 20:00:00+00:00,41.69 +2020-08-05 21:00:00+00:00,36.71 +2020-08-05 22:00:00+00:00,26.9 +2020-08-05 23:00:00+00:00,23.99 +2020-08-06 00:00:00+00:00,23.12 +2020-08-06 01:00:00+00:00,23.02 +2020-08-06 02:00:00+00:00,24.06 +2020-08-06 03:00:00+00:00,27.09 +2020-08-06 04:00:00+00:00,32.0 +2020-08-06 05:00:00+00:00,36.96 +2020-08-06 06:00:00+00:00,38.45 +2020-08-06 07:00:00+00:00,37.55 +2020-08-06 08:00:00+00:00,28.75 +2020-08-06 09:00:00+00:00,26.63 +2020-08-06 10:00:00+00:00,26.93 +2020-08-06 11:00:00+00:00,26.62 +2020-08-06 12:00:00+00:00,26.6 +2020-08-06 13:00:00+00:00,27.41 +2020-08-06 14:00:00+00:00,34.74 +2020-08-06 15:00:00+00:00,39.69 +2020-08-06 16:00:00+00:00,43.39 +2020-08-06 17:00:00+00:00,53.96 +2020-08-06 18:00:00+00:00,49.22 +2020-08-06 19:00:00+00:00,43.39 +2020-08-06 20:00:00+00:00,41.39 +2020-08-06 21:00:00+00:00,36.86 +2020-08-06 22:00:00+00:00,34.26 +2020-08-06 23:00:00+00:00,31.01 +2020-08-07 00:00:00+00:00,29.2 +2020-08-07 01:00:00+00:00,27.81 +2020-08-07 02:00:00+00:00,27.48 +2020-08-07 03:00:00+00:00,30.66 +2020-08-07 04:00:00+00:00,34.89 +2020-08-07 05:00:00+00:00,39.09 +2020-08-07 06:00:00+00:00,39.2 +2020-08-07 07:00:00+00:00,39.0 +2020-08-07 08:00:00+00:00,36.45 +2020-08-07 09:00:00+00:00,33.37 +2020-08-07 10:00:00+00:00,31.16 +2020-08-07 11:00:00+00:00,30.59 +2020-08-07 12:00:00+00:00,29.92 +2020-08-07 13:00:00+00:00,31.49 +2020-08-07 14:00:00+00:00,34.38 +2020-08-07 15:00:00+00:00,40.11 +2020-08-07 16:00:00+00:00,43.82 +2020-08-07 17:00:00+00:00,50.11 +2020-08-07 18:00:00+00:00,47.07 +2020-08-07 19:00:00+00:00,44.4 +2020-08-07 20:00:00+00:00,42.04 +2020-08-07 21:00:00+00:00,35.99 +2020-08-07 22:00:00+00:00,38.0 +2020-08-07 23:00:00+00:00,32.8 +2020-08-08 00:00:00+00:00,30.96 +2020-08-08 01:00:00+00:00,29.52 +2020-08-08 02:00:00+00:00,28.94 +2020-08-08 03:00:00+00:00,29.5 +2020-08-08 04:00:00+00:00,29.48 +2020-08-08 05:00:00+00:00,31.7 +2020-08-08 06:00:00+00:00,32.64 +2020-08-08 07:00:00+00:00,33.11 +2020-08-08 08:00:00+00:00,29.6 +2020-08-08 09:00:00+00:00,27.59 +2020-08-08 10:00:00+00:00,25.09 +2020-08-08 11:00:00+00:00,24.02 +2020-08-08 12:00:00+00:00,24.71 +2020-08-08 13:00:00+00:00,27.38 +2020-08-08 14:00:00+00:00,28.9 +2020-08-08 15:00:00+00:00,33.2 +2020-08-08 16:00:00+00:00,38.1 +2020-08-08 17:00:00+00:00,40.7 +2020-08-08 18:00:00+00:00,41.2 +2020-08-08 19:00:00+00:00,40.09 +2020-08-08 20:00:00+00:00,38.62 +2020-08-08 21:00:00+00:00,33.08 +2020-08-08 22:00:00+00:00,31.95 +2020-08-08 23:00:00+00:00,29.16 +2020-08-09 00:00:00+00:00,28.0 +2020-08-09 01:00:00+00:00,26.17 +2020-08-09 02:00:00+00:00,25.78 +2020-08-09 03:00:00+00:00,25.8 +2020-08-09 04:00:00+00:00,24.7 +2020-08-09 05:00:00+00:00,24.4 +2020-08-09 06:00:00+00:00,25.03 +2020-08-09 07:00:00+00:00,24.64 +2020-08-09 08:00:00+00:00,26.42 +2020-08-09 09:00:00+00:00,25.37 +2020-08-09 10:00:00+00:00,24.01 +2020-08-09 11:00:00+00:00,23.41 +2020-08-09 12:00:00+00:00,23.43 +2020-08-09 13:00:00+00:00,23.93 +2020-08-09 14:00:00+00:00,27.66 +2020-08-09 15:00:00+00:00,29.46 +2020-08-09 16:00:00+00:00,35.0 +2020-08-09 17:00:00+00:00,39.9 +2020-08-09 18:00:00+00:00,40.24 +2020-08-09 19:00:00+00:00,38.99 +2020-08-09 20:00:00+00:00,35.09 +2020-08-09 21:00:00+00:00,26.27 +2020-08-09 22:00:00+00:00,30.23 +2020-08-09 23:00:00+00:00,28.7 +2020-08-10 00:00:00+00:00,28.03 +2020-08-10 01:00:00+00:00,26.1 +2020-08-10 02:00:00+00:00,26.95 +2020-08-10 03:00:00+00:00,29.13 +2020-08-10 04:00:00+00:00,36.9 +2020-08-10 05:00:00+00:00,41.2 +2020-08-10 06:00:00+00:00,40.35 +2020-08-10 07:00:00+00:00,41.0 +2020-08-10 08:00:00+00:00,40.52 +2020-08-10 09:00:00+00:00,38.83 +2020-08-10 10:00:00+00:00,33.85 +2020-08-10 11:00:00+00:00,32.78 +2020-08-10 12:00:00+00:00,33.06 +2020-08-10 13:00:00+00:00,36.82 +2020-08-10 14:00:00+00:00,39.44 +2020-08-10 15:00:00+00:00,42.21 +2020-08-10 16:00:00+00:00,46.38 +2020-08-10 17:00:00+00:00,52.74 +2020-08-10 18:00:00+00:00,47.29 +2020-08-10 19:00:00+00:00,43.4 +2020-08-10 20:00:00+00:00,40.84 +2020-08-10 21:00:00+00:00,30.74 +2020-08-10 22:00:00+00:00,34.66 +2020-08-10 23:00:00+00:00,33.12 +2020-08-11 00:00:00+00:00,31.12 +2020-08-11 01:00:00+00:00,29.78 +2020-08-11 02:00:00+00:00,30.13 +2020-08-11 03:00:00+00:00,32.33 +2020-08-11 04:00:00+00:00,36.1 +2020-08-11 05:00:00+00:00,40.53 +2020-08-11 06:00:00+00:00,41.26 +2020-08-11 07:00:00+00:00,41.54 +2020-08-11 08:00:00+00:00,39.99 +2020-08-11 09:00:00+00:00,35.22 +2020-08-11 10:00:00+00:00,34.01 +2020-08-11 11:00:00+00:00,32.96 +2020-08-11 12:00:00+00:00,32.16 +2020-08-11 13:00:00+00:00,32.22 +2020-08-11 14:00:00+00:00,38.56 +2020-08-11 15:00:00+00:00,41.55 +2020-08-11 16:00:00+00:00,43.01 +2020-08-11 17:00:00+00:00,46.49 +2020-08-11 18:00:00+00:00,41.31 +2020-08-11 19:00:00+00:00,39.98 +2020-08-11 20:00:00+00:00,35.09 +2020-08-11 21:00:00+00:00,28.02 +2020-08-11 22:00:00+00:00,24.89 +2020-08-11 23:00:00+00:00,24.34 +2020-08-12 00:00:00+00:00,24.35 +2020-08-12 01:00:00+00:00,25.28 +2020-08-12 02:00:00+00:00,27.03 +2020-08-12 03:00:00+00:00,30.44 +2020-08-12 04:00:00+00:00,34.93 +2020-08-12 05:00:00+00:00,39.0 +2020-08-12 06:00:00+00:00,41.4 +2020-08-12 07:00:00+00:00,41.28 +2020-08-12 08:00:00+00:00,38.01 +2020-08-12 09:00:00+00:00,34.44 +2020-08-12 10:00:00+00:00,33.57 +2020-08-12 11:00:00+00:00,33.0 +2020-08-12 12:00:00+00:00,32.79 +2020-08-12 13:00:00+00:00,35.07 +2020-08-12 14:00:00+00:00,36.38 +2020-08-12 15:00:00+00:00,41.71 +2020-08-12 16:00:00+00:00,49.97 +2020-08-12 17:00:00+00:00,56.4 +2020-08-12 18:00:00+00:00,45.87 +2020-08-12 19:00:00+00:00,41.6 +2020-08-12 20:00:00+00:00,37.83 +2020-08-12 21:00:00+00:00,31.17 +2020-08-12 22:00:00+00:00,31.49 +2020-08-12 23:00:00+00:00,29.16 +2020-08-13 00:00:00+00:00,28.42 +2020-08-13 01:00:00+00:00,28.08 +2020-08-13 02:00:00+00:00,28.14 +2020-08-13 03:00:00+00:00,31.47 +2020-08-13 04:00:00+00:00,34.98 +2020-08-13 05:00:00+00:00,39.98 +2020-08-13 06:00:00+00:00,40.97 +2020-08-13 07:00:00+00:00,42.79 +2020-08-13 08:00:00+00:00,41.76 +2020-08-13 09:00:00+00:00,40.65 +2020-08-13 10:00:00+00:00,39.08 +2020-08-13 11:00:00+00:00,38.28 +2020-08-13 12:00:00+00:00,36.8 +2020-08-13 13:00:00+00:00,37.98 +2020-08-13 14:00:00+00:00,39.07 +2020-08-13 15:00:00+00:00,42.62 +2020-08-13 16:00:00+00:00,49.5 +2020-08-13 17:00:00+00:00,53.39 +2020-08-13 18:00:00+00:00,48.91 +2020-08-13 19:00:00+00:00,44.29 +2020-08-13 20:00:00+00:00,42.06 +2020-08-13 21:00:00+00:00,36.19 +2020-08-13 22:00:00+00:00,33.41 +2020-08-13 23:00:00+00:00,32.24 +2020-08-14 00:00:00+00:00,31.07 +2020-08-14 01:00:00+00:00,30.18 +2020-08-14 02:00:00+00:00,30.07 +2020-08-14 03:00:00+00:00,32.1 +2020-08-14 04:00:00+00:00,43.01 +2020-08-14 05:00:00+00:00,51.58 +2020-08-14 06:00:00+00:00,51.08 +2020-08-14 07:00:00+00:00,51.98 +2020-08-14 08:00:00+00:00,53.28 +2020-08-14 09:00:00+00:00,51.63 +2020-08-14 10:00:00+00:00,44.28 +2020-08-14 11:00:00+00:00,39.17 +2020-08-14 12:00:00+00:00,37.61 +2020-08-14 13:00:00+00:00,37.01 +2020-08-14 14:00:00+00:00,38.01 +2020-08-14 15:00:00+00:00,39.94 +2020-08-14 16:00:00+00:00,45.69 +2020-08-14 17:00:00+00:00,46.08 +2020-08-14 18:00:00+00:00,43.0 +2020-08-14 19:00:00+00:00,42.06 +2020-08-14 20:00:00+00:00,40.0 +2020-08-14 21:00:00+00:00,35.35 +2020-08-14 22:00:00+00:00,38.36 +2020-08-14 23:00:00+00:00,33.9 +2020-08-15 00:00:00+00:00,31.77 +2020-08-15 01:00:00+00:00,30.31 +2020-08-15 02:00:00+00:00,29.92 +2020-08-15 03:00:00+00:00,29.06 +2020-08-15 04:00:00+00:00,29.0 +2020-08-15 05:00:00+00:00,29.1 +2020-08-15 06:00:00+00:00,30.38 +2020-08-15 07:00:00+00:00,31.39 +2020-08-15 08:00:00+00:00,30.14 +2020-08-15 09:00:00+00:00,29.91 +2020-08-15 10:00:00+00:00,29.53 +2020-08-15 11:00:00+00:00,28.0 +2020-08-15 12:00:00+00:00,27.14 +2020-08-15 13:00:00+00:00,26.07 +2020-08-15 14:00:00+00:00,27.3 +2020-08-15 15:00:00+00:00,29.11 +2020-08-15 16:00:00+00:00,34.09 +2020-08-15 17:00:00+00:00,36.98 +2020-08-15 18:00:00+00:00,39.59 +2020-08-15 19:00:00+00:00,39.93 +2020-08-15 20:00:00+00:00,39.54 +2020-08-15 21:00:00+00:00,34.14 +2020-08-15 22:00:00+00:00,33.12 +2020-08-15 23:00:00+00:00,29.0 +2020-08-16 00:00:00+00:00,28.0 +2020-08-16 01:00:00+00:00,26.5 +2020-08-16 02:00:00+00:00,26.15 +2020-08-16 03:00:00+00:00,25.7 +2020-08-16 04:00:00+00:00,26.0 +2020-08-16 05:00:00+00:00,25.95 +2020-08-16 06:00:00+00:00,28.0 +2020-08-16 07:00:00+00:00,28.36 +2020-08-16 08:00:00+00:00,25.51 +2020-08-16 09:00:00+00:00,24.11 +2020-08-16 10:00:00+00:00,22.94 +2020-08-16 11:00:00+00:00,21.91 +2020-08-16 12:00:00+00:00,19.66 +2020-08-16 13:00:00+00:00,20.9 +2020-08-16 14:00:00+00:00,22.5 +2020-08-16 15:00:00+00:00,28.11 +2020-08-16 16:00:00+00:00,34.0 +2020-08-16 17:00:00+00:00,36.98 +2020-08-16 18:00:00+00:00,39.77 +2020-08-16 19:00:00+00:00,40.21 +2020-08-16 20:00:00+00:00,39.98 +2020-08-16 21:00:00+00:00,35.07 +2020-08-16 22:00:00+00:00,30.43 +2020-08-16 23:00:00+00:00,28.72 +2020-08-17 00:00:00+00:00,28.25 +2020-08-17 01:00:00+00:00,27.75 +2020-08-17 02:00:00+00:00,27.91 +2020-08-17 03:00:00+00:00,30.13 +2020-08-17 04:00:00+00:00,35.96 +2020-08-17 05:00:00+00:00,41.44 +2020-08-17 06:00:00+00:00,44.55 +2020-08-17 07:00:00+00:00,44.75 +2020-08-17 08:00:00+00:00,42.11 +2020-08-17 09:00:00+00:00,41.91 +2020-08-17 10:00:00+00:00,39.97 +2020-08-17 11:00:00+00:00,38.67 +2020-08-17 12:00:00+00:00,38.09 +2020-08-17 13:00:00+00:00,39.0 +2020-08-17 14:00:00+00:00,41.0 +2020-08-17 15:00:00+00:00,45.0 +2020-08-17 16:00:00+00:00,51.25 +2020-08-17 17:00:00+00:00,61.47 +2020-08-17 18:00:00+00:00,55.52 +2020-08-17 19:00:00+00:00,51.1 +2020-08-17 20:00:00+00:00,45.0 +2020-08-17 21:00:00+00:00,38.74 +2020-08-17 22:00:00+00:00,35.83 +2020-08-17 23:00:00+00:00,33.4 +2020-08-18 00:00:00+00:00,31.49 +2020-08-18 01:00:00+00:00,29.92 +2020-08-18 02:00:00+00:00,29.95 +2020-08-18 03:00:00+00:00,33.31 +2020-08-18 04:00:00+00:00,42.9 +2020-08-18 05:00:00+00:00,50.92 +2020-08-18 06:00:00+00:00,56.67 +2020-08-18 07:00:00+00:00,49.22 +2020-08-18 08:00:00+00:00,44.03 +2020-08-18 09:00:00+00:00,41.21 +2020-08-18 10:00:00+00:00,40.74 +2020-08-18 11:00:00+00:00,37.79 +2020-08-18 12:00:00+00:00,36.45 +2020-08-18 13:00:00+00:00,36.01 +2020-08-18 14:00:00+00:00,37.6 +2020-08-18 15:00:00+00:00,43.68 +2020-08-18 16:00:00+00:00,51.25 +2020-08-18 17:00:00+00:00,58.05 +2020-08-18 18:00:00+00:00,55.66 +2020-08-18 19:00:00+00:00,52.0 +2020-08-18 20:00:00+00:00,49.06 +2020-08-18 21:00:00+00:00,40.29 +2020-08-18 22:00:00+00:00,37.53 +2020-08-18 23:00:00+00:00,33.91 +2020-08-19 00:00:00+00:00,32.52 +2020-08-19 01:00:00+00:00,30.48 +2020-08-19 02:00:00+00:00,30.25 +2020-08-19 03:00:00+00:00,33.43 +2020-08-19 04:00:00+00:00,39.65 +2020-08-19 05:00:00+00:00,46.9 +2020-08-19 06:00:00+00:00,45.04 +2020-08-19 07:00:00+00:00,42.16 +2020-08-19 08:00:00+00:00,38.42 +2020-08-19 09:00:00+00:00,34.98 +2020-08-19 10:00:00+00:00,33.5 +2020-08-19 11:00:00+00:00,33.46 +2020-08-19 12:00:00+00:00,33.25 +2020-08-19 13:00:00+00:00,33.32 +2020-08-19 14:00:00+00:00,34.98 +2020-08-19 15:00:00+00:00,42.04 +2020-08-19 16:00:00+00:00,46.94 +2020-08-19 17:00:00+00:00,53.58 +2020-08-19 18:00:00+00:00,47.16 +2020-08-19 19:00:00+00:00,41.26 +2020-08-19 20:00:00+00:00,39.79 +2020-08-19 21:00:00+00:00,32.93 +2020-08-19 22:00:00+00:00,33.1 +2020-08-19 23:00:00+00:00,28.6 +2020-08-20 00:00:00+00:00,26.1 +2020-08-20 01:00:00+00:00,25.01 +2020-08-20 02:00:00+00:00,25.05 +2020-08-20 03:00:00+00:00,28.03 +2020-08-20 04:00:00+00:00,33.27 +2020-08-20 05:00:00+00:00,38.19 +2020-08-20 06:00:00+00:00,39.79 +2020-08-20 07:00:00+00:00,40.49 +2020-08-20 08:00:00+00:00,39.82 +2020-08-20 09:00:00+00:00,38.08 +2020-08-20 10:00:00+00:00,37.21 +2020-08-20 11:00:00+00:00,34.62 +2020-08-20 12:00:00+00:00,33.87 +2020-08-20 13:00:00+00:00,35.96 +2020-08-20 14:00:00+00:00,36.84 +2020-08-20 15:00:00+00:00,43.34 +2020-08-20 16:00:00+00:00,50.82 +2020-08-20 17:00:00+00:00,63.37 +2020-08-20 18:00:00+00:00,56.51 +2020-08-20 19:00:00+00:00,47.68 +2020-08-20 20:00:00+00:00,42.23 +2020-08-20 21:00:00+00:00,36.58 +2020-08-20 22:00:00+00:00,34.35 +2020-08-20 23:00:00+00:00,28.45 +2020-08-21 00:00:00+00:00,24.15 +2020-08-21 01:00:00+00:00,23.12 +2020-08-21 02:00:00+00:00,20.97 +2020-08-21 03:00:00+00:00,23.09 +2020-08-21 04:00:00+00:00,28.16 +2020-08-21 05:00:00+00:00,35.68 +2020-08-21 06:00:00+00:00,38.98 +2020-08-21 07:00:00+00:00,38.0 +2020-08-21 08:00:00+00:00,33.9 +2020-08-21 09:00:00+00:00,28.75 +2020-08-21 10:00:00+00:00,26.52 +2020-08-21 11:00:00+00:00,26.02 +2020-08-21 12:00:00+00:00,25.26 +2020-08-21 13:00:00+00:00,25.47 +2020-08-21 14:00:00+00:00,28.3 +2020-08-21 15:00:00+00:00,33.9 +2020-08-21 16:00:00+00:00,38.73 +2020-08-21 17:00:00+00:00,41.2 +2020-08-21 18:00:00+00:00,41.6 +2020-08-21 19:00:00+00:00,41.83 +2020-08-21 20:00:00+00:00,40.08 +2020-08-21 21:00:00+00:00,36.91 +2020-08-21 22:00:00+00:00,31.76 +2020-08-21 23:00:00+00:00,26.4 +2020-08-22 00:00:00+00:00,24.08 +2020-08-22 01:00:00+00:00,21.94 +2020-08-22 02:00:00+00:00,21.3 +2020-08-22 03:00:00+00:00,20.3 +2020-08-22 04:00:00+00:00,23.53 +2020-08-22 05:00:00+00:00,25.17 +2020-08-22 06:00:00+00:00,29.91 +2020-08-22 07:00:00+00:00,29.99 +2020-08-22 08:00:00+00:00,27.14 +2020-08-22 09:00:00+00:00,24.31 +2020-08-22 10:00:00+00:00,18.97 +2020-08-22 11:00:00+00:00,11.57 +2020-08-22 12:00:00+00:00,4.25 +2020-08-22 13:00:00+00:00,4.51 +2020-08-22 14:00:00+00:00,14.98 +2020-08-22 15:00:00+00:00,24.62 +2020-08-22 16:00:00+00:00,26.64 +2020-08-22 17:00:00+00:00,30.27 +2020-08-22 18:00:00+00:00,34.43 +2020-08-22 19:00:00+00:00,35.07 +2020-08-22 20:00:00+00:00,33.36 +2020-08-22 21:00:00+00:00,27.4 +2020-08-22 22:00:00+00:00,26.44 +2020-08-22 23:00:00+00:00,24.41 +2020-08-23 00:00:00+00:00,22.14 +2020-08-23 01:00:00+00:00,19.32 +2020-08-23 02:00:00+00:00,14.91 +2020-08-23 03:00:00+00:00,11.84 +2020-08-23 04:00:00+00:00,13.87 +2020-08-23 05:00:00+00:00,19.2 +2020-08-23 06:00:00+00:00,22.19 +2020-08-23 07:00:00+00:00,17.09 +2020-08-23 08:00:00+00:00,4.44 +2020-08-23 09:00:00+00:00,0.6 +2020-08-23 10:00:00+00:00,0.03 +2020-08-23 11:00:00+00:00,-16.18 +2020-08-23 12:00:00+00:00,-12.11 +2020-08-23 13:00:00+00:00,-3.81 +2020-08-23 14:00:00+00:00,0.09 +2020-08-23 15:00:00+00:00,19.7 +2020-08-23 16:00:00+00:00,28.0 +2020-08-23 17:00:00+00:00,33.41 +2020-08-23 18:00:00+00:00,37.32 +2020-08-23 19:00:00+00:00,38.87 +2020-08-23 20:00:00+00:00,38.07 +2020-08-23 21:00:00+00:00,31.99 +2020-08-23 22:00:00+00:00,28.73 +2020-08-23 23:00:00+00:00,27.95 +2020-08-24 00:00:00+00:00,27.14 +2020-08-24 01:00:00+00:00,25.5 +2020-08-24 02:00:00+00:00,25.5 +2020-08-24 03:00:00+00:00,28.9 +2020-08-24 04:00:00+00:00,39.79 +2020-08-24 05:00:00+00:00,53.26 +2020-08-24 06:00:00+00:00,53.13 +2020-08-24 07:00:00+00:00,53.84 +2020-08-24 08:00:00+00:00,47.1 +2020-08-24 09:00:00+00:00,48.0 +2020-08-24 10:00:00+00:00,44.01 +2020-08-24 11:00:00+00:00,42.31 +2020-08-24 12:00:00+00:00,42.1 +2020-08-24 13:00:00+00:00,41.2 +2020-08-24 14:00:00+00:00,41.92 +2020-08-24 15:00:00+00:00,50.25 +2020-08-24 16:00:00+00:00,55.87 +2020-08-24 17:00:00+00:00,66.33 +2020-08-24 18:00:00+00:00,70.16 +2020-08-24 19:00:00+00:00,61.44 +2020-08-24 20:00:00+00:00,54.9 +2020-08-24 21:00:00+00:00,43.97 +2020-08-24 22:00:00+00:00,42.16 +2020-08-24 23:00:00+00:00,37.96 +2020-08-25 00:00:00+00:00,34.48 +2020-08-25 01:00:00+00:00,32.76 +2020-08-25 02:00:00+00:00,32.68 +2020-08-25 03:00:00+00:00,34.7 +2020-08-25 04:00:00+00:00,46.9 +2020-08-25 05:00:00+00:00,48.06 +2020-08-25 06:00:00+00:00,53.66 +2020-08-25 07:00:00+00:00,45.24 +2020-08-25 08:00:00+00:00,43.07 +2020-08-25 09:00:00+00:00,41.69 +2020-08-25 10:00:00+00:00,40.07 +2020-08-25 11:00:00+00:00,37.85 +2020-08-25 12:00:00+00:00,35.6 +2020-08-25 13:00:00+00:00,35.25 +2020-08-25 14:00:00+00:00,35.65 +2020-08-25 15:00:00+00:00,38.95 +2020-08-25 16:00:00+00:00,40.99 +2020-08-25 17:00:00+00:00,42.04 +2020-08-25 18:00:00+00:00,38.03 +2020-08-25 19:00:00+00:00,35.19 +2020-08-25 20:00:00+00:00,30.05 +2020-08-25 21:00:00+00:00,20.07 +2020-08-25 22:00:00+00:00,11.16 +2020-08-25 23:00:00+00:00,3.55 +2020-08-26 00:00:00+00:00,2.78 +2020-08-26 01:00:00+00:00,2.47 +2020-08-26 02:00:00+00:00,2.94 +2020-08-26 03:00:00+00:00,16.94 +2020-08-26 04:00:00+00:00,31.09 +2020-08-26 05:00:00+00:00,37.09 +2020-08-26 06:00:00+00:00,37.81 +2020-08-26 07:00:00+00:00,34.79 +2020-08-26 08:00:00+00:00,21.7 +2020-08-26 09:00:00+00:00,19.07 +2020-08-26 10:00:00+00:00,13.95 +2020-08-26 11:00:00+00:00,8.21 +2020-08-26 12:00:00+00:00,0.31 +2020-08-26 13:00:00+00:00,-3.82 +2020-08-26 14:00:00+00:00,0.49 +2020-08-26 15:00:00+00:00,16.05 +2020-08-26 16:00:00+00:00,29.46 +2020-08-26 17:00:00+00:00,32.0 +2020-08-26 18:00:00+00:00,32.32 +2020-08-26 19:00:00+00:00,30.07 +2020-08-26 20:00:00+00:00,27.49 +2020-08-26 21:00:00+00:00,21.44 +2020-08-26 22:00:00+00:00,19.78 +2020-08-26 23:00:00+00:00,19.6 +2020-08-27 00:00:00+00:00,20.97 +2020-08-27 01:00:00+00:00,23.57 +2020-08-27 02:00:00+00:00,26.05 +2020-08-27 03:00:00+00:00,29.58 +2020-08-27 04:00:00+00:00,42.93 +2020-08-27 05:00:00+00:00,47.78 +2020-08-27 06:00:00+00:00,51.92 +2020-08-27 07:00:00+00:00,40.21 +2020-08-27 08:00:00+00:00,38.41 +2020-08-27 09:00:00+00:00,40.99 +2020-08-27 10:00:00+00:00,43.04 +2020-08-27 11:00:00+00:00,43.83 +2020-08-27 12:00:00+00:00,45.01 +2020-08-27 13:00:00+00:00,46.91 +2020-08-27 14:00:00+00:00,47.87 +2020-08-27 15:00:00+00:00,59.59 +2020-08-27 16:00:00+00:00,70.39 +2020-08-27 17:00:00+00:00,89.91 +2020-08-27 18:00:00+00:00,90.0 +2020-08-27 19:00:00+00:00,69.0 +2020-08-27 20:00:00+00:00,57.9 +2020-08-27 21:00:00+00:00,46.13 +2020-08-27 22:00:00+00:00,39.9 +2020-08-27 23:00:00+00:00,32.75 +2020-08-28 00:00:00+00:00,30.7 +2020-08-28 01:00:00+00:00,29.17 +2020-08-28 02:00:00+00:00,30.43 +2020-08-28 03:00:00+00:00,32.69 +2020-08-28 04:00:00+00:00,40.83 +2020-08-28 05:00:00+00:00,45.71 +2020-08-28 06:00:00+00:00,47.9 +2020-08-28 07:00:00+00:00,48.34 +2020-08-28 08:00:00+00:00,46.49 +2020-08-28 09:00:00+00:00,43.68 +2020-08-28 10:00:00+00:00,37.0 +2020-08-28 11:00:00+00:00,34.63 +2020-08-28 12:00:00+00:00,36.37 +2020-08-28 13:00:00+00:00,38.25 +2020-08-28 14:00:00+00:00,38.25 +2020-08-28 15:00:00+00:00,45.08 +2020-08-28 16:00:00+00:00,47.98 +2020-08-28 17:00:00+00:00,52.11 +2020-08-28 18:00:00+00:00,50.35 +2020-08-28 19:00:00+00:00,46.66 +2020-08-28 20:00:00+00:00,42.36 +2020-08-28 21:00:00+00:00,39.19 +2020-08-28 22:00:00+00:00,36.82 +2020-08-28 23:00:00+00:00,33.76 +2020-08-29 00:00:00+00:00,30.74 +2020-08-29 01:00:00+00:00,29.27 +2020-08-29 02:00:00+00:00,28.31 +2020-08-29 03:00:00+00:00,29.0 +2020-08-29 04:00:00+00:00,29.6 +2020-08-29 05:00:00+00:00,31.76 +2020-08-29 06:00:00+00:00,35.94 +2020-08-29 07:00:00+00:00,38.3 +2020-08-29 08:00:00+00:00,36.84 +2020-08-29 09:00:00+00:00,35.85 +2020-08-29 10:00:00+00:00,30.99 +2020-08-29 11:00:00+00:00,30.29 +2020-08-29 12:00:00+00:00,30.0 +2020-08-29 13:00:00+00:00,31.67 +2020-08-29 14:00:00+00:00,34.71 +2020-08-29 15:00:00+00:00,37.0 +2020-08-29 16:00:00+00:00,43.98 +2020-08-29 17:00:00+00:00,46.39 +2020-08-29 18:00:00+00:00,46.91 +2020-08-29 19:00:00+00:00,44.99 +2020-08-29 20:00:00+00:00,43.0 +2020-08-29 21:00:00+00:00,39.26 +2020-08-29 22:00:00+00:00,38.2 +2020-08-29 23:00:00+00:00,34.98 +2020-08-30 00:00:00+00:00,32.08 +2020-08-30 01:00:00+00:00,31.34 +2020-08-30 02:00:00+00:00,30.74 +2020-08-30 03:00:00+00:00,30.98 +2020-08-30 04:00:00+00:00,30.59 +2020-08-30 05:00:00+00:00,30.74 +2020-08-30 06:00:00+00:00,31.97 +2020-08-30 07:00:00+00:00,32.25 +2020-08-30 08:00:00+00:00,32.39 +2020-08-30 09:00:00+00:00,32.79 +2020-08-30 10:00:00+00:00,31.99 +2020-08-30 11:00:00+00:00,30.98 +2020-08-30 12:00:00+00:00,29.97 +2020-08-30 13:00:00+00:00,29.23 +2020-08-30 14:00:00+00:00,29.98 +2020-08-30 15:00:00+00:00,31.41 +2020-08-30 16:00:00+00:00,35.18 +2020-08-30 17:00:00+00:00,39.08 +2020-08-30 18:00:00+00:00,42.7 +2020-08-30 19:00:00+00:00,43.13 +2020-08-30 20:00:00+00:00,41.97 +2020-08-30 21:00:00+00:00,37.0 +2020-08-30 22:00:00+00:00,29.76 +2020-08-30 23:00:00+00:00,27.92 +2020-08-31 00:00:00+00:00,28.89 +2020-08-31 01:00:00+00:00,28.43 +2020-08-31 02:00:00+00:00,29.41 +2020-08-31 03:00:00+00:00,34.39 +2020-08-31 04:00:00+00:00,45.24 +2020-08-31 05:00:00+00:00,59.94 +2020-08-31 06:00:00+00:00,67.25 +2020-08-31 07:00:00+00:00,68.84 +2020-08-31 08:00:00+00:00,66.53 +2020-08-31 09:00:00+00:00,65.41 +2020-08-31 10:00:00+00:00,62.76 +2020-08-31 11:00:00+00:00,60.0 +2020-08-31 12:00:00+00:00,57.48 +2020-08-31 13:00:00+00:00,54.99 +2020-08-31 14:00:00+00:00,58.95 +2020-08-31 15:00:00+00:00,65.49 +2020-08-31 16:00:00+00:00,71.6 +2020-08-31 17:00:00+00:00,79.06 +2020-08-31 18:00:00+00:00,72.66 +2020-08-31 19:00:00+00:00,64.86 +2020-08-31 20:00:00+00:00,52.02 +2020-08-31 21:00:00+00:00,45.0 +2020-08-31 22:00:00+00:00,39.2 +2020-08-31 23:00:00+00:00,37.91 +2020-09-01 00:00:00+00:00,35.93 +2020-09-01 01:00:00+00:00,34.91 +2020-09-01 02:00:00+00:00,35.89 +2020-09-01 03:00:00+00:00,38.33 +2020-09-01 04:00:00+00:00,48.08 +2020-09-01 05:00:00+00:00,59.0 +2020-09-01 06:00:00+00:00,65.26 +2020-09-01 07:00:00+00:00,59.96 +2020-09-01 08:00:00+00:00,55.36 +2020-09-01 09:00:00+00:00,53.92 +2020-09-01 10:00:00+00:00,50.86 +2020-09-01 11:00:00+00:00,49.77 +2020-09-01 12:00:00+00:00,47.12 +2020-09-01 13:00:00+00:00,47.0 +2020-09-01 14:00:00+00:00,48.58 +2020-09-01 15:00:00+00:00,51.84 +2020-09-01 16:00:00+00:00,60.0 +2020-09-01 17:00:00+00:00,67.02 +2020-09-01 18:00:00+00:00,65.93 +2020-09-01 19:00:00+00:00,55.32 +2020-09-01 20:00:00+00:00,46.95 +2020-09-01 21:00:00+00:00,41.28 +2020-09-01 22:00:00+00:00,41.67 +2020-09-01 23:00:00+00:00,38.02 +2020-09-02 00:00:00+00:00,36.35 +2020-09-02 01:00:00+00:00,34.72 +2020-09-02 02:00:00+00:00,35.7 +2020-09-02 03:00:00+00:00,38.35 +2020-09-02 04:00:00+00:00,50.13 +2020-09-02 05:00:00+00:00,62.23 +2020-09-02 06:00:00+00:00,71.11 +2020-09-02 07:00:00+00:00,63.51 +2020-09-02 08:00:00+00:00,54.51 +2020-09-02 09:00:00+00:00,49.5 +2020-09-02 10:00:00+00:00,44.93 +2020-09-02 11:00:00+00:00,44.11 +2020-09-02 12:00:00+00:00,43.96 +2020-09-02 13:00:00+00:00,44.02 +2020-09-02 14:00:00+00:00,46.46 +2020-09-02 15:00:00+00:00,52.35 +2020-09-02 16:00:00+00:00,60.4 +2020-09-02 17:00:00+00:00,68.89 +2020-09-02 18:00:00+00:00,66.05 +2020-09-02 19:00:00+00:00,55.46 +2020-09-02 20:00:00+00:00,49.34 +2020-09-02 21:00:00+00:00,43.94 +2020-09-02 22:00:00+00:00,44.55 +2020-09-02 23:00:00+00:00,39.04 +2020-09-03 00:00:00+00:00,36.22 +2020-09-03 01:00:00+00:00,34.84 +2020-09-03 02:00:00+00:00,35.43 +2020-09-03 03:00:00+00:00,37.69 +2020-09-03 04:00:00+00:00,49.5 +2020-09-03 05:00:00+00:00,55.98 +2020-09-03 06:00:00+00:00,58.52 +2020-09-03 07:00:00+00:00,52.35 +2020-09-03 08:00:00+00:00,48.17 +2020-09-03 09:00:00+00:00,41.12 +2020-09-03 10:00:00+00:00,30.95 +2020-09-03 11:00:00+00:00,30.36 +2020-09-03 12:00:00+00:00,30.47 +2020-09-03 13:00:00+00:00,34.19 +2020-09-03 14:00:00+00:00,31.11 +2020-09-03 15:00:00+00:00,42.34 +2020-09-03 16:00:00+00:00,47.49 +2020-09-03 17:00:00+00:00,47.74 +2020-09-03 18:00:00+00:00,46.41 +2020-09-03 19:00:00+00:00,41.06 +2020-09-03 20:00:00+00:00,30.84 +2020-09-03 21:00:00+00:00,26.49 +2020-09-03 22:00:00+00:00,26.83 +2020-09-03 23:00:00+00:00,25.71 +2020-09-04 00:00:00+00:00,26.1 +2020-09-04 01:00:00+00:00,25.96 +2020-09-04 02:00:00+00:00,28.0 +2020-09-04 03:00:00+00:00,30.44 +2020-09-04 04:00:00+00:00,39.97 +2020-09-04 05:00:00+00:00,48.43 +2020-09-04 06:00:00+00:00,51.67 +2020-09-04 07:00:00+00:00,49.71 +2020-09-04 08:00:00+00:00,42.03 +2020-09-04 09:00:00+00:00,39.15 +2020-09-04 10:00:00+00:00,36.0 +2020-09-04 11:00:00+00:00,32.67 +2020-09-04 12:00:00+00:00,31.09 +2020-09-04 13:00:00+00:00,31.25 +2020-09-04 14:00:00+00:00,36.48 +2020-09-04 15:00:00+00:00,44.03 +2020-09-04 16:00:00+00:00,47.95 +2020-09-04 17:00:00+00:00,51.43 +2020-09-04 18:00:00+00:00,51.61 +2020-09-04 19:00:00+00:00,45.99 +2020-09-04 20:00:00+00:00,42.79 +2020-09-04 21:00:00+00:00,34.48 +2020-09-04 22:00:00+00:00,39.58 +2020-09-04 23:00:00+00:00,35.34 +2020-09-05 00:00:00+00:00,30.98 +2020-09-05 01:00:00+00:00,29.01 +2020-09-05 02:00:00+00:00,30.06 +2020-09-05 03:00:00+00:00,30.06 +2020-09-05 04:00:00+00:00,31.6 +2020-09-05 05:00:00+00:00,34.2 +2020-09-05 06:00:00+00:00,38.3 +2020-09-05 07:00:00+00:00,37.55 +2020-09-05 08:00:00+00:00,32.26 +2020-09-05 09:00:00+00:00,29.04 +2020-09-05 10:00:00+00:00,28.49 +2020-09-05 11:00:00+00:00,22.91 +2020-09-05 12:00:00+00:00,15.3 +2020-09-05 13:00:00+00:00,26.75 +2020-09-05 14:00:00+00:00,30.04 +2020-09-05 15:00:00+00:00,32.62 +2020-09-05 16:00:00+00:00,38.21 +2020-09-05 17:00:00+00:00,44.04 +2020-09-05 18:00:00+00:00,46.84 +2020-09-05 19:00:00+00:00,43.08 +2020-09-05 20:00:00+00:00,38.94 +2020-09-05 21:00:00+00:00,36.74 +2020-09-05 22:00:00+00:00,34.04 +2020-09-05 23:00:00+00:00,31.0 +2020-09-06 00:00:00+00:00,30.45 +2020-09-06 01:00:00+00:00,29.39 +2020-09-06 02:00:00+00:00,29.39 +2020-09-06 03:00:00+00:00,29.6 +2020-09-06 04:00:00+00:00,30.8 +2020-09-06 05:00:00+00:00,30.9 +2020-09-06 06:00:00+00:00,30.8 +2020-09-06 07:00:00+00:00,33.2 +2020-09-06 08:00:00+00:00,32.19 +2020-09-06 09:00:00+00:00,34.27 +2020-09-06 10:00:00+00:00,34.1 +2020-09-06 11:00:00+00:00,30.7 +2020-09-06 12:00:00+00:00,30.28 +2020-09-06 13:00:00+00:00,30.73 +2020-09-06 14:00:00+00:00,32.4 +2020-09-06 15:00:00+00:00,35.1 +2020-09-06 16:00:00+00:00,42.01 +2020-09-06 17:00:00+00:00,47.19 +2020-09-06 18:00:00+00:00,49.44 +2020-09-06 19:00:00+00:00,46.75 +2020-09-06 20:00:00+00:00,43.88 +2020-09-06 21:00:00+00:00,37.42 +2020-09-06 22:00:00+00:00,34.36 +2020-09-06 23:00:00+00:00,31.07 +2020-09-07 00:00:00+00:00,30.9 +2020-09-07 01:00:00+00:00,30.53 +2020-09-07 02:00:00+00:00,30.58 +2020-09-07 03:00:00+00:00,33.32 +2020-09-07 04:00:00+00:00,47.13 +2020-09-07 05:00:00+00:00,58.44 +2020-09-07 06:00:00+00:00,65.05 +2020-09-07 07:00:00+00:00,55.8 +2020-09-07 08:00:00+00:00,47.97 +2020-09-07 09:00:00+00:00,46.17 +2020-09-07 10:00:00+00:00,42.36 +2020-09-07 11:00:00+00:00,37.78 +2020-09-07 12:00:00+00:00,36.4 +2020-09-07 13:00:00+00:00,37.83 +2020-09-07 14:00:00+00:00,40.14 +2020-09-07 15:00:00+00:00,46.93 +2020-09-07 16:00:00+00:00,54.78 +2020-09-07 17:00:00+00:00,62.37 +2020-09-07 18:00:00+00:00,59.03 +2020-09-07 19:00:00+00:00,46.01 +2020-09-07 20:00:00+00:00,37.78 +2020-09-07 21:00:00+00:00,32.24 +2020-09-07 22:00:00+00:00,30.0 +2020-09-07 23:00:00+00:00,28.63 +2020-09-08 00:00:00+00:00,26.26 +2020-09-08 01:00:00+00:00,25.15 +2020-09-08 02:00:00+00:00,26.25 +2020-09-08 03:00:00+00:00,29.15 +2020-09-08 04:00:00+00:00,31.39 +2020-09-08 05:00:00+00:00,42.7 +2020-09-08 06:00:00+00:00,44.14 +2020-09-08 07:00:00+00:00,35.54 +2020-09-08 08:00:00+00:00,31.65 +2020-09-08 09:00:00+00:00,29.63 +2020-09-08 10:00:00+00:00,27.7 +2020-09-08 11:00:00+00:00,28.0 +2020-09-08 12:00:00+00:00,27.74 +2020-09-08 13:00:00+00:00,32.22 +2020-09-08 14:00:00+00:00,36.88 +2020-09-08 15:00:00+00:00,48.91 +2020-09-08 16:00:00+00:00,55.8 +2020-09-08 17:00:00+00:00,68.37 +2020-09-08 18:00:00+00:00,66.29 +2020-09-08 19:00:00+00:00,49.52 +2020-09-08 20:00:00+00:00,43.73 +2020-09-08 21:00:00+00:00,36.71 +2020-09-08 22:00:00+00:00,32.38 +2020-09-08 23:00:00+00:00,32.92 +2020-09-09 00:00:00+00:00,30.35 +2020-09-09 01:00:00+00:00,29.58 +2020-09-09 02:00:00+00:00,30.12 +2020-09-09 03:00:00+00:00,32.94 +2020-09-09 04:00:00+00:00,46.01 +2020-09-09 05:00:00+00:00,49.13 +2020-09-09 06:00:00+00:00,49.34 +2020-09-09 07:00:00+00:00,43.93 +2020-09-09 08:00:00+00:00,33.08 +2020-09-09 09:00:00+00:00,29.61 +2020-09-09 10:00:00+00:00,29.09 +2020-09-09 11:00:00+00:00,29.07 +2020-09-09 12:00:00+00:00,29.21 +2020-09-09 13:00:00+00:00,29.17 +2020-09-09 14:00:00+00:00,31.16 +2020-09-09 15:00:00+00:00,43.38 +2020-09-09 16:00:00+00:00,47.92 +2020-09-09 17:00:00+00:00,48.03 +2020-09-09 18:00:00+00:00,47.98 +2020-09-09 19:00:00+00:00,43.19 +2020-09-09 20:00:00+00:00,31.99 +2020-09-09 21:00:00+00:00,29.52 +2020-09-09 22:00:00+00:00,28.98 +2020-09-09 23:00:00+00:00,28.97 +2020-09-10 00:00:00+00:00,29.24 +2020-09-10 01:00:00+00:00,29.28 +2020-09-10 02:00:00+00:00,29.2 +2020-09-10 03:00:00+00:00,33.32 +2020-09-10 04:00:00+00:00,45.18 +2020-09-10 05:00:00+00:00,52.78 +2020-09-10 06:00:00+00:00,59.26 +2020-09-10 07:00:00+00:00,52.74 +2020-09-10 08:00:00+00:00,48.5 +2020-09-10 09:00:00+00:00,47.21 +2020-09-10 10:00:00+00:00,46.08 +2020-09-10 11:00:00+00:00,44.01 +2020-09-10 12:00:00+00:00,42.45 +2020-09-10 13:00:00+00:00,44.73 +2020-09-10 14:00:00+00:00,48.08 +2020-09-10 15:00:00+00:00,55.74 +2020-09-10 16:00:00+00:00,65.81 +2020-09-10 17:00:00+00:00,78.01 +2020-09-10 18:00:00+00:00,75.23 +2020-09-10 19:00:00+00:00,57.43 +2020-09-10 20:00:00+00:00,49.89 +2020-09-10 21:00:00+00:00,43.98 +2020-09-10 22:00:00+00:00,45.84 +2020-09-10 23:00:00+00:00,40.0 +2020-09-11 00:00:00+00:00,39.38 +2020-09-11 01:00:00+00:00,36.79 +2020-09-11 02:00:00+00:00,36.85 +2020-09-11 03:00:00+00:00,41.12 +2020-09-11 04:00:00+00:00,53.91 +2020-09-11 05:00:00+00:00,62.57 +2020-09-11 06:00:00+00:00,69.45 +2020-09-11 07:00:00+00:00,62.0 +2020-09-11 08:00:00+00:00,49.94 +2020-09-11 09:00:00+00:00,46.02 +2020-09-11 10:00:00+00:00,40.79 +2020-09-11 11:00:00+00:00,36.47 +2020-09-11 12:00:00+00:00,34.05 +2020-09-11 13:00:00+00:00,35.06 +2020-09-11 14:00:00+00:00,40.42 +2020-09-11 15:00:00+00:00,48.01 +2020-09-11 16:00:00+00:00,55.4 +2020-09-11 17:00:00+00:00,64.21 +2020-09-11 18:00:00+00:00,62.6 +2020-09-11 19:00:00+00:00,52.05 +2020-09-11 20:00:00+00:00,49.83 +2020-09-11 21:00:00+00:00,44.78 +2020-09-11 22:00:00+00:00,42.91 +2020-09-11 23:00:00+00:00,37.27 +2020-09-12 00:00:00+00:00,34.33 +2020-09-12 01:00:00+00:00,34.16 +2020-09-12 02:00:00+00:00,32.37 +2020-09-12 03:00:00+00:00,33.05 +2020-09-12 04:00:00+00:00,36.43 +2020-09-12 05:00:00+00:00,39.46 +2020-09-12 06:00:00+00:00,38.31 +2020-09-12 07:00:00+00:00,36.8 +2020-09-12 08:00:00+00:00,30.78 +2020-09-12 09:00:00+00:00,28.04 +2020-09-12 10:00:00+00:00,2.75 +2020-09-12 11:00:00+00:00,-0.12 +2020-09-12 12:00:00+00:00,-0.45 +2020-09-12 13:00:00+00:00,4.55 +2020-09-12 14:00:00+00:00,27.54 +2020-09-12 15:00:00+00:00,34.56 +2020-09-12 16:00:00+00:00,44.9 +2020-09-12 17:00:00+00:00,49.02 +2020-09-12 18:00:00+00:00,49.98 +2020-09-12 19:00:00+00:00,47.06 +2020-09-12 20:00:00+00:00,43.0 +2020-09-12 21:00:00+00:00,38.22 +2020-09-12 22:00:00+00:00,35.46 +2020-09-12 23:00:00+00:00,33.31 +2020-09-13 00:00:00+00:00,31.25 +2020-09-13 01:00:00+00:00,30.03 +2020-09-13 02:00:00+00:00,27.87 +2020-09-13 03:00:00+00:00,26.97 +2020-09-13 04:00:00+00:00,29.14 +2020-09-13 05:00:00+00:00,29.74 +2020-09-13 06:00:00+00:00,30.13 +2020-09-13 07:00:00+00:00,30.23 +2020-09-13 08:00:00+00:00,15.07 +2020-09-13 09:00:00+00:00,0.01 +2020-09-13 10:00:00+00:00,-24.08 +2020-09-13 11:00:00+00:00,-58.8 +2020-09-13 12:00:00+00:00,-49.94 +2020-09-13 13:00:00+00:00,-12.9 +2020-09-13 14:00:00+00:00,10.69 +2020-09-13 15:00:00+00:00,34.2 +2020-09-13 16:00:00+00:00,40.0 +2020-09-13 17:00:00+00:00,48.6 +2020-09-13 18:00:00+00:00,50.92 +2020-09-13 19:00:00+00:00,48.08 +2020-09-13 20:00:00+00:00,43.5 +2020-09-13 21:00:00+00:00,35.06 +2020-09-13 22:00:00+00:00,35.22 +2020-09-13 23:00:00+00:00,34.28 +2020-09-14 00:00:00+00:00,32.9 +2020-09-14 01:00:00+00:00,32.35 +2020-09-14 02:00:00+00:00,32.32 +2020-09-14 03:00:00+00:00,36.07 +2020-09-14 04:00:00+00:00,50.0 +2020-09-14 05:00:00+00:00,58.34 +2020-09-14 06:00:00+00:00,62.05 +2020-09-14 07:00:00+00:00,55.93 +2020-09-14 08:00:00+00:00,50.49 +2020-09-14 09:00:00+00:00,46.04 +2020-09-14 10:00:00+00:00,40.46 +2020-09-14 11:00:00+00:00,42.28 +2020-09-14 12:00:00+00:00,40.15 +2020-09-14 13:00:00+00:00,45.23 +2020-09-14 14:00:00+00:00,51.69 +2020-09-14 15:00:00+00:00,66.4 +2020-09-14 16:00:00+00:00,80.98 +2020-09-14 17:00:00+00:00,120.62 +2020-09-14 18:00:00+00:00,96.43 +2020-09-14 19:00:00+00:00,66.32 +2020-09-14 20:00:00+00:00,54.01 +2020-09-14 21:00:00+00:00,44.15 +2020-09-14 22:00:00+00:00,41.23 +2020-09-14 23:00:00+00:00,40.81 +2020-09-15 00:00:00+00:00,39.2 +2020-09-15 01:00:00+00:00,37.77 +2020-09-15 02:00:00+00:00,38.6 +2020-09-15 03:00:00+00:00,41.81 +2020-09-15 04:00:00+00:00,53.9 +2020-09-15 05:00:00+00:00,74.84 +2020-09-15 06:00:00+00:00,84.35 +2020-09-15 07:00:00+00:00,77.64 +2020-09-15 08:00:00+00:00,67.29 +2020-09-15 09:00:00+00:00,61.37 +2020-09-15 10:00:00+00:00,49.95 +2020-09-15 11:00:00+00:00,50.01 +2020-09-15 12:00:00+00:00,52.71 +2020-09-15 13:00:00+00:00,59.9 +2020-09-15 14:00:00+00:00,70.01 +2020-09-15 15:00:00+00:00,82.71 +2020-09-15 16:00:00+00:00,130.59 +2020-09-15 17:00:00+00:00,189.25 +2020-09-15 18:00:00+00:00,148.18 +2020-09-15 19:00:00+00:00,77.68 +2020-09-15 20:00:00+00:00,63.44 +2020-09-15 21:00:00+00:00,51.51 +2020-09-15 22:00:00+00:00,50.12 +2020-09-15 23:00:00+00:00,46.3 +2020-09-16 00:00:00+00:00,45.1 +2020-09-16 01:00:00+00:00,43.12 +2020-09-16 02:00:00+00:00,44.0 +2020-09-16 03:00:00+00:00,46.35 +2020-09-16 04:00:00+00:00,59.98 +2020-09-16 05:00:00+00:00,76.39 +2020-09-16 06:00:00+00:00,86.53 +2020-09-16 07:00:00+00:00,75.0 +2020-09-16 08:00:00+00:00,63.32 +2020-09-16 09:00:00+00:00,56.96 +2020-09-16 10:00:00+00:00,48.35 +2020-09-16 11:00:00+00:00,43.29 +2020-09-16 12:00:00+00:00,39.98 +2020-09-16 13:00:00+00:00,43.16 +2020-09-16 14:00:00+00:00,45.07 +2020-09-16 15:00:00+00:00,54.8 +2020-09-16 16:00:00+00:00,61.48 +2020-09-16 17:00:00+00:00,66.45 +2020-09-16 18:00:00+00:00,57.44 +2020-09-16 19:00:00+00:00,48.39 +2020-09-16 20:00:00+00:00,43.37 +2020-09-16 21:00:00+00:00,36.96 +2020-09-16 22:00:00+00:00,34.11 +2020-09-16 23:00:00+00:00,34.85 +2020-09-17 00:00:00+00:00,34.73 +2020-09-17 01:00:00+00:00,34.32 +2020-09-17 02:00:00+00:00,34.58 +2020-09-17 03:00:00+00:00,36.56 +2020-09-17 04:00:00+00:00,48.0 +2020-09-17 05:00:00+00:00,53.64 +2020-09-17 06:00:00+00:00,59.69 +2020-09-17 07:00:00+00:00,53.91 +2020-09-17 08:00:00+00:00,45.8 +2020-09-17 09:00:00+00:00,42.0 +2020-09-17 10:00:00+00:00,39.89 +2020-09-17 11:00:00+00:00,39.07 +2020-09-17 12:00:00+00:00,37.1 +2020-09-17 13:00:00+00:00,36.66 +2020-09-17 14:00:00+00:00,47.74 +2020-09-17 15:00:00+00:00,56.43 +2020-09-17 16:00:00+00:00,66.5 +2020-09-17 17:00:00+00:00,79.84 +2020-09-17 18:00:00+00:00,65.44 +2020-09-17 19:00:00+00:00,53.19 +2020-09-17 20:00:00+00:00,42.68 +2020-09-17 21:00:00+00:00,36.92 +2020-09-17 22:00:00+00:00,37.97 +2020-09-17 23:00:00+00:00,35.06 +2020-09-18 00:00:00+00:00,34.68 +2020-09-18 01:00:00+00:00,34.51 +2020-09-18 02:00:00+00:00,34.2 +2020-09-18 03:00:00+00:00,36.9 +2020-09-18 04:00:00+00:00,45.02 +2020-09-18 05:00:00+00:00,53.02 +2020-09-18 06:00:00+00:00,58.11 +2020-09-18 07:00:00+00:00,52.79 +2020-09-18 08:00:00+00:00,44.67 +2020-09-18 09:00:00+00:00,38.35 +2020-09-18 10:00:00+00:00,34.89 +2020-09-18 11:00:00+00:00,34.51 +2020-09-18 12:00:00+00:00,34.38 +2020-09-18 13:00:00+00:00,35.47 +2020-09-18 14:00:00+00:00,36.7 +2020-09-18 15:00:00+00:00,46.62 +2020-09-18 16:00:00+00:00,53.08 +2020-09-18 17:00:00+00:00,56.22 +2020-09-18 18:00:00+00:00,52.05 +2020-09-18 19:00:00+00:00,46.4 +2020-09-18 20:00:00+00:00,39.41 +2020-09-18 21:00:00+00:00,36.04 +2020-09-18 22:00:00+00:00,33.25 +2020-09-18 23:00:00+00:00,31.08 +2020-09-19 00:00:00+00:00,32.55 +2020-09-19 01:00:00+00:00,31.89 +2020-09-19 02:00:00+00:00,31.26 +2020-09-19 03:00:00+00:00,30.75 +2020-09-19 04:00:00+00:00,34.31 +2020-09-19 05:00:00+00:00,37.02 +2020-09-19 06:00:00+00:00,39.76 +2020-09-19 07:00:00+00:00,36.78 +2020-09-19 08:00:00+00:00,33.58 +2020-09-19 09:00:00+00:00,32.56 +2020-09-19 10:00:00+00:00,31.67 +2020-09-19 11:00:00+00:00,30.73 +2020-09-19 12:00:00+00:00,30.72 +2020-09-19 13:00:00+00:00,32.41 +2020-09-19 14:00:00+00:00,34.99 +2020-09-19 15:00:00+00:00,40.7 +2020-09-19 16:00:00+00:00,47.95 +2020-09-19 17:00:00+00:00,51.02 +2020-09-19 18:00:00+00:00,51.05 +2020-09-19 19:00:00+00:00,44.41 +2020-09-19 20:00:00+00:00,39.74 +2020-09-19 21:00:00+00:00,36.71 +2020-09-19 22:00:00+00:00,37.89 +2020-09-19 23:00:00+00:00,36.77 +2020-09-20 00:00:00+00:00,35.36 +2020-09-20 01:00:00+00:00,34.4 +2020-09-20 02:00:00+00:00,34.21 +2020-09-20 03:00:00+00:00,34.2 +2020-09-20 04:00:00+00:00,34.8 +2020-09-20 05:00:00+00:00,34.5 +2020-09-20 06:00:00+00:00,35.05 +2020-09-20 07:00:00+00:00,36.75 +2020-09-20 08:00:00+00:00,34.2 +2020-09-20 09:00:00+00:00,31.34 +2020-09-20 10:00:00+00:00,31.13 +2020-09-20 11:00:00+00:00,31.02 +2020-09-20 12:00:00+00:00,32.34 +2020-09-20 13:00:00+00:00,32.95 +2020-09-20 14:00:00+00:00,34.2 +2020-09-20 15:00:00+00:00,38.31 +2020-09-20 16:00:00+00:00,46.47 +2020-09-20 17:00:00+00:00,51.7 +2020-09-20 18:00:00+00:00,51.91 +2020-09-20 19:00:00+00:00,47.9 +2020-09-20 20:00:00+00:00,44.65 +2020-09-20 21:00:00+00:00,38.31 +2020-09-20 22:00:00+00:00,37.24 +2020-09-20 23:00:00+00:00,35.46 +2020-09-21 00:00:00+00:00,35.6 +2020-09-21 01:00:00+00:00,35.54 +2020-09-21 02:00:00+00:00,36.07 +2020-09-21 03:00:00+00:00,43.11 +2020-09-21 04:00:00+00:00,54.36 +2020-09-21 05:00:00+00:00,73.19 +2020-09-21 06:00:00+00:00,85.0 +2020-09-21 07:00:00+00:00,72.09 +2020-09-21 08:00:00+00:00,54.4 +2020-09-21 09:00:00+00:00,48.13 +2020-09-21 10:00:00+00:00,44.25 +2020-09-21 11:00:00+00:00,40.98 +2020-09-21 12:00:00+00:00,44.01 +2020-09-21 13:00:00+00:00,50.96 +2020-09-21 14:00:00+00:00,56.0 +2020-09-21 15:00:00+00:00,65.0 +2020-09-21 16:00:00+00:00,90.81 +2020-09-21 17:00:00+00:00,200.04 +2020-09-21 18:00:00+00:00,98.13 +2020-09-21 19:00:00+00:00,62.52 +2020-09-21 20:00:00+00:00,54.67 +2020-09-21 21:00:00+00:00,48.73 +2020-09-21 22:00:00+00:00,42.93 +2020-09-21 23:00:00+00:00,43.17 +2020-09-22 00:00:00+00:00,42.05 +2020-09-22 01:00:00+00:00,40.61 +2020-09-22 02:00:00+00:00,41.03 +2020-09-22 03:00:00+00:00,44.16 +2020-09-22 04:00:00+00:00,52.97 +2020-09-22 05:00:00+00:00,72.89 +2020-09-22 06:00:00+00:00,73.09 +2020-09-22 07:00:00+00:00,61.85 +2020-09-22 08:00:00+00:00,52.05 +2020-09-22 09:00:00+00:00,48.02 +2020-09-22 10:00:00+00:00,44.95 +2020-09-22 11:00:00+00:00,43.26 +2020-09-22 12:00:00+00:00,42.71 +2020-09-22 13:00:00+00:00,48.32 +2020-09-22 14:00:00+00:00,53.93 +2020-09-22 15:00:00+00:00,58.92 +2020-09-22 16:00:00+00:00,70.0 +2020-09-22 17:00:00+00:00,86.4 +2020-09-22 18:00:00+00:00,70.0 +2020-09-22 19:00:00+00:00,55.47 +2020-09-22 20:00:00+00:00,50.19 +2020-09-22 21:00:00+00:00,40.55 +2020-09-22 22:00:00+00:00,43.51 +2020-09-22 23:00:00+00:00,41.08 +2020-09-23 00:00:00+00:00,39.71 +2020-09-23 01:00:00+00:00,38.45 +2020-09-23 02:00:00+00:00,37.12 +2020-09-23 03:00:00+00:00,39.52 +2020-09-23 04:00:00+00:00,51.92 +2020-09-23 05:00:00+00:00,53.21 +2020-09-23 06:00:00+00:00,58.32 +2020-09-23 07:00:00+00:00,52.56 +2020-09-23 08:00:00+00:00,48.94 +2020-09-23 09:00:00+00:00,48.92 +2020-09-23 10:00:00+00:00,43.94 +2020-09-23 11:00:00+00:00,41.33 +2020-09-23 12:00:00+00:00,43.58 +2020-09-23 13:00:00+00:00,43.46 +2020-09-23 14:00:00+00:00,42.18 +2020-09-23 15:00:00+00:00,54.93 +2020-09-23 16:00:00+00:00,56.43 +2020-09-23 17:00:00+00:00,60.8 +2020-09-23 18:00:00+00:00,53.01 +2020-09-23 19:00:00+00:00,49.89 +2020-09-23 20:00:00+00:00,39.02 +2020-09-23 21:00:00+00:00,32.45 +2020-09-23 22:00:00+00:00,29.95 +2020-09-23 23:00:00+00:00,28.44 +2020-09-24 00:00:00+00:00,26.47 +2020-09-24 01:00:00+00:00,22.26 +2020-09-24 02:00:00+00:00,19.08 +2020-09-24 03:00:00+00:00,26.55 +2020-09-24 04:00:00+00:00,39.18 +2020-09-24 05:00:00+00:00,49.97 +2020-09-24 06:00:00+00:00,51.46 +2020-09-24 07:00:00+00:00,36.45 +2020-09-24 08:00:00+00:00,33.01 +2020-09-24 09:00:00+00:00,29.84 +2020-09-24 10:00:00+00:00,31.35 +2020-09-24 11:00:00+00:00,31.42 +2020-09-24 12:00:00+00:00,35.79 +2020-09-24 13:00:00+00:00,37.43 +2020-09-24 14:00:00+00:00,39.76 +2020-09-24 15:00:00+00:00,46.84 +2020-09-24 16:00:00+00:00,53.93 +2020-09-24 17:00:00+00:00,67.57 +2020-09-24 18:00:00+00:00,54.94 +2020-09-24 19:00:00+00:00,47.63 +2020-09-24 20:00:00+00:00,42.01 +2020-09-24 21:00:00+00:00,35.09 +2020-09-24 22:00:00+00:00,33.34 +2020-09-24 23:00:00+00:00,31.92 +2020-09-25 00:00:00+00:00,29.03 +2020-09-25 01:00:00+00:00,27.2 +2020-09-25 02:00:00+00:00,28.26 +2020-09-25 03:00:00+00:00,30.1 +2020-09-25 04:00:00+00:00,41.74 +2020-09-25 05:00:00+00:00,51.93 +2020-09-25 06:00:00+00:00,56.48 +2020-09-25 07:00:00+00:00,52.95 +2020-09-25 08:00:00+00:00,48.17 +2020-09-25 09:00:00+00:00,47.27 +2020-09-25 10:00:00+00:00,46.23 +2020-09-25 11:00:00+00:00,41.07 +2020-09-25 12:00:00+00:00,35.76 +2020-09-25 13:00:00+00:00,34.43 +2020-09-25 14:00:00+00:00,35.1 +2020-09-25 15:00:00+00:00,45.72 +2020-09-25 16:00:00+00:00,50.94 +2020-09-25 17:00:00+00:00,53.1 +2020-09-25 18:00:00+00:00,51.21 +2020-09-25 19:00:00+00:00,46.03 +2020-09-25 20:00:00+00:00,39.82 +2020-09-25 21:00:00+00:00,35.12 +2020-09-25 22:00:00+00:00,34.01 +2020-09-25 23:00:00+00:00,30.71 +2020-09-26 00:00:00+00:00,28.03 +2020-09-26 01:00:00+00:00,26.39 +2020-09-26 02:00:00+00:00,25.94 +2020-09-26 03:00:00+00:00,27.17 +2020-09-26 04:00:00+00:00,28.97 +2020-09-26 05:00:00+00:00,31.98 +2020-09-26 06:00:00+00:00,36.72 +2020-09-26 07:00:00+00:00,41.22 +2020-09-26 08:00:00+00:00,39.4 +2020-09-26 09:00:00+00:00,38.13 +2020-09-26 10:00:00+00:00,34.08 +2020-09-26 11:00:00+00:00,32.03 +2020-09-26 12:00:00+00:00,28.9 +2020-09-26 13:00:00+00:00,27.92 +2020-09-26 14:00:00+00:00,29.18 +2020-09-26 15:00:00+00:00,33.95 +2020-09-26 16:00:00+00:00,39.49 +2020-09-26 17:00:00+00:00,45.49 +2020-09-26 18:00:00+00:00,41.25 +2020-09-26 19:00:00+00:00,34.08 +2020-09-26 20:00:00+00:00,31.76 +2020-09-26 21:00:00+00:00,28.34 +2020-09-26 22:00:00+00:00,0.75 +2020-09-26 23:00:00+00:00,19.92 +2020-09-27 00:00:00+00:00,17.99 +2020-09-27 01:00:00+00:00,12.25 +2020-09-27 02:00:00+00:00,12.0 +2020-09-27 03:00:00+00:00,14.39 +2020-09-27 04:00:00+00:00,17.86 +2020-09-27 05:00:00+00:00,21.7 +2020-09-27 06:00:00+00:00,27.11 +2020-09-27 07:00:00+00:00,32.0 +2020-09-27 08:00:00+00:00,32.99 +2020-09-27 09:00:00+00:00,34.26 +2020-09-27 10:00:00+00:00,35.8 +2020-09-27 11:00:00+00:00,32.4 +2020-09-27 12:00:00+00:00,31.99 +2020-09-27 13:00:00+00:00,32.01 +2020-09-27 14:00:00+00:00,33.32 +2020-09-27 15:00:00+00:00,37.63 +2020-09-27 16:00:00+00:00,46.23 +2020-09-27 17:00:00+00:00,51.95 +2020-09-27 18:00:00+00:00,50.0 +2020-09-27 19:00:00+00:00,46.48 +2020-09-27 20:00:00+00:00,41.76 +2020-09-27 21:00:00+00:00,37.84 +2020-09-27 22:00:00+00:00,33.3 +2020-09-27 23:00:00+00:00,30.82 +2020-09-28 00:00:00+00:00,29.5 +2020-09-28 01:00:00+00:00,28.6 +2020-09-28 02:00:00+00:00,28.72 +2020-09-28 03:00:00+00:00,30.89 +2020-09-28 04:00:00+00:00,47.04 +2020-09-28 05:00:00+00:00,55.64 +2020-09-28 06:00:00+00:00,59.31 +2020-09-28 07:00:00+00:00,56.54 +2020-09-28 08:00:00+00:00,53.0 +2020-09-28 09:00:00+00:00,51.72 +2020-09-28 10:00:00+00:00,49.16 +2020-09-28 11:00:00+00:00,48.0 +2020-09-28 12:00:00+00:00,46.0 +2020-09-28 13:00:00+00:00,47.04 +2020-09-28 14:00:00+00:00,48.46 +2020-09-28 15:00:00+00:00,52.93 +2020-09-28 16:00:00+00:00,61.14 +2020-09-28 17:00:00+00:00,80.03 +2020-09-28 18:00:00+00:00,58.0 +2020-09-28 19:00:00+00:00,52.24 +2020-09-28 20:00:00+00:00,46.16 +2020-09-28 21:00:00+00:00,40.01 +2020-09-28 22:00:00+00:00,37.85 +2020-09-28 23:00:00+00:00,37.43 +2020-09-29 00:00:00+00:00,36.85 +2020-09-29 01:00:00+00:00,34.79 +2020-09-29 02:00:00+00:00,36.48 +2020-09-29 03:00:00+00:00,39.51 +2020-09-29 04:00:00+00:00,51.58 +2020-09-29 05:00:00+00:00,67.87 +2020-09-29 06:00:00+00:00,84.65 +2020-09-29 07:00:00+00:00,76.0 +2020-09-29 08:00:00+00:00,69.4 +2020-09-29 09:00:00+00:00,66.62 +2020-09-29 10:00:00+00:00,59.37 +2020-09-29 11:00:00+00:00,54.25 +2020-09-29 12:00:00+00:00,52.24 +2020-09-29 13:00:00+00:00,53.0 +2020-09-29 14:00:00+00:00,56.95 +2020-09-29 15:00:00+00:00,62.43 +2020-09-29 16:00:00+00:00,73.69 +2020-09-29 17:00:00+00:00,128.31 +2020-09-29 18:00:00+00:00,73.14 +2020-09-29 19:00:00+00:00,57.5 +2020-09-29 20:00:00+00:00,51.57 +2020-09-29 21:00:00+00:00,45.36 +2020-09-29 22:00:00+00:00,41.7 +2020-09-29 23:00:00+00:00,41.78 +2020-09-30 00:00:00+00:00,39.9 +2020-09-30 01:00:00+00:00,37.95 +2020-09-30 02:00:00+00:00,37.79 +2020-09-30 03:00:00+00:00,41.89 +2020-09-30 04:00:00+00:00,51.32 +2020-09-30 05:00:00+00:00,62.37 +2020-09-30 06:00:00+00:00,72.8 +2020-09-30 07:00:00+00:00,65.76 +2020-09-30 08:00:00+00:00,55.16 +2020-09-30 09:00:00+00:00,52.29 +2020-09-30 10:00:00+00:00,49.17 +2020-09-30 11:00:00+00:00,47.4 +2020-09-30 12:00:00+00:00,45.04 +2020-09-30 13:00:00+00:00,45.19 +2020-09-30 14:00:00+00:00,48.94 +2020-09-30 15:00:00+00:00,53.98 +2020-09-30 16:00:00+00:00,60.14 +2020-09-30 17:00:00+00:00,72.43 +2020-09-30 18:00:00+00:00,55.34 +2020-09-30 19:00:00+00:00,49.92 +2020-09-30 20:00:00+00:00,42.79 +2020-09-30 21:00:00+00:00,35.02 +2020-09-30 22:00:00+00:00,34.4 diff --git a/docs/notebooks/data/raw/tmy_dresden.csv b/docs/notebooks/data/raw/tmy_dresden.csv new file mode 100644 index 000000000..176ef3468 --- /dev/null +++ b/docs/notebooks/data/raw/tmy_dresden.csv @@ -0,0 +1,8761 @@ +time,temperature_C,ghi_W_m2,dni_W_m2,dhi_W_m2,wind_speed_m_s,relative_humidity_percent +2020-01-01 00:00:00,-1.77,0.0,-0.0,0.0,2.87,88.15 +2020-01-01 01:00:00,-2.63,0.0,-0.0,0.0,2.78,88.43 +2020-01-01 02:00:00,-3.49,0.0,-0.0,0.0,2.68,88.71 +2020-01-01 03:00:00,-4.35,0.0,-0.0,0.0,2.59,88.99 +2020-01-01 04:00:00,-5.22,0.0,-0.0,0.0,2.5,89.28 +2020-01-01 05:00:00,-6.08,0.0,-0.0,0.0,2.41,89.56 +2020-01-01 06:00:00,-6.94,0.0,-0.0,0.0,2.31,89.84 +2020-01-01 07:00:00,-7.8,0.0,-0.0,0.0,2.22,90.12 +2020-01-01 08:00:00,-5.5,43.0,36.5,39.0,3.59,88.05 +2020-01-01 09:00:00,-5.19,45.0,0.0,45.0,3.24,81.25 +2020-01-01 10:00:00,-4.84,63.0,0.0,63.0,3.03,84.6 +2020-01-01 11:00:00,-4.41,57.0,0.0,57.0,2.97,81.3 +2020-01-01 12:00:00,-3.98,56.0,0.0,56.0,3.24,78.15 +2020-01-01 13:00:00,-3.74,42.0,0.0,42.0,3.1,78.15 +2020-01-01 14:00:00,-3.65,19.0,0.0,19.0,2.76,78.2 +2020-01-01 15:00:00,-3.63,0.0,-0.0,0.0,3.03,78.2 +2020-01-01 16:00:00,-3.66,0.0,-0.0,0.0,2.62,78.2 +2020-01-01 17:00:00,-3.72,0.0,-0.0,0.0,2.14,84.7 +2020-01-01 18:00:00,-3.7,0.0,-0.0,0.0,2.14,84.7 +2020-01-01 19:00:00,-3.78,0.0,-0.0,0.0,2.0,84.7 +2020-01-01 20:00:00,-3.63,0.0,-0.0,0.0,2.21,84.8 +2020-01-01 21:00:00,-3.62,0.0,-0.0,0.0,1.66,84.8 +2020-01-01 22:00:00,-3.6,0.0,-0.0,0.0,1.59,84.8 +2020-01-01 23:00:00,-3.57,0.0,-0.0,0.0,1.66,84.8 +2020-01-02 00:00:00,-3.55,0.0,-0.0,0.0,1.59,88.25 +2020-01-02 01:00:00,-3.02,0.0,-0.0,0.0,1.38,88.25 +2020-01-02 02:00:00,-2.98,0.0,-0.0,0.0,1.31,88.25 +2020-01-02 03:00:00,-2.98,0.0,-0.0,0.0,1.24,91.85 +2020-01-02 04:00:00,-3.27,0.0,-0.0,0.0,1.1,95.55 +2020-01-02 05:00:00,-3.09,0.0,-0.0,0.0,1.45,91.85 +2020-01-02 06:00:00,-3.08,0.0,-0.0,0.0,1.31,91.85 +2020-01-02 07:00:00,-3.09,0.0,-0.0,0.0,1.17,91.85 +2020-01-02 08:00:00,-3.18,0.0,0.0,0.0,1.24,91.85 +2020-01-02 09:00:00,-2.92,0.0,0.0,0.0,1.59,91.85 +2020-01-02 10:00:00,-2.45,81.0,0.0,81.0,2.0,88.25 +2020-01-02 11:00:00,-1.96,0.0,0.0,0.0,2.41,81.55 +2020-01-02 12:00:00,-1.7,68.0,0.0,68.0,2.55,81.55 +2020-01-02 13:00:00,-1.68,0.0,0.0,0.0,2.62,81.55 +2020-01-02 14:00:00,-1.72,21.0,0.0,21.0,2.55,84.85 +2020-01-02 15:00:00,-2.02,0.0,-0.0,0.0,2.48,88.3 +2020-01-02 16:00:00,-2.52,0.0,-0.0,0.0,2.07,95.55 +2020-01-02 17:00:00,-2.97,0.0,-0.0,0.0,2.14,95.55 +2020-01-02 18:00:00,-3.41,0.0,-0.0,0.0,2.07,95.55 +2020-01-02 19:00:00,-3.9,0.0,-0.0,0.0,1.66,95.5 +2020-01-02 20:00:00,-4.56,0.0,-0.0,0.0,1.31,91.75 +2020-01-02 21:00:00,-5.05,0.0,-0.0,0.0,1.17,95.5 +2020-01-02 22:00:00,-5.45,0.0,-0.0,0.0,1.17,95.45 +2020-01-02 23:00:00,-5.83,0.0,-0.0,0.0,1.17,95.45 +2020-01-03 00:00:00,-6.38,0.0,-0.0,0.0,1.31,95.45 +2020-01-03 01:00:00,-7.02,0.0,-0.0,0.0,1.45,95.45 +2020-01-03 02:00:00,-7.5,0.0,-0.0,0.0,1.59,95.4 +2020-01-03 03:00:00,-7.62,0.0,-0.0,0.0,1.72,91.6 +2020-01-03 04:00:00,-7.48,0.0,-0.0,0.0,1.79,91.6 +2020-01-03 05:00:00,-7.32,0.0,-0.0,0.0,1.93,91.6 +2020-01-03 06:00:00,-7.03,0.0,-0.0,0.0,1.93,91.6 +2020-01-03 07:00:00,-6.78,0.0,-0.0,0.0,2.14,87.95 +2020-01-03 08:00:00,-6.44,70.0,234.63,44.0,2.21,84.45 +2020-01-03 09:00:00,-5.47,0.0,0.0,0.0,2.41,74.8 +2020-01-03 10:00:00,-4.08,205.0,378.45,107.0,2.9,58.6 +2020-01-03 11:00:00,-2.98,0.0,0.0,0.0,3.1,49.8 +2020-01-03 12:00:00,-2.22,197.0,338.72,110.0,3.1,49.8 +2020-01-03 13:00:00,-1.97,0.0,0.0,0.0,3.03,45.9 +2020-01-03 14:00:00,-2.24,59.0,161.79,42.0,2.69,49.8 +2020-01-03 15:00:00,-3.08,0.0,-0.0,0.0,2.76,47.75 +2020-01-03 16:00:00,-3.73,0.0,-0.0,0.0,2.97,51.65 +2020-01-03 17:00:00,-4.02,0.0,-0.0,0.0,3.17,49.55 +2020-01-03 18:00:00,-4.11,0.0,-0.0,0.0,3.45,49.55 +2020-01-03 19:00:00,-4.04,0.0,-0.0,0.0,3.72,63.7 +2020-01-03 20:00:00,-3.78,0.0,-0.0,0.0,3.93,66.35 +2020-01-03 21:00:00,-3.33,0.0,-0.0,0.0,4.14,69.25 +2020-01-03 22:00:00,-2.55,0.0,-0.0,0.0,4.48,72.25 +2020-01-03 23:00:00,-2.05,0.0,-0.0,0.0,4.76,75.3 +2020-01-04 00:00:00,-1.77,0.0,-0.0,0.0,4.83,81.55 +2020-01-04 01:00:00,-1.68,0.0,-0.0,0.0,4.76,84.85 +2020-01-04 02:00:00,-1.5,0.0,-0.0,0.0,4.69,84.95 +2020-01-04 03:00:00,-1.42,0.0,-0.0,0.0,4.55,88.35 +2020-01-04 04:00:00,-1.35,0.0,-0.0,0.0,4.41,88.35 +2020-01-04 05:00:00,-1.35,0.0,-0.0,0.0,4.28,88.35 +2020-01-04 06:00:00,-1.27,0.0,-0.0,0.0,4.07,88.35 +2020-01-04 07:00:00,-0.83,0.0,-0.0,0.0,4.14,91.95 +2020-01-04 08:00:00,-0.65,26.0,0.0,26.0,4.07,88.45 +2020-01-04 09:00:00,-0.36,0.0,0.0,0.0,3.93,88.45 +2020-01-04 10:00:00,-0.19,67.0,0.0,67.0,4.76,91.95 +2020-01-04 11:00:00,-0.08,0.0,0.0,0.0,5.1,88.45 +2020-01-04 12:00:00,-0.16,116.0,34.77,107.0,3.93,91.95 +2020-01-04 13:00:00,-0.21,0.0,0.0,0.0,3.79,88.45 +2020-01-04 14:00:00,-0.39,14.0,0.0,14.0,4.21,88.45 +2020-01-04 15:00:00,-0.6,0.0,-0.0,0.0,4.28,88.45 +2020-01-04 16:00:00,-0.69,0.0,-0.0,0.0,4.21,91.95 +2020-01-04 17:00:00,-0.74,0.0,-0.0,0.0,4.14,88.4 +2020-01-04 18:00:00,-0.82,0.0,-0.0,0.0,4.14,88.4 +2020-01-04 19:00:00,-0.88,0.0,-0.0,0.0,4.07,88.4 +2020-01-04 20:00:00,-0.97,0.0,-0.0,0.0,4.14,88.4 +2020-01-04 21:00:00,-1.06,0.0,-0.0,0.0,4.21,88.4 +2020-01-04 22:00:00,-1.09,0.0,-0.0,0.0,4.21,88.4 +2020-01-04 23:00:00,-1.16,0.0,-0.0,0.0,4.07,88.4 +2020-01-05 00:00:00,-1.18,0.0,-0.0,0.0,4.0,91.9 +2020-01-05 01:00:00,-1.21,0.0,-0.0,0.0,3.72,91.9 +2020-01-05 02:00:00,-1.22,0.0,-0.0,0.0,3.45,95.6 +2020-01-05 03:00:00,-1.27,0.0,-0.0,0.0,3.1,95.6 +2020-01-05 04:00:00,-1.31,0.0,-0.0,0.0,2.69,95.6 +2020-01-05 05:00:00,-1.69,0.0,-0.0,0.0,2.14,99.4 +2020-01-05 06:00:00,-1.98,0.0,-0.0,0.0,2.28,95.55 +2020-01-05 07:00:00,-1.75,0.0,-0.0,0.0,2.83,99.4 +2020-01-05 08:00:00,-2.11,29.0,0.0,29.0,2.34,95.55 +2020-01-05 09:00:00,-2.65,0.0,0.0,0.0,2.41,95.55 +2020-01-05 10:00:00,-3.19,204.0,362.77,109.0,2.62,95.55 +2020-01-05 11:00:00,-3.77,0.0,0.0,0.0,2.9,91.8 +2020-01-05 12:00:00,-4.41,44.0,0.0,44.0,3.17,91.75 +2020-01-05 13:00:00,-5.4,0.0,0.0,0.0,3.93,88.05 +2020-01-05 14:00:00,-6.14,7.0,0.0,7.0,3.66,84.5 +2020-01-05 15:00:00,-6.85,0.0,-0.0,0.0,3.72,87.95 +2020-01-05 16:00:00,-7.51,0.0,-0.0,0.0,3.52,84.35 +2020-01-05 17:00:00,-8.26,0.0,-0.0,0.0,3.45,84.2 +2020-01-05 18:00:00,-8.8,0.0,-0.0,0.0,3.24,84.15 +2020-01-05 19:00:00,-8.96,0.0,-0.0,0.0,3.24,84.15 +2020-01-05 20:00:00,-9.73,0.0,-0.0,0.0,2.9,84.1 +2020-01-05 21:00:00,-10.3,0.0,-0.0,0.0,2.55,87.65 +2020-01-05 22:00:00,-11.22,0.0,-0.0,0.0,2.14,83.95 +2020-01-05 23:00:00,-12.51,0.0,-0.0,0.0,2.0,87.45 +2020-01-06 00:00:00,-14.15,0.0,-0.0,0.0,1.93,87.35 +2020-01-06 01:00:00,-15.69,0.0,-0.0,0.0,1.72,87.2 +2020-01-06 02:00:00,-16.52,0.0,-0.0,0.0,1.79,91.05 +2020-01-06 03:00:00,-17.51,0.0,-0.0,0.0,1.93,91.0 +2020-01-06 04:00:00,-19.15,0.0,-0.0,0.0,2.0,90.9 +2020-01-06 05:00:00,-19.41,0.0,-0.0,0.0,2.07,95.05 +2020-01-06 06:00:00,-19.32,0.0,-0.0,0.0,2.0,90.9 +2020-01-06 07:00:00,-18.5,0.0,-0.0,0.0,1.93,90.9 +2020-01-06 08:00:00,-19.67,65.0,149.7,48.0,2.07,90.85 +2020-01-06 09:00:00,-17.93,0.0,0.0,0.0,1.59,90.95 +2020-01-06 10:00:00,-14.27,146.0,79.69,125.0,0.83,80.1 +2020-01-06 11:00:00,-12.33,0.0,0.0,0.0,0.34,73.55 +2020-01-06 12:00:00,-11.19,160.0,117.82,129.0,0.14,67.7 +2020-01-06 13:00:00,-10.71,0.0,0.0,0.0,0.07,67.8 +2020-01-06 14:00:00,-10.87,60.0,124.51,46.0,0.07,73.8 +2020-01-06 15:00:00,-11.45,0.0,-0.0,0.0,0.07,77.0 +2020-01-06 16:00:00,-12.39,0.0,-0.0,0.0,0.76,83.75 +2020-01-06 17:00:00,-15.7,0.0,-0.0,0.0,1.79,87.2 +2020-01-06 18:00:00,-19.2,0.0,-0.0,0.0,2.34,90.9 +2020-01-06 19:00:00,-20.44,0.0,-0.0,0.0,2.21,90.8 +2020-01-06 20:00:00,-22.11,0.0,-0.0,0.0,2.41,90.65 +2020-01-06 21:00:00,-23.56,0.0,-0.0,0.0,2.41,90.55 +2020-01-06 22:00:00,-24.36,0.0,-0.0,0.0,2.28,78.7 +2020-01-06 23:00:00,-24.75,0.0,-0.0,0.0,2.28,78.6 +2020-01-07 00:00:00,-23.83,0.0,-0.0,0.0,2.14,82.45 +2020-01-07 01:00:00,-23.38,0.0,-0.0,0.0,2.21,82.55 +2020-01-07 02:00:00,-22.61,0.0,-0.0,0.0,2.21,82.6 +2020-01-07 03:00:00,-21.49,0.0,-0.0,0.0,2.21,82.7 +2020-01-07 04:00:00,-20.21,0.0,-0.0,0.0,2.28,79.2 +2020-01-07 05:00:00,-19.24,0.0,-0.0,0.0,2.48,79.35 +2020-01-07 06:00:00,-18.14,0.0,-0.0,0.0,2.55,83.2 +2020-01-07 07:00:00,-18.09,0.0,-0.0,0.0,2.62,83.2 +2020-01-07 08:00:00,-17.29,65.0,156.9,47.0,2.62,83.3 +2020-01-07 09:00:00,-15.06,0.0,0.0,0.0,2.69,79.95 +2020-01-07 10:00:00,-11.69,84.0,0.0,84.0,2.97,73.75 +2020-01-07 11:00:00,-8.31,0.0,0.0,0.0,3.45,65.4 +2020-01-07 12:00:00,-6.38,86.0,3.77,85.0,3.86,60.5 +2020-01-07 13:00:00,-5.7,0.0,0.0,0.0,4.28,66.05 +2020-01-07 14:00:00,-5.39,27.0,0.0,27.0,4.0,77.9 +2020-01-07 15:00:00,-5.18,0.0,-0.0,0.0,3.72,78.0 +2020-01-07 16:00:00,-5.12,0.0,-0.0,0.0,3.52,81.25 +2020-01-07 17:00:00,-4.97,0.0,-0.0,0.0,3.45,84.6 +2020-01-07 18:00:00,-4.66,0.0,-0.0,0.0,3.38,84.65 +2020-01-07 19:00:00,-5.16,0.0,-0.0,0.0,3.24,88.1 +2020-01-07 20:00:00,-5.06,0.0,-0.0,0.0,3.1,91.75 +2020-01-07 21:00:00,-5.09,0.0,-0.0,0.0,2.83,91.75 +2020-01-07 22:00:00,-5.22,0.0,-0.0,0.0,2.62,95.45 +2020-01-07 23:00:00,-5.42,0.0,-0.0,0.0,2.41,95.45 +2020-01-08 00:00:00,-5.59,0.0,-0.0,0.0,2.21,91.7 +2020-01-08 01:00:00,-5.76,0.0,-0.0,0.0,2.14,95.45 +2020-01-08 02:00:00,-6.02,0.0,-0.0,0.0,2.0,91.7 +2020-01-08 03:00:00,-6.33,0.0,-0.0,0.0,1.93,95.45 +2020-01-08 04:00:00,-6.42,0.0,-0.0,0.0,1.86,95.45 +2020-01-08 05:00:00,-6.57,0.0,-0.0,0.0,1.79,91.65 +2020-01-08 06:00:00,-6.97,0.0,-0.0,0.0,1.72,91.6 +2020-01-08 07:00:00,-7.09,0.0,-0.0,0.0,1.38,91.6 +2020-01-08 08:00:00,-7.21,52.0,51.72,46.0,1.24,91.6 +2020-01-08 09:00:00,-6.7,0.0,0.0,0.0,1.45,91.65 +2020-01-08 10:00:00,-6.03,85.0,0.0,85.0,1.66,88.0 +2020-01-08 11:00:00,-5.32,0.0,0.0,0.0,1.72,84.55 +2020-01-08 12:00:00,-4.92,166.0,141.86,128.0,1.59,78.0 +2020-01-08 13:00:00,-4.86,0.0,0.0,0.0,1.45,78.0 +2020-01-08 14:00:00,-5.39,61.0,110.28,48.0,1.24,77.9 +2020-01-08 15:00:00,-6.94,0.0,-0.0,0.0,1.45,77.7 +2020-01-08 16:00:00,-9.78,0.0,-0.0,0.0,2.21,80.55 +2020-01-08 17:00:00,-12.48,0.0,-0.0,0.0,2.28,87.45 +2020-01-08 18:00:00,-14.29,0.0,-0.0,0.0,2.34,83.65 +2020-01-08 19:00:00,-14.62,0.0,-0.0,0.0,2.48,91.2 +2020-01-08 20:00:00,-14.87,0.0,-0.0,0.0,2.62,91.15 +2020-01-08 21:00:00,-14.47,0.0,-0.0,0.0,2.69,91.2 +2020-01-08 22:00:00,-14.08,0.0,-0.0,0.0,2.69,87.35 +2020-01-08 23:00:00,-13.85,0.0,-0.0,0.0,2.62,87.35 +2020-01-09 00:00:00,-14.1,0.0,-0.0,0.0,2.62,87.35 +2020-01-09 01:00:00,-14.76,0.0,-0.0,0.0,2.48,83.6 +2020-01-09 02:00:00,-15.29,0.0,-0.0,0.0,2.48,83.55 +2020-01-09 03:00:00,-15.74,0.0,-0.0,0.0,2.48,87.2 +2020-01-09 04:00:00,-16.1,0.0,-0.0,0.0,2.55,87.15 +2020-01-09 05:00:00,-16.32,0.0,-0.0,0.0,2.55,91.05 +2020-01-09 06:00:00,-16.47,0.0,-0.0,0.0,2.48,87.1 +2020-01-09 07:00:00,-14.95,0.0,-0.0,0.0,2.55,91.15 +2020-01-09 08:00:00,-13.46,70.0,195.88,47.0,2.55,87.4 +2020-01-09 09:00:00,-11.76,0.0,0.0,0.0,2.41,83.9 +2020-01-09 10:00:00,-9.92,177.0,185.73,127.0,2.28,84.05 +2020-01-09 11:00:00,-7.91,0.0,0.0,0.0,2.07,77.55 +2020-01-09 12:00:00,-6.47,161.0,125.73,127.0,1.66,74.6 +2020-01-09 13:00:00,-5.92,0.0,0.0,0.0,1.45,74.7 +2020-01-09 14:00:00,-6.29,53.0,57.97,46.0,1.52,77.75 +2020-01-09 15:00:00,-7.72,0.0,0.0,0.0,2.07,77.6 +2020-01-09 16:00:00,-9.61,0.0,-0.0,0.0,2.07,80.65 +2020-01-09 17:00:00,-11.24,0.0,-0.0,0.0,2.0,83.95 +2020-01-09 18:00:00,-12.23,0.0,-0.0,0.0,1.93,87.5 +2020-01-09 19:00:00,-11.78,0.0,-0.0,0.0,2.07,87.5 +2020-01-09 20:00:00,-11.89,0.0,-0.0,0.0,2.14,87.5 +2020-01-09 21:00:00,-11.55,0.0,-0.0,0.0,2.14,83.9 +2020-01-09 22:00:00,-10.83,0.0,-0.0,0.0,2.14,83.95 +2020-01-09 23:00:00,-10.55,0.0,-0.0,0.0,2.14,80.5 +2020-01-10 00:00:00,-9.85,0.0,-0.0,0.0,2.0,80.55 +2020-01-10 01:00:00,-10.0,0.0,-0.0,0.0,2.14,80.55 +2020-01-10 02:00:00,-9.97,0.0,-0.0,0.0,2.07,80.55 +2020-01-10 03:00:00,-9.62,0.0,-0.0,0.0,1.93,80.65 +2020-01-10 04:00:00,-9.28,0.0,-0.0,0.0,1.79,80.65 +2020-01-10 05:00:00,-9.03,0.0,-0.0,0.0,1.86,80.7 +2020-01-10 06:00:00,-8.93,0.0,-0.0,0.0,2.07,80.7 +2020-01-10 07:00:00,-7.89,0.0,-0.0,0.0,2.34,80.85 +2020-01-10 08:00:00,-7.44,48.0,42.03,43.0,2.34,84.35 +2020-01-10 09:00:00,-6.25,0.0,0.0,0.0,2.21,84.45 +2020-01-10 10:00:00,-5.01,126.0,40.54,115.0,2.34,81.25 +2020-01-10 11:00:00,-3.97,0.0,0.0,0.0,2.48,78.15 +2020-01-10 12:00:00,-3.23,184.0,212.38,126.0,2.55,75.1 +2020-01-10 13:00:00,-2.85,0.0,0.0,0.0,2.48,75.2 +2020-01-10 14:00:00,-3.19,65.0,129.33,49.0,2.28,78.2 +2020-01-10 15:00:00,-4.45,0.0,0.0,0.0,2.41,78.05 +2020-01-10 16:00:00,-5.86,0.0,-0.0,0.0,2.69,81.1 +2020-01-10 17:00:00,-6.76,0.0,-0.0,0.0,2.97,81.0 +2020-01-10 18:00:00,-7.19,0.0,-0.0,0.0,3.1,77.7 +2020-01-10 19:00:00,-6.95,0.0,-0.0,0.0,3.24,81.0 +2020-01-10 20:00:00,-6.84,0.0,-0.0,0.0,3.31,81.0 +2020-01-10 21:00:00,-6.95,0.0,-0.0,0.0,3.38,81.0 +2020-01-10 22:00:00,-7.12,0.0,-0.0,0.0,3.31,77.7 +2020-01-10 23:00:00,-7.38,0.0,-0.0,0.0,3.17,80.9 +2020-01-11 00:00:00,-7.69,0.0,-0.0,0.0,3.17,77.6 +2020-01-11 01:00:00,-7.78,0.0,-0.0,0.0,3.17,80.85 +2020-01-11 02:00:00,-8.13,0.0,-0.0,0.0,3.03,80.85 +2020-01-11 03:00:00,-8.57,0.0,-0.0,0.0,2.76,80.8 +2020-01-11 04:00:00,-9.22,0.0,-0.0,0.0,2.69,80.7 +2020-01-11 05:00:00,-9.72,0.0,-0.0,0.0,2.83,80.65 +2020-01-11 06:00:00,-9.88,0.0,-0.0,0.0,3.17,84.05 +2020-01-11 07:00:00,-8.84,0.0,-0.0,0.0,3.31,84.15 +2020-01-11 08:00:00,-8.42,74.0,215.58,48.0,3.1,84.2 +2020-01-11 09:00:00,-7.37,0.0,0.0,0.0,2.76,80.9 +2020-01-11 10:00:00,-5.95,188.0,219.29,128.0,2.69,74.7 +2020-01-11 11:00:00,-4.47,0.0,0.0,0.0,2.55,71.95 +2020-01-11 12:00:00,-3.39,180.0,177.62,131.0,2.41,72.1 +2020-01-11 13:00:00,-2.82,0.0,0.0,0.0,2.34,72.25 +2020-01-11 14:00:00,-3.12,69.0,141.99,51.0,2.55,72.25 +2020-01-11 15:00:00,-4.05,0.0,0.0,0.0,2.76,75.05 +2020-01-11 16:00:00,-5.71,0.0,-0.0,0.0,2.97,77.9 +2020-01-11 17:00:00,-6.78,0.0,-0.0,0.0,3.1,81.0 +2020-01-11 18:00:00,-7.6,0.0,-0.0,0.0,3.17,80.9 +2020-01-11 19:00:00,-7.78,0.0,-0.0,0.0,3.38,80.85 +2020-01-11 20:00:00,-7.81,0.0,-0.0,0.0,3.38,80.85 +2020-01-11 21:00:00,-7.81,0.0,-0.0,0.0,3.38,80.85 +2020-01-11 22:00:00,-7.88,0.0,-0.0,0.0,3.38,80.85 +2020-01-11 23:00:00,-7.84,0.0,-0.0,0.0,3.52,80.85 +2020-01-12 00:00:00,-7.71,0.0,-0.0,0.0,3.45,77.6 +2020-01-12 01:00:00,-7.87,0.0,-0.0,0.0,3.38,77.55 +2020-01-12 02:00:00,-8.24,0.0,-0.0,0.0,3.38,80.8 +2020-01-12 03:00:00,-8.61,0.0,-0.0,0.0,3.45,77.45 +2020-01-12 04:00:00,-8.91,0.0,-0.0,0.0,3.59,80.7 +2020-01-12 05:00:00,-9.06,0.0,-0.0,0.0,3.72,77.35 +2020-01-12 06:00:00,-8.99,0.0,-0.0,0.0,3.93,77.35 +2020-01-12 07:00:00,-8.72,0.0,0.0,0.0,4.14,74.25 +2020-01-12 08:00:00,-8.65,78.0,236.98,49.0,4.14,74.25 +2020-01-12 09:00:00,-7.56,126.0,120.19,100.0,4.28,71.4 +2020-01-12 10:00:00,-6.23,209.0,307.99,124.0,4.0,71.55 +2020-01-12 11:00:00,-4.61,222.0,282.57,138.0,3.93,63.55 +2020-01-12 12:00:00,-3.32,201.0,254.71,130.0,4.14,63.8 +2020-01-12 13:00:00,-2.63,156.0,261.65,98.0,4.0,63.9 +2020-01-12 14:00:00,-2.9,72.0,138.54,54.0,4.0,61.35 +2020-01-12 15:00:00,-4.02,0.0,0.0,0.0,3.93,63.7 +2020-01-12 16:00:00,-5.23,0.0,-0.0,0.0,4.14,68.85 +2020-01-12 17:00:00,-5.83,0.0,-0.0,0.0,4.34,71.65 +2020-01-12 18:00:00,-5.89,0.0,-0.0,0.0,4.48,71.65 +2020-01-12 19:00:00,-7.14,0.0,-0.0,0.0,4.83,71.5 +2020-01-12 20:00:00,-7.11,0.0,-0.0,0.0,4.83,71.5 +2020-01-12 21:00:00,-7.13,0.0,-0.0,0.0,4.83,74.55 +2020-01-12 22:00:00,-7.23,0.0,-0.0,0.0,4.83,74.55 +2020-01-12 23:00:00,-7.27,0.0,-0.0,0.0,4.69,74.45 +2020-01-13 00:00:00,-7.35,0.0,-0.0,0.0,4.48,74.45 +2020-01-13 01:00:00,-7.25,0.0,-0.0,0.0,4.34,74.45 +2020-01-13 02:00:00,-7.19,0.0,-0.0,0.0,4.28,71.5 +2020-01-13 03:00:00,-7.06,0.0,-0.0,0.0,4.41,71.5 +2020-01-13 04:00:00,-6.91,0.0,-0.0,0.0,4.34,71.5 +2020-01-13 05:00:00,-6.91,0.0,-0.0,0.0,4.34,71.5 +2020-01-13 06:00:00,-6.81,0.0,-0.0,0.0,4.28,71.5 +2020-01-13 07:00:00,-6.52,0.0,0.0,0.0,4.41,71.55 +2020-01-13 08:00:00,-6.13,72.0,177.04,50.0,4.41,71.65 +2020-01-13 09:00:00,-4.8,130.0,137.28,100.0,4.21,68.95 +2020-01-13 10:00:00,-3.01,229.0,427.32,110.0,4.21,63.9 +2020-01-13 11:00:00,-1.4,243.0,393.28,125.0,3.72,66.8 +2020-01-13 12:00:00,-0.25,222.0,365.58,119.0,3.86,64.35 +2020-01-13 13:00:00,0.12,181.0,435.89,83.0,3.59,64.45 +2020-01-13 14:00:00,-0.44,69.0,105.12,55.0,3.45,67.0 +2020-01-13 15:00:00,-1.76,0.0,0.0,0.0,3.38,72.3 +2020-01-13 16:00:00,-3.26,0.0,-0.0,0.0,3.38,72.1 +2020-01-13 17:00:00,-4.31,0.0,-0.0,0.0,3.38,74.95 +2020-01-13 18:00:00,-5.04,0.0,-0.0,0.0,3.66,74.9 +2020-01-13 19:00:00,-6.43,0.0,-0.0,0.0,3.86,77.75 +2020-01-13 20:00:00,-6.54,0.0,-0.0,0.0,4.14,77.75 +2020-01-13 21:00:00,-6.6,0.0,-0.0,0.0,4.07,77.75 +2020-01-13 22:00:00,-6.89,0.0,-0.0,0.0,3.59,81.0 +2020-01-13 23:00:00,-7.36,0.0,-0.0,0.0,3.38,80.9 +2020-01-14 00:00:00,-7.93,0.0,-0.0,0.0,3.38,80.85 +2020-01-14 01:00:00,-8.43,0.0,-0.0,0.0,3.38,80.8 +2020-01-14 02:00:00,-8.78,0.0,-0.0,0.0,3.38,84.15 +2020-01-14 03:00:00,-9.06,0.0,-0.0,0.0,3.31,80.7 +2020-01-14 04:00:00,-9.3,0.0,-0.0,0.0,3.17,84.1 +2020-01-14 05:00:00,-9.64,0.0,-0.0,0.0,3.03,80.65 +2020-01-14 06:00:00,-10.03,0.0,-0.0,0.0,2.9,80.55 +2020-01-14 07:00:00,-8.75,0.0,0.0,0.0,2.9,84.15 +2020-01-14 08:00:00,-8.49,65.0,102.95,52.0,2.69,80.8 +2020-01-14 09:00:00,-7.14,99.0,36.23,91.0,2.34,77.7 +2020-01-14 10:00:00,-5.38,137.0,46.25,124.0,2.21,74.8 +2020-01-14 11:00:00,-3.6,105.0,3.3,104.0,1.93,72.1 +2020-01-14 12:00:00,-2.09,105.0,7.02,103.0,1.72,72.3 +2020-01-14 13:00:00,-1.34,98.0,30.69,91.0,1.59,75.35 +2020-01-14 14:00:00,-1.21,55.0,36.62,50.0,1.45,78.45 +2020-01-14 15:00:00,-2.03,0.0,0.0,0.0,1.72,81.55 +2020-01-14 16:00:00,-2.98,0.0,-0.0,0.0,1.86,81.5 +2020-01-14 17:00:00,-3.57,0.0,-0.0,0.0,1.93,81.45 +2020-01-14 18:00:00,-3.8,0.0,-0.0,0.0,2.0,81.4 +2020-01-14 19:00:00,-3.83,0.0,-0.0,0.0,2.07,84.7 +2020-01-14 20:00:00,-3.81,0.0,-0.0,0.0,2.14,88.2 +2020-01-14 21:00:00,-3.32,0.0,-0.0,0.0,2.14,88.25 +2020-01-14 22:00:00,-2.55,0.0,-0.0,0.0,2.07,91.85 +2020-01-14 23:00:00,-1.48,0.0,-0.0,0.0,2.07,91.9 +2020-01-15 00:00:00,-0.63,0.0,-0.0,0.0,2.14,91.95 +2020-01-15 01:00:00,-0.19,0.0,-0.0,0.0,2.14,95.6 +2020-01-15 02:00:00,0.06,0.0,-0.0,0.0,2.21,95.6 +2020-01-15 03:00:00,0.21,0.0,-0.0,0.0,2.28,95.6 +2020-01-15 04:00:00,0.3,0.0,-0.0,0.0,2.28,99.4 +2020-01-15 05:00:00,0.37,0.0,-0.0,0.0,2.14,95.65 +2020-01-15 06:00:00,0.41,0.0,-0.0,0.0,2.07,95.65 +2020-01-15 07:00:00,0.36,0.0,0.0,0.0,2.07,95.65 +2020-01-15 08:00:00,0.49,49.0,31.15,45.0,1.93,95.65 +2020-01-15 09:00:00,0.74,63.0,0.0,63.0,1.72,99.4 +2020-01-15 10:00:00,1.02,53.0,0.0,53.0,1.93,95.65 +2020-01-15 11:00:00,1.17,67.0,0.0,67.0,1.79,99.35 +2020-01-15 12:00:00,1.28,65.0,0.0,65.0,1.86,99.35 +2020-01-15 13:00:00,1.33,73.0,4.32,72.0,1.86,99.35 +2020-01-15 14:00:00,1.14,35.0,0.0,35.0,1.72,99.35 +2020-01-15 15:00:00,0.87,0.0,0.0,0.0,1.52,99.35 +2020-01-15 16:00:00,0.7,0.0,-0.0,0.0,1.38,99.4 +2020-01-15 17:00:00,0.51,0.0,-0.0,0.0,1.24,99.4 +2020-01-15 18:00:00,0.33,0.0,-0.0,0.0,1.24,100.0 +2020-01-15 19:00:00,0.23,0.0,-0.0,0.0,1.31,99.4 +2020-01-15 20:00:00,-0.15,0.0,-0.0,0.0,1.24,95.6 +2020-01-15 21:00:00,-0.34,0.0,-0.0,0.0,1.1,99.4 +2020-01-15 22:00:00,-0.65,0.0,-0.0,0.0,1.03,95.6 +2020-01-15 23:00:00,-0.77,0.0,-0.0,0.0,0.9,99.4 +2020-01-16 00:00:00,-0.93,0.0,-0.0,0.0,0.9,99.4 +2020-01-16 01:00:00,-0.69,0.0,-0.0,0.0,0.69,99.4 +2020-01-16 02:00:00,-0.19,0.0,-0.0,0.0,0.48,99.4 +2020-01-16 03:00:00,-0.28,0.0,-0.0,0.0,0.48,95.6 +2020-01-16 04:00:00,-0.98,0.0,-0.0,0.0,0.9,95.6 +2020-01-16 05:00:00,-1.37,0.0,-0.0,0.0,1.1,99.4 +2020-01-16 06:00:00,-1.77,0.0,-0.0,0.0,1.24,99.4 +2020-01-16 07:00:00,-1.22,0.0,0.0,0.0,0.97,99.4 +2020-01-16 08:00:00,-1.46,66.0,99.5,53.0,0.97,99.4 +2020-01-16 09:00:00,-1.22,92.0,22.14,87.0,1.45,99.4 +2020-01-16 10:00:00,-0.53,142.0,52.33,127.0,1.66,95.6 +2020-01-16 11:00:00,0.0,229.0,288.03,140.0,1.79,92.0 +2020-01-16 12:00:00,0.14,222.0,329.51,126.0,1.79,92.0 +2020-01-16 13:00:00,0.31,166.0,272.48,102.0,2.0,88.45 +2020-01-16 14:00:00,0.28,76.0,118.49,59.0,2.14,88.45 +2020-01-16 15:00:00,-0.24,0.0,0.0,0.0,1.86,91.95 +2020-01-16 16:00:00,-0.59,0.0,-0.0,0.0,2.14,88.45 +2020-01-16 17:00:00,-0.85,0.0,-0.0,0.0,2.41,91.95 +2020-01-16 18:00:00,-1.07,0.0,-0.0,0.0,2.69,88.4 +2020-01-16 19:00:00,-1.06,0.0,-0.0,0.0,2.62,91.95 +2020-01-16 20:00:00,-1.11,0.0,-0.0,0.0,2.83,91.95 +2020-01-16 21:00:00,-1.54,0.0,-0.0,0.0,3.03,91.9 +2020-01-16 22:00:00,-1.36,0.0,-0.0,0.0,3.17,91.9 +2020-01-16 23:00:00,-1.71,0.0,-0.0,0.0,3.03,95.55 +2020-01-17 00:00:00,-1.88,0.0,-0.0,0.0,2.97,91.85 +2020-01-17 01:00:00,-2.05,0.0,-0.0,0.0,2.9,91.85 +2020-01-17 02:00:00,-2.15,0.0,-0.0,0.0,2.83,91.85 +2020-01-17 03:00:00,-2.22,0.0,-0.0,0.0,2.83,95.55 +2020-01-17 04:00:00,-2.27,0.0,-0.0,0.0,2.83,95.55 +2020-01-17 05:00:00,-2.23,0.0,-0.0,0.0,2.83,95.55 +2020-01-17 06:00:00,-2.24,0.0,-0.0,0.0,2.9,95.55 +2020-01-17 07:00:00,-1.73,0.0,0.0,0.0,2.83,91.85 +2020-01-17 08:00:00,-1.85,28.0,0.0,28.0,2.62,91.85 +2020-01-17 09:00:00,-1.18,108.0,52.52,96.0,2.41,88.35 +2020-01-17 10:00:00,-0.12,182.0,162.29,135.0,2.21,81.8 +2020-01-17 11:00:00,0.96,236.0,323.51,135.0,2.55,78.8 +2020-01-17 12:00:00,1.66,65.0,0.0,65.0,2.48,82.0 +2020-01-17 13:00:00,1.92,28.0,0.0,28.0,2.21,82.05 +2020-01-17 14:00:00,1.9,68.0,67.99,58.0,2.07,82.05 +2020-01-17 15:00:00,1.63,0.0,0.0,0.0,2.14,85.25 +2020-01-17 16:00:00,1.53,0.0,-0.0,0.0,2.21,85.25 +2020-01-17 17:00:00,1.54,0.0,-0.0,0.0,2.34,85.25 +2020-01-17 18:00:00,1.57,0.0,-0.0,0.0,2.41,85.25 +2020-01-17 19:00:00,1.86,0.0,-0.0,0.0,2.69,85.3 +2020-01-17 20:00:00,1.9,0.0,-0.0,0.0,2.69,85.3 +2020-01-17 21:00:00,2.04,0.0,-0.0,0.0,2.55,88.65 +2020-01-17 22:00:00,2.12,0.0,-0.0,0.0,2.55,88.65 +2020-01-17 23:00:00,1.93,0.0,-0.0,0.0,2.41,88.65 +2020-01-18 00:00:00,1.72,0.0,-0.0,0.0,2.34,88.6 +2020-01-18 01:00:00,1.96,0.0,-0.0,0.0,2.62,88.65 +2020-01-18 02:00:00,2.23,0.0,-0.0,0.0,2.69,88.65 +2020-01-18 03:00:00,2.15,0.0,-0.0,0.0,2.41,88.65 +2020-01-18 04:00:00,1.77,0.0,-0.0,0.0,2.07,92.05 +2020-01-18 05:00:00,1.34,0.0,-0.0,0.0,2.21,95.65 +2020-01-18 06:00:00,1.01,0.0,-0.0,0.0,2.41,92.05 +2020-01-18 07:00:00,0.93,0.0,0.0,0.0,2.48,92.05 +2020-01-18 08:00:00,1.22,59.0,51.66,52.0,2.9,92.05 +2020-01-18 09:00:00,1.83,97.0,25.94,91.0,3.31,92.05 +2020-01-18 10:00:00,2.73,257.0,574.05,89.0,3.52,88.65 +2020-01-18 11:00:00,3.31,263.0,462.75,117.0,3.79,88.7 +2020-01-18 12:00:00,3.74,242.0,432.53,113.0,4.07,85.45 +2020-01-18 13:00:00,3.83,209.0,574.25,70.0,4.07,85.45 +2020-01-18 14:00:00,3.72,56.0,19.9,53.0,4.0,85.45 +2020-01-18 15:00:00,3.26,6.0,0.0,6.0,3.93,88.7 +2020-01-18 16:00:00,3.01,0.0,-0.0,0.0,3.93,85.4 +2020-01-18 17:00:00,3.19,0.0,-0.0,0.0,4.14,85.4 +2020-01-18 18:00:00,3.58,0.0,-0.0,0.0,4.76,82.25 +2020-01-18 19:00:00,2.97,0.0,-0.0,0.0,4.21,85.4 +2020-01-18 20:00:00,3.0,0.0,-0.0,0.0,4.34,85.4 +2020-01-18 21:00:00,2.97,0.0,-0.0,0.0,4.69,85.4 +2020-01-18 22:00:00,2.95,0.0,-0.0,0.0,5.24,85.4 +2020-01-18 23:00:00,2.84,0.0,-0.0,0.0,5.38,88.65 +2020-01-19 00:00:00,2.54,0.0,-0.0,0.0,5.38,88.65 +2020-01-19 01:00:00,2.26,0.0,-0.0,0.0,5.31,88.65 +2020-01-19 02:00:00,2.01,0.0,-0.0,0.0,4.97,85.3 +2020-01-19 03:00:00,1.67,0.0,-0.0,0.0,4.55,85.25 +2020-01-19 04:00:00,1.09,0.0,-0.0,0.0,4.14,85.2 +2020-01-19 05:00:00,0.72,0.0,-0.0,0.0,3.59,88.5 +2020-01-19 06:00:00,0.34,0.0,-0.0,0.0,2.97,85.15 +2020-01-19 07:00:00,0.36,0.0,0.0,0.0,2.34,88.5 +2020-01-19 08:00:00,0.16,102.0,405.52,46.0,2.21,88.45 +2020-01-19 09:00:00,1.05,167.0,281.87,101.0,2.55,88.55 +2020-01-19 10:00:00,2.27,281.0,720.02,68.0,3.31,85.3 +2020-01-19 11:00:00,2.67,295.0,649.08,88.0,4.34,79.0 +2020-01-19 12:00:00,2.86,192.0,162.34,143.0,4.62,79.0 +2020-01-19 13:00:00,2.97,110.0,36.62,101.0,5.17,76.1 +2020-01-19 14:00:00,2.7,41.0,0.0,41.0,5.1,79.0 +2020-01-19 15:00:00,2.13,1.0,0.0,1.0,5.31,85.3 +2020-01-19 16:00:00,2.19,0.0,-0.0,0.0,5.72,85.3 +2020-01-19 17:00:00,2.54,0.0,-0.0,0.0,6.14,82.15 +2020-01-19 18:00:00,2.7,0.0,-0.0,0.0,5.86,79.0 +2020-01-19 19:00:00,2.93,0.0,-0.0,0.0,4.9,76.1 +2020-01-19 20:00:00,3.08,0.0,-0.0,0.0,4.55,76.1 +2020-01-19 21:00:00,2.65,0.0,-0.0,0.0,4.41,79.0 +2020-01-19 22:00:00,2.75,0.0,-0.0,0.0,4.14,79.0 +2020-01-19 23:00:00,3.05,0.0,-0.0,0.0,3.86,79.1 +2020-01-20 00:00:00,2.83,0.0,-0.0,0.0,3.72,82.15 +2020-01-20 01:00:00,2.65,0.0,-0.0,0.0,3.66,85.35 +2020-01-20 02:00:00,2.61,0.0,-0.0,0.0,3.66,85.35 +2020-01-20 03:00:00,2.79,0.0,-0.0,0.0,3.59,85.35 +2020-01-20 04:00:00,2.81,0.0,-0.0,0.0,3.31,85.35 +2020-01-20 05:00:00,2.77,0.0,-0.0,0.0,2.9,85.35 +2020-01-20 06:00:00,2.79,0.0,-0.0,0.0,2.55,88.65 +2020-01-20 07:00:00,2.97,0.0,0.0,0.0,2.34,88.7 +2020-01-20 08:00:00,2.84,51.0,21.31,48.0,2.07,95.7 +2020-01-20 09:00:00,2.97,73.0,0.0,73.0,1.86,92.15 +2020-01-20 10:00:00,3.55,25.0,0.0,25.0,1.66,92.15 +2020-01-20 11:00:00,4.79,210.0,179.89,152.0,1.52,92.25 +2020-01-20 12:00:00,4.81,165.0,81.82,140.0,1.45,92.25 +2020-01-20 13:00:00,4.78,123.0,60.1,108.0,1.31,92.25 +2020-01-20 14:00:00,4.95,62.0,25.26,58.0,1.31,88.85 +2020-01-20 15:00:00,3.84,10.0,0.0,10.0,1.38,95.7 +2020-01-20 16:00:00,1.55,0.0,-0.0,0.0,1.86,95.65 +2020-01-20 17:00:00,0.03,0.0,-0.0,0.0,1.93,95.6 +2020-01-20 18:00:00,-0.2,0.0,-0.0,0.0,1.52,99.4 +2020-01-20 19:00:00,-1.86,0.0,-0.0,0.0,1.72,99.4 +2020-01-20 20:00:00,-1.73,0.0,-0.0,0.0,1.72,99.4 +2020-01-20 21:00:00,-1.45,0.0,-0.0,0.0,1.59,99.4 +2020-01-20 22:00:00,-1.29,0.0,-0.0,0.0,1.52,99.4 +2020-01-20 23:00:00,-1.29,0.0,-0.0,0.0,1.38,99.4 +2020-01-21 00:00:00,-1.4,0.0,-0.0,0.0,1.24,99.4 +2020-01-21 01:00:00,-1.53,0.0,-0.0,0.0,1.17,95.6 +2020-01-21 02:00:00,-1.61,0.0,-0.0,0.0,1.1,95.6 +2020-01-21 03:00:00,-1.68,0.0,-0.0,0.0,1.03,99.4 +2020-01-21 04:00:00,-1.43,0.0,-0.0,0.0,0.83,99.4 +2020-01-21 05:00:00,-1.6,0.0,-0.0,0.0,0.83,95.6 +2020-01-21 06:00:00,-1.12,0.0,-0.0,0.0,0.62,95.6 +2020-01-21 07:00:00,0.08,0.0,0.0,0.0,0.55,95.6 +2020-01-21 08:00:00,0.36,72.0,97.48,58.0,0.55,95.65 +2020-01-21 09:00:00,0.43,63.0,0.0,63.0,0.97,99.4 +2020-01-21 10:00:00,1.21,132.0,26.45,124.0,1.17,99.35 +2020-01-21 11:00:00,1.36,102.0,0.0,102.0,1.31,95.65 +2020-01-21 12:00:00,1.66,81.0,0.0,81.0,1.38,99.4 +2020-01-21 13:00:00,1.54,77.0,0.0,77.0,1.45,99.4 +2020-01-21 14:00:00,1.4,40.0,0.0,40.0,1.38,99.4 +2020-01-21 15:00:00,1.14,9.0,0.0,9.0,1.38,99.35 +2020-01-21 16:00:00,0.98,0.0,-0.0,0.0,1.59,99.35 +2020-01-21 17:00:00,0.88,0.0,-0.0,0.0,1.66,99.35 +2020-01-21 18:00:00,0.82,0.0,-0.0,0.0,1.86,100.0 +2020-01-21 19:00:00,0.93,0.0,-0.0,0.0,1.79,95.65 +2020-01-21 20:00:00,0.81,0.0,-0.0,0.0,1.79,99.4 +2020-01-21 21:00:00,0.76,0.0,-0.0,0.0,1.86,99.4 +2020-01-21 22:00:00,0.66,0.0,-0.0,0.0,1.93,95.65 +2020-01-21 23:00:00,0.41,0.0,-0.0,0.0,2.07,95.65 +2020-01-22 00:00:00,0.33,0.0,-0.0,0.0,2.07,99.4 +2020-01-22 01:00:00,-0.07,0.0,-0.0,0.0,2.21,95.6 +2020-01-22 02:00:00,-0.09,0.0,-0.0,0.0,2.28,95.6 +2020-01-22 03:00:00,-0.23,0.0,-0.0,0.0,2.21,95.6 +2020-01-22 04:00:00,-0.26,0.0,-0.0,0.0,2.14,95.6 +2020-01-22 05:00:00,-0.07,0.0,-0.0,0.0,2.14,92.0 +2020-01-22 06:00:00,-0.34,0.0,-0.0,0.0,2.0,95.6 +2020-01-22 07:00:00,-0.89,0.0,0.0,0.0,1.59,95.6 +2020-01-22 08:00:00,-1.32,113.0,470.81,44.0,1.45,95.6 +2020-01-22 09:00:00,-0.54,170.0,254.64,108.0,1.31,91.95 +2020-01-22 10:00:00,1.46,303.0,794.21,60.0,1.86,85.25 +2020-01-22 11:00:00,2.75,329.0,806.7,63.0,2.0,79.0 +2020-01-22 12:00:00,3.42,303.0,747.15,69.0,2.28,76.15 +2020-01-22 13:00:00,2.83,242.0,722.49,56.0,2.55,82.15 +2020-01-22 14:00:00,2.37,123.0,384.85,59.0,2.48,82.15 +2020-01-22 15:00:00,1.29,22.0,239.32,11.0,2.76,88.55 +2020-01-22 16:00:00,0.17,0.0,-0.0,0.0,3.1,92.0 +2020-01-22 17:00:00,-0.15,0.0,-0.0,0.0,3.24,88.45 +2020-01-22 18:00:00,-0.28,0.0,-0.0,0.0,3.45,91.95 +2020-01-22 19:00:00,0.27,0.0,-0.0,0.0,3.45,88.45 +2020-01-22 20:00:00,0.37,0.0,-0.0,0.0,3.72,85.15 +2020-01-22 21:00:00,0.06,0.0,-0.0,0.0,3.86,88.45 +2020-01-22 22:00:00,-0.23,0.0,-0.0,0.0,4.0,88.45 +2020-01-22 23:00:00,-0.35,0.0,-0.0,0.0,4.0,88.45 +2020-01-23 00:00:00,-0.38,0.0,-0.0,0.0,4.0,88.45 +2020-01-23 01:00:00,-1.02,0.0,-0.0,0.0,4.07,88.4 +2020-01-23 02:00:00,-0.88,0.0,-0.0,0.0,4.07,91.95 +2020-01-23 03:00:00,-0.94,0.0,-0.0,0.0,4.0,91.95 +2020-01-23 04:00:00,-1.05,0.0,-0.0,0.0,4.0,91.95 +2020-01-23 05:00:00,-1.35,0.0,-0.0,0.0,4.21,91.9 +2020-01-23 06:00:00,-1.49,0.0,-0.0,0.0,4.41,91.9 +2020-01-23 07:00:00,-1.66,0.0,0.0,0.0,4.69,88.35 +2020-01-23 08:00:00,-1.4,126.0,601.62,36.0,4.69,91.9 +2020-01-23 09:00:00,-0.92,225.0,668.53,60.0,5.24,88.4 +2020-01-23 10:00:00,-0.24,174.0,93.68,145.0,5.66,88.45 +2020-01-23 11:00:00,0.52,179.0,71.96,155.0,5.72,85.15 +2020-01-23 12:00:00,1.15,162.0,59.91,143.0,5.93,85.2 +2020-01-23 13:00:00,1.7,55.0,0.0,55.0,5.86,85.25 +2020-01-23 14:00:00,1.95,52.0,5.87,51.0,6.07,82.05 +2020-01-23 15:00:00,2.07,14.0,20.01,13.0,5.79,82.05 +2020-01-23 16:00:00,1.97,0.0,-0.0,0.0,5.72,85.3 +2020-01-23 17:00:00,2.06,0.0,-0.0,0.0,5.93,88.65 +2020-01-23 18:00:00,2.3,0.0,-0.0,0.0,5.79,88.65 +2020-01-23 19:00:00,1.91,0.0,-0.0,0.0,5.24,85.3 +2020-01-23 20:00:00,2.08,0.0,-0.0,0.0,4.41,85.3 +2020-01-23 21:00:00,2.54,0.0,-0.0,0.0,3.79,85.35 +2020-01-23 22:00:00,3.39,0.0,-0.0,0.0,3.93,82.25 +2020-01-23 23:00:00,3.71,0.0,-0.0,0.0,4.48,82.25 +2020-01-24 00:00:00,3.46,0.0,-0.0,0.0,4.69,79.15 +2020-01-24 01:00:00,3.74,0.0,-0.0,0.0,4.76,82.25 +2020-01-24 02:00:00,3.44,0.0,-0.0,0.0,4.9,82.25 +2020-01-24 03:00:00,3.23,0.0,-0.0,0.0,4.97,85.4 +2020-01-24 04:00:00,3.08,0.0,-0.0,0.0,4.97,85.4 +2020-01-24 05:00:00,2.84,0.0,-0.0,0.0,4.9,85.35 +2020-01-24 06:00:00,2.53,0.0,-0.0,0.0,4.76,82.15 +2020-01-24 07:00:00,2.48,6.0,0.0,6.0,3.79,85.35 +2020-01-24 08:00:00,2.52,59.0,26.19,55.0,3.45,85.35 +2020-01-24 09:00:00,2.84,75.0,0.0,75.0,3.31,85.35 +2020-01-24 10:00:00,3.35,137.0,25.54,129.0,3.72,82.2 +2020-01-24 11:00:00,3.53,327.0,752.74,73.0,3.38,76.15 +2020-01-24 12:00:00,3.94,312.0,769.0,65.0,3.45,70.6 +2020-01-24 13:00:00,3.89,214.0,440.47,97.0,2.83,67.9 +2020-01-24 14:00:00,3.73,143.0,555.84,46.0,2.0,70.5 +2020-01-24 15:00:00,3.11,22.0,111.0,16.0,1.45,76.1 +2020-01-24 16:00:00,2.12,0.0,-0.0,0.0,1.52,78.95 +2020-01-24 17:00:00,1.09,0.0,-0.0,0.0,1.45,85.2 +2020-01-24 18:00:00,0.78,0.0,-0.0,0.0,1.24,88.5 +2020-01-24 19:00:00,0.59,0.0,-0.0,0.0,1.31,85.15 +2020-01-24 20:00:00,-0.27,0.0,-0.0,0.0,1.45,88.45 +2020-01-24 21:00:00,-0.85,0.0,-0.0,0.0,1.45,88.4 +2020-01-24 22:00:00,-1.28,0.0,-0.0,0.0,1.52,88.35 +2020-01-24 23:00:00,-1.34,0.0,-0.0,0.0,1.59,88.35 +2020-01-25 00:00:00,-1.58,0.0,-0.0,0.0,1.72,88.35 +2020-01-25 01:00:00,-1.69,0.0,-0.0,0.0,1.79,91.85 +2020-01-25 02:00:00,-1.39,0.0,-0.0,0.0,1.72,88.35 +2020-01-25 03:00:00,-1.05,0.0,-0.0,0.0,1.66,91.95 +2020-01-25 04:00:00,-0.42,0.0,-0.0,0.0,1.52,91.95 +2020-01-25 05:00:00,0.0,0.0,-0.0,0.0,1.38,95.6 +2020-01-25 06:00:00,0.19,0.0,-0.0,0.0,1.24,95.6 +2020-01-25 07:00:00,0.25,4.0,0.0,4.0,1.31,95.6 +2020-01-25 08:00:00,0.95,45.0,0.0,45.0,1.1,95.65 +2020-01-25 09:00:00,1.79,149.0,126.1,117.0,1.45,95.65 +2020-01-25 10:00:00,2.33,170.0,72.55,147.0,1.45,92.1 +2020-01-25 11:00:00,2.39,189.0,82.01,161.0,1.1,88.65 +2020-01-25 12:00:00,2.84,183.0,92.21,153.0,0.76,88.65 +2020-01-25 13:00:00,2.99,165.0,148.24,125.0,0.76,85.4 +2020-01-25 14:00:00,2.97,118.0,251.79,73.0,0.97,85.4 +2020-01-25 15:00:00,2.41,34.0,343.68,14.0,1.31,88.65 +2020-01-25 16:00:00,1.31,0.0,-0.0,0.0,1.59,95.65 +2020-01-25 17:00:00,0.59,0.0,-0.0,0.0,1.59,95.65 +2020-01-25 18:00:00,-0.24,0.0,-0.0,0.0,1.66,99.4 +2020-01-25 19:00:00,-0.68,0.0,-0.0,0.0,2.0,99.4 +2020-01-25 20:00:00,-1.2,0.0,-0.0,0.0,2.0,99.4 +2020-01-25 21:00:00,-1.72,0.0,-0.0,0.0,2.0,95.55 +2020-01-25 22:00:00,-2.03,0.0,-0.0,0.0,1.93,95.55 +2020-01-25 23:00:00,-2.11,0.0,-0.0,0.0,1.86,91.85 +2020-01-26 00:00:00,-2.03,0.0,-0.0,0.0,1.79,91.85 +2020-01-26 01:00:00,-2.36,0.0,-0.0,0.0,1.79,95.55 +2020-01-26 02:00:00,-2.57,0.0,-0.0,0.0,1.86,95.55 +2020-01-26 03:00:00,-2.4,0.0,-0.0,0.0,1.79,95.55 +2020-01-26 04:00:00,-2.41,0.0,-0.0,0.0,1.86,95.55 +2020-01-26 05:00:00,-2.36,0.0,-0.0,0.0,1.93,95.55 +2020-01-26 06:00:00,-2.6,0.0,-0.0,0.0,1.93,95.55 +2020-01-26 07:00:00,-2.9,7.0,0.0,7.0,1.93,91.85 +2020-01-26 08:00:00,-2.05,124.0,464.32,50.0,1.66,91.85 +2020-01-26 09:00:00,0.46,195.0,345.75,106.0,1.59,85.15 +2020-01-26 10:00:00,1.94,317.0,788.42,64.0,2.0,78.95 +2020-01-26 11:00:00,2.79,335.0,746.75,77.0,2.14,76.0 +2020-01-26 12:00:00,3.14,309.0,676.7,86.0,2.28,76.1 +2020-01-26 13:00:00,3.29,255.0,700.44,63.0,2.34,76.1 +2020-01-26 14:00:00,2.96,127.0,306.02,71.0,1.93,79.1 +2020-01-26 15:00:00,2.17,30.0,176.3,19.0,1.93,85.3 +2020-01-26 16:00:00,1.17,0.0,-0.0,0.0,1.93,92.05 +2020-01-26 17:00:00,0.47,0.0,-0.0,0.0,1.79,92.0 +2020-01-26 18:00:00,-0.36,0.0,-0.0,0.0,1.79,95.6 +2020-01-26 19:00:00,-0.86,0.0,-0.0,0.0,1.45,95.6 +2020-01-26 20:00:00,-1.47,0.0,-0.0,0.0,1.45,95.6 +2020-01-26 21:00:00,-1.5,0.0,-0.0,0.0,1.38,95.6 +2020-01-26 22:00:00,-1.58,0.0,-0.0,0.0,1.31,95.6 +2020-01-26 23:00:00,-1.26,0.0,-0.0,0.0,1.17,95.6 +2020-01-27 00:00:00,-1.07,0.0,-0.0,0.0,1.03,91.95 +2020-01-27 01:00:00,-1.0,0.0,-0.0,0.0,0.97,91.95 +2020-01-27 02:00:00,-0.94,0.0,-0.0,0.0,0.9,91.95 +2020-01-27 03:00:00,-0.92,0.0,-0.0,0.0,0.83,95.6 +2020-01-27 04:00:00,-1.02,0.0,-0.0,0.0,0.9,95.6 +2020-01-27 05:00:00,-0.93,0.0,-0.0,0.0,0.9,95.6 +2020-01-27 06:00:00,-0.89,0.0,-0.0,0.0,0.97,95.6 +2020-01-27 07:00:00,-1.24,8.0,0.0,8.0,0.69,95.6 +2020-01-27 08:00:00,-1.05,88.0,116.68,69.0,0.69,91.95 +2020-01-27 09:00:00,-0.01,61.0,0.0,61.0,0.9,88.45 +2020-01-27 10:00:00,0.82,134.0,15.39,129.0,1.1,88.5 +2020-01-27 11:00:00,1.34,170.0,42.9,155.0,1.59,88.55 +2020-01-27 12:00:00,1.54,110.0,3.0,109.0,2.14,85.25 +2020-01-27 13:00:00,1.37,77.0,0.0,77.0,2.28,85.25 +2020-01-27 14:00:00,1.27,49.0,0.0,49.0,2.21,92.05 +2020-01-27 15:00:00,0.89,15.0,0.0,15.0,1.93,92.05 +2020-01-27 16:00:00,0.35,0.0,-0.0,0.0,1.72,95.65 +2020-01-27 17:00:00,0.0,0.0,-0.0,0.0,2.07,95.6 +2020-01-27 18:00:00,-0.3,0.0,-0.0,0.0,2.28,99.4 +2020-01-27 19:00:00,-0.78,0.0,-0.0,0.0,2.62,99.4 +2020-01-27 20:00:00,-1.13,0.0,-0.0,0.0,2.76,91.95 +2020-01-27 21:00:00,-1.35,0.0,-0.0,0.0,2.41,95.6 +2020-01-27 22:00:00,-1.49,0.0,-0.0,0.0,2.0,91.9 +2020-01-27 23:00:00,-1.56,0.0,-0.0,0.0,2.07,91.9 +2020-01-28 00:00:00,-1.68,0.0,-0.0,0.0,2.14,95.55 +2020-01-28 01:00:00,-1.87,0.0,-0.0,0.0,2.14,91.85 +2020-01-28 02:00:00,-1.92,0.0,-0.0,0.0,1.93,91.85 +2020-01-28 03:00:00,-1.91,0.0,-0.0,0.0,1.86,91.85 +2020-01-28 04:00:00,-1.93,0.0,-0.0,0.0,1.86,91.85 +2020-01-28 05:00:00,-1.89,0.0,-0.0,0.0,1.79,91.85 +2020-01-28 06:00:00,-1.9,0.0,-0.0,0.0,1.66,91.85 +2020-01-28 07:00:00,-1.71,10.0,0.0,10.0,1.79,95.55 +2020-01-28 08:00:00,-1.46,66.0,24.04,62.0,1.86,91.9 +2020-01-28 09:00:00,-1.11,151.0,105.67,123.0,2.14,88.4 +2020-01-28 10:00:00,-0.55,274.0,434.74,131.0,2.34,88.45 +2020-01-28 11:00:00,0.07,69.0,0.0,69.0,2.48,85.1 +2020-01-28 12:00:00,0.49,126.0,5.91,124.0,2.48,78.75 +2020-01-28 13:00:00,0.72,176.0,152.0,133.0,2.41,78.75 +2020-01-28 14:00:00,0.72,109.0,130.39,84.0,2.28,75.7 +2020-01-28 15:00:00,0.52,42.0,310.04,20.0,2.14,75.7 +2020-01-28 16:00:00,-0.49,0.0,-0.0,0.0,1.86,81.75 +2020-01-28 17:00:00,-1.5,0.0,-0.0,0.0,1.79,88.35 +2020-01-28 18:00:00,-2.45,0.0,-0.0,0.0,1.45,95.55 +2020-01-28 19:00:00,-3.38,0.0,-0.0,0.0,1.86,95.55 +2020-01-28 20:00:00,-3.29,0.0,-0.0,0.0,2.0,99.4 +2020-01-28 21:00:00,-2.96,0.0,-0.0,0.0,2.21,95.55 +2020-01-28 22:00:00,-2.65,0.0,-0.0,0.0,2.48,95.55 +2020-01-28 23:00:00,-2.52,0.0,-0.0,0.0,2.41,95.55 +2020-01-29 00:00:00,-2.61,0.0,-0.0,0.0,2.48,95.55 +2020-01-29 01:00:00,-2.8,0.0,-0.0,0.0,2.21,91.85 +2020-01-29 02:00:00,-2.73,0.0,-0.0,0.0,1.86,95.55 +2020-01-29 03:00:00,-2.56,0.0,-0.0,0.0,1.79,95.55 +2020-01-29 04:00:00,-2.43,0.0,-0.0,0.0,1.93,95.55 +2020-01-29 05:00:00,-2.28,0.0,-0.0,0.0,2.07,95.55 +2020-01-29 06:00:00,-2.17,0.0,-0.0,0.0,2.0,95.55 +2020-01-29 07:00:00,-2.07,5.0,0.0,5.0,2.48,95.55 +2020-01-29 08:00:00,-1.84,69.0,29.4,64.0,3.31,91.85 +2020-01-29 09:00:00,-1.65,59.0,0.0,59.0,3.66,84.95 +2020-01-29 10:00:00,-1.24,61.0,0.0,61.0,3.17,84.95 +2020-01-29 11:00:00,-0.58,65.0,0.0,65.0,2.97,78.6 +2020-01-29 12:00:00,-0.33,56.0,0.0,56.0,2.76,78.6 +2020-01-29 13:00:00,-0.26,48.0,0.0,48.0,2.62,78.6 +2020-01-29 14:00:00,-0.33,33.0,0.0,33.0,2.55,81.75 +2020-01-29 15:00:00,-0.71,16.0,0.0,16.0,2.14,85.0 +2020-01-29 16:00:00,-1.22,0.0,-0.0,0.0,2.0,88.35 +2020-01-29 17:00:00,-1.5,0.0,-0.0,0.0,2.07,88.35 +2020-01-29 18:00:00,-1.69,0.0,-0.0,0.0,2.07,91.85 +2020-01-29 19:00:00,-2.23,0.0,-0.0,0.0,2.83,91.85 +2020-01-29 20:00:00,-2.27,0.0,-0.0,0.0,2.55,91.85 +2020-01-29 21:00:00,-2.37,0.0,-0.0,0.0,2.28,91.85 +2020-01-29 22:00:00,-2.53,0.0,-0.0,0.0,2.21,91.85 +2020-01-29 23:00:00,-2.62,0.0,-0.0,0.0,1.86,88.25 +2020-01-30 00:00:00,-2.69,0.0,-0.0,0.0,2.21,88.25 +2020-01-30 01:00:00,-2.84,0.0,-0.0,0.0,2.14,88.25 +2020-01-30 02:00:00,-3.02,0.0,-0.0,0.0,1.86,88.25 +2020-01-30 03:00:00,-3.01,0.0,-0.0,0.0,2.0,84.85 +2020-01-30 04:00:00,-3.12,0.0,-0.0,0.0,2.14,88.25 +2020-01-30 05:00:00,-3.3,0.0,-0.0,0.0,2.28,88.25 +2020-01-30 06:00:00,-3.14,0.0,-0.0,0.0,2.41,84.85 +2020-01-30 07:00:00,-3.02,6.0,0.0,6.0,2.69,84.85 +2020-01-30 08:00:00,-2.9,71.0,28.76,66.0,2.62,81.5 +2020-01-30 09:00:00,-2.71,44.0,0.0,44.0,2.48,81.5 +2020-01-30 10:00:00,-2.3,77.0,0.0,77.0,2.48,81.5 +2020-01-30 11:00:00,-2.08,88.0,0.0,88.0,2.41,75.3 +2020-01-30 12:00:00,-1.72,199.0,95.06,166.0,2.34,78.35 +2020-01-30 13:00:00,-1.53,56.0,0.0,56.0,2.28,75.35 +2020-01-30 14:00:00,-1.55,44.0,0.0,44.0,2.21,75.35 +2020-01-30 15:00:00,-1.62,20.0,0.0,20.0,2.14,78.45 +2020-01-30 16:00:00,-1.86,0.0,-0.0,0.0,1.93,81.55 +2020-01-30 17:00:00,-2.03,0.0,-0.0,0.0,1.72,84.85 +2020-01-30 18:00:00,-2.28,0.0,-0.0,0.0,1.52,88.25 +2020-01-30 19:00:00,-2.33,0.0,-0.0,0.0,2.28,88.25 +2020-01-30 20:00:00,-2.32,0.0,-0.0,0.0,2.0,91.85 +2020-01-30 21:00:00,-2.24,0.0,-0.0,0.0,1.24,95.55 +2020-01-30 22:00:00,-2.16,0.0,-0.0,0.0,0.97,91.85 +2020-01-30 23:00:00,-2.04,0.0,-0.0,0.0,1.03,91.85 +2020-01-31 00:00:00,-1.91,0.0,-0.0,0.0,1.38,95.55 +2020-01-31 01:00:00,-1.13,0.0,-0.0,0.0,1.59,95.6 +2020-01-31 02:00:00,-0.91,0.0,-0.0,0.0,1.79,95.6 +2020-01-31 03:00:00,-0.79,0.0,-0.0,0.0,1.93,95.6 +2020-01-31 04:00:00,-0.72,0.0,-0.0,0.0,1.93,99.4 +2020-01-31 05:00:00,-0.7,0.0,-0.0,0.0,2.28,95.6 +2020-01-31 06:00:00,-0.74,0.0,-0.0,0.0,2.41,95.6 +2020-01-31 07:00:00,-0.7,9.0,0.0,9.0,2.0,91.95 +2020-01-31 08:00:00,-0.53,83.0,56.26,73.0,3.03,85.05 +2020-01-31 09:00:00,-0.54,87.0,0.0,87.0,3.45,88.45 +2020-01-31 10:00:00,-0.16,64.0,0.0,64.0,3.52,81.75 +2020-01-31 11:00:00,-0.03,72.0,0.0,72.0,3.52,78.65 +2020-01-31 12:00:00,0.13,68.0,0.0,68.0,3.31,78.65 +2020-01-31 13:00:00,0.28,153.0,64.07,134.0,3.31,78.65 +2020-01-31 14:00:00,0.24,53.0,0.0,53.0,3.31,78.65 +2020-01-31 15:00:00,-0.3,29.0,23.75,27.0,2.55,81.75 +2020-01-31 16:00:00,-3.77,0.0,-0.0,0.0,2.26,87.1 +2020-01-31 17:00:00,-3.11,0.0,-0.0,0.0,2.32,87.42 +2020-01-31 18:00:00,-2.46,0.0,-0.0,0.0,2.39,87.74 +2020-01-31 19:00:00,-1.8,0.0,-0.0,0.0,2.46,88.05 +2020-01-31 20:00:00,-1.14,0.0,-0.0,0.0,2.52,88.37 +2020-01-31 21:00:00,-0.49,0.0,-0.0,0.0,2.59,88.68 +2020-01-31 22:00:00,0.17,0.0,-0.0,0.0,2.66,89.0 +2020-01-31 23:00:00,0.82,0.0,-0.0,0.0,2.73,89.32 +2020-02-01 00:00:00,1.48,0.0,-0.0,0.0,2.79,89.63 +2020-02-01 01:00:00,2.13,0.0,-0.0,0.0,2.86,89.95 +2020-02-01 02:00:00,2.79,0.0,-0.0,0.0,2.93,90.27 +2020-02-01 03:00:00,3.44,0.0,-0.0,0.0,2.99,90.58 +2020-02-01 04:00:00,4.1,0.0,-0.0,0.0,3.06,90.9 +2020-02-01 05:00:00,4.76,0.0,-0.0,0.0,3.13,91.21 +2020-02-01 06:00:00,5.41,0.0,-0.0,0.0,3.19,91.53 +2020-02-01 07:00:00,6.07,3.0,0.0,3.0,3.26,91.85 +2020-02-01 08:00:00,3.68,111.0,192.62,76.0,1.66,95.7 +2020-02-01 09:00:00,3.96,120.0,24.89,113.0,2.14,92.2 +2020-02-01 10:00:00,4.43,151.0,23.12,143.0,2.34,85.55 +2020-02-01 11:00:00,4.72,177.0,40.36,162.0,2.34,79.3 +2020-02-01 12:00:00,5.17,161.0,30.87,150.0,2.41,68.1 +2020-02-01 13:00:00,5.37,97.0,3.32,96.0,2.28,65.55 +2020-02-01 14:00:00,5.39,63.0,4.76,62.0,2.0,63.05 +2020-02-01 15:00:00,5.11,31.0,33.82,28.0,1.66,63.05 +2020-02-01 16:00:00,4.29,0.0,-0.0,0.0,1.86,67.9 +2020-02-01 17:00:00,3.41,0.0,-0.0,0.0,2.14,70.5 +2020-02-01 18:00:00,2.96,0.0,-0.0,0.0,2.34,73.2 +2020-02-01 19:00:00,2.13,0.0,-0.0,0.0,2.28,78.95 +2020-02-01 20:00:00,1.48,0.0,-0.0,0.0,2.48,82.0 +2020-02-01 21:00:00,0.97,0.0,-0.0,0.0,2.76,81.95 +2020-02-01 22:00:00,0.54,0.0,-0.0,0.0,2.83,81.9 +2020-02-01 23:00:00,0.23,0.0,-0.0,0.0,2.83,81.8 +2020-02-02 00:00:00,0.02,0.0,-0.0,0.0,2.9,81.8 +2020-02-02 01:00:00,0.08,0.0,-0.0,0.0,2.9,81.8 +2020-02-02 02:00:00,0.07,0.0,-0.0,0.0,2.83,85.1 +2020-02-02 03:00:00,-0.08,0.0,-0.0,0.0,2.69,85.1 +2020-02-02 04:00:00,-0.35,0.0,-0.0,0.0,2.55,88.45 +2020-02-02 05:00:00,-0.73,0.0,-0.0,0.0,2.41,88.4 +2020-02-02 06:00:00,-1.12,0.0,-0.0,0.0,2.41,88.4 +2020-02-02 07:00:00,-1.26,31.0,241.46,17.0,2.55,88.35 +2020-02-02 08:00:00,0.05,152.0,532.92,53.0,2.62,85.1 +2020-02-02 09:00:00,1.92,259.0,637.34,77.0,3.17,78.95 +2020-02-02 10:00:00,3.24,330.0,676.07,93.0,3.59,70.4 +2020-02-02 11:00:00,4.3,357.0,677.62,102.0,3.72,60.4 +2020-02-02 12:00:00,4.89,337.0,648.02,103.0,3.93,55.95 +2020-02-02 13:00:00,5.5,151.0,52.3,135.0,4.48,46.1 +2020-02-02 14:00:00,5.45,42.0,0.0,42.0,3.86,48.0 +2020-02-02 15:00:00,5.0,10.0,0.0,10.0,3.1,51.8 +2020-02-02 16:00:00,4.41,0.0,-0.0,0.0,2.76,53.8 +2020-02-02 17:00:00,4.06,0.0,-0.0,0.0,2.55,58.05 +2020-02-02 18:00:00,3.52,0.0,-0.0,0.0,2.41,62.7 +2020-02-02 19:00:00,2.19,0.0,-0.0,0.0,2.14,73.0 +2020-02-02 20:00:00,1.33,0.0,-0.0,0.0,2.41,78.8 +2020-02-02 21:00:00,0.82,0.0,-0.0,0.0,2.55,85.15 +2020-02-02 22:00:00,0.71,0.0,-0.0,0.0,2.62,88.5 +2020-02-02 23:00:00,1.03,0.0,-0.0,0.0,2.69,85.2 +2020-02-03 00:00:00,1.38,0.0,-0.0,0.0,2.55,85.25 +2020-02-03 01:00:00,1.34,0.0,-0.0,0.0,2.41,88.55 +2020-02-03 02:00:00,1.28,0.0,-0.0,0.0,2.41,92.05 +2020-02-03 03:00:00,1.32,0.0,-0.0,0.0,2.55,92.05 +2020-02-03 04:00:00,1.3,0.0,-0.0,0.0,2.62,92.05 +2020-02-03 05:00:00,1.33,0.0,-0.0,0.0,2.76,92.05 +2020-02-03 06:00:00,1.37,0.0,-0.0,0.0,2.76,88.6 +2020-02-03 07:00:00,1.48,16.0,16.16,15.0,2.62,92.05 +2020-02-03 08:00:00,1.39,84.0,42.12,76.0,2.28,92.05 +2020-02-03 09:00:00,1.68,144.0,55.18,128.0,2.76,95.65 +2020-02-03 10:00:00,1.91,252.0,242.17,166.0,3.1,88.65 +2020-02-03 11:00:00,2.58,57.0,0.0,57.0,3.59,76.0 +2020-02-03 12:00:00,3.08,127.0,2.73,126.0,3.31,70.4 +2020-02-03 13:00:00,3.19,72.0,0.0,72.0,3.03,70.4 +2020-02-03 14:00:00,2.87,41.0,0.0,41.0,2.9,76.0 +2020-02-03 15:00:00,1.99,18.0,0.0,18.0,2.21,82.05 +2020-02-03 16:00:00,1.05,0.0,-0.0,0.0,1.79,88.55 +2020-02-03 17:00:00,0.06,0.0,-0.0,0.0,2.0,92.0 +2020-02-03 18:00:00,-0.56,0.0,-0.0,0.0,1.79,91.95 +2020-02-03 19:00:00,-1.51,0.0,-0.0,0.0,1.59,95.6 +2020-02-03 20:00:00,-2.04,0.0,-0.0,0.0,1.38,95.55 +2020-02-03 21:00:00,-2.11,0.0,-0.0,0.0,1.31,95.55 +2020-02-03 22:00:00,-2.18,0.0,-0.0,0.0,1.24,95.55 +2020-02-03 23:00:00,-1.7,0.0,-0.0,0.0,1.03,99.4 +2020-02-04 00:00:00,-1.09,0.0,-0.0,0.0,0.83,91.95 +2020-02-04 01:00:00,-0.93,0.0,-0.0,0.0,0.69,91.95 +2020-02-04 02:00:00,-0.63,0.0,-0.0,0.0,0.55,88.45 +2020-02-04 03:00:00,-0.76,0.0,-0.0,0.0,0.62,91.95 +2020-02-04 04:00:00,-1.41,0.0,-0.0,0.0,0.9,91.9 +2020-02-04 05:00:00,-1.38,0.0,-0.0,0.0,0.9,95.6 +2020-02-04 06:00:00,-1.19,0.0,-0.0,0.0,0.9,95.6 +2020-02-04 07:00:00,-0.89,20.0,15.18,19.0,1.79,99.4 +2020-02-04 08:00:00,-0.42,39.0,0.0,39.0,2.07,91.95 +2020-02-04 09:00:00,0.0,68.0,0.0,68.0,2.28,88.45 +2020-02-04 10:00:00,0.56,58.0,0.0,58.0,2.21,85.15 +2020-02-04 11:00:00,0.88,87.0,0.0,87.0,2.21,81.95 +2020-02-04 12:00:00,1.32,48.0,0.0,48.0,2.07,78.8 +2020-02-04 13:00:00,1.78,56.0,0.0,56.0,2.0,72.95 +2020-02-04 14:00:00,1.66,40.0,0.0,40.0,2.34,70.1 +2020-02-04 15:00:00,0.97,30.0,9.76,29.0,2.21,70.05 +2020-02-04 16:00:00,0.19,0.0,-0.0,0.0,2.28,75.6 +2020-02-04 17:00:00,-0.64,0.0,-0.0,0.0,2.0,75.55 +2020-02-04 18:00:00,-1.2,0.0,-0.0,0.0,1.66,81.65 +2020-02-04 19:00:00,-1.66,0.0,-0.0,0.0,1.52,88.3 +2020-02-04 20:00:00,-1.88,0.0,-0.0,0.0,1.24,88.3 +2020-02-04 21:00:00,-1.76,0.0,-0.0,0.0,1.24,88.3 +2020-02-04 22:00:00,-1.79,0.0,-0.0,0.0,1.24,88.3 +2020-02-04 23:00:00,-1.49,0.0,-0.0,0.0,1.31,88.35 +2020-02-05 00:00:00,-1.4,0.0,-0.0,0.0,1.31,88.35 +2020-02-05 01:00:00,-1.25,0.0,-0.0,0.0,1.31,91.9 +2020-02-05 02:00:00,-1.09,0.0,-0.0,0.0,1.86,91.95 +2020-02-05 03:00:00,-1.35,0.0,-0.0,0.0,1.52,99.4 +2020-02-05 04:00:00,-1.45,0.0,-0.0,0.0,1.72,99.4 +2020-02-05 05:00:00,-1.79,0.0,-0.0,0.0,1.86,99.4 +2020-02-05 06:00:00,-2.21,0.0,-0.0,0.0,2.07,95.55 +2020-02-05 07:00:00,-2.57,22.0,28.58,20.0,1.52,91.85 +2020-02-05 08:00:00,-2.19,149.0,372.76,75.0,2.41,91.85 +2020-02-05 09:00:00,-1.26,212.0,250.86,137.0,2.55,81.65 +2020-02-05 10:00:00,-0.23,278.0,310.02,165.0,2.83,69.75 +2020-02-05 11:00:00,0.22,117.0,0.0,117.0,2.9,67.1 +2020-02-05 12:00:00,0.4,184.0,42.6,168.0,2.83,64.55 +2020-02-05 13:00:00,0.49,135.0,18.72,129.0,2.76,62.0 +2020-02-05 14:00:00,0.08,142.0,174.77,102.0,2.55,67.1 +2020-02-05 15:00:00,-0.36,35.0,18.66,33.0,2.34,69.75 +2020-02-05 16:00:00,-1.22,0.0,-0.0,0.0,2.07,75.35 +2020-02-05 17:00:00,-2.21,0.0,-0.0,0.0,2.07,78.35 +2020-02-05 18:00:00,-2.88,0.0,-0.0,0.0,2.14,78.3 +2020-02-05 19:00:00,-3.18,0.0,-0.0,0.0,2.07,81.45 +2020-02-05 20:00:00,-3.19,0.0,-0.0,0.0,2.28,72.1 +2020-02-05 21:00:00,-3.48,0.0,-0.0,0.0,2.21,69.25 +2020-02-05 22:00:00,-3.72,0.0,-0.0,0.0,2.07,69.15 +2020-02-05 23:00:00,-4.0,0.0,-0.0,0.0,2.0,66.35 +2020-02-06 00:00:00,-4.24,0.0,-0.0,0.0,1.79,69.05 +2020-02-06 01:00:00,-4.95,0.0,-0.0,0.0,1.66,68.95 +2020-02-06 02:00:00,-5.4,0.0,-0.0,0.0,1.59,71.75 +2020-02-06 03:00:00,-6.05,0.0,-0.0,0.0,1.52,74.7 +2020-02-06 04:00:00,-6.44,0.0,-0.0,0.0,1.45,77.75 +2020-02-06 05:00:00,-6.64,0.0,-0.0,0.0,1.52,77.75 +2020-02-06 06:00:00,-6.86,0.0,-0.0,0.0,1.59,81.0 +2020-02-06 07:00:00,-5.73,38.0,175.24,25.0,1.59,74.7 +2020-02-06 08:00:00,-4.23,161.0,443.47,71.0,1.66,69.05 +2020-02-06 09:00:00,-3.1,269.0,563.24,98.0,2.28,61.35 +2020-02-06 10:00:00,-1.92,342.0,617.4,114.0,2.83,58.95 +2020-02-06 11:00:00,-1.07,378.0,669.97,113.0,2.9,59.2 +2020-02-06 12:00:00,-0.34,330.0,486.13,145.0,2.9,56.95 +2020-02-06 13:00:00,0.11,290.0,565.52,106.0,2.83,57.1 +2020-02-06 14:00:00,0.24,194.0,496.31,78.0,2.76,57.1 +2020-02-06 15:00:00,-0.27,75.0,348.58,36.0,2.21,59.35 +2020-02-06 16:00:00,-1.44,0.0,-0.0,0.0,2.07,64.1 +2020-02-06 17:00:00,-2.36,0.0,-0.0,0.0,2.07,66.65 +2020-02-06 18:00:00,-2.9,0.0,-0.0,0.0,2.14,69.35 +2020-02-06 19:00:00,-3.14,0.0,-0.0,0.0,2.55,69.35 +2020-02-06 20:00:00,-3.12,0.0,-0.0,0.0,2.76,69.35 +2020-02-06 21:00:00,-3.16,0.0,-0.0,0.0,2.83,69.35 +2020-02-06 22:00:00,-3.08,0.0,-0.0,0.0,2.76,69.35 +2020-02-06 23:00:00,-3.2,0.0,-0.0,0.0,2.55,75.1 +2020-02-07 00:00:00,-3.31,0.0,-0.0,0.0,2.14,78.2 +2020-02-07 01:00:00,-3.52,0.0,-0.0,0.0,2.14,78.2 +2020-02-07 02:00:00,-3.14,0.0,-0.0,0.0,2.14,78.3 +2020-02-07 03:00:00,-3.1,0.0,-0.0,0.0,2.07,78.3 +2020-02-07 04:00:00,-2.97,0.0,-0.0,0.0,2.0,81.5 +2020-02-07 05:00:00,-2.85,0.0,-0.0,0.0,1.93,84.85 +2020-02-07 06:00:00,-2.55,0.0,-0.0,0.0,1.79,84.85 +2020-02-07 07:00:00,-3.53,36.0,114.7,27.0,1.72,84.8 +2020-02-07 08:00:00,-2.59,133.0,212.09,89.0,1.45,84.85 +2020-02-07 09:00:00,-1.09,237.0,353.54,128.0,1.45,81.7 +2020-02-07 10:00:00,0.05,307.0,416.94,151.0,1.79,72.65 +2020-02-07 11:00:00,1.18,373.0,639.19,117.0,1.31,67.3 +2020-02-07 12:00:00,1.52,346.0,570.6,126.0,1.03,67.4 +2020-02-07 13:00:00,1.59,286.0,526.73,112.0,1.66,70.1 +2020-02-07 14:00:00,1.44,156.0,217.93,104.0,2.0,70.1 +2020-02-07 15:00:00,0.93,48.0,51.45,42.0,2.0,72.85 +2020-02-07 16:00:00,-0.25,0.0,-0.0,0.0,1.93,81.75 +2020-02-07 17:00:00,-1.05,0.0,-0.0,0.0,1.79,85.0 +2020-02-07 18:00:00,-1.56,0.0,-0.0,0.0,1.59,84.95 +2020-02-07 19:00:00,-2.24,0.0,-0.0,0.0,1.45,81.55 +2020-02-07 20:00:00,-2.8,0.0,-0.0,0.0,1.31,81.5 +2020-02-07 21:00:00,-3.41,0.0,-0.0,0.0,1.17,84.8 +2020-02-07 22:00:00,-3.83,0.0,-0.0,0.0,1.1,88.2 +2020-02-07 23:00:00,-3.93,0.0,-0.0,0.0,1.17,88.2 +2020-02-08 00:00:00,-3.56,0.0,-0.0,0.0,1.31,84.8 +2020-02-08 01:00:00,-3.62,0.0,-0.0,0.0,1.38,84.8 +2020-02-08 02:00:00,-3.64,0.0,-0.0,0.0,1.52,84.8 +2020-02-08 03:00:00,-3.5,0.0,-0.0,0.0,1.66,88.25 +2020-02-08 04:00:00,-3.54,0.0,-0.0,0.0,1.79,88.25 +2020-02-08 05:00:00,-3.43,0.0,-0.0,0.0,1.79,88.25 +2020-02-08 06:00:00,-3.36,0.0,-0.0,0.0,1.59,88.25 +2020-02-08 07:00:00,-3.52,5.0,0.0,5.0,1.38,88.25 +2020-02-08 08:00:00,-3.25,81.0,18.86,77.0,1.45,88.25 +2020-02-08 09:00:00,-2.81,106.0,3.19,105.0,1.52,81.5 +2020-02-08 10:00:00,-2.41,108.0,0.0,108.0,1.45,78.35 +2020-02-08 11:00:00,-1.85,88.0,0.0,88.0,1.38,78.35 +2020-02-08 12:00:00,-1.44,78.0,0.0,78.0,1.31,75.35 +2020-02-08 13:00:00,-1.44,98.0,0.0,98.0,1.45,72.4 +2020-02-08 14:00:00,-1.46,68.0,0.0,68.0,1.31,72.4 +2020-02-08 15:00:00,-1.76,28.0,0.0,28.0,1.45,75.3 +2020-02-08 16:00:00,-2.18,0.0,-0.0,0.0,1.38,75.3 +2020-02-08 17:00:00,-2.46,0.0,-0.0,0.0,1.31,78.35 +2020-02-08 18:00:00,-2.68,0.0,-0.0,0.0,1.03,81.5 +2020-02-08 19:00:00,-2.83,0.0,-0.0,0.0,0.76,81.5 +2020-02-08 20:00:00,-2.97,0.0,-0.0,0.0,0.48,81.5 +2020-02-08 21:00:00,-2.97,0.0,-0.0,0.0,0.55,81.5 +2020-02-08 22:00:00,-2.92,0.0,-0.0,0.0,0.55,81.5 +2020-02-08 23:00:00,-3.02,0.0,-0.0,0.0,0.62,81.5 +2020-02-09 00:00:00,-3.1,0.0,-0.0,0.0,0.83,81.5 +2020-02-09 01:00:00,-2.92,0.0,-0.0,0.0,1.1,84.85 +2020-02-09 02:00:00,-2.65,0.0,-0.0,0.0,1.17,78.35 +2020-02-09 03:00:00,-3.21,0.0,-0.0,0.0,1.31,81.45 +2020-02-09 04:00:00,-3.59,0.0,-0.0,0.0,1.45,81.45 +2020-02-09 05:00:00,-3.4,0.0,-0.0,0.0,1.59,81.45 +2020-02-09 06:00:00,-3.11,0.0,-0.0,0.0,1.72,78.3 +2020-02-09 07:00:00,-2.59,36.0,68.72,30.0,1.59,88.3 +2020-02-09 08:00:00,-1.65,162.0,369.12,82.0,1.59,81.65 +2020-02-09 09:00:00,-0.56,294.0,669.89,81.0,1.86,75.55 +2020-02-09 10:00:00,0.84,365.0,689.95,100.0,1.93,69.95 +2020-02-09 11:00:00,2.09,402.0,747.67,95.0,2.07,62.35 +2020-02-09 12:00:00,2.73,386.0,753.07,88.0,1.93,57.7 +2020-02-09 13:00:00,2.77,317.0,672.7,88.0,1.72,57.7 +2020-02-09 14:00:00,2.88,219.0,627.78,63.0,1.59,55.55 +2020-02-09 15:00:00,2.05,87.0,372.41,40.0,1.66,64.9 +2020-02-09 16:00:00,0.66,0.0,-0.0,0.0,2.21,72.75 +2020-02-09 17:00:00,-0.44,0.0,-0.0,0.0,2.28,78.6 +2020-02-09 18:00:00,-0.75,0.0,-0.0,0.0,2.28,81.7 +2020-02-09 19:00:00,-1.7,0.0,-0.0,0.0,2.07,88.3 +2020-02-09 20:00:00,-1.8,0.0,-0.0,0.0,2.0,88.3 +2020-02-09 21:00:00,-1.92,0.0,-0.0,0.0,1.86,88.3 +2020-02-09 22:00:00,-2.1,0.0,-0.0,0.0,1.66,88.3 +2020-02-09 23:00:00,-2.21,0.0,-0.0,0.0,1.52,88.3 +2020-02-10 00:00:00,-2.5,0.0,-0.0,0.0,1.45,88.3 +2020-02-10 01:00:00,-2.53,0.0,-0.0,0.0,1.31,88.3 +2020-02-10 02:00:00,-2.27,0.0,-0.0,0.0,1.17,91.85 +2020-02-10 03:00:00,-2.63,0.0,-0.0,0.0,1.24,88.3 +2020-02-10 04:00:00,-2.43,0.0,-0.0,0.0,1.17,91.85 +2020-02-10 05:00:00,-2.59,0.0,-0.0,0.0,1.24,88.3 +2020-02-10 06:00:00,-2.33,0.0,-0.0,0.0,1.24,91.85 +2020-02-10 07:00:00,-2.23,15.0,0.0,15.0,0.97,91.85 +2020-02-10 08:00:00,-0.81,51.0,0.0,51.0,0.83,88.4 +2020-02-10 09:00:00,0.6,96.0,0.0,96.0,1.03,75.7 +2020-02-10 10:00:00,1.44,135.0,2.57,134.0,1.31,67.4 +2020-02-10 11:00:00,2.25,148.0,4.81,146.0,1.59,62.35 +2020-02-10 12:00:00,2.29,173.0,19.96,165.0,1.59,62.35 +2020-02-10 13:00:00,2.71,101.0,0.0,101.0,1.52,60.05 +2020-02-10 14:00:00,2.71,100.0,19.73,95.0,1.24,60.05 +2020-02-10 15:00:00,2.42,50.0,30.53,46.0,0.48,62.5 +2020-02-10 16:00:00,1.98,0.0,-0.0,0.0,0.34,70.2 +2020-02-10 17:00:00,0.9,0.0,-0.0,0.0,0.9,70.05 +2020-02-10 18:00:00,-0.47,0.0,-0.0,0.0,1.45,78.6 +2020-02-10 19:00:00,-0.75,0.0,-0.0,0.0,1.38,85.0 +2020-02-10 20:00:00,-0.71,0.0,-0.0,0.0,1.52,85.0 +2020-02-10 21:00:00,-0.87,0.0,-0.0,0.0,1.66,85.0 +2020-02-10 22:00:00,-1.25,0.0,-0.0,0.0,1.72,88.35 +2020-02-10 23:00:00,-1.55,0.0,-0.0,0.0,1.79,88.35 +2020-02-11 00:00:00,-2.08,0.0,-0.0,0.0,1.86,88.3 +2020-02-11 01:00:00,-2.06,0.0,-0.0,0.0,1.93,91.85 +2020-02-11 02:00:00,-1.8,0.0,-0.0,0.0,2.0,91.85 +2020-02-11 03:00:00,-1.79,0.0,-0.0,0.0,2.07,91.85 +2020-02-11 04:00:00,-2.02,0.0,-0.0,0.0,2.21,88.3 +2020-02-11 05:00:00,-2.14,0.0,-0.0,0.0,2.28,88.3 +2020-02-11 06:00:00,-1.94,0.0,-0.0,0.0,2.41,88.3 +2020-02-11 07:00:00,-1.06,56.0,248.73,32.0,2.97,85.0 +2020-02-11 08:00:00,0.27,197.0,622.98,56.0,2.83,85.1 +2020-02-11 09:00:00,1.9,250.0,341.57,138.0,3.79,73.0 +2020-02-11 10:00:00,2.74,260.0,177.54,190.0,4.34,65.0 +2020-02-11 11:00:00,3.52,405.0,715.09,104.0,4.83,57.95 +2020-02-11 12:00:00,3.69,186.0,29.55,174.0,4.76,57.95 +2020-02-11 13:00:00,3.65,158.0,31.37,147.0,4.07,62.7 +2020-02-11 14:00:00,3.24,45.0,0.0,45.0,3.86,70.4 +2020-02-11 15:00:00,2.84,45.0,14.72,43.0,3.72,73.1 +2020-02-11 16:00:00,2.51,0.0,-0.0,0.0,3.31,76.0 +2020-02-11 17:00:00,2.32,0.0,-0.0,0.0,3.17,78.95 +2020-02-11 18:00:00,2.18,0.0,-0.0,0.0,3.03,78.95 +2020-02-11 19:00:00,2.02,0.0,-0.0,0.0,2.83,78.95 +2020-02-11 20:00:00,1.79,0.0,-0.0,0.0,2.9,82.0 +2020-02-11 21:00:00,1.79,0.0,-0.0,0.0,2.97,82.0 +2020-02-11 22:00:00,1.61,0.0,-0.0,0.0,2.9,85.25 +2020-02-11 23:00:00,1.53,0.0,-0.0,0.0,2.97,85.25 +2020-02-12 00:00:00,1.29,0.0,-0.0,0.0,3.1,88.55 +2020-02-12 01:00:00,1.35,0.0,-0.0,0.0,3.03,92.05 +2020-02-12 02:00:00,0.92,0.0,-0.0,0.0,2.83,88.55 +2020-02-12 03:00:00,0.49,0.0,-0.0,0.0,2.62,88.5 +2020-02-12 04:00:00,0.01,0.0,-0.0,0.0,2.69,88.45 +2020-02-12 05:00:00,-0.06,0.0,-0.0,0.0,2.76,88.45 +2020-02-12 06:00:00,0.01,0.0,-0.0,0.0,2.9,88.45 +2020-02-12 07:00:00,0.24,62.0,266.78,35.0,2.69,92.0 +2020-02-12 08:00:00,1.3,205.0,640.02,57.0,2.76,88.55 +2020-02-12 09:00:00,2.28,192.0,108.12,156.0,4.07,78.95 +2020-02-12 10:00:00,2.55,233.0,102.64,192.0,3.79,79.0 +2020-02-12 11:00:00,3.06,272.0,147.83,209.0,3.72,76.1 +2020-02-12 12:00:00,3.51,134.0,2.43,133.0,3.59,70.5 +2020-02-12 13:00:00,3.6,122.0,2.81,121.0,3.66,67.8 +2020-02-12 14:00:00,3.13,52.0,0.0,52.0,3.72,65.1 +2020-02-12 15:00:00,2.83,12.0,0.0,12.0,2.9,67.6 +2020-02-12 16:00:00,1.8,0.0,-0.0,0.0,2.07,70.1 +2020-02-12 17:00:00,0.43,0.0,-0.0,0.0,2.07,72.75 +2020-02-12 18:00:00,-0.57,0.0,-0.0,0.0,2.28,75.55 +2020-02-12 19:00:00,-0.84,0.0,-0.0,0.0,2.69,85.0 +2020-02-12 20:00:00,-1.02,0.0,-0.0,0.0,2.48,85.0 +2020-02-12 21:00:00,-1.4,0.0,-0.0,0.0,2.41,88.35 +2020-02-12 22:00:00,-1.57,0.0,-0.0,0.0,2.48,84.95 +2020-02-12 23:00:00,-1.78,0.0,-0.0,0.0,2.41,88.3 +2020-02-13 00:00:00,-2.05,0.0,-0.0,0.0,2.34,88.3 +2020-02-13 01:00:00,-2.33,0.0,-0.0,0.0,2.21,84.85 +2020-02-13 02:00:00,-2.81,0.0,-0.0,0.0,2.0,88.25 +2020-02-13 03:00:00,-3.46,0.0,-0.0,0.0,1.93,88.25 +2020-02-13 04:00:00,-3.67,0.0,-0.0,0.0,1.86,84.8 +2020-02-13 05:00:00,-3.93,0.0,-0.0,0.0,1.86,88.2 +2020-02-13 06:00:00,-4.06,0.0,-0.0,0.0,1.86,84.7 +2020-02-13 07:00:00,-2.84,67.0,292.44,36.0,1.59,84.85 +2020-02-13 08:00:00,-0.71,206.0,596.88,65.0,1.52,85.0 +2020-02-13 09:00:00,1.36,325.0,727.6,79.0,1.93,67.3 +2020-02-13 10:00:00,2.26,405.0,788.27,86.0,1.45,62.35 +2020-02-13 11:00:00,2.93,385.0,523.83,159.0,1.1,53.4 +2020-02-13 12:00:00,3.53,197.0,33.61,183.0,0.83,49.4 +2020-02-13 13:00:00,3.52,253.0,227.15,171.0,0.41,47.45 +2020-02-13 14:00:00,3.32,207.0,368.56,108.0,0.34,49.25 +2020-02-13 15:00:00,2.9,112.0,487.39,41.0,0.9,53.4 +2020-02-13 16:00:00,1.8,0.0,0.0,0.0,1.52,62.25 +2020-02-13 17:00:00,0.23,0.0,-0.0,0.0,1.59,69.85 +2020-02-13 18:00:00,-1.42,0.0,-0.0,0.0,1.72,75.35 +2020-02-13 19:00:00,-2.18,0.0,-0.0,0.0,1.66,75.3 +2020-02-13 20:00:00,-2.51,0.0,-0.0,0.0,1.66,75.3 +2020-02-13 21:00:00,-2.7,0.0,-0.0,0.0,1.59,78.3 +2020-02-13 22:00:00,-2.67,0.0,-0.0,0.0,1.52,75.3 +2020-02-13 23:00:00,-2.38,0.0,-0.0,0.0,1.38,75.3 +2020-02-14 00:00:00,-1.92,0.0,-0.0,0.0,1.17,78.35 +2020-02-14 01:00:00,-1.54,0.0,-0.0,0.0,1.1,78.45 +2020-02-14 02:00:00,-1.71,0.0,-0.0,0.0,1.1,81.55 +2020-02-14 03:00:00,-1.84,0.0,-0.0,0.0,1.1,81.55 +2020-02-14 04:00:00,-2.25,0.0,-0.0,0.0,1.17,78.35 +2020-02-14 05:00:00,-2.59,0.0,-0.0,0.0,1.24,78.35 +2020-02-14 06:00:00,-2.94,0.0,-0.0,0.0,1.24,81.5 +2020-02-14 07:00:00,-2.16,74.0,333.69,37.0,0.97,88.3 +2020-02-14 08:00:00,-1.74,213.0,609.24,66.0,0.69,88.3 +2020-02-14 09:00:00,0.39,333.0,736.97,80.0,1.45,75.7 +2020-02-14 10:00:00,1.65,408.0,768.35,93.0,1.79,67.4 +2020-02-14 11:00:00,2.63,428.0,716.64,115.0,2.07,57.7 +2020-02-14 12:00:00,3.06,309.0,248.91,204.0,2.07,51.3 +2020-02-14 13:00:00,3.2,265.0,251.21,173.0,2.0,47.3 +2020-02-14 14:00:00,3.15,229.0,485.92,96.0,2.0,45.4 +2020-02-14 15:00:00,2.37,124.0,577.71,37.0,1.79,51.15 +2020-02-14 16:00:00,1.03,0.0,0.0,0.0,1.93,59.7 +2020-02-14 17:00:00,-0.46,0.0,-0.0,0.0,1.86,69.75 +2020-02-14 18:00:00,-2.08,0.0,-0.0,0.0,1.86,78.35 +2020-02-14 19:00:00,-3.17,0.0,-0.0,0.0,1.79,81.5 +2020-02-14 20:00:00,-3.24,0.0,-0.0,0.0,1.66,84.8 +2020-02-14 21:00:00,-3.52,0.0,-0.0,0.0,1.72,84.8 +2020-02-14 22:00:00,-4.08,0.0,-0.0,0.0,1.86,88.2 +2020-02-14 23:00:00,-4.42,0.0,-0.0,0.0,2.0,88.15 +2020-02-15 00:00:00,-4.63,0.0,-0.0,0.0,2.0,88.15 +2020-02-15 01:00:00,-4.89,0.0,-0.0,0.0,2.0,88.1 +2020-02-15 02:00:00,-4.94,0.0,-0.0,0.0,2.07,88.1 +2020-02-15 03:00:00,-4.92,0.0,-0.0,0.0,2.07,88.1 +2020-02-15 04:00:00,-4.82,0.0,-0.0,0.0,2.28,88.1 +2020-02-15 05:00:00,-4.52,0.0,-0.0,0.0,2.48,84.65 +2020-02-15 06:00:00,-4.02,0.0,-0.0,0.0,2.62,84.7 +2020-02-15 07:00:00,-3.21,43.0,25.9,40.0,2.55,88.25 +2020-02-15 08:00:00,-1.54,138.0,109.57,111.0,2.69,84.95 +2020-02-15 09:00:00,0.01,228.0,177.88,166.0,3.86,72.65 +2020-02-15 10:00:00,1.06,296.0,235.97,198.0,4.48,64.65 +2020-02-15 11:00:00,1.9,303.0,194.52,217.0,4.69,57.6 +2020-02-15 12:00:00,2.41,306.0,236.44,205.0,4.76,51.15 +2020-02-15 13:00:00,2.72,257.0,218.05,176.0,4.69,51.15 +2020-02-15 14:00:00,2.71,125.0,35.87,115.0,4.55,51.15 +2020-02-15 15:00:00,2.27,64.0,38.58,58.0,4.14,53.15 +2020-02-15 16:00:00,1.72,0.0,0.0,0.0,4.21,55.2 +2020-02-15 17:00:00,1.48,0.0,-0.0,0.0,4.21,55.2 +2020-02-15 18:00:00,1.41,0.0,-0.0,0.0,4.21,55.2 +2020-02-15 19:00:00,1.22,0.0,-0.0,0.0,4.21,70.05 +2020-02-15 20:00:00,1.29,0.0,-0.0,0.0,4.07,70.05 +2020-02-15 21:00:00,0.96,0.0,-0.0,0.0,3.59,70.05 +2020-02-15 22:00:00,0.96,0.0,-0.0,0.0,3.38,72.85 +2020-02-15 23:00:00,1.17,0.0,-0.0,0.0,2.97,78.8 +2020-02-16 00:00:00,1.06,0.0,-0.0,0.0,2.55,85.2 +2020-02-16 01:00:00,1.47,0.0,-0.0,0.0,2.21,92.05 +2020-02-16 02:00:00,1.76,0.0,-0.0,0.0,2.07,95.65 +2020-02-16 03:00:00,1.93,0.0,-0.0,0.0,2.48,95.65 +2020-02-16 04:00:00,1.88,0.0,-0.0,0.0,2.48,95.65 +2020-02-16 05:00:00,1.67,0.0,-0.0,0.0,2.14,99.4 +2020-02-16 06:00:00,1.4,0.0,-0.0,0.0,2.0,99.4 +2020-02-16 07:00:00,0.55,86.0,421.96,35.0,2.41,99.4 +2020-02-16 08:00:00,1.67,222.0,624.0,65.0,2.9,95.65 +2020-02-16 09:00:00,2.62,338.0,731.91,79.0,3.66,88.65 +2020-02-16 10:00:00,3.97,416.0,777.29,89.0,3.24,82.3 +2020-02-16 11:00:00,5.15,437.0,735.15,108.0,3.1,76.4 +2020-02-16 12:00:00,6.1,420.0,725.95,106.0,3.24,68.3 +2020-02-16 13:00:00,6.54,354.0,679.51,98.0,3.59,63.35 +2020-02-16 14:00:00,6.27,251.0,605.73,79.0,3.52,63.25 +2020-02-16 15:00:00,5.46,127.0,523.4,43.0,2.76,68.2 +2020-02-16 16:00:00,3.78,0.0,0.0,0.0,2.62,76.15 +2020-02-16 17:00:00,2.37,0.0,-0.0,0.0,2.62,82.15 +2020-02-16 18:00:00,1.42,0.0,-0.0,0.0,2.62,88.6 +2020-02-16 19:00:00,1.19,0.0,-0.0,0.0,2.55,92.05 +2020-02-16 20:00:00,0.57,0.0,-0.0,0.0,2.41,92.0 +2020-02-16 21:00:00,0.14,0.0,-0.0,0.0,2.21,92.0 +2020-02-16 22:00:00,-0.67,0.0,-0.0,0.0,2.07,95.6 +2020-02-16 23:00:00,-1.47,0.0,-0.0,0.0,2.0,91.9 +2020-02-17 00:00:00,-2.21,0.0,-0.0,0.0,2.0,91.85 +2020-02-17 01:00:00,-2.42,0.0,-0.0,0.0,1.79,88.3 +2020-02-17 02:00:00,-2.36,0.0,-0.0,0.0,1.59,88.3 +2020-02-17 03:00:00,-2.53,0.0,-0.0,0.0,1.52,88.3 +2020-02-17 04:00:00,-2.47,0.0,-0.0,0.0,1.38,91.85 +2020-02-17 05:00:00,-2.55,0.0,-0.0,0.0,1.31,91.85 +2020-02-17 06:00:00,-2.18,0.0,-0.0,0.0,1.17,91.85 +2020-02-17 07:00:00,-0.7,44.0,23.82,41.0,0.9,91.95 +2020-02-17 08:00:00,0.24,151.0,136.26,116.0,0.69,95.6 +2020-02-17 09:00:00,2.33,224.0,144.75,172.0,0.9,82.05 +2020-02-17 10:00:00,3.1,285.0,180.7,208.0,1.45,76.1 +2020-02-17 11:00:00,3.65,290.0,145.71,224.0,1.79,65.2 +2020-02-17 12:00:00,3.99,276.0,139.29,215.0,1.86,62.8 +2020-02-17 13:00:00,3.93,215.0,89.0,181.0,2.14,60.4 +2020-02-17 14:00:00,3.61,165.0,110.69,133.0,2.21,62.7 +2020-02-17 15:00:00,3.1,76.0,60.44,66.0,1.86,70.4 +2020-02-17 16:00:00,2.16,0.0,0.0,0.0,1.93,75.95 +2020-02-17 17:00:00,1.39,0.0,-0.0,0.0,1.86,82.0 +2020-02-17 18:00:00,0.85,0.0,-0.0,0.0,1.86,88.55 +2020-02-17 19:00:00,0.03,0.0,-0.0,0.0,1.59,92.0 +2020-02-17 20:00:00,-0.76,0.0,-0.0,0.0,1.45,95.6 +2020-02-17 21:00:00,-0.7,0.0,-0.0,0.0,1.24,99.4 +2020-02-17 22:00:00,-0.61,0.0,-0.0,0.0,1.17,95.6 +2020-02-17 23:00:00,-0.45,0.0,-0.0,0.0,1.1,99.4 +2020-02-18 00:00:00,-0.29,0.0,-0.0,0.0,1.03,99.4 +2020-02-18 01:00:00,-0.23,0.0,-0.0,0.0,1.03,100.0 +2020-02-18 02:00:00,-0.04,0.0,-0.0,0.0,1.17,99.4 +2020-02-18 03:00:00,-0.01,0.0,-0.0,0.0,1.17,99.4 +2020-02-18 04:00:00,-0.21,0.0,-0.0,0.0,1.03,100.0 +2020-02-18 05:00:00,-0.62,0.0,-0.0,0.0,0.9,99.4 +2020-02-18 06:00:00,-0.32,0.0,-0.0,0.0,0.69,95.6 +2020-02-18 07:00:00,-0.42,57.0,53.38,50.0,0.83,95.6 +2020-02-18 08:00:00,0.41,139.0,83.91,117.0,0.97,85.15 +2020-02-18 09:00:00,1.46,164.0,27.42,154.0,1.38,75.85 +2020-02-18 10:00:00,2.01,85.0,0.0,85.0,1.38,67.5 +2020-02-18 11:00:00,2.41,82.0,0.0,82.0,1.1,65.0 +2020-02-18 12:00:00,2.52,89.0,0.0,89.0,0.9,65.0 +2020-02-18 13:00:00,2.54,94.0,0.0,94.0,0.76,65.0 +2020-02-18 14:00:00,2.62,69.0,0.0,69.0,0.62,65.0 +2020-02-18 15:00:00,2.56,68.0,29.34,63.0,0.62,62.5 +2020-02-18 16:00:00,2.07,0.0,0.0,0.0,1.1,67.5 +2020-02-18 17:00:00,0.8,0.0,-0.0,0.0,1.38,78.75 +2020-02-18 18:00:00,-0.71,0.0,-0.0,0.0,1.52,85.0 +2020-02-18 19:00:00,-1.22,0.0,-0.0,0.0,1.66,88.35 +2020-02-18 20:00:00,-1.48,0.0,-0.0,0.0,1.72,84.95 +2020-02-18 21:00:00,-1.95,0.0,-0.0,0.0,1.72,88.3 +2020-02-18 22:00:00,-2.41,0.0,-0.0,0.0,1.72,84.85 +2020-02-18 23:00:00,-2.64,0.0,-0.0,0.0,1.79,84.85 +2020-02-19 00:00:00,-2.79,0.0,-0.0,0.0,1.93,84.85 +2020-02-19 01:00:00,-2.9,0.0,-0.0,0.0,2.0,84.85 +2020-02-19 02:00:00,-3.1,0.0,-0.0,0.0,1.93,81.5 +2020-02-19 03:00:00,-3.33,0.0,-0.0,0.0,1.86,84.8 +2020-02-19 04:00:00,-3.47,0.0,-0.0,0.0,1.86,84.8 +2020-02-19 05:00:00,-3.55,0.0,-0.0,0.0,1.86,84.8 +2020-02-19 06:00:00,-3.5,0.0,-0.0,0.0,1.86,84.8 +2020-02-19 07:00:00,-2.62,63.0,73.33,53.0,1.79,81.55 +2020-02-19 08:00:00,-0.79,229.0,530.74,87.0,1.93,78.5 +2020-02-19 09:00:00,0.49,317.0,478.21,140.0,2.21,67.2 +2020-02-19 10:00:00,1.25,353.0,363.75,194.0,2.21,59.7 +2020-02-19 11:00:00,1.8,435.0,607.84,153.0,1.93,57.45 +2020-02-19 12:00:00,2.35,425.0,639.51,138.0,1.66,55.3 +2020-02-19 13:00:00,2.7,386.0,751.26,91.0,1.59,57.7 +2020-02-19 14:00:00,2.42,235.0,377.38,122.0,1.79,57.7 +2020-02-19 15:00:00,2.04,143.0,541.55,48.0,1.66,59.95 +2020-02-19 16:00:00,0.9,2.0,0.0,2.0,1.72,64.65 +2020-02-19 17:00:00,-0.25,0.0,-0.0,0.0,1.72,72.6 +2020-02-19 18:00:00,-1.14,0.0,-0.0,0.0,1.52,75.45 +2020-02-19 19:00:00,-1.72,0.0,-0.0,0.0,1.38,81.55 +2020-02-19 20:00:00,-2.1,0.0,-0.0,0.0,1.38,81.55 +2020-02-19 21:00:00,-2.25,0.0,-0.0,0.0,1.38,81.55 +2020-02-19 22:00:00,-2.5,0.0,-0.0,0.0,1.52,81.55 +2020-02-19 23:00:00,-2.74,0.0,-0.0,0.0,1.66,84.85 +2020-02-20 00:00:00,-3.08,0.0,-0.0,0.0,1.86,84.85 +2020-02-20 01:00:00,-3.06,0.0,-0.0,0.0,1.79,84.85 +2020-02-20 02:00:00,-3.08,0.0,-0.0,0.0,1.72,84.85 +2020-02-20 03:00:00,-3.19,0.0,-0.0,0.0,1.72,88.25 +2020-02-20 04:00:00,-3.35,0.0,-0.0,0.0,1.72,88.25 +2020-02-20 05:00:00,-3.56,0.0,-0.0,0.0,1.72,84.8 +2020-02-20 06:00:00,-3.82,0.0,-0.0,0.0,1.72,88.2 +2020-02-20 07:00:00,-3.56,77.0,141.16,57.0,1.45,88.25 +2020-02-20 08:00:00,-1.26,217.0,413.94,104.0,1.52,78.45 +2020-02-20 09:00:00,0.08,317.0,455.21,146.0,1.72,69.85 +2020-02-20 10:00:00,1.33,426.0,684.49,123.0,1.93,62.15 +2020-02-20 11:00:00,2.42,462.0,724.21,122.0,2.07,55.45 +2020-02-20 12:00:00,3.12,451.0,752.9,109.0,2.0,53.4 +2020-02-20 13:00:00,3.35,354.0,540.18,139.0,2.14,53.4 +2020-02-20 14:00:00,3.03,187.0,144.44,143.0,2.28,57.8 +2020-02-20 15:00:00,2.37,132.0,376.9,64.0,2.41,62.5 +2020-02-20 16:00:00,0.9,3.0,0.0,3.0,2.55,70.05 +2020-02-20 17:00:00,-0.6,0.0,-0.0,0.0,2.48,78.6 +2020-02-20 18:00:00,-1.48,0.0,-0.0,0.0,2.48,84.95 +2020-02-20 19:00:00,-1.78,0.0,-0.0,0.0,2.41,84.85 +2020-02-20 20:00:00,-2.1,0.0,-0.0,0.0,2.28,81.55 +2020-02-20 21:00:00,-2.53,0.0,-0.0,0.0,2.0,81.55 +2020-02-20 22:00:00,-3.3,0.0,-0.0,0.0,1.86,84.8 +2020-02-20 23:00:00,-4.07,0.0,-0.0,0.0,1.72,84.7 +2020-02-21 00:00:00,-4.63,0.0,-0.0,0.0,1.86,84.65 +2020-02-21 01:00:00,-5.0,0.0,-0.0,0.0,2.0,88.1 +2020-02-21 02:00:00,-5.12,0.0,-0.0,0.0,2.07,84.6 +2020-02-21 03:00:00,-4.97,0.0,-0.0,0.0,2.21,84.6 +2020-02-21 04:00:00,-4.88,0.0,-0.0,0.0,2.21,84.6 +2020-02-21 05:00:00,-4.89,0.0,-0.0,0.0,2.07,84.6 +2020-02-21 06:00:00,-4.99,0.0,-0.0,0.0,1.86,84.6 +2020-02-21 07:00:00,-4.16,81.0,142.81,60.0,1.79,84.7 +2020-02-21 08:00:00,-3.34,143.0,68.23,124.0,2.76,81.45 +2020-02-21 09:00:00,-2.26,298.0,341.02,168.0,3.17,75.3 +2020-02-21 10:00:00,-1.2,459.0,832.12,86.0,3.31,72.4 +2020-02-21 11:00:00,-0.29,502.0,894.67,77.0,3.45,64.35 +2020-02-21 12:00:00,0.69,488.0,909.24,70.0,3.79,57.2 +2020-02-21 13:00:00,0.99,407.0,813.15,79.0,3.72,52.85 +2020-02-21 14:00:00,1.23,147.0,41.96,134.0,3.93,52.85 +2020-02-21 15:00:00,0.83,137.0,377.51,67.0,3.72,54.95 +2020-02-21 16:00:00,-0.13,5.0,0.0,5.0,2.97,59.45 +2020-02-21 17:00:00,-0.84,0.0,-0.0,0.0,2.62,66.9 +2020-02-21 18:00:00,-1.5,0.0,-0.0,0.0,2.34,72.4 +2020-02-21 19:00:00,-1.44,0.0,-0.0,0.0,2.76,72.4 +2020-02-21 20:00:00,-1.77,0.0,-0.0,0.0,2.55,78.35 +2020-02-21 21:00:00,-2.44,0.0,-0.0,0.0,2.41,78.35 +2020-02-21 22:00:00,-3.01,0.0,-0.0,0.0,2.34,81.5 +2020-02-21 23:00:00,-3.55,0.0,-0.0,0.0,2.28,81.45 +2020-02-22 00:00:00,-4.01,0.0,-0.0,0.0,2.14,84.7 +2020-02-22 01:00:00,-4.3,0.0,-0.0,0.0,2.07,84.65 +2020-02-22 02:00:00,-4.73,0.0,-0.0,0.0,2.07,84.6 +2020-02-22 03:00:00,-4.82,0.0,-0.0,0.0,2.21,84.6 +2020-02-22 04:00:00,-4.76,0.0,-0.0,0.0,2.34,84.6 +2020-02-22 05:00:00,-4.55,0.0,-0.0,0.0,2.41,81.3 +2020-02-22 06:00:00,-4.37,0.0,0.0,0.0,2.41,81.3 +2020-02-22 07:00:00,-4.04,121.0,504.99,44.0,2.48,78.15 +2020-02-22 08:00:00,-2.94,275.0,764.03,58.0,3.17,69.35 +2020-02-22 09:00:00,-1.63,400.0,863.45,66.0,4.28,61.55 +2020-02-22 10:00:00,-0.73,478.0,887.91,75.0,4.21,59.2 +2020-02-22 11:00:00,0.09,509.0,890.52,81.0,4.28,52.6 +2020-02-22 12:00:00,0.26,482.0,846.91,88.0,4.41,50.5 +2020-02-22 13:00:00,0.41,350.0,455.06,164.0,4.48,46.6 +2020-02-22 14:00:00,0.39,275.0,536.49,106.0,4.41,44.75 +2020-02-22 15:00:00,0.09,132.0,288.81,77.0,4.21,46.5 +2020-02-22 16:00:00,-0.63,6.0,0.0,6.0,3.45,50.35 +2020-02-22 17:00:00,-1.57,0.0,-0.0,0.0,3.1,54.4 +2020-02-22 18:00:00,-2.2,0.0,-0.0,0.0,2.97,58.95 +2020-02-22 19:00:00,-2.48,0.0,-0.0,0.0,3.1,58.95 +2020-02-22 20:00:00,-2.97,0.0,-0.0,0.0,2.97,61.35 +2020-02-22 21:00:00,-3.19,0.0,-0.0,0.0,2.83,63.8 +2020-02-22 22:00:00,-3.4,0.0,-0.0,0.0,2.83,63.8 +2020-02-22 23:00:00,-3.44,0.0,-0.0,0.0,2.76,63.8 +2020-02-23 00:00:00,-3.71,0.0,-0.0,0.0,2.76,66.35 +2020-02-23 01:00:00,-4.66,0.0,-0.0,0.0,2.76,66.25 +2020-02-23 02:00:00,-5.03,0.0,-0.0,0.0,2.9,68.95 +2020-02-23 03:00:00,-5.28,0.0,-0.0,0.0,3.17,68.85 +2020-02-23 04:00:00,-5.52,0.0,-0.0,0.0,3.17,66.05 +2020-02-23 05:00:00,-5.66,0.0,-0.0,0.0,3.31,63.35 +2020-02-23 06:00:00,-5.83,0.0,0.0,0.0,3.38,63.25 +2020-02-23 07:00:00,-5.51,126.0,512.76,45.0,3.59,68.85 +2020-02-23 08:00:00,-5.34,280.0,752.73,62.0,3.72,66.05 +2020-02-23 09:00:00,-4.82,392.0,769.49,90.0,3.59,60.85 +2020-02-23 10:00:00,-4.19,493.0,916.16,72.0,3.45,58.5 +2020-02-23 11:00:00,-3.51,529.0,944.01,70.0,3.45,54.0 +2020-02-23 12:00:00,-3.04,502.0,898.61,79.0,3.52,51.95 +2020-02-23 13:00:00,-3.03,425.0,828.29,82.0,3.52,51.95 +2020-02-23 14:00:00,-3.07,312.0,758.87,69.0,3.52,51.95 +2020-02-23 15:00:00,-3.28,172.0,634.45,48.0,3.52,54.0 +2020-02-23 16:00:00,-3.96,0.0,0.0,0.0,3.24,56.2 +2020-02-23 17:00:00,-4.88,0.0,-0.0,0.0,2.9,60.85 +2020-02-23 18:00:00,-5.46,0.0,-0.0,0.0,2.83,60.75 +2020-02-23 19:00:00,-5.68,0.0,-0.0,0.0,2.83,60.75 +2020-02-23 20:00:00,-5.95,0.0,-0.0,0.0,2.62,63.25 +2020-02-23 21:00:00,-6.16,0.0,-0.0,0.0,2.21,65.95 +2020-02-23 22:00:00,-6.4,0.0,-0.0,0.0,2.07,68.65 +2020-02-23 23:00:00,-6.59,0.0,-0.0,0.0,2.07,68.65 +2020-02-24 00:00:00,-6.83,0.0,-0.0,0.0,2.28,71.5 +2020-02-24 01:00:00,-6.65,0.0,-0.0,0.0,2.28,71.55 +2020-02-24 02:00:00,-6.82,0.0,-0.0,0.0,2.14,74.55 +2020-02-24 03:00:00,-6.96,0.0,-0.0,0.0,2.14,71.5 +2020-02-24 04:00:00,-6.88,0.0,-0.0,0.0,2.28,71.5 +2020-02-24 05:00:00,-6.69,0.0,-0.0,0.0,2.21,65.8 +2020-02-24 06:00:00,-6.05,0.0,0.0,0.0,2.21,65.95 +2020-02-24 07:00:00,-6.1,67.0,30.58,62.0,1.59,74.7 +2020-02-24 08:00:00,-4.44,230.0,365.79,122.0,2.76,78.05 +2020-02-24 09:00:00,-3.11,382.0,690.69,107.0,3.86,66.6 +2020-02-24 10:00:00,-2.06,444.0,647.02,143.0,4.28,56.6 +2020-02-24 11:00:00,-1.55,463.0,607.92,164.0,4.55,44.15 +2020-02-24 12:00:00,-1.25,414.0,464.04,193.0,4.69,40.55 +2020-02-24 13:00:00,-1.31,278.0,171.64,206.0,4.83,40.55 +2020-02-24 14:00:00,-1.84,189.0,110.63,153.0,4.83,42.2 +2020-02-24 15:00:00,-2.44,168.0,558.72,56.0,4.83,42.2 +2020-02-24 16:00:00,-3.28,13.0,18.82,12.0,4.55,45.65 +2020-02-24 17:00:00,-3.97,0.0,-0.0,0.0,4.21,47.45 +2020-02-24 18:00:00,-4.24,0.0,-0.0,0.0,4.21,49.4 +2020-02-24 19:00:00,-5.03,0.0,-0.0,0.0,3.66,49.25 +2020-02-24 20:00:00,-5.64,0.0,-0.0,0.0,3.31,51.25 +2020-02-24 21:00:00,-6.19,0.0,-0.0,0.0,3.03,55.7 +2020-02-24 22:00:00,-6.66,0.0,-0.0,0.0,2.76,58.0 +2020-02-24 23:00:00,-7.1,0.0,-0.0,0.0,2.55,60.4 +2020-02-25 00:00:00,-7.46,0.0,-0.0,0.0,2.55,62.9 +2020-02-25 01:00:00,-7.85,0.0,-0.0,0.0,2.55,65.5 +2020-02-25 02:00:00,-8.14,0.0,-0.0,0.0,2.55,62.75 +2020-02-25 03:00:00,-8.5,0.0,-0.0,0.0,2.48,62.65 +2020-02-25 04:00:00,-8.87,0.0,-0.0,0.0,2.48,62.5 +2020-02-25 05:00:00,-9.32,0.0,-0.0,0.0,2.34,65.15 +2020-02-25 06:00:00,-9.67,0.0,0.0,0.0,2.34,62.4 +2020-02-25 07:00:00,-9.77,148.0,644.55,39.0,2.76,65.05 +2020-02-25 08:00:00,-9.09,302.0,820.78,55.0,3.17,57.35 +2020-02-25 09:00:00,-8.1,431.0,921.08,59.0,3.24,50.55 +2020-02-25 10:00:00,-6.95,512.0,940.73,69.0,3.24,46.6 +2020-02-25 11:00:00,-5.87,548.0,968.87,66.0,3.38,41.15 +2020-02-25 12:00:00,-5.05,512.0,884.22,86.0,3.52,38.0 +2020-02-25 13:00:00,-4.52,444.0,861.45,78.0,3.66,34.95 +2020-02-25 14:00:00,-4.35,329.0,792.45,67.0,3.72,33.45 +2020-02-25 15:00:00,-4.58,187.0,686.23,46.0,3.66,34.95 +2020-02-25 16:00:00,-5.18,18.0,34.52,16.0,3.59,36.35 +2020-02-25 17:00:00,-5.87,0.0,-0.0,0.0,3.24,41.15 +2020-02-25 18:00:00,-6.46,0.0,-0.0,0.0,2.97,44.75 +2020-02-25 19:00:00,-7.04,0.0,-0.0,0.0,2.83,48.7 +2020-02-25 20:00:00,-7.49,0.0,-0.0,0.0,2.41,52.95 +2020-02-25 21:00:00,-7.88,0.0,-0.0,0.0,2.07,55.15 +2020-02-25 22:00:00,-8.28,0.0,-0.0,0.0,1.79,57.5 +2020-02-25 23:00:00,-8.61,0.0,-0.0,0.0,1.66,57.5 +2020-02-26 00:00:00,-8.87,0.0,-0.0,0.0,1.52,62.5 +2020-02-26 01:00:00,-9.13,0.0,-0.0,0.0,1.38,65.25 +2020-02-26 02:00:00,-9.44,0.0,-0.0,0.0,1.38,68.0 +2020-02-26 03:00:00,-9.37,0.0,-0.0,0.0,1.59,71.0 +2020-02-26 04:00:00,-9.16,0.0,-0.0,0.0,2.0,71.1 +2020-02-26 05:00:00,-9.24,0.0,-0.0,0.0,2.34,74.1 +2020-02-26 06:00:00,-9.58,0.0,0.0,0.0,2.76,71.0 +2020-02-26 07:00:00,-9.59,152.0,629.43,42.0,3.93,59.75 +2020-02-26 08:00:00,-9.27,297.0,750.02,67.0,3.79,59.75 +2020-02-26 09:00:00,-8.56,424.0,849.56,76.0,3.59,55.05 +2020-02-26 10:00:00,-7.79,421.0,474.15,195.0,3.45,50.55 +2020-02-26 11:00:00,-6.99,350.0,184.84,257.0,3.45,48.7 +2020-02-26 12:00:00,-6.37,283.0,84.13,242.0,3.79,44.75 +2020-02-26 13:00:00,-6.11,219.0,46.48,199.0,4.0,43.0 +2020-02-26 14:00:00,-6.2,161.0,41.69,147.0,4.0,43.0 +2020-02-26 15:00:00,-6.41,111.0,90.27,92.0,3.86,44.75 +2020-02-26 16:00:00,-6.78,16.0,15.93,15.0,3.72,44.65 +2020-02-26 17:00:00,-7.25,0.0,-0.0,0.0,3.52,46.5 +2020-02-26 18:00:00,-7.67,0.0,-0.0,0.0,3.31,46.5 +2020-02-26 19:00:00,-8.35,0.0,-0.0,0.0,3.31,50.45 +2020-02-26 20:00:00,-8.93,0.0,-0.0,0.0,3.1,52.55 +2020-02-26 21:00:00,-9.44,0.0,-0.0,0.0,2.97,54.75 +2020-02-26 22:00:00,-9.86,0.0,-0.0,0.0,2.76,54.65 +2020-02-26 23:00:00,-10.16,0.0,-0.0,0.0,2.76,54.65 +2020-02-27 00:00:00,-10.49,0.0,-0.0,0.0,2.55,54.5 +2020-02-27 01:00:00,-10.93,0.0,-0.0,0.0,2.41,54.35 +2020-02-27 02:00:00,-11.11,0.0,-0.0,0.0,2.34,54.35 +2020-02-27 03:00:00,-11.35,0.0,-0.0,0.0,2.41,56.7 +2020-02-27 04:00:00,-11.63,0.0,-0.0,0.0,2.41,54.25 +2020-02-27 05:00:00,-11.84,0.0,-0.0,0.0,2.48,56.55 +2020-02-27 06:00:00,-12.05,0.0,0.0,0.0,2.41,56.55 +2020-02-27 07:00:00,-12.11,155.0,609.55,45.0,2.97,67.5 +2020-02-27 08:00:00,-11.15,309.0,787.39,63.0,3.1,62.05 +2020-02-27 09:00:00,-9.76,441.0,902.73,66.0,3.17,54.65 +2020-02-27 10:00:00,-8.09,530.0,970.17,62.0,3.52,44.35 +2020-02-27 11:00:00,-6.77,566.0,994.49,60.0,3.72,40.85 +2020-02-27 12:00:00,-5.93,536.0,937.39,74.0,3.86,37.7 +2020-02-27 13:00:00,-4.92,461.0,890.66,73.0,5.17,33.3 +2020-02-27 14:00:00,-4.84,343.0,809.32,67.0,5.03,31.85 +2020-02-27 15:00:00,-5.1,188.0,593.99,60.0,4.62,31.85 +2020-02-27 16:00:00,-5.7,23.0,29.6,21.0,4.21,34.65 +2020-02-27 17:00:00,-6.65,0.0,-0.0,0.0,3.66,37.55 +2020-02-27 18:00:00,-7.41,0.0,-0.0,0.0,3.38,42.55 +2020-02-27 19:00:00,-8.48,0.0,-0.0,0.0,3.52,50.45 +2020-02-27 20:00:00,-8.96,0.0,-0.0,0.0,3.45,52.55 +2020-02-27 21:00:00,-9.55,0.0,-0.0,0.0,3.1,54.75 +2020-02-27 22:00:00,-10.03,0.0,-0.0,0.0,2.83,54.65 +2020-02-27 23:00:00,-10.42,0.0,-0.0,0.0,2.55,54.5 +2020-02-28 00:00:00,-10.73,0.0,-0.0,0.0,2.48,54.5 +2020-02-28 01:00:00,-11.03,0.0,-0.0,0.0,2.48,54.35 +2020-02-28 02:00:00,-11.3,0.0,-0.0,0.0,2.48,56.7 +2020-02-28 03:00:00,-11.53,0.0,-0.0,0.0,2.69,56.7 +2020-02-28 04:00:00,-11.74,0.0,-0.0,0.0,2.76,56.7 +2020-02-28 05:00:00,-11.94,0.0,-0.0,0.0,2.83,56.55 +2020-02-28 06:00:00,-12.14,5.0,0.0,5.0,2.9,56.55 +2020-02-28 07:00:00,-12.22,162.0,633.71,44.0,3.52,67.5 +2020-02-28 08:00:00,-11.27,298.0,672.48,84.0,3.72,61.95 +2020-02-28 09:00:00,-9.96,429.0,814.31,86.0,3.79,52.3 +2020-02-28 10:00:00,-8.74,488.0,735.42,129.0,3.72,48.1 +2020-02-28 11:00:00,-7.62,551.0,905.77,85.0,3.66,42.55 +2020-02-28 12:00:00,-6.79,536.0,920.95,77.0,3.66,42.7 +2020-02-28 13:00:00,-6.19,467.0,897.94,71.0,3.72,39.4 +2020-02-28 14:00:00,-5.89,349.0,817.39,66.0,3.86,41.15 +2020-02-28 15:00:00,-6.08,202.0,698.43,48.0,4.07,41.15 +2020-02-28 16:00:00,-9.4,25.0,41.44,22.0,3.69,41.97 +2020-02-28 17:00:00,-8.84,0.0,-0.0,0.0,3.63,46.49 +2020-02-28 18:00:00,-8.27,0.0,-0.0,0.0,3.57,51.0 +2020-02-28 19:00:00,-7.7,0.0,-0.0,0.0,3.51,55.52 +2020-02-28 20:00:00,-7.13,0.0,-0.0,0.0,3.45,60.03 +2020-02-28 21:00:00,-6.57,0.0,-0.0,0.0,3.39,64.55 +2020-02-28 22:00:00,-6.0,0.0,-0.0,0.0,3.32,69.06 +2020-02-28 23:00:00,-5.43,0.0,-0.0,0.0,3.26,73.58 +2020-03-01 00:00:00,-4.86,0.0,-0.0,0.0,3.2,78.09 +2020-03-01 01:00:00,-4.3,0.0,-0.0,0.0,3.14,82.61 +2020-03-01 02:00:00,-3.73,0.0,-0.0,0.0,3.08,87.12 +2020-03-01 03:00:00,-3.16,0.0,-0.0,0.0,3.02,91.64 +2020-03-01 04:00:00,-2.59,0.0,-0.0,0.0,2.96,96.15 +2020-03-01 05:00:00,-2.03,0.0,-0.0,0.0,2.89,100.0 +2020-03-01 06:00:00,-1.46,2.0,0.0,2.0,2.83,100.0 +2020-03-01 07:00:00,-0.89,38.0,0.0,38.0,2.77,100.0 +2020-03-01 08:00:00,-2.05,55.0,0.0,55.0,2.83,95.55 +2020-03-01 09:00:00,-1.41,117.0,0.0,117.0,2.76,88.35 +2020-03-01 10:00:00,-0.87,66.0,0.0,66.0,2.9,85.05 +2020-03-01 11:00:00,-0.64,58.0,0.0,58.0,3.52,85.05 +2020-03-01 12:00:00,-0.64,502.0,751.74,119.0,3.66,88.45 +2020-03-01 13:00:00,-0.65,61.0,0.0,61.0,3.38,88.45 +2020-03-01 14:00:00,-0.57,60.0,0.0,60.0,3.24,88.45 +2020-03-01 15:00:00,-0.48,54.0,0.0,54.0,3.03,88.45 +2020-03-01 16:00:00,-0.54,34.0,97.54,26.0,2.48,88.45 +2020-03-01 17:00:00,-0.83,0.0,-0.0,0.0,1.93,88.45 +2020-03-01 18:00:00,-1.16,0.0,-0.0,0.0,1.79,91.95 +2020-03-01 19:00:00,-1.86,0.0,-0.0,0.0,2.07,88.35 +2020-03-01 20:00:00,-2.66,0.0,-0.0,0.0,2.41,88.25 +2020-03-01 21:00:00,-3.37,0.0,-0.0,0.0,2.69,81.45 +2020-03-01 22:00:00,-3.99,0.0,-0.0,0.0,2.83,84.65 +2020-03-01 23:00:00,-4.24,0.0,-0.0,0.0,2.9,81.3 +2020-03-02 00:00:00,-3.95,0.0,-0.0,0.0,2.9,84.65 +2020-03-02 01:00:00,-3.04,0.0,-0.0,0.0,2.83,84.8 +2020-03-02 02:00:00,-1.83,0.0,-0.0,0.0,2.9,84.95 +2020-03-02 03:00:00,-0.42,0.0,-0.0,0.0,2.97,88.45 +2020-03-02 04:00:00,0.74,0.0,-0.0,0.0,2.97,85.2 +2020-03-02 05:00:00,1.56,0.0,-0.0,0.0,3.1,85.25 +2020-03-02 06:00:00,1.97,3.0,0.0,3.0,3.17,82.05 +2020-03-02 07:00:00,2.26,59.0,4.91,58.0,3.31,82.15 +2020-03-02 08:00:00,3.03,71.0,0.0,71.0,3.38,82.2 +2020-03-02 09:00:00,3.68,105.0,0.0,105.0,3.52,82.25 +2020-03-02 10:00:00,4.27,96.0,0.0,96.0,3.72,82.3 +2020-03-02 11:00:00,4.95,245.0,28.22,230.0,3.52,82.35 +2020-03-02 12:00:00,6.04,122.0,0.0,122.0,3.66,79.4 +2020-03-02 13:00:00,6.51,103.0,0.0,103.0,3.79,79.5 +2020-03-02 14:00:00,6.53,64.0,0.0,64.0,3.59,79.5 +2020-03-02 15:00:00,5.94,36.0,0.0,36.0,3.38,82.5 +2020-03-02 16:00:00,5.08,14.0,0.0,14.0,3.03,85.55 +2020-03-02 17:00:00,4.22,0.0,-0.0,0.0,2.76,88.8 +2020-03-02 18:00:00,3.63,0.0,-0.0,0.0,2.28,92.15 +2020-03-02 19:00:00,2.77,0.0,-0.0,0.0,1.86,92.15 +2020-03-02 20:00:00,2.67,0.0,-0.0,0.0,2.07,88.7 +2020-03-02 21:00:00,2.69,0.0,-0.0,0.0,2.41,88.7 +2020-03-02 22:00:00,2.29,0.0,-0.0,0.0,2.14,92.15 +2020-03-02 23:00:00,1.7,0.0,-0.0,0.0,1.72,92.1 +2020-03-03 00:00:00,1.26,0.0,-0.0,0.0,1.66,92.05 +2020-03-03 01:00:00,0.83,0.0,-0.0,0.0,1.52,92.05 +2020-03-03 02:00:00,0.15,0.0,-0.0,0.0,0.0,92.0 +2020-03-03 03:00:00,0.01,0.0,-0.0,0.0,0.0,92.0 +2020-03-03 04:00:00,-0.14,0.0,-0.0,0.0,1.45,92.0 +2020-03-03 05:00:00,-0.46,0.0,-0.0,0.0,1.52,95.6 +2020-03-03 06:00:00,-0.62,21.0,87.38,16.0,1.52,91.95 +2020-03-03 07:00:00,0.92,90.0,38.17,82.0,0.97,92.05 +2020-03-03 08:00:00,2.04,60.0,0.0,60.0,1.1,88.65 +2020-03-03 09:00:00,2.77,187.0,17.99,179.0,2.76,82.2 +2020-03-03 10:00:00,3.12,263.0,52.8,236.0,2.14,79.1 +2020-03-03 11:00:00,3.38,324.0,109.82,265.0,1.79,79.1 +2020-03-03 12:00:00,3.68,179.0,1.92,178.0,1.66,73.3 +2020-03-03 13:00:00,3.68,212.0,30.27,198.0,1.45,73.3 +2020-03-03 14:00:00,3.87,193.0,73.58,166.0,1.17,70.5 +2020-03-03 15:00:00,4.57,45.0,0.0,45.0,1.03,67.9 +2020-03-03 16:00:00,3.58,24.0,10.92,23.0,1.17,73.2 +2020-03-03 17:00:00,2.54,0.0,-0.0,0.0,1.52,76.0 +2020-03-03 18:00:00,3.07,0.0,-0.0,0.0,1.24,73.2 +2020-03-03 19:00:00,3.52,0.0,-0.0,0.0,0.76,73.2 +2020-03-03 20:00:00,2.99,0.0,-0.0,0.0,1.1,73.2 +2020-03-03 21:00:00,1.35,0.0,-0.0,0.0,1.38,78.9 +2020-03-03 22:00:00,-0.26,0.0,-0.0,0.0,1.59,81.8 +2020-03-03 23:00:00,-1.19,0.0,-0.0,0.0,1.66,85.0 +2020-03-04 00:00:00,-1.63,0.0,-0.0,0.0,1.66,88.35 +2020-03-04 01:00:00,-1.82,0.0,-0.0,0.0,1.66,84.95 +2020-03-04 02:00:00,-2.21,0.0,-0.0,0.0,1.79,84.85 +2020-03-04 03:00:00,-1.97,0.0,-0.0,0.0,1.79,81.55 +2020-03-04 04:00:00,-1.36,0.0,-0.0,0.0,1.66,75.45 +2020-03-04 05:00:00,-1.08,0.0,-0.0,0.0,1.59,78.5 +2020-03-04 06:00:00,-1.45,29.0,158.47,19.0,1.59,78.45 +2020-03-04 07:00:00,0.6,179.0,533.55,64.0,1.03,81.9 +2020-03-04 08:00:00,2.86,321.0,652.86,94.0,0.76,73.2 +2020-03-04 09:00:00,4.05,438.0,714.64,116.0,1.1,70.5 +2020-03-04 10:00:00,4.58,508.0,709.64,141.0,1.93,70.6 +2020-03-04 11:00:00,5.18,512.0,611.51,180.0,2.76,60.65 +2020-03-04 12:00:00,5.87,397.0,275.62,252.0,2.9,56.2 +2020-03-04 13:00:00,6.09,445.0,656.25,138.0,3.17,56.2 +2020-03-04 14:00:00,7.37,250.0,201.57,175.0,3.45,48.4 +2020-03-04 15:00:00,7.15,173.0,289.33,102.0,3.38,50.2 +2020-03-04 16:00:00,5.97,44.0,103.74,34.0,2.83,54.05 +2020-03-04 17:00:00,4.55,0.0,-0.0,0.0,2.9,58.05 +2020-03-04 18:00:00,3.73,0.0,-0.0,0.0,3.24,60.3 +2020-03-04 19:00:00,3.47,0.0,-0.0,0.0,3.86,65.1 +2020-03-04 20:00:00,3.53,0.0,-0.0,0.0,4.0,67.7 +2020-03-04 21:00:00,3.51,0.0,-0.0,0.0,4.07,70.4 +2020-03-04 22:00:00,3.35,0.0,-0.0,0.0,4.0,70.4 +2020-03-04 23:00:00,3.33,0.0,-0.0,0.0,4.07,73.2 +2020-03-05 00:00:00,2.97,0.0,-0.0,0.0,3.86,73.2 +2020-03-05 01:00:00,2.06,0.0,-0.0,0.0,3.66,78.95 +2020-03-05 02:00:00,1.72,0.0,-0.0,0.0,3.59,78.95 +2020-03-05 03:00:00,1.57,0.0,-0.0,0.0,3.38,82.0 +2020-03-05 04:00:00,1.49,0.0,-0.0,0.0,3.17,82.0 +2020-03-05 05:00:00,1.49,0.0,-0.0,0.0,3.17,85.25 +2020-03-05 06:00:00,1.31,19.0,14.49,18.0,2.97,85.25 +2020-03-05 07:00:00,1.68,154.0,302.47,87.0,2.9,82.05 +2020-03-05 08:00:00,2.95,278.0,384.54,142.0,2.97,79.1 +2020-03-05 09:00:00,4.89,409.0,565.18,151.0,3.03,76.3 +2020-03-05 10:00:00,6.38,395.0,288.73,244.0,2.9,70.95 +2020-03-05 11:00:00,8.23,441.0,359.1,244.0,2.83,63.7 +2020-03-05 12:00:00,8.77,389.0,253.95,254.0,2.69,61.45 +2020-03-05 13:00:00,9.33,345.0,262.08,221.0,2.48,59.25 +2020-03-05 14:00:00,9.5,116.0,2.65,115.0,2.69,61.55 +2020-03-05 15:00:00,8.99,55.0,0.0,55.0,2.62,61.45 +2020-03-05 16:00:00,7.9,23.0,0.0,23.0,2.14,68.6 +2020-03-05 17:00:00,6.42,0.0,-0.0,0.0,2.0,73.7 +2020-03-05 18:00:00,5.5,0.0,-0.0,0.0,1.86,79.35 +2020-03-05 19:00:00,4.51,0.0,-0.0,0.0,1.72,85.5 +2020-03-05 20:00:00,3.57,0.0,-0.0,0.0,1.52,92.15 +2020-03-05 21:00:00,2.81,0.0,-0.0,0.0,1.52,88.7 +2020-03-05 22:00:00,2.27,0.0,-0.0,0.0,1.45,92.15 +2020-03-05 23:00:00,1.96,0.0,-0.0,0.0,1.38,92.1 +2020-03-06 00:00:00,2.11,0.0,-0.0,0.0,1.38,95.65 +2020-03-06 01:00:00,2.25,0.0,-0.0,0.0,1.66,92.15 +2020-03-06 02:00:00,2.42,0.0,-0.0,0.0,1.93,95.7 +2020-03-06 03:00:00,2.88,0.0,-0.0,0.0,2.07,95.7 +2020-03-06 04:00:00,2.99,0.0,-0.0,0.0,2.21,99.4 +2020-03-06 05:00:00,2.44,0.0,-0.0,0.0,2.48,99.4 +2020-03-06 06:00:00,2.39,12.0,0.0,12.0,3.79,95.7 +2020-03-06 07:00:00,2.05,104.0,52.75,92.0,3.66,95.65 +2020-03-06 08:00:00,2.1,151.0,22.24,143.0,4.14,95.65 +2020-03-06 09:00:00,2.73,127.0,0.0,127.0,5.24,88.7 +2020-03-06 10:00:00,3.62,236.0,24.58,223.0,4.97,88.7 +2020-03-06 11:00:00,4.57,250.0,23.45,237.0,5.1,79.2 +2020-03-06 12:00:00,5.79,161.0,0.0,161.0,5.03,65.65 +2020-03-06 13:00:00,6.47,59.0,0.0,59.0,4.83,58.55 +2020-03-06 14:00:00,6.9,70.0,0.0,70.0,4.48,54.3 +2020-03-06 15:00:00,6.9,44.0,0.0,44.0,3.31,54.3 +2020-03-06 16:00:00,6.14,44.0,66.07,37.0,2.07,60.75 +2020-03-06 17:00:00,4.83,0.0,-0.0,0.0,1.86,65.4 +2020-03-06 18:00:00,3.24,0.0,-0.0,0.0,2.14,73.2 +2020-03-06 19:00:00,1.85,0.0,-0.0,0.0,1.86,78.95 +2020-03-06 20:00:00,0.45,0.0,-0.0,0.0,2.14,85.15 +2020-03-06 21:00:00,-0.35,0.0,-0.0,0.0,2.34,85.1 +2020-03-06 22:00:00,-0.89,0.0,-0.0,0.0,2.48,85.05 +2020-03-06 23:00:00,-0.89,0.0,-0.0,0.0,2.55,85.05 +2020-03-07 00:00:00,-0.76,0.0,-0.0,0.0,2.69,88.45 +2020-03-07 01:00:00,-0.53,0.0,-0.0,0.0,2.76,88.45 +2020-03-07 02:00:00,-0.54,0.0,-0.0,0.0,2.62,88.45 +2020-03-07 03:00:00,-0.82,0.0,-0.0,0.0,2.41,88.45 +2020-03-07 04:00:00,-1.24,0.0,-0.0,0.0,2.28,88.4 +2020-03-07 05:00:00,-1.61,0.0,-0.0,0.0,2.21,88.35 +2020-03-07 06:00:00,-1.69,28.0,37.05,25.0,2.07,88.35 +2020-03-07 07:00:00,0.25,190.0,496.73,74.0,1.79,88.5 +2020-03-07 08:00:00,2.62,318.0,533.29,123.0,2.48,79.0 +2020-03-07 09:00:00,3.66,378.0,377.92,201.0,2.83,70.5 +2020-03-07 10:00:00,4.62,253.0,31.8,236.0,2.07,67.9 +2020-03-07 11:00:00,5.32,358.0,139.3,280.0,1.38,63.05 +2020-03-07 12:00:00,5.51,109.0,0.0,109.0,0.97,63.05 +2020-03-07 13:00:00,5.26,171.0,4.13,169.0,0.83,63.05 +2020-03-07 14:00:00,5.42,222.0,103.25,182.0,0.62,63.05 +2020-03-07 15:00:00,5.3,142.0,107.61,114.0,0.69,63.05 +2020-03-07 16:00:00,4.98,72.0,343.25,34.0,0.9,65.4 +2020-03-07 17:00:00,4.06,0.0,-0.0,0.0,1.45,73.3 +2020-03-07 18:00:00,2.17,0.0,-0.0,0.0,1.45,82.15 +2020-03-07 19:00:00,2.35,0.0,-0.0,0.0,0.21,82.15 +2020-03-07 20:00:00,2.03,0.0,-0.0,0.0,0.41,85.3 +2020-03-07 21:00:00,1.72,0.0,-0.0,0.0,0.62,85.3 +2020-03-07 22:00:00,1.42,0.0,-0.0,0.0,0.48,85.25 +2020-03-07 23:00:00,1.15,0.0,-0.0,0.0,0.48,85.25 +2020-03-08 00:00:00,0.87,0.0,-0.0,0.0,0.69,88.55 +2020-03-08 01:00:00,0.65,0.0,-0.0,0.0,0.9,85.2 +2020-03-08 02:00:00,0.42,0.0,-0.0,0.0,0.97,88.5 +2020-03-08 03:00:00,-0.86,0.0,-0.0,0.0,1.17,91.95 +2020-03-08 04:00:00,-1.79,0.0,-0.0,0.0,1.52,95.6 +2020-03-08 05:00:00,-2.27,0.0,-0.0,0.0,1.66,95.55 +2020-03-08 06:00:00,-2.02,6.0,0.0,6.0,1.45,99.4 +2020-03-08 07:00:00,-0.67,0.0,0.0,0.0,1.17,95.6 +2020-03-08 08:00:00,1.41,167.0,32.29,155.0,1.72,88.6 +2020-03-08 09:00:00,2.62,164.0,4.22,162.0,1.86,88.65 +2020-03-08 10:00:00,3.39,345.0,142.48,268.0,2.0,85.4 +2020-03-08 11:00:00,4.29,452.0,341.23,259.0,2.41,73.4 +2020-03-08 12:00:00,4.97,213.0,9.12,208.0,2.34,68.0 +2020-03-08 13:00:00,5.34,116.0,0.0,116.0,2.41,63.05 +2020-03-08 14:00:00,5.52,108.0,0.0,108.0,2.34,63.05 +2020-03-08 15:00:00,5.51,130.0,64.13,113.0,2.28,63.05 +2020-03-08 16:00:00,5.04,47.0,51.97,41.0,1.59,65.4 +2020-03-08 17:00:00,3.74,0.0,-0.0,0.0,1.24,76.15 +2020-03-08 18:00:00,3.38,0.0,-0.0,0.0,0.97,73.2 +2020-03-08 19:00:00,1.7,0.0,-0.0,0.0,1.1,78.95 +2020-03-08 20:00:00,-0.13,0.0,-0.0,0.0,1.52,85.1 +2020-03-08 21:00:00,-0.94,0.0,-0.0,0.0,1.66,88.4 +2020-03-08 22:00:00,-1.43,0.0,-0.0,0.0,1.66,88.35 +2020-03-08 23:00:00,-1.27,0.0,-0.0,0.0,1.59,85.0 +2020-03-09 00:00:00,-1.55,0.0,-0.0,0.0,1.52,88.35 +2020-03-09 01:00:00,-2.03,0.0,-0.0,0.0,1.52,91.85 +2020-03-09 02:00:00,-2.35,0.0,-0.0,0.0,1.45,88.3 +2020-03-09 03:00:00,-2.55,0.0,-0.0,0.0,1.45,91.85 +2020-03-09 04:00:00,-2.74,0.0,-0.0,0.0,1.52,91.85 +2020-03-09 05:00:00,-2.84,0.0,-0.0,0.0,1.59,88.25 +2020-03-09 06:00:00,-2.77,38.0,64.49,32.0,1.59,88.25 +2020-03-09 07:00:00,-1.12,0.0,0.0,0.0,1.31,95.6 +2020-03-09 08:00:00,3.19,315.0,452.74,144.0,1.24,82.2 +2020-03-09 09:00:00,5.16,468.0,722.57,121.0,2.14,68.1 +2020-03-09 10:00:00,6.19,553.0,787.19,123.0,3.24,60.85 +2020-03-09 11:00:00,6.74,567.0,729.99,150.0,3.59,56.45 +2020-03-09 12:00:00,7.14,446.0,350.48,252.0,3.59,54.3 +2020-03-09 13:00:00,7.17,490.0,758.67,115.0,3.52,52.2 +2020-03-09 14:00:00,7.15,377.0,694.21,101.0,3.59,50.2 +2020-03-09 15:00:00,6.88,124.0,48.15,111.0,3.38,50.2 +2020-03-09 16:00:00,6.12,52.0,66.56,44.0,2.62,56.2 +2020-03-09 17:00:00,4.9,0.0,-0.0,0.0,2.28,62.95 +2020-03-09 18:00:00,3.77,0.0,-0.0,0.0,2.21,67.8 +2020-03-09 19:00:00,2.75,0.0,-0.0,0.0,2.0,73.2 +2020-03-09 20:00:00,1.94,0.0,-0.0,0.0,1.93,82.05 +2020-03-09 21:00:00,1.24,0.0,-0.0,0.0,2.0,85.25 +2020-03-09 22:00:00,0.63,0.0,-0.0,0.0,2.07,85.2 +2020-03-09 23:00:00,0.41,0.0,-0.0,0.0,1.93,88.5 +2020-03-10 00:00:00,0.13,0.0,-0.0,0.0,1.86,88.5 +2020-03-10 01:00:00,0.41,0.0,-0.0,0.0,1.66,92.0 +2020-03-10 02:00:00,0.31,0.0,-0.0,0.0,1.52,92.0 +2020-03-10 03:00:00,-0.14,0.0,-0.0,0.0,1.38,92.0 +2020-03-10 04:00:00,-0.04,0.0,-0.0,0.0,1.31,95.6 +2020-03-10 05:00:00,-0.08,0.0,-0.0,0.0,1.31,95.6 +2020-03-10 06:00:00,-0.01,34.0,30.27,31.0,1.31,95.6 +2020-03-10 07:00:00,1.43,90.0,11.92,87.0,0.97,95.65 +2020-03-10 08:00:00,3.21,113.0,0.0,113.0,1.72,88.7 +2020-03-10 09:00:00,4.5,109.0,0.0,109.0,2.0,85.5 +2020-03-10 10:00:00,5.35,91.0,0.0,91.0,2.34,79.35 +2020-03-10 11:00:00,6.19,114.0,0.0,114.0,2.62,73.7 +2020-03-10 12:00:00,6.76,77.0,0.0,77.0,2.76,73.8 +2020-03-10 13:00:00,7.16,129.0,0.0,129.0,2.83,73.8 +2020-03-10 14:00:00,7.23,142.0,4.97,140.0,2.9,73.9 +2020-03-10 15:00:00,7.05,59.0,0.0,59.0,2.76,76.65 +2020-03-10 16:00:00,6.69,46.0,32.02,42.0,2.83,76.65 +2020-03-10 17:00:00,6.01,0.0,-0.0,0.0,2.76,79.4 +2020-03-10 18:00:00,5.56,0.0,-0.0,0.0,2.69,79.35 +2020-03-10 19:00:00,5.04,0.0,-0.0,0.0,2.34,82.35 +2020-03-10 20:00:00,4.6,0.0,-0.0,0.0,2.14,85.5 +2020-03-10 21:00:00,4.14,0.0,-0.0,0.0,1.93,85.5 +2020-03-10 22:00:00,3.51,0.0,-0.0,0.0,1.66,95.7 +2020-03-10 23:00:00,3.07,0.0,-0.0,0.0,1.52,92.15 +2020-03-11 00:00:00,2.59,0.0,-0.0,0.0,1.31,95.7 +2020-03-11 01:00:00,2.49,0.0,-0.0,0.0,1.24,95.7 +2020-03-11 02:00:00,2.4,0.0,-0.0,0.0,1.24,95.7 +2020-03-11 03:00:00,2.23,0.0,-0.0,0.0,1.24,95.7 +2020-03-11 04:00:00,2.31,0.0,-0.0,0.0,1.31,95.7 +2020-03-11 05:00:00,2.14,0.0,-0.0,0.0,1.38,95.7 +2020-03-11 06:00:00,1.93,23.0,0.0,23.0,1.38,95.65 +2020-03-11 07:00:00,2.35,75.0,0.0,75.0,1.45,95.7 +2020-03-11 08:00:00,3.24,124.0,2.57,123.0,1.93,92.15 +2020-03-11 09:00:00,4.04,211.0,18.29,202.0,2.28,88.75 +2020-03-11 10:00:00,4.82,163.0,0.0,163.0,3.17,76.3 +2020-03-11 11:00:00,5.86,117.0,0.0,117.0,3.45,65.65 +2020-03-11 12:00:00,5.73,68.0,0.0,68.0,3.31,65.65 +2020-03-11 13:00:00,5.84,61.0,0.0,61.0,3.1,65.65 +2020-03-11 14:00:00,5.52,67.0,0.0,67.0,2.83,68.1 +2020-03-11 15:00:00,5.34,47.0,0.0,47.0,2.97,70.8 +2020-03-11 16:00:00,4.94,34.0,7.71,33.0,2.76,73.45 +2020-03-11 17:00:00,4.35,0.0,-0.0,0.0,2.55,76.25 +2020-03-11 18:00:00,3.65,0.0,-0.0,0.0,2.34,79.15 +2020-03-11 19:00:00,3.21,0.0,-0.0,0.0,2.28,82.2 +2020-03-11 20:00:00,2.83,0.0,-0.0,0.0,2.21,82.2 +2020-03-11 21:00:00,2.37,0.0,-0.0,0.0,2.14,85.35 +2020-03-11 22:00:00,1.88,0.0,-0.0,0.0,1.93,88.65 +2020-03-11 23:00:00,1.61,0.0,-0.0,0.0,1.79,92.05 +2020-03-12 00:00:00,1.42,0.0,-0.0,0.0,0.0,92.05 +2020-03-12 01:00:00,1.3,0.0,-0.0,0.0,0.0,88.6 +2020-03-12 02:00:00,1.27,0.0,-0.0,0.0,1.79,92.05 +2020-03-12 03:00:00,1.29,0.0,-0.0,0.0,1.52,92.05 +2020-03-12 04:00:00,1.25,0.0,-0.0,0.0,1.45,95.65 +2020-03-12 05:00:00,1.26,0.0,-0.0,0.0,1.66,95.65 +2020-03-12 06:00:00,1.31,20.0,0.0,20.0,2.14,92.05 +2020-03-12 07:00:00,1.53,62.0,0.0,62.0,2.41,92.05 +2020-03-12 08:00:00,1.84,97.0,0.0,97.0,2.69,88.65 +2020-03-12 09:00:00,2.2,87.0,0.0,87.0,2.83,82.15 +2020-03-12 10:00:00,2.26,92.0,0.0,92.0,2.69,82.15 +2020-03-12 11:00:00,2.5,106.0,0.0,106.0,2.55,82.15 +2020-03-12 12:00:00,2.93,66.0,0.0,66.0,2.48,79.1 +2020-03-12 13:00:00,2.94,83.0,0.0,83.0,2.41,79.1 +2020-03-12 14:00:00,2.99,121.0,0.0,121.0,2.34,79.1 +2020-03-12 15:00:00,3.1,94.0,7.03,92.0,2.62,79.1 +2020-03-12 16:00:00,2.97,41.0,7.44,40.0,2.69,79.1 +2020-03-12 17:00:00,2.71,0.0,-0.0,0.0,2.14,79.1 +2020-03-12 18:00:00,2.51,0.0,-0.0,0.0,2.14,85.35 +2020-03-12 19:00:00,1.99,0.0,-0.0,0.0,2.07,88.65 +2020-03-12 20:00:00,1.79,0.0,-0.0,0.0,1.93,88.65 +2020-03-12 21:00:00,1.64,0.0,-0.0,0.0,1.79,88.65 +2020-03-12 22:00:00,1.48,0.0,-0.0,0.0,1.72,92.05 +2020-03-12 23:00:00,1.3,0.0,-0.0,0.0,2.0,92.05 +2020-03-13 00:00:00,1.24,0.0,-0.0,0.0,2.07,92.05 +2020-03-13 01:00:00,0.99,0.0,-0.0,0.0,1.38,99.35 +2020-03-13 02:00:00,1.0,0.0,-0.0,0.0,1.59,99.35 +2020-03-13 03:00:00,0.99,0.0,-0.0,0.0,1.59,99.35 +2020-03-13 04:00:00,1.02,0.0,-0.0,0.0,1.66,99.35 +2020-03-13 05:00:00,0.99,0.0,-0.0,0.0,1.93,99.35 +2020-03-13 06:00:00,0.96,14.0,0.0,14.0,2.41,95.65 +2020-03-13 07:00:00,0.91,60.0,0.0,60.0,2.62,92.05 +2020-03-13 08:00:00,1.27,72.0,0.0,72.0,2.83,92.05 +2020-03-13 09:00:00,1.5,80.0,0.0,80.0,3.1,92.05 +2020-03-13 10:00:00,1.59,69.0,0.0,69.0,3.24,88.6 +2020-03-13 11:00:00,1.88,65.0,0.0,65.0,3.31,85.3 +2020-03-13 12:00:00,1.7,58.0,0.0,58.0,3.24,82.05 +2020-03-13 13:00:00,2.02,69.0,0.0,69.0,3.1,82.05 +2020-03-13 14:00:00,2.46,59.0,0.0,59.0,3.1,79.0 +2020-03-13 15:00:00,2.35,35.0,0.0,35.0,3.03,79.0 +2020-03-13 16:00:00,2.16,23.0,0.0,23.0,2.9,79.0 +2020-03-13 17:00:00,1.95,0.0,-0.0,0.0,2.62,85.3 +2020-03-13 18:00:00,1.71,0.0,-0.0,0.0,2.48,85.3 +2020-03-13 19:00:00,0.84,0.0,-0.0,0.0,2.62,85.2 +2020-03-13 20:00:00,0.65,0.0,-0.0,0.0,2.28,85.2 +2020-03-13 21:00:00,0.41,0.0,-0.0,0.0,1.86,88.5 +2020-03-13 22:00:00,-0.09,0.0,-0.0,0.0,2.14,92.0 +2020-03-13 23:00:00,-0.31,0.0,-0.0,0.0,1.93,92.0 +2020-03-14 00:00:00,-0.38,0.0,-0.0,0.0,2.0,88.45 +2020-03-14 01:00:00,-0.85,0.0,-0.0,0.0,2.0,88.45 +2020-03-14 02:00:00,-1.23,0.0,-0.0,0.0,1.93,91.95 +2020-03-14 03:00:00,-1.65,0.0,-0.0,0.0,1.72,91.9 +2020-03-14 04:00:00,-1.9,0.0,-0.0,0.0,1.52,91.9 +2020-03-14 05:00:00,-2.47,0.0,-0.0,0.0,1.38,95.55 +2020-03-14 06:00:00,-2.15,43.0,24.27,40.0,1.38,95.55 +2020-03-14 07:00:00,-0.76,196.0,289.69,116.0,1.93,88.45 +2020-03-14 08:00:00,0.16,355.0,509.94,147.0,2.07,81.9 +2020-03-14 09:00:00,1.25,245.0,35.3,227.0,2.48,78.9 +2020-03-14 10:00:00,2.24,231.0,8.69,226.0,2.14,73.1 +2020-03-14 11:00:00,2.99,409.0,176.9,303.0,1.66,70.4 +2020-03-14 12:00:00,4.02,466.0,341.1,268.0,1.79,67.8 +2020-03-14 13:00:00,4.6,523.0,776.58,119.0,2.14,67.9 +2020-03-14 14:00:00,4.75,407.0,714.7,105.0,2.0,65.4 +2020-03-14 15:00:00,4.56,241.0,469.2,103.0,1.93,65.3 +2020-03-14 16:00:00,3.92,85.0,208.82,55.0,2.28,67.8 +2020-03-14 17:00:00,2.86,0.0,-0.0,0.0,1.86,70.4 +2020-03-14 18:00:00,1.83,0.0,-0.0,0.0,1.72,75.95 +2020-03-14 19:00:00,1.85,0.0,-0.0,0.0,1.66,78.95 +2020-03-14 20:00:00,1.38,0.0,-0.0,0.0,1.72,82.0 +2020-03-14 21:00:00,1.19,0.0,-0.0,0.0,1.79,82.0 +2020-03-14 22:00:00,1.17,0.0,-0.0,0.0,1.86,82.0 +2020-03-14 23:00:00,1.12,0.0,-0.0,0.0,2.0,82.0 +2020-03-15 00:00:00,1.19,0.0,-0.0,0.0,2.14,82.0 +2020-03-15 01:00:00,1.51,0.0,-0.0,0.0,2.28,85.25 +2020-03-15 02:00:00,1.44,0.0,-0.0,0.0,2.48,88.6 +2020-03-15 03:00:00,1.23,0.0,-0.0,0.0,2.62,92.05 +2020-03-15 04:00:00,0.99,0.0,-0.0,0.0,2.76,95.65 +2020-03-15 05:00:00,0.83,0.0,-0.0,0.0,2.62,95.65 +2020-03-15 06:00:00,0.81,26.0,0.0,26.0,2.41,95.65 +2020-03-15 07:00:00,0.94,21.0,0.0,21.0,2.48,95.65 +2020-03-15 08:00:00,1.46,108.0,0.0,108.0,3.03,95.65 +2020-03-15 09:00:00,2.84,124.0,0.0,124.0,3.72,79.1 +2020-03-15 10:00:00,3.53,175.0,0.0,175.0,3.93,79.1 +2020-03-15 11:00:00,4.28,110.0,0.0,110.0,4.21,76.25 +2020-03-15 12:00:00,4.46,80.0,0.0,80.0,4.14,76.25 +2020-03-15 13:00:00,4.44,80.0,0.0,80.0,4.28,73.4 +2020-03-15 14:00:00,4.33,49.0,0.0,49.0,4.34,76.25 +2020-03-15 15:00:00,4.23,66.0,0.0,66.0,4.21,76.25 +2020-03-15 16:00:00,3.77,49.0,13.49,47.0,3.31,79.15 +2020-03-15 17:00:00,2.83,0.0,-0.0,0.0,2.69,85.4 +2020-03-15 18:00:00,2.15,0.0,-0.0,0.0,2.69,88.65 +2020-03-15 19:00:00,1.36,0.0,-0.0,0.0,2.69,92.05 +2020-03-15 20:00:00,1.25,0.0,-0.0,0.0,2.76,92.05 +2020-03-15 21:00:00,1.28,0.0,-0.0,0.0,3.03,92.05 +2020-03-15 22:00:00,1.02,0.0,-0.0,0.0,3.1,92.05 +2020-03-15 23:00:00,0.68,0.0,-0.0,0.0,2.97,88.55 +2020-03-16 00:00:00,0.6,0.0,-0.0,0.0,2.97,92.0 +2020-03-16 01:00:00,0.79,0.0,-0.0,0.0,2.83,88.55 +2020-03-16 02:00:00,0.54,0.0,-0.0,0.0,2.76,92.0 +2020-03-16 03:00:00,0.01,0.0,-0.0,0.0,2.69,92.0 +2020-03-16 04:00:00,-0.05,0.0,-0.0,0.0,2.9,92.0 +2020-03-16 05:00:00,-0.09,0.0,-0.0,0.0,2.9,92.0 +2020-03-16 06:00:00,-0.2,81.0,220.64,51.0,3.38,92.0 +2020-03-16 07:00:00,0.85,226.0,409.15,108.0,3.66,85.2 +2020-03-16 08:00:00,1.64,390.0,650.08,117.0,3.79,78.95 +2020-03-16 09:00:00,2.99,466.0,532.94,188.0,3.72,73.2 +2020-03-16 10:00:00,4.65,395.0,173.9,293.0,3.59,65.4 +2020-03-16 11:00:00,6.42,575.0,612.9,201.0,3.86,58.55 +2020-03-16 12:00:00,7.52,399.0,174.26,296.0,3.79,54.4 +2020-03-16 13:00:00,8.08,260.0,39.59,239.0,3.86,52.45 +2020-03-16 14:00:00,8.27,104.0,0.0,104.0,3.79,50.6 +2020-03-16 15:00:00,7.9,74.0,0.0,74.0,3.38,52.45 +2020-03-16 16:00:00,7.43,70.0,71.92,59.0,2.83,56.55 +2020-03-16 17:00:00,6.35,0.0,-0.0,0.0,2.48,60.85 +2020-03-16 18:00:00,4.69,0.0,-0.0,0.0,2.34,68.0 +2020-03-16 19:00:00,3.63,0.0,-0.0,0.0,2.28,76.1 +2020-03-16 20:00:00,2.53,0.0,-0.0,0.0,2.21,79.0 +2020-03-16 21:00:00,1.32,0.0,-0.0,0.0,2.14,85.25 +2020-03-16 22:00:00,0.28,0.0,-0.0,0.0,2.07,88.5 +2020-03-16 23:00:00,-0.63,0.0,-0.0,0.0,2.0,91.95 +2020-03-17 00:00:00,-1.32,0.0,-0.0,0.0,1.93,91.95 +2020-03-17 01:00:00,-1.95,0.0,-0.0,0.0,1.86,95.55 +2020-03-17 02:00:00,-2.17,0.0,-0.0,0.0,1.79,91.85 +2020-03-17 03:00:00,-2.37,0.0,-0.0,0.0,1.79,91.85 +2020-03-17 04:00:00,-2.55,0.0,-0.0,0.0,1.79,95.55 +2020-03-17 05:00:00,-2.6,0.0,-0.0,0.0,1.72,95.55 +2020-03-17 06:00:00,-1.99,90.0,267.33,52.0,1.52,95.55 +2020-03-17 07:00:00,1.12,261.0,611.15,81.0,1.03,85.25 +2020-03-17 08:00:00,4.61,417.0,751.24,97.0,0.97,79.2 +2020-03-17 09:00:00,6.44,534.0,796.21,114.0,1.1,70.95 +2020-03-17 10:00:00,7.77,616.0,842.61,117.0,1.1,63.6 +2020-03-17 11:00:00,8.89,645.0,854.33,119.0,1.03,59.15 +2020-03-17 12:00:00,9.7,517.0,454.44,246.0,0.83,55.05 +2020-03-17 13:00:00,10.22,558.0,855.32,100.0,0.48,53.1 +2020-03-17 14:00:00,10.41,428.0,750.16,100.0,0.41,53.1 +2020-03-17 15:00:00,10.25,275.0,616.2,85.0,0.9,53.1 +2020-03-17 16:00:00,9.49,116.0,431.6,48.0,1.38,59.25 +2020-03-17 17:00:00,7.67,0.0,-0.0,0.0,1.86,68.5 +2020-03-17 18:00:00,5.64,0.0,-0.0,0.0,1.66,79.35 +2020-03-17 19:00:00,6.6,0.0,-0.0,0.0,0.69,65.75 +2020-03-17 20:00:00,6.28,0.0,-0.0,0.0,0.28,65.75 +2020-03-17 21:00:00,5.62,0.0,-0.0,0.0,1.17,70.8 +2020-03-17 22:00:00,1.65,0.0,-0.0,0.0,1.79,78.95 +2020-03-17 23:00:00,0.15,0.0,-0.0,0.0,2.0,85.15 +2020-03-18 00:00:00,-0.76,0.0,-0.0,0.0,2.0,88.45 +2020-03-18 01:00:00,-1.41,0.0,-0.0,0.0,2.07,91.9 +2020-03-18 02:00:00,-1.5,0.0,-0.0,0.0,2.14,91.9 +2020-03-18 03:00:00,-1.48,0.0,-0.0,0.0,2.28,91.9 +2020-03-18 04:00:00,-1.39,0.0,-0.0,0.0,2.34,88.4 +2020-03-18 05:00:00,-1.26,0.0,-0.0,0.0,2.41,88.4 +2020-03-18 06:00:00,-0.58,91.0,235.96,56.0,2.48,88.45 +2020-03-18 07:00:00,2.71,256.0,545.49,92.0,2.62,79.1 +2020-03-18 08:00:00,4.64,409.0,694.5,109.0,3.52,76.25 +2020-03-18 09:00:00,5.44,530.0,768.74,120.0,4.0,70.8 +2020-03-18 10:00:00,6.09,604.0,787.81,133.0,4.07,68.2 +2020-03-18 11:00:00,6.68,643.0,835.58,124.0,4.0,65.85 +2020-03-18 12:00:00,7.1,489.0,364.05,270.0,4.0,68.4 +2020-03-18 13:00:00,7.13,527.0,716.02,140.0,4.0,71.05 +2020-03-18 14:00:00,6.78,223.0,54.29,199.0,3.79,71.05 +2020-03-18 15:00:00,6.11,81.0,0.0,81.0,3.52,76.5 +2020-03-18 16:00:00,5.47,60.0,24.67,56.0,3.17,76.4 +2020-03-18 17:00:00,4.85,0.0,-0.0,0.0,2.41,79.3 +2020-03-18 18:00:00,4.13,0.0,-0.0,0.0,2.0,85.45 +2020-03-18 19:00:00,3.44,0.0,-0.0,0.0,2.0,88.7 +2020-03-18 20:00:00,3.19,0.0,-0.0,0.0,1.86,88.7 +2020-03-18 21:00:00,3.12,0.0,-0.0,0.0,1.72,88.7 +2020-03-18 22:00:00,3.1,0.0,-0.0,0.0,1.52,92.15 +2020-03-18 23:00:00,3.01,0.0,-0.0,0.0,1.31,92.15 +2020-03-19 00:00:00,3.05,0.0,-0.0,0.0,1.24,92.15 +2020-03-19 01:00:00,3.47,0.0,-0.0,0.0,1.24,99.4 +2020-03-19 02:00:00,3.46,0.0,-0.0,0.0,1.24,99.4 +2020-03-19 03:00:00,3.4,0.0,-0.0,0.0,1.31,99.4 +2020-03-19 04:00:00,3.55,0.0,-0.0,0.0,1.45,99.4 +2020-03-19 05:00:00,3.51,0.0,-0.0,0.0,1.45,100.0 +2020-03-19 06:00:00,3.54,48.0,12.94,46.0,1.93,100.0 +2020-03-19 07:00:00,3.46,89.0,0.0,89.0,2.07,100.0 +2020-03-19 08:00:00,3.62,132.0,0.0,132.0,2.0,100.0 +2020-03-19 09:00:00,3.99,190.0,3.71,188.0,1.79,99.4 +2020-03-19 10:00:00,4.89,174.0,0.0,174.0,1.93,88.85 +2020-03-19 11:00:00,5.59,315.0,41.5,289.0,2.07,82.4 +2020-03-19 12:00:00,6.42,331.0,67.57,290.0,2.41,76.55 +2020-03-19 13:00:00,6.54,207.0,7.33,203.0,2.34,76.55 +2020-03-19 14:00:00,6.69,221.0,49.23,199.0,2.69,71.05 +2020-03-19 15:00:00,6.66,195.0,160.53,144.0,2.62,68.4 +2020-03-19 16:00:00,6.24,96.0,173.94,67.0,2.07,70.95 +2020-03-19 17:00:00,5.52,0.0,0.0,0.0,1.72,79.35 +2020-03-19 18:00:00,4.78,0.0,-0.0,0.0,1.72,79.3 +2020-03-19 19:00:00,4.28,0.0,-0.0,0.0,2.07,88.8 +2020-03-19 20:00:00,4.07,0.0,-0.0,0.0,2.07,88.75 +2020-03-19 21:00:00,3.91,0.0,-0.0,0.0,2.14,88.75 +2020-03-19 22:00:00,3.78,0.0,-0.0,0.0,2.28,85.45 +2020-03-19 23:00:00,3.82,0.0,-0.0,0.0,2.62,85.45 +2020-03-20 00:00:00,3.52,0.0,-0.0,0.0,2.83,85.4 +2020-03-20 01:00:00,3.16,0.0,-0.0,0.0,3.03,82.2 +2020-03-20 02:00:00,2.84,0.0,-0.0,0.0,3.24,82.2 +2020-03-20 03:00:00,2.34,0.0,-0.0,0.0,3.31,85.35 +2020-03-20 04:00:00,2.16,0.0,-0.0,0.0,3.59,85.35 +2020-03-20 05:00:00,1.97,0.0,-0.0,0.0,3.66,88.65 +2020-03-20 06:00:00,2.05,44.0,6.22,43.0,3.66,88.65 +2020-03-20 07:00:00,2.7,89.0,0.0,89.0,3.93,85.4 +2020-03-20 08:00:00,3.12,114.0,0.0,114.0,4.55,82.2 +2020-03-20 09:00:00,3.69,119.0,0.0,119.0,4.83,76.15 +2020-03-20 10:00:00,4.18,112.0,0.0,112.0,4.76,73.4 +2020-03-20 11:00:00,4.67,90.0,0.0,90.0,4.69,70.7 +2020-03-20 12:00:00,5.24,77.0,0.0,77.0,4.76,68.1 +2020-03-20 13:00:00,5.43,282.0,50.87,254.0,4.62,70.8 +2020-03-20 14:00:00,5.53,88.0,0.0,88.0,4.21,70.8 +2020-03-20 15:00:00,5.53,100.0,3.1,99.0,4.62,73.55 +2020-03-20 16:00:00,5.31,63.0,23.35,59.0,3.52,76.4 +2020-03-20 17:00:00,4.99,0.0,0.0,0.0,3.59,79.3 +2020-03-20 18:00:00,4.66,0.0,-0.0,0.0,3.59,79.3 +2020-03-20 19:00:00,4.35,0.0,-0.0,0.0,3.86,82.3 +2020-03-20 20:00:00,4.32,0.0,-0.0,0.0,3.93,85.5 +2020-03-20 21:00:00,4.27,0.0,-0.0,0.0,3.86,85.5 +2020-03-20 22:00:00,4.27,0.0,-0.0,0.0,3.66,88.8 +2020-03-20 23:00:00,4.2,0.0,-0.0,0.0,3.52,92.2 +2020-03-21 00:00:00,4.13,0.0,-0.0,0.0,3.45,95.7 +2020-03-21 01:00:00,4.14,0.0,-0.0,0.0,3.45,95.75 +2020-03-21 02:00:00,3.92,0.0,-0.0,0.0,3.59,99.4 +2020-03-21 03:00:00,3.6,0.0,-0.0,0.0,3.52,99.4 +2020-03-21 04:00:00,3.19,0.0,-0.0,0.0,3.45,95.7 +2020-03-21 05:00:00,2.96,0.0,0.0,0.0,3.31,95.7 +2020-03-21 06:00:00,3.03,68.0,41.95,61.0,3.17,95.7 +2020-03-21 07:00:00,3.87,86.0,0.0,86.0,3.38,95.7 +2020-03-21 08:00:00,4.58,224.0,51.13,201.0,4.0,92.2 +2020-03-21 09:00:00,4.7,212.0,7.26,208.0,3.79,92.25 +2020-03-21 10:00:00,5.32,198.0,1.63,197.0,3.38,88.85 +2020-03-21 11:00:00,6.31,106.0,0.0,106.0,3.93,79.5 +2020-03-21 12:00:00,7.27,123.0,0.0,123.0,4.83,71.15 +2020-03-21 13:00:00,7.32,85.0,0.0,85.0,5.03,71.15 +2020-03-21 14:00:00,7.25,150.0,2.19,149.0,5.1,68.5 +2020-03-21 15:00:00,7.32,78.0,0.0,78.0,5.03,68.5 +2020-03-21 16:00:00,6.79,60.0,17.06,57.0,4.0,73.8 +2020-03-21 17:00:00,6.12,0.0,0.0,0.0,3.72,79.4 +2020-03-21 18:00:00,5.5,0.0,-0.0,0.0,3.93,85.6 +2020-03-21 19:00:00,4.81,0.0,-0.0,0.0,4.0,88.85 +2020-03-21 20:00:00,4.4,0.0,-0.0,0.0,4.14,92.2 +2020-03-21 21:00:00,4.24,0.0,-0.0,0.0,4.0,92.2 +2020-03-21 22:00:00,4.09,0.0,-0.0,0.0,4.07,95.7 +2020-03-21 23:00:00,3.94,0.0,-0.0,0.0,4.21,95.7 +2020-03-22 00:00:00,3.75,0.0,-0.0,0.0,4.14,95.7 +2020-03-22 01:00:00,3.6,0.0,-0.0,0.0,4.0,99.4 +2020-03-22 02:00:00,3.43,0.0,-0.0,0.0,3.72,99.4 +2020-03-22 03:00:00,3.35,0.0,-0.0,0.0,3.59,99.4 +2020-03-22 04:00:00,3.24,0.0,-0.0,0.0,3.79,95.7 +2020-03-22 05:00:00,3.07,0.0,0.0,0.0,3.72,92.15 +2020-03-22 06:00:00,3.04,60.0,17.34,57.0,4.14,92.15 +2020-03-22 07:00:00,3.74,124.0,15.38,119.0,2.76,95.7 +2020-03-22 08:00:00,4.57,192.0,19.75,183.0,0.0,99.4 +2020-03-22 09:00:00,5.38,176.0,0.0,176.0,0.0,95.75 +2020-03-22 10:00:00,6.06,122.0,0.0,122.0,3.38,92.3 +2020-03-22 11:00:00,6.63,405.0,129.17,322.0,3.03,92.3 +2020-03-22 12:00:00,7.14,187.0,0.0,187.0,3.1,89.0 +2020-03-22 13:00:00,7.7,50.0,0.0,50.0,3.31,79.7 +2020-03-22 14:00:00,7.17,220.0,39.04,202.0,3.31,79.65 +2020-03-22 15:00:00,7.09,162.0,57.31,143.0,2.97,82.6 +2020-03-22 16:00:00,6.42,89.0,94.27,72.0,2.83,79.5 +2020-03-22 17:00:00,5.48,0.0,0.0,0.0,2.07,85.6 +2020-03-22 18:00:00,4.59,0.0,-0.0,0.0,1.79,92.2 +2020-03-22 19:00:00,3.98,0.0,-0.0,0.0,1.93,92.15 +2020-03-22 20:00:00,3.8,0.0,-0.0,0.0,1.79,92.15 +2020-03-22 21:00:00,3.34,0.0,-0.0,0.0,1.72,95.7 +2020-03-22 22:00:00,3.16,0.0,-0.0,0.0,1.59,95.7 +2020-03-22 23:00:00,2.93,0.0,-0.0,0.0,1.52,92.15 +2020-03-23 00:00:00,2.55,0.0,-0.0,0.0,1.59,95.7 +2020-03-23 01:00:00,2.68,0.0,-0.0,0.0,1.59,92.15 +2020-03-23 02:00:00,2.85,0.0,-0.0,0.0,1.59,92.15 +2020-03-23 03:00:00,2.92,0.0,-0.0,0.0,1.52,95.7 +2020-03-23 04:00:00,2.98,0.0,-0.0,0.0,1.52,95.7 +2020-03-23 05:00:00,2.74,0.0,0.0,0.0,1.45,95.7 +2020-03-23 06:00:00,2.92,126.0,351.54,63.0,1.45,95.7 +2020-03-23 07:00:00,3.83,142.0,30.21,132.0,2.21,95.7 +2020-03-23 08:00:00,4.64,275.0,119.13,220.0,2.21,92.2 +2020-03-23 09:00:00,5.49,291.0,53.37,261.0,2.62,82.4 +2020-03-23 10:00:00,6.41,393.0,127.87,313.0,2.9,70.95 +2020-03-23 11:00:00,7.21,91.0,0.0,91.0,3.38,63.5 +2020-03-23 12:00:00,7.37,161.0,0.0,161.0,3.72,63.5 +2020-03-23 13:00:00,7.22,113.0,0.0,113.0,4.0,65.95 +2020-03-23 14:00:00,7.0,110.0,0.0,110.0,4.14,65.85 +2020-03-23 15:00:00,6.54,71.0,0.0,71.0,3.79,70.95 +2020-03-23 16:00:00,5.7,63.0,16.23,60.0,2.97,76.5 +2020-03-23 17:00:00,5.03,0.0,0.0,0.0,2.83,82.35 +2020-03-23 18:00:00,4.21,0.0,-0.0,0.0,2.62,85.5 +2020-03-23 19:00:00,3.88,0.0,-0.0,0.0,2.41,85.45 +2020-03-23 20:00:00,3.7,0.0,-0.0,0.0,2.28,85.45 +2020-03-23 21:00:00,3.32,0.0,-0.0,0.0,2.21,88.7 +2020-03-23 22:00:00,2.95,0.0,-0.0,0.0,2.14,88.7 +2020-03-23 23:00:00,2.74,0.0,-0.0,0.0,2.0,88.7 +2020-03-24 00:00:00,2.38,0.0,-0.0,0.0,1.93,92.15 +2020-03-24 01:00:00,2.32,0.0,-0.0,0.0,1.86,92.15 +2020-03-24 02:00:00,1.89,0.0,-0.0,0.0,1.86,95.65 +2020-03-24 03:00:00,1.68,0.0,-0.0,0.0,1.93,95.65 +2020-03-24 04:00:00,1.45,0.0,-0.0,0.0,1.86,99.4 +2020-03-24 05:00:00,1.22,0.0,0.0,0.0,1.72,95.65 +2020-03-24 06:00:00,1.54,83.0,59.34,72.0,2.41,95.65 +2020-03-24 07:00:00,2.14,138.0,20.77,131.0,1.86,95.7 +2020-03-24 08:00:00,2.53,143.0,0.0,143.0,2.55,92.15 +2020-03-24 09:00:00,3.76,125.0,0.0,125.0,2.48,88.75 +2020-03-24 10:00:00,5.28,223.0,3.17,221.0,2.62,79.35 +2020-03-24 11:00:00,6.09,493.0,267.98,318.0,2.76,73.65 +2020-03-24 12:00:00,6.84,475.0,264.18,308.0,2.97,65.85 +2020-03-24 13:00:00,6.98,420.0,254.44,275.0,2.97,65.85 +2020-03-24 14:00:00,6.98,430.0,599.43,148.0,2.83,63.35 +2020-03-24 15:00:00,7.12,301.0,593.0,99.0,2.9,58.65 +2020-03-24 16:00:00,6.72,150.0,496.55,56.0,2.55,56.45 +2020-03-24 17:00:00,6.07,0.0,0.0,0.0,1.66,60.75 +2020-03-24 18:00:00,4.94,0.0,-0.0,0.0,0.97,73.45 +2020-03-24 19:00:00,5.73,0.0,-0.0,0.0,0.62,58.45 +2020-03-24 20:00:00,2.66,0.0,-0.0,0.0,1.59,76.1 +2020-03-24 21:00:00,1.95,0.0,-0.0,0.0,1.72,78.95 +2020-03-24 22:00:00,1.76,0.0,-0.0,0.0,1.93,78.95 +2020-03-24 23:00:00,2.16,0.0,-0.0,0.0,2.0,76.0 +2020-03-25 00:00:00,1.92,0.0,-0.0,0.0,2.0,78.95 +2020-03-25 01:00:00,2.15,0.0,-0.0,0.0,1.93,76.0 +2020-03-25 02:00:00,2.48,0.0,-0.0,0.0,1.86,76.0 +2020-03-25 03:00:00,2.96,0.0,-0.0,0.0,2.0,73.2 +2020-03-25 04:00:00,3.37,0.0,-0.0,0.0,2.07,76.1 +2020-03-25 05:00:00,3.59,0.0,0.0,0.0,2.0,82.2 +2020-03-25 06:00:00,3.93,53.0,5.22,52.0,2.0,85.45 +2020-03-25 07:00:00,5.54,140.0,23.32,132.0,2.07,82.4 +2020-03-25 08:00:00,7.02,296.0,149.98,225.0,3.1,76.65 +2020-03-25 09:00:00,7.87,273.0,34.88,253.0,3.24,68.6 +2020-03-25 10:00:00,8.66,190.0,0.0,190.0,3.24,68.7 +2020-03-25 11:00:00,9.0,187.0,0.0,187.0,3.31,63.8 +2020-03-25 12:00:00,8.81,129.0,0.0,129.0,3.1,66.25 +2020-03-25 13:00:00,8.52,153.0,0.0,153.0,3.17,71.35 +2020-03-25 14:00:00,8.27,136.0,0.0,136.0,3.1,74.05 +2020-03-25 15:00:00,8.03,74.0,0.0,74.0,2.69,76.8 +2020-03-25 16:00:00,7.7,66.0,15.48,63.0,2.69,79.7 +2020-03-25 17:00:00,6.96,1.0,0.0,1.0,2.41,82.6 +2020-03-25 18:00:00,6.24,0.0,-0.0,0.0,2.34,88.95 +2020-03-25 19:00:00,5.44,0.0,-0.0,0.0,2.34,92.25 +2020-03-25 20:00:00,5.13,0.0,-0.0,0.0,2.34,95.75 +2020-03-25 21:00:00,4.89,0.0,-0.0,0.0,2.28,95.75 +2020-03-25 22:00:00,4.67,0.0,-0.0,0.0,2.14,95.75 +2020-03-25 23:00:00,4.56,0.0,-0.0,0.0,2.14,99.4 +2020-03-26 00:00:00,4.46,0.0,-0.0,0.0,2.28,99.4 +2020-03-26 01:00:00,4.15,0.0,-0.0,0.0,2.28,99.4 +2020-03-26 02:00:00,4.11,0.0,-0.0,0.0,2.14,100.0 +2020-03-26 03:00:00,4.17,0.0,-0.0,0.0,2.07,99.4 +2020-03-26 04:00:00,4.29,0.0,-0.0,0.0,2.14,99.4 +2020-03-26 05:00:00,4.44,2.0,0.0,2.0,2.14,99.4 +2020-03-26 06:00:00,4.71,52.0,0.0,52.0,2.07,99.4 +2020-03-26 07:00:00,5.68,96.0,0.0,96.0,3.1,99.4 +2020-03-26 08:00:00,6.37,204.0,18.78,195.0,3.52,95.8 +2020-03-26 09:00:00,6.96,310.0,62.17,274.0,3.17,89.0 +2020-03-26 10:00:00,8.28,247.0,6.23,243.0,3.52,74.05 +2020-03-26 11:00:00,8.85,385.0,87.43,327.0,3.24,71.4 +2020-03-26 12:00:00,9.18,429.0,165.1,323.0,3.03,68.8 +2020-03-26 13:00:00,9.52,437.0,283.07,273.0,2.69,66.35 +2020-03-26 14:00:00,9.96,383.0,373.2,204.0,2.41,61.65 +2020-03-26 15:00:00,10.17,312.0,614.95,97.0,1.72,59.35 +2020-03-26 16:00:00,9.91,155.0,479.33,60.0,1.17,61.65 +2020-03-26 17:00:00,8.89,7.0,0.0,7.0,0.62,79.85 +2020-03-26 18:00:00,8.21,0.0,-0.0,0.0,0.97,66.15 +2020-03-26 19:00:00,4.22,0.0,-0.0,0.0,2.07,85.5 +2020-03-26 20:00:00,2.32,0.0,-0.0,0.0,2.21,88.65 +2020-03-26 21:00:00,1.48,0.0,-0.0,0.0,2.28,88.6 +2020-03-26 22:00:00,1.24,0.0,-0.0,0.0,2.48,88.6 +2020-03-26 23:00:00,1.25,0.0,-0.0,0.0,2.62,88.6 +2020-03-27 00:00:00,1.59,0.0,-0.0,0.0,2.9,88.6 +2020-03-27 01:00:00,2.07,0.0,-0.0,0.0,3.24,88.65 +2020-03-27 02:00:00,2.49,0.0,-0.0,0.0,3.59,85.35 +2020-03-27 03:00:00,2.78,0.0,-0.0,0.0,4.0,82.2 +2020-03-27 04:00:00,3.0,0.0,-0.0,0.0,4.28,82.2 +2020-03-27 05:00:00,3.1,2.0,0.0,2.0,4.48,82.2 +2020-03-27 06:00:00,3.89,140.0,304.37,78.0,4.55,82.25 +2020-03-27 07:00:00,4.34,321.0,630.89,97.0,4.48,85.5 +2020-03-27 08:00:00,6.14,482.0,775.29,106.0,4.97,76.5 +2020-03-27 09:00:00,8.03,543.0,591.83,197.0,5.1,66.05 +2020-03-27 10:00:00,10.01,662.0,800.36,144.0,4.9,59.35 +2020-03-27 11:00:00,11.76,603.0,513.13,260.0,4.34,51.5 +2020-03-27 12:00:00,13.25,552.0,423.55,278.0,3.93,46.35 +2020-03-27 13:00:00,14.1,596.0,811.56,122.0,3.45,48.15 +2020-03-27 14:00:00,14.56,371.0,313.93,219.0,3.1,50.1 +2020-03-27 15:00:00,14.5,196.0,96.03,162.0,2.69,53.95 +2020-03-27 16:00:00,13.04,62.0,4.94,61.0,2.97,62.3 +2020-03-27 17:00:00,11.39,1.0,0.0,1.0,3.59,69.25 +2020-03-27 18:00:00,9.93,0.0,-0.0,0.0,2.9,77.1 +2020-03-27 19:00:00,9.38,0.0,-0.0,0.0,2.14,79.9 +2020-03-27 20:00:00,8.75,0.0,-0.0,0.0,1.72,79.85 +2020-03-27 21:00:00,7.77,0.0,-0.0,0.0,1.45,85.85 +2020-03-27 22:00:00,6.94,0.0,-0.0,0.0,1.45,92.35 +2020-03-27 23:00:00,5.87,0.0,-0.0,0.0,1.59,92.3 +2020-03-28 00:00:00,5.26,0.0,-0.0,0.0,1.59,92.25 +2020-03-28 01:00:00,4.77,0.0,-0.0,0.0,1.72,92.25 +2020-03-28 02:00:00,4.28,0.0,-0.0,0.0,1.79,92.2 +2020-03-28 03:00:00,3.58,0.0,-0.0,0.0,1.86,95.7 +2020-03-28 04:00:00,2.95,0.0,-0.0,0.0,2.07,92.15 +2020-03-28 05:00:00,2.17,14.0,42.2,12.0,2.21,92.15 +2020-03-28 06:00:00,3.02,158.0,429.05,68.0,2.0,92.15 +2020-03-28 07:00:00,7.09,339.0,709.19,83.0,1.72,85.75 +2020-03-28 08:00:00,10.12,467.0,703.06,122.0,2.48,69.0 +2020-03-28 09:00:00,11.56,596.0,804.89,121.0,3.86,57.55 +2020-03-28 10:00:00,12.61,682.0,868.98,115.0,4.34,53.6 +2020-03-28 11:00:00,13.6,686.0,807.72,142.0,4.62,53.85 +2020-03-28 12:00:00,14.85,531.0,368.24,291.0,5.79,50.25 +2020-03-28 13:00:00,15.34,323.0,71.34,281.0,5.38,46.75 +2020-03-28 14:00:00,15.11,116.0,0.0,116.0,3.79,50.25 +2020-03-28 15:00:00,14.46,104.0,0.0,104.0,3.72,52.0 +2020-03-28 16:00:00,12.79,143.0,318.85,77.0,3.31,60.05 +2020-03-28 17:00:00,11.66,10.0,22.46,9.0,3.1,69.25 +2020-03-28 18:00:00,10.63,0.0,-0.0,0.0,3.59,69.1 +2020-03-28 19:00:00,10.51,0.0,-0.0,0.0,3.45,71.7 +2020-03-28 20:00:00,10.08,0.0,-0.0,0.0,3.79,69.0 +2020-03-28 21:00:00,9.89,0.0,-0.0,0.0,4.21,66.45 +2020-03-28 22:00:00,9.58,0.0,-0.0,0.0,3.93,74.2 +2020-03-28 23:00:00,9.37,0.0,-0.0,0.0,3.59,74.2 +2020-03-29 00:00:00,8.83,0.0,-0.0,0.0,3.38,79.85 +2020-03-29 01:00:00,8.48,0.0,-0.0,0.0,3.17,82.75 +2020-03-29 02:00:00,7.99,0.0,-0.0,0.0,2.97,82.7 +2020-03-29 03:00:00,7.46,0.0,-0.0,0.0,2.76,85.8 +2020-03-29 04:00:00,7.49,0.0,-0.0,0.0,3.31,82.65 +2020-03-29 05:00:00,7.93,14.0,18.68,13.0,4.48,76.8 +2020-03-29 06:00:00,7.8,176.0,537.51,60.0,5.59,73.95 +2020-03-29 07:00:00,7.57,294.0,422.5,139.0,5.38,76.7 +2020-03-29 08:00:00,8.18,484.0,747.36,113.0,6.28,68.7 +2020-03-29 09:00:00,8.99,353.0,102.41,292.0,5.86,66.25 +2020-03-29 10:00:00,9.78,677.0,830.13,131.0,5.79,64.0 +2020-03-29 11:00:00,10.23,639.0,613.11,223.0,5.59,64.1 +2020-03-29 12:00:00,10.51,530.0,353.37,298.0,5.45,64.1 +2020-03-29 13:00:00,10.55,352.0,102.81,291.0,5.24,64.1 +2020-03-29 14:00:00,10.62,467.0,681.33,131.0,5.1,64.1 +2020-03-29 15:00:00,10.53,337.0,688.94,87.0,4.97,61.75 +2020-03-29 16:00:00,10.26,161.0,435.28,69.0,4.55,59.5 +2020-03-29 17:00:00,9.51,16.0,61.31,13.0,3.93,59.25 +2020-03-29 18:00:00,8.54,0.0,-0.0,0.0,3.38,63.7 +2020-03-29 19:00:00,7.91,0.0,-0.0,0.0,3.24,54.55 +2020-03-29 20:00:00,7.32,0.0,-0.0,0.0,3.52,56.55 +2020-03-29 21:00:00,6.78,0.0,-0.0,0.0,3.59,65.85 +2020-03-29 22:00:00,6.33,0.0,-0.0,0.0,3.38,70.95 +2020-03-29 23:00:00,5.82,0.0,-0.0,0.0,2.9,76.5 +2020-03-30 00:00:00,5.22,0.0,-0.0,0.0,2.55,79.35 +2020-03-30 01:00:00,4.88,0.0,-0.0,0.0,2.34,85.55 +2020-03-30 02:00:00,4.57,0.0,-0.0,0.0,2.41,88.8 +2020-03-30 03:00:00,4.63,0.0,-0.0,0.0,2.28,88.8 +2020-03-30 04:00:00,4.83,0.0,-0.0,0.0,2.48,82.35 +2020-03-30 05:00:00,5.02,4.0,0.0,4.0,2.69,82.35 +2020-03-30 06:00:00,5.81,37.0,0.0,37.0,2.76,82.5 +2020-03-30 07:00:00,7.28,65.0,0.0,65.0,3.17,76.7 +2020-03-30 08:00:00,8.06,246.0,47.8,222.0,3.45,82.7 +2020-03-30 09:00:00,9.62,374.0,131.44,295.0,3.52,79.9 +2020-03-30 10:00:00,11.58,351.0,58.83,312.0,4.34,71.85 +2020-03-30 11:00:00,12.43,385.0,79.01,331.0,4.83,64.55 +2020-03-30 12:00:00,12.42,319.0,34.78,296.0,4.48,66.95 +2020-03-30 13:00:00,12.6,201.0,1.67,200.0,4.69,66.95 +2020-03-30 14:00:00,12.26,76.0,0.0,76.0,4.76,64.55 +2020-03-30 15:00:00,11.57,144.0,16.34,138.0,3.45,71.85 +2020-03-30 16:00:00,11.15,77.0,18.54,73.0,3.38,74.45 +2020-03-30 17:00:00,10.34,12.0,18.75,11.0,2.9,77.15 +2020-03-30 18:00:00,9.65,0.0,-0.0,0.0,2.55,79.9 +2020-03-30 19:00:00,8.07,0.0,-0.0,0.0,2.28,89.05 +2020-03-30 20:00:00,7.19,0.0,-0.0,0.0,2.34,89.0 +2020-03-30 21:00:00,6.55,0.0,-0.0,0.0,2.34,92.3 +2020-03-30 22:00:00,5.96,0.0,-0.0,0.0,2.34,92.3 +2020-03-30 23:00:00,5.51,0.0,-0.0,0.0,2.28,92.25 +2020-03-31 00:00:00,5.85,0.0,-0.0,0.0,1.93,92.3 +2020-03-31 01:00:00,5.82,0.0,-0.0,0.0,1.72,92.3 +2020-03-31 02:00:00,5.57,0.0,-0.0,0.0,1.59,92.25 +2020-03-31 03:00:00,5.41,0.0,-0.0,0.0,1.38,95.75 +2020-03-31 04:00:00,5.21,0.0,-0.0,0.0,1.31,95.75 +2020-03-31 05:00:00,5.2,5.0,0.0,5.0,1.1,95.75 +2020-03-31 06:00:00,5.64,37.0,0.0,37.0,0.62,99.4 +2020-03-31 07:00:00,7.3,157.0,23.78,148.0,0.21,95.8 +2020-03-31 08:00:00,8.12,119.0,0.0,119.0,0.55,92.4 +2020-03-31 09:00:00,8.46,101.0,0.0,101.0,1.17,89.1 +2020-03-31 10:00:00,9.32,130.0,0.0,130.0,1.72,79.9 +2020-03-31 11:00:00,10.02,202.0,0.0,202.0,1.72,79.95 +2020-03-31 12:00:00,9.97,109.0,0.0,109.0,1.66,82.95 +2020-03-31 13:00:00,9.91,137.0,0.0,137.0,1.72,82.95 +2020-03-31 14:00:00,9.67,89.0,0.0,89.0,1.66,89.15 +2020-03-31 15:00:00,9.6,39.0,0.0,39.0,2.34,89.15 +2020-03-31 16:00:00,6.32,86.0,27.27,80.0,2.88,96.41 +2020-03-31 17:00:00,6.69,12.0,0.0,12.0,3.0,95.89 +2020-03-31 18:00:00,7.05,0.0,-0.0,0.0,3.11,95.38 +2020-03-31 19:00:00,7.41,0.0,-0.0,0.0,3.23,94.86 +2020-03-31 20:00:00,7.78,0.0,-0.0,0.0,3.35,94.35 +2020-03-31 21:00:00,8.14,0.0,-0.0,0.0,3.47,93.83 +2020-03-31 22:00:00,8.5,0.0,-0.0,0.0,3.58,93.32 +2020-03-31 23:00:00,8.86,0.0,-0.0,0.0,3.7,92.8 +2020-04-01 00:00:00,9.23,0.0,-0.0,0.0,3.82,92.29 +2020-04-01 01:00:00,9.59,0.0,-0.0,0.0,3.93,91.77 +2020-04-01 02:00:00,9.95,0.0,-0.0,0.0,4.05,91.26 +2020-04-01 03:00:00,10.32,0.0,-0.0,0.0,4.17,90.75 +2020-04-01 04:00:00,10.68,0.0,-0.0,0.0,4.29,90.23 +2020-04-01 05:00:00,11.04,30.0,167.29,19.0,4.4,89.72 +2020-04-01 06:00:00,11.41,60.0,0.0,60.0,4.52,89.2 +2020-04-01 07:00:00,11.77,329.0,567.97,114.0,4.64,88.69 +2020-04-01 08:00:00,12.74,442.0,520.01,178.0,5.03,86.25 +2020-04-01 09:00:00,13.15,473.0,326.5,275.0,5.79,80.35 +2020-04-01 10:00:00,14.14,408.0,115.26,331.0,5.66,69.75 +2020-04-01 11:00:00,14.93,445.0,145.27,345.0,6.07,60.5 +2020-04-01 12:00:00,15.21,389.0,94.6,326.0,5.59,60.5 +2020-04-01 13:00:00,15.43,491.0,386.79,258.0,5.38,58.45 +2020-04-01 14:00:00,15.16,361.0,258.97,231.0,4.55,60.5 +2020-04-01 15:00:00,14.95,244.0,207.24,167.0,3.17,62.75 +2020-04-01 16:00:00,14.81,155.0,331.79,82.0,2.69,64.95 +2020-04-01 17:00:00,13.94,9.0,0.0,9.0,2.0,67.25 +2020-04-01 18:00:00,12.67,0.0,-0.0,0.0,1.93,72.05 +2020-04-01 19:00:00,12.22,0.0,-0.0,0.0,2.14,74.6 +2020-04-01 20:00:00,11.57,0.0,-0.0,0.0,2.28,77.3 +2020-04-01 21:00:00,11.16,0.0,-0.0,0.0,2.21,83.05 +2020-04-01 22:00:00,10.87,0.0,-0.0,0.0,2.07,86.1 +2020-04-01 23:00:00,11.02,0.0,-0.0,0.0,1.86,89.3 +2020-04-02 00:00:00,10.52,0.0,-0.0,0.0,1.72,92.5 +2020-04-02 01:00:00,9.92,0.0,-0.0,0.0,1.66,95.9 +2020-04-02 02:00:00,9.15,0.0,-0.0,0.0,1.72,99.4 +2020-04-02 03:00:00,8.61,0.0,-0.0,0.0,1.72,99.4 +2020-04-02 04:00:00,8.24,0.0,-0.0,0.0,1.79,99.4 +2020-04-02 05:00:00,7.95,16.0,0.0,16.0,1.72,99.4 +2020-04-02 06:00:00,9.77,139.0,183.95,96.0,1.03,95.85 +2020-04-02 07:00:00,12.73,238.0,166.53,174.0,0.9,89.4 +2020-04-02 08:00:00,15.17,446.0,529.96,174.0,0.83,75.1 +2020-04-02 09:00:00,16.66,530.0,495.29,227.0,0.97,65.35 +2020-04-02 10:00:00,17.88,531.0,341.68,301.0,1.1,63.2 +2020-04-02 11:00:00,18.4,511.0,259.65,331.0,0.76,58.9 +2020-04-02 12:00:00,18.62,584.0,493.57,253.0,0.55,56.95 +2020-04-02 13:00:00,18.71,528.0,504.22,222.0,1.17,59.05 +2020-04-02 14:00:00,18.8,431.0,495.72,180.0,1.93,59.05 +2020-04-02 15:00:00,18.68,309.0,484.23,127.0,2.28,59.05 +2020-04-02 16:00:00,18.22,162.0,378.93,77.0,2.21,63.3 +2020-04-02 17:00:00,16.82,25.0,112.81,18.0,2.41,67.75 +2020-04-02 18:00:00,14.96,0.0,-0.0,0.0,2.62,72.45 +2020-04-02 19:00:00,14.43,0.0,-0.0,0.0,2.76,75.0 +2020-04-02 20:00:00,13.46,0.0,-0.0,0.0,2.83,77.6 +2020-04-02 21:00:00,12.69,0.0,-0.0,0.0,2.83,83.25 +2020-04-02 22:00:00,12.11,0.0,-0.0,0.0,2.9,83.15 +2020-04-02 23:00:00,11.78,0.0,-0.0,0.0,3.03,86.15 +2020-04-03 00:00:00,11.43,0.0,-0.0,0.0,2.97,86.15 +2020-04-03 01:00:00,11.1,0.0,-0.0,0.0,2.97,86.1 +2020-04-03 02:00:00,10.73,0.0,-0.0,0.0,3.03,89.25 +2020-04-03 03:00:00,10.36,0.0,-0.0,0.0,3.03,89.25 +2020-04-03 04:00:00,10.04,0.0,-0.0,0.0,2.97,92.5 +2020-04-03 05:00:00,9.61,38.0,166.96,25.0,2.9,92.45 +2020-04-03 06:00:00,10.49,188.0,467.32,76.0,2.83,89.25 +2020-04-03 07:00:00,12.83,345.0,599.92,111.0,2.62,86.25 +2020-04-03 08:00:00,15.5,489.0,684.31,134.0,2.83,72.55 +2020-04-03 09:00:00,17.57,596.0,706.59,160.0,2.9,65.45 +2020-04-03 10:00:00,19.17,660.0,706.29,181.0,2.9,59.15 +2020-04-03 11:00:00,20.23,555.0,336.66,320.0,2.97,55.3 +2020-04-03 12:00:00,20.94,619.0,571.66,233.0,3.17,53.5 +2020-04-03 13:00:00,21.38,571.0,631.43,185.0,3.17,49.95 +2020-04-03 14:00:00,21.43,455.0,563.98,167.0,3.1,49.95 +2020-04-03 15:00:00,21.22,304.0,426.19,142.0,2.9,49.95 +2020-04-03 16:00:00,20.46,174.0,437.47,74.0,2.21,55.45 +2020-04-03 17:00:00,18.66,33.0,195.8,20.0,2.41,63.4 +2020-04-03 18:00:00,16.75,0.0,-0.0,0.0,2.69,65.35 +2020-04-03 19:00:00,16.68,0.0,-0.0,0.0,2.69,63.05 +2020-04-03 20:00:00,15.75,0.0,-0.0,0.0,2.9,65.15 +2020-04-03 21:00:00,15.14,0.0,-0.0,0.0,2.97,67.45 +2020-04-03 22:00:00,14.64,0.0,-0.0,0.0,3.1,69.85 +2020-04-03 23:00:00,14.09,0.0,-0.0,0.0,2.97,72.3 +2020-04-04 00:00:00,12.81,0.0,-0.0,0.0,2.21,80.3 +2020-04-04 01:00:00,11.45,0.0,-0.0,0.0,1.93,86.15 +2020-04-04 02:00:00,11.22,0.0,-0.0,0.0,1.93,92.55 +2020-04-04 03:00:00,11.06,0.0,-0.0,0.0,2.28,95.9 +2020-04-04 04:00:00,10.65,0.0,-0.0,0.0,2.34,99.4 +2020-04-04 05:00:00,10.25,36.0,107.31,27.0,1.93,100.0 +2020-04-04 06:00:00,9.64,17.0,0.0,17.0,1.72,100.0 +2020-04-04 07:00:00,9.84,53.0,0.0,53.0,2.41,100.0 +2020-04-04 08:00:00,9.43,68.0,0.0,68.0,2.41,99.4 +2020-04-04 09:00:00,8.98,97.0,0.0,97.0,2.69,99.4 +2020-04-04 10:00:00,8.98,116.0,0.0,116.0,3.86,92.45 +2020-04-04 11:00:00,8.49,63.0,0.0,63.0,3.79,92.4 +2020-04-04 12:00:00,8.55,121.0,0.0,121.0,4.69,95.85 +2020-04-04 13:00:00,8.92,535.0,500.24,227.0,4.0,85.95 +2020-04-04 14:00:00,9.96,464.0,600.07,155.0,4.48,69.0 +2020-04-04 15:00:00,10.99,160.0,23.42,151.0,4.14,61.85 +2020-04-04 16:00:00,11.34,68.0,4.29,67.0,3.38,61.85 +2020-04-04 17:00:00,10.94,33.0,155.54,22.0,2.21,64.25 +2020-04-04 18:00:00,9.94,0.0,-0.0,0.0,2.21,66.45 +2020-04-04 19:00:00,9.19,0.0,-0.0,0.0,2.41,68.8 +2020-04-04 20:00:00,8.66,0.0,-0.0,0.0,2.69,74.05 +2020-04-04 21:00:00,7.9,0.0,-0.0,0.0,2.76,76.8 +2020-04-04 22:00:00,7.31,0.0,-0.0,0.0,2.69,85.75 +2020-04-04 23:00:00,7.01,0.0,-0.0,0.0,2.76,89.0 +2020-04-05 00:00:00,6.45,0.0,-0.0,0.0,2.55,92.3 +2020-04-05 01:00:00,6.53,0.0,-0.0,0.0,2.34,92.3 +2020-04-05 02:00:00,5.99,0.0,-0.0,0.0,2.34,88.95 +2020-04-05 03:00:00,5.68,0.0,-0.0,0.0,2.48,92.3 +2020-04-05 04:00:00,5.45,0.0,-0.0,0.0,2.28,88.9 +2020-04-05 05:00:00,5.06,44.0,144.69,31.0,2.07,88.85 +2020-04-05 06:00:00,6.0,185.0,365.99,93.0,1.86,88.95 +2020-04-05 07:00:00,8.87,353.0,578.0,121.0,2.0,82.85 +2020-04-05 08:00:00,10.64,510.0,719.33,129.0,2.62,77.15 +2020-04-05 09:00:00,12.03,549.0,494.06,239.0,3.24,62.1 +2020-04-05 10:00:00,13.01,605.0,489.75,268.0,4.34,53.7 +2020-04-05 11:00:00,13.37,622.0,483.4,280.0,4.28,50.0 +2020-04-05 12:00:00,13.63,551.0,353.66,309.0,3.52,48.15 +2020-04-05 13:00:00,14.32,461.0,269.33,294.0,3.59,44.75 +2020-04-05 14:00:00,14.41,338.0,171.42,249.0,3.52,43.25 +2020-04-05 15:00:00,14.09,166.0,28.31,155.0,3.24,44.75 +2020-04-05 16:00:00,13.62,117.0,80.15,98.0,2.62,48.15 +2020-04-05 17:00:00,12.62,30.0,79.97,24.0,2.14,51.65 +2020-04-05 18:00:00,11.43,0.0,-0.0,0.0,1.93,55.4 +2020-04-05 19:00:00,11.16,0.0,-0.0,0.0,1.79,61.85 +2020-04-05 20:00:00,10.71,0.0,-0.0,0.0,1.93,64.1 +2020-04-05 21:00:00,10.02,0.0,-0.0,0.0,2.0,66.45 +2020-04-05 22:00:00,9.45,0.0,-0.0,0.0,2.0,68.9 +2020-04-05 23:00:00,9.88,0.0,-0.0,0.0,2.76,66.45 +2020-04-06 00:00:00,10.2,0.0,-0.0,0.0,3.24,66.45 +2020-04-06 01:00:00,9.78,0.0,-0.0,0.0,3.24,66.35 +2020-04-06 02:00:00,9.54,0.0,-0.0,0.0,3.17,66.35 +2020-04-06 03:00:00,9.1,0.0,-0.0,0.0,3.1,71.4 +2020-04-06 04:00:00,9.16,0.0,-0.0,0.0,3.1,74.15 +2020-04-06 05:00:00,9.2,17.0,0.0,17.0,3.1,76.95 +2020-04-06 06:00:00,9.66,61.0,0.0,61.0,3.24,77.0 +2020-04-06 07:00:00,10.42,212.0,78.63,180.0,2.9,80.05 +2020-04-06 08:00:00,11.65,111.0,0.0,111.0,4.0,77.3 +2020-04-06 09:00:00,12.14,482.0,313.01,284.0,4.14,77.35 +2020-04-06 10:00:00,12.68,371.0,64.94,326.0,4.62,77.45 +2020-04-06 11:00:00,13.42,589.0,408.63,298.0,4.9,74.85 +2020-04-06 12:00:00,14.23,347.0,47.91,314.0,4.9,74.95 +2020-04-06 13:00:00,14.94,371.0,112.12,301.0,4.97,72.45 +2020-04-06 14:00:00,15.8,236.0,32.48,219.0,5.03,70.0 +2020-04-06 15:00:00,16.27,0.0,0.0,0.0,4.83,67.65 +2020-04-06 16:00:00,16.05,0.0,0.0,0.0,4.41,67.65 +2020-04-06 17:00:00,15.54,26.0,37.82,23.0,4.0,70.0 +2020-04-06 18:00:00,14.91,0.0,-0.0,0.0,3.93,72.45 +2020-04-06 19:00:00,13.99,0.0,-0.0,0.0,3.38,77.65 +2020-04-06 20:00:00,13.41,0.0,-0.0,0.0,3.52,80.4 +2020-04-06 21:00:00,13.2,0.0,-0.0,0.0,3.24,83.25 +2020-04-06 22:00:00,12.81,0.0,-0.0,0.0,2.97,86.25 +2020-04-06 23:00:00,12.46,0.0,-0.0,0.0,2.83,86.25 +2020-04-07 00:00:00,12.5,0.0,-0.0,0.0,2.9,86.25 +2020-04-07 01:00:00,12.65,0.0,-0.0,0.0,3.1,89.4 +2020-04-07 02:00:00,12.65,0.0,-0.0,0.0,3.24,89.4 +2020-04-07 03:00:00,12.54,0.0,-0.0,0.0,3.17,89.4 +2020-04-07 04:00:00,12.44,0.0,-0.0,0.0,3.24,89.4 +2020-04-07 05:00:00,12.6,60.0,255.71,34.0,3.72,89.4 +2020-04-07 06:00:00,13.76,218.0,543.94,75.0,4.14,83.35 +2020-04-07 07:00:00,15.9,377.0,664.22,103.0,3.79,75.15 +2020-04-07 08:00:00,17.73,506.0,679.21,139.0,4.97,70.3 +2020-04-07 09:00:00,19.11,614.0,715.13,158.0,5.79,63.5 +2020-04-07 10:00:00,19.95,528.0,289.48,326.0,6.48,59.35 +2020-04-07 11:00:00,20.49,616.0,463.21,284.0,6.97,55.45 +2020-04-07 12:00:00,20.5,549.0,344.81,310.0,7.52,49.8 +2020-04-07 13:00:00,20.1,572.0,588.61,202.0,7.72,46.2 +2020-04-07 14:00:00,19.32,274.0,64.45,240.0,7.52,47.65 +2020-04-07 15:00:00,17.9,155.0,15.12,149.0,6.14,52.75 +2020-04-07 16:00:00,16.3,66.0,0.0,66.0,6.28,56.5 +2020-04-07 17:00:00,14.8,23.0,11.96,22.0,6.21,64.95 +2020-04-07 18:00:00,13.32,0.0,-0.0,0.0,6.28,74.75 +2020-04-07 19:00:00,11.56,0.0,-0.0,0.0,6.48,69.25 +2020-04-07 20:00:00,10.48,0.0,-0.0,0.0,6.07,64.1 +2020-04-07 21:00:00,9.58,0.0,-0.0,0.0,6.41,59.25 +2020-04-07 22:00:00,8.52,0.0,-0.0,0.0,5.66,56.8 +2020-04-07 23:00:00,7.83,0.0,-0.0,0.0,5.45,56.7 +2020-04-08 00:00:00,7.15,0.0,-0.0,0.0,5.45,61.0 +2020-04-08 01:00:00,6.42,0.0,-0.0,0.0,5.38,63.25 +2020-04-08 02:00:00,5.91,0.0,-0.0,0.0,5.31,63.25 +2020-04-08 03:00:00,5.56,0.0,-0.0,0.0,5.24,65.65 +2020-04-08 04:00:00,5.23,0.0,-0.0,0.0,5.17,68.1 +2020-04-08 05:00:00,5.04,44.0,74.4,36.0,5.1,68.1 +2020-04-08 06:00:00,5.48,81.0,3.72,80.0,4.48,68.2 +2020-04-08 07:00:00,6.51,247.0,129.19,193.0,5.59,68.3 +2020-04-08 08:00:00,7.81,272.0,49.49,245.0,6.0,63.5 +2020-04-08 09:00:00,8.76,224.0,3.11,222.0,6.14,61.3 +2020-04-08 10:00:00,9.51,222.0,0.0,222.0,6.21,57.05 +2020-04-08 11:00:00,10.17,197.0,0.0,197.0,6.0,55.05 +2020-04-08 12:00:00,11.5,387.0,73.12,336.0,6.34,53.35 +2020-04-08 13:00:00,12.96,377.0,107.46,309.0,6.83,53.7 +2020-04-08 14:00:00,13.63,478.0,581.19,169.0,7.17,53.85 +2020-04-08 15:00:00,14.1,351.0,578.62,119.0,7.59,53.95 +2020-04-08 16:00:00,13.85,136.0,124.21,105.0,7.31,58.0 +2020-04-08 17:00:00,13.2,20.0,0.0,20.0,7.17,60.05 +2020-04-08 18:00:00,12.84,0.0,-0.0,0.0,7.45,62.2 +2020-04-08 19:00:00,12.65,0.0,-0.0,0.0,7.93,62.2 +2020-04-08 20:00:00,12.31,0.0,-0.0,0.0,8.07,64.45 +2020-04-08 21:00:00,10.96,0.0,-0.0,0.0,7.59,69.15 +2020-04-08 22:00:00,9.48,0.0,-0.0,0.0,7.31,68.9 +2020-04-08 23:00:00,8.1,0.0,-0.0,0.0,6.76,68.6 +2020-04-09 00:00:00,7.15,0.0,-0.0,0.0,6.28,71.05 +2020-04-09 01:00:00,6.39,0.0,-0.0,0.0,5.86,70.95 +2020-04-09 02:00:00,5.84,0.0,-0.0,0.0,5.59,68.3 +2020-04-09 03:00:00,5.41,0.0,-0.0,0.0,5.31,70.85 +2020-04-09 04:00:00,5.05,0.0,-0.0,0.0,5.1,73.55 +2020-04-09 05:00:00,4.75,34.0,17.65,32.0,4.9,76.3 +2020-04-09 06:00:00,5.15,108.0,21.88,102.0,5.72,73.55 +2020-04-09 07:00:00,5.71,197.0,40.15,180.0,5.45,76.5 +2020-04-09 08:00:00,6.27,294.0,65.36,258.0,5.31,76.55 +2020-04-09 09:00:00,6.93,435.0,172.95,323.0,5.31,73.8 +2020-04-09 10:00:00,7.73,461.0,145.63,358.0,5.45,71.15 +2020-04-09 11:00:00,8.55,696.0,637.96,233.0,5.45,68.7 +2020-04-09 12:00:00,9.41,600.0,428.91,299.0,5.45,63.9 +2020-04-09 13:00:00,10.0,434.0,183.68,317.0,5.45,59.35 +2020-04-09 14:00:00,10.32,0.0,0.0,0.0,5.38,57.15 +2020-04-09 15:00:00,10.58,349.0,523.43,137.0,5.1,53.1 +2020-04-09 16:00:00,10.52,191.0,390.23,92.0,4.83,53.1 +2020-04-09 17:00:00,10.13,53.0,260.65,29.0,3.66,55.05 +2020-04-09 18:00:00,9.04,0.0,-0.0,0.0,3.1,56.9 +2020-04-09 19:00:00,8.17,0.0,-0.0,0.0,2.76,61.2 +2020-04-09 20:00:00,7.17,0.0,-0.0,0.0,2.55,65.85 +2020-04-09 21:00:00,6.23,0.0,-0.0,0.0,2.34,68.3 +2020-04-09 22:00:00,5.3,0.0,-0.0,0.0,2.21,70.85 +2020-04-09 23:00:00,4.3,0.0,-0.0,0.0,2.14,73.45 +2020-04-10 00:00:00,3.34,0.0,-0.0,0.0,2.07,76.15 +2020-04-10 01:00:00,2.48,0.0,-0.0,0.0,2.07,82.15 +2020-04-10 02:00:00,1.71,0.0,-0.0,0.0,2.07,88.6 +2020-04-10 03:00:00,1.19,0.0,-0.0,0.0,2.14,88.55 +2020-04-10 04:00:00,0.86,0.0,-0.0,0.0,2.14,85.2 +2020-04-10 05:00:00,0.98,73.0,277.07,40.0,2.14,85.2 +2020-04-10 06:00:00,3.71,230.0,511.04,87.0,2.0,76.15 +2020-04-10 07:00:00,7.59,393.0,643.64,117.0,2.9,63.5 +2020-04-10 08:00:00,9.29,547.0,755.5,127.0,3.59,61.45 +2020-04-10 09:00:00,10.81,663.0,803.09,139.0,4.0,55.15 +2020-04-10 10:00:00,12.0,735.0,830.15,144.0,4.34,49.6 +2020-04-10 11:00:00,12.95,761.0,849.11,141.0,4.34,46.2 +2020-04-10 12:00:00,13.72,716.0,787.52,160.0,4.34,46.35 +2020-04-10 13:00:00,14.27,642.0,773.7,146.0,4.34,48.25 +2020-04-10 14:00:00,14.54,528.0,744.73,126.0,4.34,46.6 +2020-04-10 15:00:00,14.45,377.0,660.05,107.0,4.14,46.6 +2020-04-10 16:00:00,14.06,211.0,515.93,78.0,3.79,48.25 +2020-04-10 17:00:00,13.07,54.0,238.86,31.0,2.83,53.7 +2020-04-10 18:00:00,11.47,0.0,-0.0,0.0,2.34,57.55 +2020-04-10 19:00:00,10.5,0.0,-0.0,0.0,2.34,59.5 +2020-04-10 20:00:00,9.07,0.0,-0.0,0.0,2.14,66.25 +2020-04-10 21:00:00,7.58,0.0,-0.0,0.0,1.93,76.7 +2020-04-10 22:00:00,6.76,0.0,-0.0,0.0,1.72,79.5 +2020-04-10 23:00:00,6.05,0.0,-0.0,0.0,1.86,76.55 +2020-04-11 00:00:00,5.12,0.0,-0.0,0.0,2.0,79.35 +2020-04-11 01:00:00,4.23,0.0,-0.0,0.0,2.07,82.3 +2020-04-11 02:00:00,3.56,0.0,-0.0,0.0,1.93,85.45 +2020-04-11 03:00:00,3.22,0.0,-0.0,0.0,1.86,88.7 +2020-04-11 04:00:00,3.17,0.0,-0.0,0.0,1.86,88.7 +2020-04-11 05:00:00,3.4,48.0,48.07,42.0,1.79,85.45 +2020-04-11 06:00:00,6.27,156.0,115.64,123.0,1.38,82.55 +2020-04-11 07:00:00,11.01,328.0,334.0,183.0,1.31,69.15 +2020-04-11 08:00:00,13.2,419.0,292.34,255.0,1.79,57.9 +2020-04-11 09:00:00,14.53,531.0,371.21,287.0,2.0,48.4 +2020-04-11 10:00:00,15.74,649.0,547.1,257.0,1.93,45.15 +2020-04-11 11:00:00,16.63,638.0,465.6,296.0,2.14,42.15 +2020-04-11 12:00:00,17.44,609.0,449.17,290.0,2.41,42.3 +2020-04-11 13:00:00,18.0,526.0,385.96,277.0,2.69,42.45 +2020-04-11 14:00:00,18.16,379.0,226.19,256.0,2.97,42.45 +2020-04-11 15:00:00,18.1,265.0,183.99,189.0,2.97,42.45 +2020-04-11 16:00:00,17.38,149.0,137.48,113.0,2.0,50.75 +2020-04-11 17:00:00,16.03,34.0,29.85,31.0,1.93,52.5 +2020-04-11 18:00:00,14.61,0.0,-0.0,0.0,2.28,54.1 +2020-04-11 19:00:00,13.81,0.0,-0.0,0.0,2.48,55.9 +2020-04-11 20:00:00,12.73,0.0,-0.0,0.0,2.83,57.75 +2020-04-11 21:00:00,12.08,0.0,-0.0,0.0,3.1,59.85 +2020-04-11 22:00:00,11.85,0.0,-0.0,0.0,3.31,59.7 +2020-04-11 23:00:00,11.62,0.0,-0.0,0.0,3.31,59.7 +2020-04-12 00:00:00,11.24,0.0,-0.0,0.0,3.17,61.85 +2020-04-12 01:00:00,11.08,0.0,-0.0,0.0,3.1,61.85 +2020-04-12 02:00:00,11.11,0.0,-0.0,0.0,3.17,64.25 +2020-04-12 03:00:00,11.14,0.0,-0.0,0.0,3.66,66.65 +2020-04-12 04:00:00,10.86,0.0,-0.0,0.0,3.93,69.15 +2020-04-12 05:00:00,10.31,24.0,0.0,24.0,3.93,79.95 +2020-04-12 06:00:00,10.75,53.0,0.0,53.0,3.52,80.05 +2020-04-12 07:00:00,11.94,127.0,0.0,127.0,4.83,71.95 +2020-04-12 08:00:00,11.31,122.0,0.0,122.0,5.66,77.2 +2020-04-12 09:00:00,9.86,107.0,0.0,107.0,5.24,82.95 +2020-04-12 10:00:00,9.37,138.0,0.0,138.0,5.24,82.9 +2020-04-12 11:00:00,10.75,151.0,0.0,151.0,4.97,83.0 +2020-04-12 12:00:00,9.54,625.0,475.96,285.0,6.55,74.2 +2020-04-12 13:00:00,9.54,196.0,0.0,196.0,6.0,77.0 +2020-04-12 14:00:00,10.12,330.0,120.49,264.0,6.28,66.45 +2020-04-12 15:00:00,10.07,313.0,323.72,178.0,6.83,55.05 +2020-04-12 16:00:00,9.83,182.0,274.54,109.0,6.62,50.85 +2020-04-12 17:00:00,9.17,51.0,133.77,37.0,6.0,46.95 +2020-04-12 18:00:00,8.2,0.0,-0.0,0.0,5.66,48.55 +2020-04-12 19:00:00,7.17,0.0,-0.0,0.0,5.59,52.2 +2020-04-12 20:00:00,6.45,0.0,-0.0,0.0,5.31,54.15 +2020-04-12 21:00:00,6.0,0.0,-0.0,0.0,5.38,54.15 +2020-04-12 22:00:00,5.48,0.0,-0.0,0.0,5.52,58.45 +2020-04-12 23:00:00,5.12,0.0,-0.0,0.0,5.59,60.65 +2020-04-13 00:00:00,4.87,0.0,-0.0,0.0,5.66,63.05 +2020-04-13 01:00:00,4.51,0.0,-0.0,0.0,5.72,65.4 +2020-04-13 02:00:00,4.38,0.0,-0.0,0.0,5.86,65.4 +2020-04-13 03:00:00,4.11,0.0,-0.0,0.0,5.93,70.6 +2020-04-13 04:00:00,3.63,0.0,-0.0,0.0,6.0,76.15 +2020-04-13 05:00:00,3.47,33.0,0.0,33.0,5.86,76.15 +2020-04-13 06:00:00,3.63,45.0,0.0,45.0,6.0,79.15 +2020-04-13 07:00:00,4.02,81.0,0.0,81.0,5.66,85.5 +2020-04-13 08:00:00,4.23,129.0,0.0,129.0,4.48,95.75 +2020-04-13 09:00:00,4.82,170.0,0.0,170.0,4.48,95.75 +2020-04-13 10:00:00,4.7,152.0,0.0,152.0,4.34,99.4 +2020-04-13 11:00:00,3.78,148.0,0.0,148.0,7.24,92.2 +2020-04-13 12:00:00,3.97,101.0,0.0,101.0,6.9,88.8 +2020-04-13 13:00:00,4.46,127.0,0.0,127.0,6.62,88.85 +2020-04-13 14:00:00,4.7,179.0,1.81,178.0,6.76,92.25 +2020-04-13 15:00:00,4.68,112.0,0.0,112.0,6.0,92.25 +2020-04-13 16:00:00,4.77,39.0,0.0,39.0,5.59,95.75 +2020-04-13 17:00:00,4.84,33.0,18.38,31.0,5.17,92.25 +2020-04-13 18:00:00,4.83,0.0,-0.0,0.0,4.76,92.25 +2020-04-13 19:00:00,4.64,0.0,-0.0,0.0,4.07,92.25 +2020-04-13 20:00:00,4.74,0.0,-0.0,0.0,4.0,92.25 +2020-04-13 21:00:00,4.87,0.0,-0.0,0.0,4.28,92.25 +2020-04-13 22:00:00,4.87,0.0,-0.0,0.0,4.62,88.85 +2020-04-13 23:00:00,4.58,0.0,-0.0,0.0,4.83,92.25 +2020-04-14 00:00:00,4.19,0.0,-0.0,0.0,4.76,92.2 +2020-04-14 01:00:00,3.63,0.0,-0.0,0.0,4.41,95.7 +2020-04-14 02:00:00,3.36,0.0,-0.0,0.0,3.52,95.7 +2020-04-14 03:00:00,3.29,0.0,-0.0,0.0,3.31,95.7 +2020-04-14 04:00:00,3.27,0.0,-0.0,0.0,3.93,99.4 +2020-04-14 05:00:00,3.42,26.0,0.0,26.0,4.0,92.15 +2020-04-14 06:00:00,3.55,43.0,0.0,43.0,4.07,95.7 +2020-04-14 07:00:00,4.3,105.0,0.0,105.0,3.59,88.85 +2020-04-14 08:00:00,4.52,113.0,0.0,113.0,3.24,92.25 +2020-04-14 09:00:00,4.7,127.0,0.0,127.0,3.17,92.25 +2020-04-14 10:00:00,5.33,124.0,0.0,124.0,3.03,88.9 +2020-04-14 11:00:00,5.81,144.0,0.0,144.0,3.17,85.7 +2020-04-14 12:00:00,6.52,129.0,0.0,129.0,2.9,92.3 +2020-04-14 13:00:00,7.07,145.0,0.0,145.0,2.76,92.35 +2020-04-14 14:00:00,7.37,69.0,0.0,69.0,2.97,85.8 +2020-04-14 15:00:00,7.56,117.0,0.0,117.0,2.83,85.8 +2020-04-14 16:00:00,7.51,73.0,0.0,73.0,2.9,85.8 +2020-04-14 17:00:00,7.02,11.0,0.0,11.0,2.55,85.75 +2020-04-14 18:00:00,6.11,0.0,-0.0,0.0,2.28,85.7 +2020-04-14 19:00:00,4.91,0.0,-0.0,0.0,1.59,92.25 +2020-04-14 20:00:00,4.54,0.0,-0.0,0.0,1.59,92.25 +2020-04-14 21:00:00,4.56,0.0,-0.0,0.0,1.52,95.75 +2020-04-14 22:00:00,4.34,0.0,-0.0,0.0,1.52,92.25 +2020-04-14 23:00:00,4.15,0.0,-0.0,0.0,1.45,95.75 +2020-04-15 00:00:00,3.97,0.0,-0.0,0.0,1.45,95.75 +2020-04-15 01:00:00,3.88,0.0,-0.0,0.0,1.45,95.75 +2020-04-15 02:00:00,3.87,0.0,-0.0,0.0,1.45,95.75 +2020-04-15 03:00:00,3.81,0.0,-0.0,0.0,1.31,95.75 +2020-04-15 04:00:00,3.82,0.0,-0.0,0.0,1.17,95.75 +2020-04-15 05:00:00,3.8,26.0,0.0,26.0,1.1,95.75 +2020-04-15 06:00:00,3.92,60.0,0.0,60.0,1.72,95.75 +2020-04-15 07:00:00,4.54,163.0,6.6,160.0,1.66,92.25 +2020-04-15 08:00:00,5.9,341.0,101.61,282.0,2.14,82.55 +2020-04-15 09:00:00,6.94,327.0,35.51,303.0,2.41,76.65 +2020-04-15 10:00:00,7.95,158.0,0.0,158.0,2.48,71.25 +2020-04-15 11:00:00,8.91,263.0,2.66,261.0,2.69,71.4 +2020-04-15 12:00:00,9.73,546.0,264.31,354.0,2.9,71.5 +2020-04-15 13:00:00,10.06,122.0,0.0,122.0,3.17,71.6 +2020-04-15 14:00:00,9.73,117.0,0.0,117.0,3.31,68.9 +2020-04-15 15:00:00,9.59,174.0,18.66,166.0,3.17,66.35 +2020-04-15 16:00:00,9.49,80.0,3.6,79.0,2.48,66.35 +2020-04-15 17:00:00,8.95,38.0,17.09,36.0,1.79,68.8 +2020-04-15 18:00:00,8.02,0.0,-0.0,0.0,1.52,73.95 +2020-04-15 19:00:00,6.72,0.0,-0.0,0.0,1.52,85.7 +2020-04-15 20:00:00,5.98,0.0,-0.0,0.0,1.24,82.55 +2020-04-15 21:00:00,5.67,0.0,-0.0,0.0,1.17,82.5 +2020-04-15 22:00:00,5.59,0.0,-0.0,0.0,1.17,82.5 +2020-04-15 23:00:00,5.3,0.0,-0.0,0.0,1.24,79.4 +2020-04-16 00:00:00,4.98,0.0,-0.0,0.0,1.24,82.4 +2020-04-16 01:00:00,4.84,0.0,-0.0,0.0,1.31,82.4 +2020-04-16 02:00:00,4.41,0.0,-0.0,0.0,1.38,82.35 +2020-04-16 03:00:00,4.08,0.0,-0.0,0.0,1.38,85.5 +2020-04-16 04:00:00,3.49,0.0,-0.0,0.0,1.45,85.45 +2020-04-16 05:00:00,3.0,106.0,373.63,49.0,1.45,88.7 +2020-04-16 06:00:00,6.18,265.0,551.04,93.0,0.55,85.7 +2020-04-16 07:00:00,8.85,439.0,715.58,110.0,0.34,79.85 +2020-04-16 08:00:00,10.62,584.0,780.67,127.0,0.34,69.1 +2020-04-16 09:00:00,11.49,608.0,514.36,258.0,1.1,59.7 +2020-04-16 10:00:00,12.16,442.0,98.85,369.0,1.38,53.5 +2020-04-16 11:00:00,12.93,548.0,226.35,377.0,1.38,48.0 +2020-04-16 12:00:00,13.16,397.0,62.98,351.0,1.31,48.0 +2020-04-16 13:00:00,13.56,345.0,51.15,311.0,1.03,44.65 +2020-04-16 14:00:00,13.56,306.0,72.8,265.0,1.1,44.65 +2020-04-16 15:00:00,13.45,268.0,150.27,203.0,1.31,44.65 +2020-04-16 16:00:00,13.21,198.0,294.54,115.0,1.52,46.2 +2020-04-16 17:00:00,12.23,70.0,222.87,43.0,1.59,57.65 +2020-04-16 18:00:00,10.75,0.0,-0.0,0.0,1.79,61.75 +2020-04-16 19:00:00,9.68,0.0,-0.0,0.0,1.45,74.2 +2020-04-16 20:00:00,9.81,0.0,-0.0,0.0,1.1,63.9 +2020-04-16 21:00:00,10.77,0.0,-0.0,0.0,0.48,57.3 +2020-04-16 22:00:00,10.57,0.0,-0.0,0.0,0.28,55.15 +2020-04-16 23:00:00,10.3,0.0,-0.0,0.0,0.69,57.15 +2020-04-17 00:00:00,9.96,0.0,-0.0,0.0,0.9,57.15 +2020-04-17 01:00:00,8.84,0.0,-0.0,0.0,1.1,61.45 +2020-04-17 02:00:00,8.59,0.0,-0.0,0.0,1.03,63.7 +2020-04-17 03:00:00,8.25,0.0,-0.0,0.0,1.1,66.05 +2020-04-17 04:00:00,7.9,0.0,-0.0,0.0,1.1,66.05 +2020-04-17 05:00:00,7.49,30.0,0.0,30.0,1.17,71.15 +2020-04-17 06:00:00,8.82,77.0,0.0,77.0,0.62,74.05 +2020-04-17 07:00:00,10.94,192.0,19.37,183.0,0.34,71.75 +2020-04-17 08:00:00,12.17,286.0,38.98,263.0,0.62,64.45 +2020-04-17 09:00:00,12.84,399.0,91.99,336.0,1.1,59.95 +2020-04-17 10:00:00,13.58,663.0,504.94,288.0,1.31,53.85 +2020-04-17 11:00:00,13.88,447.0,92.17,377.0,1.38,50.1 +2020-04-17 12:00:00,13.86,215.0,0.0,215.0,1.38,50.0 +2020-04-17 13:00:00,14.09,405.0,112.19,330.0,1.24,48.25 +2020-04-17 14:00:00,14.02,394.0,213.43,273.0,1.17,46.5 +2020-04-17 15:00:00,13.98,217.0,55.0,193.0,1.03,46.5 +2020-04-17 16:00:00,13.57,155.0,108.51,124.0,0.48,51.9 +2020-04-17 17:00:00,13.41,75.0,255.57,43.0,0.14,51.9 +2020-04-17 18:00:00,13.07,0.0,-0.0,0.0,0.41,53.7 +2020-04-17 19:00:00,12.53,0.0,-0.0,0.0,0.34,47.85 +2020-04-17 20:00:00,12.38,0.0,-0.0,0.0,0.21,46.1 +2020-04-17 21:00:00,12.15,0.0,-0.0,0.0,0.69,45.95 +2020-04-17 22:00:00,11.45,0.0,-0.0,0.0,0.97,49.45 +2020-04-17 23:00:00,10.58,0.0,-0.0,0.0,1.03,55.15 +2020-04-18 00:00:00,9.32,0.0,-0.0,0.0,1.31,63.8 +2020-04-18 01:00:00,8.59,0.0,-0.0,0.0,1.45,68.7 +2020-04-18 02:00:00,7.8,0.0,-0.0,0.0,1.52,73.9 +2020-04-18 03:00:00,7.2,0.0,-0.0,0.0,1.52,73.8 +2020-04-18 04:00:00,6.85,0.0,0.0,0.0,1.45,73.8 +2020-04-18 05:00:00,6.95,115.0,379.83,53.0,1.38,76.65 +2020-04-18 06:00:00,9.23,280.0,589.34,90.0,0.55,76.95 +2020-04-18 07:00:00,11.98,445.0,702.8,115.0,0.21,59.85 +2020-04-18 08:00:00,13.66,592.0,773.53,132.0,0.41,50.0 +2020-04-18 09:00:00,14.89,705.0,818.29,141.0,1.17,48.4 +2020-04-18 10:00:00,15.74,779.0,854.31,141.0,1.79,41.9 +2020-04-18 11:00:00,16.17,251.0,1.31,250.0,1.79,40.5 +2020-04-18 12:00:00,16.49,668.0,528.45,278.0,1.79,39.15 +2020-04-18 13:00:00,16.45,391.0,92.23,329.0,1.93,40.6 +2020-04-18 14:00:00,16.41,398.0,215.54,275.0,2.07,40.6 +2020-04-18 15:00:00,16.08,385.0,547.57,144.0,2.0,43.65 +2020-04-18 16:00:00,15.3,239.0,518.01,89.0,1.72,54.25 +2020-04-18 17:00:00,14.51,81.0,294.0,43.0,1.72,56.15 +2020-04-18 18:00:00,13.18,0.0,-0.0,0.0,1.79,62.3 +2020-04-18 19:00:00,11.62,0.0,-0.0,0.0,1.93,69.25 +2020-04-18 20:00:00,9.83,0.0,-0.0,0.0,2.07,77.0 +2020-04-18 21:00:00,8.63,0.0,-0.0,0.0,2.14,79.75 +2020-04-18 22:00:00,7.68,0.0,-0.0,0.0,2.21,82.65 +2020-04-18 23:00:00,6.8,0.0,-0.0,0.0,2.28,82.55 +2020-04-19 00:00:00,6.19,0.0,-0.0,0.0,2.28,79.5 +2020-04-19 01:00:00,5.68,0.0,-0.0,0.0,2.28,82.5 +2020-04-19 02:00:00,5.11,0.0,-0.0,0.0,2.28,82.4 +2020-04-19 03:00:00,4.64,0.0,-0.0,0.0,2.21,82.35 +2020-04-19 04:00:00,4.12,0.0,0.0,0.0,2.21,85.5 +2020-04-19 05:00:00,4.46,116.0,338.34,59.0,1.93,85.55 +2020-04-19 06:00:00,7.94,281.0,565.03,96.0,1.38,85.85 +2020-04-19 07:00:00,12.75,449.0,695.71,119.0,1.24,69.45 +2020-04-19 08:00:00,14.99,594.0,766.01,135.0,1.45,54.25 +2020-04-19 09:00:00,16.35,711.0,821.89,141.0,1.72,48.8 +2020-04-19 10:00:00,17.33,785.0,856.34,142.0,2.14,45.45 +2020-04-19 11:00:00,17.99,801.0,851.02,148.0,2.28,42.45 +2020-04-19 12:00:00,18.78,761.0,815.63,156.0,2.55,41.05 +2020-04-19 13:00:00,19.11,689.0,813.7,139.0,2.62,38.2 +2020-04-19 14:00:00,19.24,568.0,764.39,129.0,2.69,38.2 +2020-04-19 15:00:00,19.0,414.0,675.91,114.0,2.83,38.2 +2020-04-19 16:00:00,18.37,245.0,535.08,88.0,2.76,44.05 +2020-04-19 17:00:00,16.76,83.0,277.64,46.0,2.55,52.65 +2020-04-19 18:00:00,14.76,0.0,-0.0,0.0,2.55,60.4 +2020-04-19 19:00:00,13.73,0.0,-0.0,0.0,2.41,62.45 +2020-04-19 20:00:00,12.21,0.0,-0.0,0.0,2.55,69.35 +2020-04-19 21:00:00,11.03,0.0,-0.0,0.0,2.41,74.45 +2020-04-19 22:00:00,9.99,0.0,-0.0,0.0,2.41,79.95 +2020-04-19 23:00:00,9.12,0.0,-0.0,0.0,2.34,82.85 +2020-04-20 00:00:00,8.18,0.0,-0.0,0.0,2.21,89.05 +2020-04-20 01:00:00,7.24,0.0,-0.0,0.0,2.14,92.35 +2020-04-20 02:00:00,6.45,0.0,-0.0,0.0,2.07,92.3 +2020-04-20 03:00:00,5.84,0.0,-0.0,0.0,2.07,88.95 +2020-04-20 04:00:00,5.39,0.0,0.0,0.0,2.14,88.9 +2020-04-20 05:00:00,5.86,117.0,322.51,61.0,1.93,88.95 +2020-04-20 06:00:00,9.55,277.0,517.5,105.0,1.52,85.95 +2020-04-20 07:00:00,13.84,447.0,678.43,122.0,1.31,72.2 +2020-04-20 08:00:00,16.05,594.0,758.7,136.0,1.17,60.75 +2020-04-20 09:00:00,17.56,710.0,814.05,142.0,0.83,56.75 +2020-04-20 10:00:00,18.58,787.0,859.76,138.0,0.97,51.15 +2020-04-20 11:00:00,19.28,812.0,877.93,135.0,1.1,49.45 +2020-04-20 12:00:00,19.81,776.0,855.86,138.0,1.24,47.8 +2020-04-20 13:00:00,20.31,695.0,827.01,133.0,1.17,44.6 +2020-04-20 14:00:00,20.56,582.0,808.04,115.0,1.1,41.55 +2020-04-20 15:00:00,20.37,423.0,706.08,107.0,1.24,43.0 +2020-04-20 16:00:00,19.9,256.0,585.4,82.0,1.52,46.1 +2020-04-20 17:00:00,18.6,89.0,327.85,44.0,1.66,56.95 +2020-04-20 18:00:00,16.36,0.0,-0.0,0.0,2.07,62.95 +2020-04-20 19:00:00,14.86,0.0,-0.0,0.0,1.52,72.4 +2020-04-20 20:00:00,12.58,0.0,-0.0,0.0,1.93,74.7 +2020-04-20 21:00:00,10.74,0.0,-0.0,0.0,2.07,83.0 +2020-04-20 22:00:00,9.49,0.0,-0.0,0.0,1.93,82.9 +2020-04-20 23:00:00,8.61,0.0,-0.0,0.0,1.86,85.9 +2020-04-21 00:00:00,7.72,0.0,-0.0,0.0,1.86,89.0 +2020-04-21 01:00:00,6.95,0.0,-0.0,0.0,1.86,89.0 +2020-04-21 02:00:00,6.4,0.0,-0.0,0.0,1.86,88.95 +2020-04-21 03:00:00,5.99,0.0,-0.0,0.0,1.79,85.7 +2020-04-21 04:00:00,5.76,0.0,0.0,0.0,1.72,88.9 +2020-04-21 05:00:00,6.88,125.0,358.07,61.0,1.38,85.75 +2020-04-21 06:00:00,11.39,292.0,581.17,96.0,0.55,77.3 +2020-04-21 07:00:00,15.46,455.0,686.4,123.0,0.28,62.85 +2020-04-21 08:00:00,17.64,599.0,753.23,141.0,0.48,52.75 +2020-04-21 09:00:00,18.97,718.0,820.64,142.0,1.24,47.65 +2020-04-21 10:00:00,19.99,795.0,865.85,138.0,1.72,43.0 +2020-04-21 11:00:00,20.58,807.0,849.15,149.0,1.79,40.1 +2020-04-21 12:00:00,20.92,762.0,798.31,164.0,2.0,40.1 +2020-04-21 13:00:00,20.87,664.0,701.18,185.0,2.28,40.1 +2020-04-21 14:00:00,20.81,567.0,739.43,137.0,2.21,40.1 +2020-04-21 15:00:00,20.7,420.0,675.98,115.0,2.14,40.1 +2020-04-21 16:00:00,20.03,257.0,571.4,85.0,2.34,44.6 +2020-04-21 17:00:00,18.27,95.0,354.04,45.0,2.41,52.9 +2020-04-21 18:00:00,16.5,0.0,-0.0,0.0,2.41,52.65 +2020-04-21 19:00:00,15.39,0.0,-0.0,0.0,2.34,50.5 +2020-04-21 20:00:00,13.47,0.0,-0.0,0.0,2.34,55.9 +2020-04-21 21:00:00,11.9,0.0,-0.0,0.0,2.41,62.1 +2020-04-21 22:00:00,10.72,0.0,-0.0,0.0,2.41,66.55 +2020-04-21 23:00:00,9.79,0.0,-0.0,0.0,2.34,71.5 +2020-04-22 00:00:00,9.09,0.0,-0.0,0.0,2.28,74.15 +2020-04-22 01:00:00,8.48,0.0,-0.0,0.0,2.14,76.85 +2020-04-22 02:00:00,7.95,0.0,-0.0,0.0,2.07,79.7 +2020-04-22 03:00:00,7.45,0.0,-0.0,0.0,1.93,82.65 +2020-04-22 04:00:00,6.99,0.0,0.0,0.0,1.86,82.6 +2020-04-22 05:00:00,7.69,137.0,429.9,58.0,1.59,85.8 +2020-04-22 06:00:00,11.45,296.0,581.78,97.0,1.1,80.15 +2020-04-22 07:00:00,16.13,458.0,686.14,123.0,0.9,65.25 +2020-04-22 08:00:00,18.29,605.0,764.25,137.0,1.1,58.9 +2020-04-22 09:00:00,19.88,721.0,818.74,143.0,1.38,55.2 +2020-04-22 10:00:00,21.2,794.0,852.28,144.0,1.79,49.95 +2020-04-22 11:00:00,22.08,804.0,836.13,153.0,2.14,40.5 +2020-04-22 12:00:00,22.54,769.0,811.78,158.0,2.34,36.4 +2020-04-22 13:00:00,22.75,693.0,798.07,145.0,2.62,35.1 +2020-04-22 14:00:00,22.74,579.0,770.85,128.0,2.83,35.1 +2020-04-22 15:00:00,22.48,427.0,692.59,112.0,2.97,35.1 +2020-04-22 16:00:00,21.89,263.0,587.33,84.0,2.55,40.35 +2020-04-22 17:00:00,20.22,101.0,378.88,46.0,2.14,47.9 +2020-04-22 18:00:00,18.04,0.0,-0.0,0.0,2.41,52.9 +2020-04-22 19:00:00,16.51,0.0,-0.0,0.0,2.34,56.6 +2020-04-22 20:00:00,14.7,0.0,-0.0,0.0,2.48,64.95 +2020-04-22 21:00:00,13.35,0.0,-0.0,0.0,2.48,72.1 +2020-04-22 22:00:00,12.24,0.0,-0.0,0.0,2.48,74.6 +2020-04-22 23:00:00,11.28,0.0,-0.0,0.0,2.48,77.2 +2020-04-23 00:00:00,10.36,0.0,-0.0,0.0,2.41,77.15 +2020-04-23 01:00:00,9.62,0.0,-0.0,0.0,2.34,79.9 +2020-04-23 02:00:00,9.08,0.0,-0.0,0.0,2.41,79.85 +2020-04-23 03:00:00,8.6,0.0,-0.0,0.0,2.34,82.75 +2020-04-23 04:00:00,8.18,3.0,0.0,3.0,2.28,82.7 +2020-04-23 05:00:00,8.76,145.0,471.6,56.0,2.0,82.75 +2020-04-23 06:00:00,12.2,310.0,631.52,91.0,1.52,80.2 +2020-04-23 07:00:00,16.26,470.0,716.43,117.0,1.93,62.95 +2020-04-23 08:00:00,18.11,612.0,773.6,135.0,2.28,56.85 +2020-04-23 09:00:00,19.71,727.0,828.22,139.0,2.76,53.25 +2020-04-23 10:00:00,21.04,766.0,754.13,188.0,3.52,46.5 +2020-04-23 11:00:00,21.51,802.0,820.74,160.0,4.07,40.35 +2020-04-23 12:00:00,21.71,701.0,584.51,259.0,4.34,40.35 +2020-04-23 13:00:00,21.78,688.0,769.41,157.0,4.69,40.35 +2020-04-23 14:00:00,21.61,572.0,737.39,138.0,4.9,38.9 +2020-04-23 15:00:00,20.89,425.0,674.09,116.0,4.69,41.55 +2020-04-23 16:00:00,20.25,260.0,554.31,89.0,4.21,44.6 +2020-04-23 17:00:00,19.16,97.0,308.56,51.0,4.0,45.95 +2020-04-23 18:00:00,17.55,0.0,-0.0,0.0,3.72,47.3 +2020-04-23 19:00:00,16.65,0.0,-0.0,0.0,3.66,43.8 +2020-04-23 20:00:00,15.61,0.0,-0.0,0.0,3.45,45.15 +2020-04-23 21:00:00,14.63,0.0,-0.0,0.0,3.31,44.9 +2020-04-23 22:00:00,13.68,0.0,-0.0,0.0,3.17,46.35 +2020-04-23 23:00:00,12.65,0.0,-0.0,0.0,2.9,49.7 +2020-04-24 00:00:00,11.53,0.0,-0.0,0.0,2.55,55.4 +2020-04-24 01:00:00,10.46,0.0,-0.0,0.0,2.41,61.75 +2020-04-24 02:00:00,9.54,0.0,-0.0,0.0,2.34,66.35 +2020-04-24 03:00:00,8.78,0.0,-0.0,0.0,2.41,74.05 +2020-04-24 04:00:00,8.11,2.0,0.0,2.0,2.34,76.8 +2020-04-24 05:00:00,8.3,152.0,490.71,57.0,2.21,79.7 +2020-04-24 06:00:00,10.48,314.0,623.16,95.0,2.34,71.7 +2020-04-24 07:00:00,12.72,477.0,720.14,119.0,2.83,66.95 +2020-04-24 08:00:00,14.52,619.0,776.48,137.0,3.03,60.4 +2020-04-24 09:00:00,16.16,731.0,818.06,147.0,3.38,54.45 +2020-04-24 10:00:00,17.47,807.0,863.44,142.0,3.79,47.3 +2020-04-24 11:00:00,18.35,818.0,845.02,154.0,3.86,42.45 +2020-04-24 12:00:00,18.87,777.0,805.63,165.0,3.66,39.55 +2020-04-24 13:00:00,19.14,702.0,795.89,150.0,3.72,36.8 +2020-04-24 14:00:00,19.14,568.0,692.54,158.0,3.79,35.45 +2020-04-24 15:00:00,18.8,431.0,675.42,119.0,3.93,35.35 +2020-04-24 16:00:00,18.31,269.0,579.79,88.0,3.79,36.55 +2020-04-24 17:00:00,17.33,109.0,398.78,48.0,3.03,40.6 +2020-04-24 18:00:00,15.67,0.0,-0.0,0.0,2.48,41.9 +2020-04-24 19:00:00,13.89,0.0,-0.0,0.0,1.93,50.1 +2020-04-24 20:00:00,12.33,0.0,-0.0,0.0,1.59,57.65 +2020-04-24 21:00:00,12.33,0.0,-0.0,0.0,1.38,51.5 +2020-04-24 22:00:00,13.22,0.0,-0.0,0.0,1.1,46.2 +2020-04-24 23:00:00,12.92,0.0,-0.0,0.0,0.69,44.5 +2020-04-25 00:00:00,12.38,0.0,-0.0,0.0,0.41,46.1 +2020-04-25 01:00:00,11.56,0.0,-0.0,0.0,0.69,49.45 +2020-04-25 02:00:00,10.79,0.0,-0.0,0.0,0.97,53.1 +2020-04-25 03:00:00,9.34,0.0,-0.0,0.0,1.24,57.05 +2020-04-25 04:00:00,8.21,4.0,0.0,4.0,1.31,66.05 +2020-04-25 05:00:00,8.62,87.0,55.44,76.0,1.03,66.15 +2020-04-25 06:00:00,9.77,299.0,528.08,111.0,1.03,74.2 +2020-04-25 07:00:00,10.68,372.0,299.13,222.0,2.28,80.05 +2020-04-25 08:00:00,11.82,325.0,57.62,289.0,3.52,74.55 +2020-04-25 09:00:00,12.12,339.0,30.65,317.0,3.72,69.35 +2020-04-25 10:00:00,12.55,136.0,0.0,136.0,3.66,69.45 +2020-04-25 11:00:00,12.35,231.0,0.0,231.0,3.24,71.95 +2020-04-25 12:00:00,13.04,113.0,0.0,113.0,3.1,69.55 +2020-04-25 13:00:00,13.74,257.0,4.3,254.0,3.24,67.15 +2020-04-25 14:00:00,13.98,153.0,0.0,153.0,3.31,64.85 +2020-04-25 15:00:00,14.23,125.0,0.0,125.0,3.72,62.55 +2020-04-25 16:00:00,14.14,160.0,79.15,135.0,3.45,60.3 +2020-04-25 17:00:00,13.75,100.0,280.57,56.0,2.69,62.45 +2020-04-25 18:00:00,12.9,0.0,-0.0,0.0,2.0,64.65 +2020-04-25 19:00:00,11.26,0.0,-0.0,0.0,1.52,74.45 +2020-04-25 20:00:00,9.89,0.0,-0.0,0.0,1.31,79.95 +2020-04-25 21:00:00,9.36,0.0,-0.0,0.0,1.31,79.9 +2020-04-25 22:00:00,8.74,0.0,-0.0,0.0,1.38,85.9 +2020-04-25 23:00:00,7.9,0.0,-0.0,0.0,1.45,85.85 +2020-04-26 00:00:00,8.59,0.0,-0.0,0.0,1.17,85.9 +2020-04-26 01:00:00,9.28,0.0,-0.0,0.0,0.76,82.85 +2020-04-26 02:00:00,9.11,0.0,-0.0,0.0,0.76,82.85 +2020-04-26 03:00:00,8.23,0.0,-0.0,0.0,1.1,89.05 +2020-04-26 04:00:00,7.82,2.0,0.0,2.0,1.1,85.85 +2020-04-26 05:00:00,7.53,46.0,0.0,46.0,0.97,89.0 +2020-04-26 06:00:00,9.63,143.0,19.42,136.0,0.69,89.15 +2020-04-26 07:00:00,10.94,312.0,144.35,239.0,1.1,86.1 +2020-04-26 08:00:00,11.98,212.0,3.18,210.0,1.72,83.15 +2020-04-26 09:00:00,12.84,316.0,19.4,302.0,2.0,86.25 +2020-04-26 10:00:00,13.87,346.0,20.58,330.0,2.48,80.4 +2020-04-26 11:00:00,14.41,170.0,0.0,170.0,2.97,77.75 +2020-04-26 12:00:00,13.12,193.0,0.0,193.0,3.72,77.5 +2020-04-26 13:00:00,12.25,200.0,0.0,200.0,2.9,86.2 +2020-04-26 14:00:00,12.6,340.0,91.85,285.0,3.79,77.45 +2020-04-26 15:00:00,13.14,374.0,407.32,183.0,2.62,74.75 +2020-04-26 16:00:00,13.86,272.0,572.82,89.0,2.69,72.2 +2020-04-26 17:00:00,13.48,107.0,336.13,53.0,2.14,74.85 +2020-04-26 18:00:00,12.1,0.0,0.0,0.0,2.07,83.15 +2020-04-26 19:00:00,11.01,0.0,-0.0,0.0,1.72,86.1 +2020-04-26 20:00:00,10.03,0.0,-0.0,0.0,1.72,92.5 +2020-04-26 21:00:00,9.38,0.0,-0.0,0.0,2.0,92.45 +2020-04-26 22:00:00,8.91,0.0,-0.0,0.0,2.07,95.85 +2020-04-26 23:00:00,8.61,0.0,-0.0,0.0,1.86,95.85 +2020-04-27 00:00:00,8.12,0.0,-0.0,0.0,1.72,95.8 +2020-04-27 01:00:00,7.29,0.0,-0.0,0.0,1.79,99.4 +2020-04-27 02:00:00,6.87,0.0,-0.0,0.0,1.66,95.8 +2020-04-27 03:00:00,6.35,0.0,-0.0,0.0,1.66,95.8 +2020-04-27 04:00:00,6.02,2.0,0.0,2.0,1.59,95.8 +2020-04-27 05:00:00,7.04,86.0,43.32,77.0,1.31,99.4 +2020-04-27 06:00:00,9.32,177.0,60.29,155.0,1.1,95.85 +2020-04-27 07:00:00,11.74,177.0,5.88,174.0,1.45,89.3 +2020-04-27 08:00:00,13.2,160.0,0.0,160.0,1.86,80.35 +2020-04-27 09:00:00,14.33,287.0,9.65,280.0,2.0,72.3 +2020-04-27 10:00:00,15.06,261.0,1.28,260.0,1.86,67.45 +2020-04-27 11:00:00,16.09,585.0,249.97,386.0,1.93,65.25 +2020-04-27 12:00:00,16.7,558.0,240.35,373.0,2.14,65.35 +2020-04-27 13:00:00,17.4,480.0,193.31,344.0,2.28,65.35 +2020-04-27 14:00:00,17.35,305.0,53.15,273.0,2.55,65.35 +2020-04-27 15:00:00,16.98,193.0,19.05,184.0,2.34,65.35 +2020-04-27 16:00:00,15.55,106.0,6.19,104.0,1.59,77.85 +2020-04-27 17:00:00,14.63,40.0,0.0,40.0,1.31,80.55 +2020-04-27 18:00:00,13.73,0.0,0.0,0.0,1.72,83.35 +2020-04-27 19:00:00,11.64,0.0,-0.0,0.0,1.03,92.55 +2020-04-27 20:00:00,10.83,0.0,-0.0,0.0,1.1,99.4 +2020-04-27 21:00:00,10.18,0.0,-0.0,0.0,0.9,95.9 +2020-04-27 22:00:00,9.19,0.0,-0.0,0.0,1.31,99.4 +2020-04-27 23:00:00,8.7,0.0,-0.0,0.0,1.31,95.85 +2020-04-28 00:00:00,9.75,0.0,-0.0,0.0,0.97,92.45 +2020-04-28 01:00:00,10.72,0.0,-0.0,0.0,0.69,89.25 +2020-04-28 02:00:00,10.68,0.0,-0.0,0.0,0.69,89.25 +2020-04-28 03:00:00,10.43,0.0,-0.0,0.0,0.69,89.25 +2020-04-28 04:00:00,10.23,12.0,18.54,11.0,0.83,92.5 +2020-04-28 05:00:00,10.24,52.0,0.0,52.0,0.9,92.5 +2020-04-28 06:00:00,11.6,79.0,0.0,79.0,0.55,92.55 +2020-04-28 07:00:00,14.59,186.0,7.78,182.0,1.24,77.75 +2020-04-28 08:00:00,15.68,577.0,590.64,201.0,1.31,75.15 +2020-04-28 09:00:00,16.62,617.0,444.55,293.0,1.52,72.7 +2020-04-28 10:00:00,17.49,719.0,577.54,266.0,1.86,70.3 +2020-04-28 11:00:00,18.45,688.0,459.09,321.0,2.14,63.4 +2020-04-28 12:00:00,19.38,678.0,495.51,295.0,2.55,57.1 +2020-04-28 13:00:00,19.94,437.0,128.75,346.0,2.9,53.35 +2020-04-28 14:00:00,20.25,285.0,36.34,263.0,3.38,53.35 +2020-04-28 15:00:00,19.92,285.0,134.53,221.0,3.59,55.2 +2020-04-28 16:00:00,17.62,231.0,306.16,131.0,2.07,70.3 +2020-04-28 17:00:00,17.58,87.0,130.78,65.0,1.93,67.85 +2020-04-28 18:00:00,16.38,0.0,0.0,0.0,1.93,70.1 +2020-04-28 19:00:00,14.74,0.0,-0.0,0.0,2.07,75.0 +2020-04-28 20:00:00,12.98,0.0,-0.0,0.0,2.0,83.25 +2020-04-28 21:00:00,11.48,0.0,-0.0,0.0,2.07,86.15 +2020-04-28 22:00:00,10.29,0.0,-0.0,0.0,2.07,89.2 +2020-04-28 23:00:00,9.43,0.0,-0.0,0.0,2.21,85.95 +2020-04-29 00:00:00,8.95,0.0,-0.0,0.0,2.34,82.85 +2020-04-29 01:00:00,8.47,0.0,-0.0,0.0,2.41,82.75 +2020-04-29 02:00:00,8.04,0.0,-0.0,0.0,2.34,82.7 +2020-04-29 03:00:00,7.56,0.0,-0.0,0.0,2.34,82.65 +2020-04-29 04:00:00,7.23,12.0,0.0,12.0,2.34,82.6 +2020-04-29 05:00:00,8.16,144.0,281.35,83.0,2.14,82.7 +2020-04-29 06:00:00,12.12,287.0,382.89,144.0,2.0,71.95 +2020-04-29 07:00:00,14.8,416.0,401.59,208.0,2.55,69.85 +2020-04-29 08:00:00,16.84,589.0,610.6,198.0,3.31,63.05 +2020-04-29 09:00:00,18.58,721.0,745.52,175.0,4.14,56.95 +2020-04-29 10:00:00,19.9,782.0,750.25,191.0,4.76,51.4 +2020-04-29 11:00:00,20.69,628.0,315.2,375.0,5.38,43.1 +2020-04-29 12:00:00,20.67,183.0,0.0,183.0,5.31,41.55 +2020-04-29 13:00:00,20.61,116.0,0.0,116.0,4.97,41.55 +2020-04-29 14:00:00,20.32,224.0,6.57,220.0,5.1,44.6 +2020-04-29 15:00:00,19.72,303.0,166.98,223.0,4.69,46.1 +2020-04-29 16:00:00,18.65,25.0,0.0,25.0,3.24,51.15 +2020-04-29 17:00:00,18.03,89.0,127.94,67.0,3.17,51.0 +2020-04-29 18:00:00,16.73,0.0,0.0,0.0,2.83,52.65 +2020-04-29 19:00:00,15.23,0.0,-0.0,0.0,2.9,50.35 +2020-04-29 20:00:00,14.33,0.0,-0.0,0.0,3.1,53.95 +2020-04-29 21:00:00,13.6,0.0,-0.0,0.0,2.76,55.9 +2020-04-29 22:00:00,12.61,0.0,-0.0,0.0,2.62,59.95 +2020-04-29 23:00:00,11.66,0.0,-0.0,0.0,2.55,64.35 +2020-04-30 00:00:00,10.86,0.0,-0.0,0.0,2.62,66.65 +2020-04-30 01:00:00,10.27,0.0,-0.0,0.0,2.62,71.6 +2020-04-30 02:00:00,9.74,0.0,-0.0,0.0,2.69,77.0 +2020-04-30 03:00:00,9.26,0.0,-0.0,0.0,2.76,79.85 +2020-04-30 04:00:00,8.76,9.0,0.0,9.0,2.69,82.75 +2020-04-30 05:00:00,9.18,150.0,302.88,83.0,2.62,82.85 +2020-04-30 06:00:00,10.61,301.0,431.63,138.0,2.97,77.15 +2020-04-30 07:00:00,12.1,439.0,471.39,193.0,3.38,77.35 +2020-04-30 08:00:00,13.44,625.0,726.67,157.0,3.45,72.2 +2020-04-30 09:00:00,14.86,631.0,452.54,298.0,3.72,67.35 +2020-04-30 10:00:00,16.17,615.0,305.93,373.0,4.07,60.75 +2020-04-30 11:00:00,17.19,796.0,729.67,208.0,4.28,58.7 +2020-04-30 12:00:00,18.08,809.0,858.52,140.0,4.34,52.9 +2020-04-30 13:00:00,18.37,732.0,842.83,131.0,4.34,51.0 +2020-04-30 14:00:00,18.42,603.0,773.09,130.0,5.1,45.85 +2020-04-30 15:00:00,18.06,436.0,619.8,137.0,4.76,47.4 +2020-04-30 16:00:00,14.48,280.0,539.5,100.0,3.6,46.83 +2020-04-30 17:00:00,14.35,105.0,216.33,67.0,3.44,50.03 +2020-04-30 18:00:00,14.22,0.0,0.0,0.0,3.28,53.23 +2020-04-30 19:00:00,14.09,0.0,-0.0,0.0,3.11,56.43 +2020-04-30 20:00:00,13.95,0.0,-0.0,0.0,2.95,59.62 +2020-04-30 21:00:00,13.82,0.0,-0.0,0.0,2.79,62.82 +2020-04-30 22:00:00,13.69,0.0,-0.0,0.0,2.63,66.02 +2020-04-30 23:00:00,13.56,0.0,-0.0,0.0,2.47,69.22 +2020-05-01 00:00:00,13.43,0.0,-0.0,0.0,2.31,72.42 +2020-05-01 01:00:00,13.3,0.0,-0.0,0.0,2.15,75.62 +2020-05-01 02:00:00,13.17,0.0,-0.0,0.0,1.99,78.81 +2020-05-01 03:00:00,13.03,0.0,-0.0,0.0,1.83,82.01 +2020-05-01 04:00:00,12.9,22.0,27.7,20.0,1.67,85.21 +2020-05-01 05:00:00,12.77,152.0,278.57,88.0,1.51,88.41 +2020-05-01 06:00:00,12.64,318.0,497.8,126.0,1.35,91.61 +2020-05-01 07:00:00,12.51,484.0,644.08,143.0,1.19,94.81 +2020-05-01 08:00:00,20.64,624.0,715.65,158.0,1.93,61.6 +2020-05-01 09:00:00,22.35,741.0,794.55,151.0,2.07,55.8 +2020-05-01 10:00:00,23.76,802.0,802.58,162.0,2.0,50.55 +2020-05-01 11:00:00,24.82,828.0,832.5,152.0,1.93,47.4 +2020-05-01 12:00:00,25.43,770.0,738.55,190.0,1.72,42.75 +2020-05-01 13:00:00,25.53,661.0,604.86,226.0,1.38,41.25 +2020-05-01 14:00:00,25.69,598.0,749.09,135.0,1.31,39.95 +2020-05-01 15:00:00,25.53,448.0,668.81,121.0,1.17,42.75 +2020-05-01 16:00:00,25.23,285.0,549.16,98.0,0.55,50.95 +2020-05-01 17:00:00,24.46,125.0,344.33,62.0,1.24,54.35 +2020-05-01 18:00:00,22.2,0.0,0.0,0.0,1.38,66.35 +2020-05-01 19:00:00,19.13,0.0,-0.0,0.0,1.93,70.55 +2020-05-01 20:00:00,17.48,0.0,-0.0,0.0,2.0,78.1 +2020-05-01 21:00:00,16.12,0.0,-0.0,0.0,2.14,83.6 +2020-05-01 22:00:00,15.16,0.0,-0.0,0.0,1.86,89.55 +2020-05-01 23:00:00,14.51,0.0,-0.0,0.0,1.79,89.5 +2020-05-02 00:00:00,13.81,0.0,-0.0,0.0,1.59,95.95 +2020-05-02 01:00:00,13.44,0.0,-0.0,0.0,1.45,92.65 +2020-05-02 02:00:00,13.3,0.0,-0.0,0.0,1.38,95.95 +2020-05-02 03:00:00,13.47,0.0,-0.0,0.0,1.31,92.65 +2020-05-02 04:00:00,12.85,25.0,39.19,22.0,1.52,92.6 +2020-05-02 05:00:00,13.39,151.0,256.54,91.0,1.31,95.95 +2020-05-02 06:00:00,17.24,317.0,479.98,130.0,1.03,80.85 +2020-05-02 07:00:00,19.75,486.0,643.41,143.0,1.1,73.15 +2020-05-02 08:00:00,21.6,633.0,742.45,147.0,1.45,61.85 +2020-05-02 09:00:00,23.22,743.0,793.78,151.0,1.79,58.0 +2020-05-02 10:00:00,24.69,804.0,800.73,163.0,2.14,52.6 +2020-05-02 11:00:00,25.88,825.0,819.63,157.0,2.41,49.3 +2020-05-02 12:00:00,26.67,776.0,752.28,183.0,2.69,43.15 +2020-05-02 13:00:00,25.97,695.0,713.15,180.0,3.31,46.0 +2020-05-02 14:00:00,25.05,581.0,671.35,164.0,3.1,52.75 +2020-05-02 15:00:00,25.1,232.0,42.67,211.0,3.52,54.55 +2020-05-02 16:00:00,24.24,161.0,55.25,142.0,3.31,60.25 +2020-05-02 17:00:00,22.92,58.0,10.72,56.0,2.83,66.45 +2020-05-02 18:00:00,21.56,5.0,0.0,5.0,2.48,71.0 +2020-05-02 19:00:00,20.07,0.0,-0.0,0.0,2.41,68.3 +2020-05-02 20:00:00,18.77,0.0,-0.0,0.0,2.21,75.55 +2020-05-02 21:00:00,17.6,0.0,-0.0,0.0,2.21,80.85 +2020-05-02 22:00:00,16.56,0.0,-0.0,0.0,1.72,80.8 +2020-05-02 23:00:00,14.99,0.0,-0.0,0.0,1.79,86.5 +2020-05-03 00:00:00,14.56,0.0,-0.0,0.0,2.41,89.5 +2020-05-03 01:00:00,14.24,0.0,-0.0,0.0,2.41,92.7 +2020-05-03 02:00:00,13.85,0.0,-0.0,0.0,2.34,95.95 +2020-05-03 03:00:00,13.61,0.0,-0.0,0.0,2.48,95.95 +2020-05-03 04:00:00,13.42,5.0,0.0,5.0,2.48,92.65 +2020-05-03 05:00:00,14.0,40.0,0.0,40.0,2.97,92.7 +2020-05-03 06:00:00,14.94,128.0,5.08,126.0,3.45,89.55 +2020-05-03 07:00:00,15.77,137.0,0.0,137.0,3.24,89.6 +2020-05-03 08:00:00,16.7,291.0,24.32,275.0,2.83,89.65 +2020-05-03 09:00:00,16.67,477.0,148.2,366.0,3.52,89.65 +2020-05-03 10:00:00,17.14,226.0,0.0,226.0,3.86,86.65 +2020-05-03 11:00:00,17.27,427.0,55.02,382.0,3.72,86.65 +2020-05-03 12:00:00,17.01,126.0,0.0,126.0,3.86,83.7 +2020-05-03 13:00:00,17.2,319.0,22.07,303.0,3.93,80.85 +2020-05-03 14:00:00,17.03,288.0,33.65,267.0,3.59,78.1 +2020-05-03 15:00:00,17.28,315.0,177.68,227.0,3.17,78.1 +2020-05-03 16:00:00,17.44,53.0,0.0,53.0,3.17,75.4 +2020-05-03 17:00:00,17.31,12.0,0.0,12.0,2.41,75.4 +2020-05-03 18:00:00,16.68,1.0,0.0,1.0,2.07,75.3 +2020-05-03 19:00:00,15.29,0.0,-0.0,0.0,1.72,86.5 +2020-05-03 20:00:00,14.58,0.0,-0.0,0.0,1.72,86.45 +2020-05-03 21:00:00,14.0,0.0,-0.0,0.0,1.86,89.5 +2020-05-03 22:00:00,13.48,0.0,-0.0,0.0,2.28,89.45 +2020-05-03 23:00:00,13.32,0.0,-0.0,0.0,2.48,86.3 +2020-05-04 00:00:00,12.97,0.0,-0.0,0.0,2.55,83.25 +2020-05-04 01:00:00,12.66,0.0,-0.0,0.0,2.62,83.25 +2020-05-04 02:00:00,12.16,0.0,-0.0,0.0,2.62,83.15 +2020-05-04 03:00:00,11.59,0.0,-0.0,0.0,2.41,83.1 +2020-05-04 04:00:00,11.05,45.0,200.01,28.0,2.21,83.05 +2020-05-04 05:00:00,11.35,115.0,70.28,98.0,1.93,86.1 +2020-05-04 06:00:00,13.31,349.0,599.29,111.0,2.14,77.5 +2020-05-04 07:00:00,14.95,510.0,701.64,131.0,2.07,72.45 +2020-05-04 08:00:00,16.38,650.0,760.72,147.0,2.28,60.75 +2020-05-04 09:00:00,17.36,593.0,332.42,343.0,2.69,50.85 +2020-05-04 10:00:00,18.07,752.0,602.62,266.0,2.9,47.4 +2020-05-04 11:00:00,18.68,781.0,644.51,252.0,3.66,44.2 +2020-05-04 12:00:00,19.12,658.0,394.21,345.0,3.59,41.15 +2020-05-04 13:00:00,19.35,567.0,320.08,334.0,3.38,41.15 +2020-05-04 14:00:00,19.35,513.0,405.03,259.0,2.97,41.15 +2020-05-04 15:00:00,19.37,452.0,626.01,140.0,2.55,42.7 +2020-05-04 16:00:00,19.19,293.0,530.59,107.0,1.93,44.3 +2020-05-04 17:00:00,18.37,140.0,407.93,61.0,0.83,63.3 +2020-05-04 18:00:00,18.32,9.0,0.0,9.0,0.28,49.15 +2020-05-04 19:00:00,14.22,0.0,-0.0,0.0,1.86,72.3 +2020-05-04 20:00:00,12.34,0.0,-0.0,0.0,2.21,74.6 +2020-05-04 21:00:00,11.26,0.0,-0.0,0.0,2.28,77.2 +2020-05-04 22:00:00,10.4,0.0,-0.0,0.0,2.21,77.15 +2020-05-04 23:00:00,9.67,0.0,-0.0,0.0,2.14,82.9 +2020-05-05 00:00:00,9.12,0.0,-0.0,0.0,2.21,82.85 +2020-05-05 01:00:00,8.65,0.0,-0.0,0.0,2.28,82.75 +2020-05-05 02:00:00,8.37,0.0,-0.0,0.0,2.28,82.75 +2020-05-05 03:00:00,8.08,0.0,-0.0,0.0,2.21,82.7 +2020-05-05 04:00:00,7.68,38.0,89.8,30.0,2.07,85.8 +2020-05-05 05:00:00,9.05,178.0,374.36,86.0,1.59,85.95 +2020-05-05 06:00:00,13.38,335.0,516.51,128.0,0.97,74.75 +2020-05-05 07:00:00,16.09,499.0,651.26,145.0,0.76,65.25 +2020-05-05 08:00:00,17.99,628.0,680.31,176.0,0.55,58.9 +2020-05-05 09:00:00,19.64,760.0,810.53,148.0,0.83,53.25 +2020-05-05 10:00:00,20.64,422.0,53.13,379.0,0.83,48.05 +2020-05-05 11:00:00,20.69,276.0,1.21,275.0,0.97,49.8 +2020-05-05 12:00:00,18.63,390.0,37.65,360.0,2.9,65.65 +2020-05-05 13:00:00,16.7,65.0,0.0,65.0,2.34,78.0 +2020-05-05 14:00:00,16.68,59.0,0.0,59.0,1.31,75.3 +2020-05-05 15:00:00,16.13,76.0,0.0,76.0,1.52,77.95 +2020-05-05 16:00:00,15.29,34.0,0.0,34.0,0.9,83.5 +2020-05-05 17:00:00,14.71,26.0,0.0,26.0,0.34,86.45 +2020-05-05 18:00:00,13.95,10.0,23.73,9.0,0.34,89.5 +2020-05-05 19:00:00,12.78,0.0,-0.0,0.0,1.24,95.95 +2020-05-05 20:00:00,12.24,0.0,-0.0,0.0,1.31,99.4 +2020-05-05 21:00:00,11.65,0.0,-0.0,0.0,1.1,99.4 +2020-05-05 22:00:00,11.27,0.0,-0.0,0.0,1.24,100.0 +2020-05-05 23:00:00,10.91,0.0,-0.0,0.0,1.79,99.35 +2020-05-06 00:00:00,10.05,0.0,-0.0,0.0,2.07,99.4 +2020-05-06 01:00:00,8.23,0.0,-0.0,0.0,2.14,99.4 +2020-05-06 02:00:00,6.8,0.0,-0.0,0.0,2.34,95.8 +2020-05-06 03:00:00,6.15,0.0,-0.0,0.0,2.0,92.3 +2020-05-06 04:00:00,5.75,18.0,0.0,18.0,1.86,95.75 +2020-05-06 05:00:00,5.61,30.0,0.0,30.0,1.59,92.3 +2020-05-06 06:00:00,5.7,59.0,0.0,59.0,1.31,92.3 +2020-05-06 07:00:00,5.89,125.0,0.0,125.0,1.66,88.95 +2020-05-06 08:00:00,6.38,223.0,1.5,222.0,1.38,85.75 +2020-05-06 09:00:00,6.98,251.0,1.32,250.0,1.45,89.0 +2020-05-06 10:00:00,7.85,209.0,0.0,209.0,1.38,85.8 +2020-05-06 11:00:00,8.52,324.0,7.26,318.0,1.24,79.75 +2020-05-06 12:00:00,8.86,293.0,5.0,289.0,0.83,82.75 +2020-05-06 13:00:00,9.36,129.0,0.0,129.0,1.17,85.95 +2020-05-06 14:00:00,9.46,55.0,0.0,55.0,1.52,82.9 +2020-05-06 15:00:00,9.7,78.0,0.0,78.0,1.93,82.9 +2020-05-06 16:00:00,9.55,60.0,0.0,60.0,2.34,82.9 +2020-05-06 17:00:00,9.37,53.0,0.0,53.0,2.28,85.95 +2020-05-06 18:00:00,9.06,5.0,0.0,5.0,1.86,85.95 +2020-05-06 19:00:00,7.3,0.0,-0.0,0.0,1.66,95.8 +2020-05-06 20:00:00,7.17,0.0,-0.0,0.0,1.59,95.8 +2020-05-06 21:00:00,7.05,0.0,-0.0,0.0,1.59,95.8 +2020-05-06 22:00:00,6.87,0.0,-0.0,0.0,1.59,92.35 +2020-05-06 23:00:00,6.73,0.0,-0.0,0.0,1.31,92.35 +2020-05-07 00:00:00,6.58,0.0,-0.0,0.0,1.1,92.35 +2020-05-07 01:00:00,6.65,0.0,-0.0,0.0,0.97,92.35 +2020-05-07 02:00:00,6.63,0.0,-0.0,0.0,0.9,92.35 +2020-05-07 03:00:00,6.15,0.0,-0.0,0.0,1.03,95.8 +2020-05-07 04:00:00,6.12,10.0,0.0,10.0,0.97,95.8 +2020-05-07 05:00:00,6.92,128.0,90.83,105.0,1.79,92.35 +2020-05-07 06:00:00,8.05,114.0,0.0,114.0,1.66,89.05 +2020-05-07 07:00:00,8.71,107.0,0.0,107.0,0.9,89.1 +2020-05-07 08:00:00,9.56,226.0,1.49,225.0,0.9,82.9 +2020-05-07 09:00:00,10.83,376.0,39.43,346.0,0.9,80.05 +2020-05-07 10:00:00,11.98,275.0,1.23,274.0,1.1,71.95 +2020-05-07 11:00:00,12.9,434.0,51.87,391.0,1.52,66.95 +2020-05-07 12:00:00,13.55,438.0,66.07,385.0,1.72,64.75 +2020-05-07 13:00:00,14.2,574.0,321.9,337.0,1.72,60.3 +2020-05-07 14:00:00,14.29,423.0,182.46,307.0,1.86,60.3 +2020-05-07 15:00:00,14.21,323.0,165.52,239.0,1.79,60.3 +2020-05-07 16:00:00,13.94,285.0,435.82,128.0,1.93,60.3 +2020-05-07 17:00:00,13.33,106.0,117.64,82.0,2.14,64.65 +2020-05-07 18:00:00,12.57,18.0,101.07,13.0,2.14,66.95 +2020-05-07 19:00:00,11.41,0.0,-0.0,0.0,2.0,71.85 +2020-05-07 20:00:00,10.57,0.0,-0.0,0.0,1.86,77.15 +2020-05-07 21:00:00,9.63,0.0,-0.0,0.0,1.86,82.9 +2020-05-07 22:00:00,9.04,0.0,-0.0,0.0,1.93,85.95 +2020-05-07 23:00:00,8.97,0.0,-0.0,0.0,2.0,82.85 +2020-05-08 00:00:00,8.96,0.0,-0.0,0.0,2.0,82.85 +2020-05-08 01:00:00,8.83,0.0,-0.0,0.0,2.07,89.1 +2020-05-08 02:00:00,8.71,0.0,-0.0,0.0,2.07,89.1 +2020-05-08 03:00:00,8.44,0.0,-0.0,0.0,2.14,89.1 +2020-05-08 04:00:00,8.08,8.0,0.0,8.0,2.21,89.05 +2020-05-08 05:00:00,8.81,52.0,0.0,52.0,2.28,89.1 +2020-05-08 06:00:00,10.43,255.0,165.4,187.0,2.55,80.05 +2020-05-08 07:00:00,12.8,465.0,468.15,206.0,2.48,83.25 +2020-05-08 08:00:00,14.58,408.0,118.79,328.0,2.76,69.85 +2020-05-08 09:00:00,15.76,533.0,206.91,375.0,2.62,70.0 +2020-05-08 10:00:00,16.52,370.0,22.02,352.0,2.55,60.85 +2020-05-08 11:00:00,17.18,554.0,163.54,418.0,2.34,54.7 +2020-05-08 12:00:00,18.13,270.0,1.24,269.0,2.48,51.0 +2020-05-08 13:00:00,18.93,232.0,0.0,232.0,2.34,47.55 +2020-05-08 14:00:00,19.47,314.0,43.85,286.0,2.07,44.3 +2020-05-08 15:00:00,19.24,426.0,470.18,186.0,1.72,45.95 +2020-05-08 16:00:00,19.0,309.0,558.63,106.0,1.79,49.45 +2020-05-08 17:00:00,18.29,159.0,467.72,62.0,1.66,58.9 +2020-05-08 18:00:00,16.88,19.0,75.37,15.0,2.0,60.85 +2020-05-08 19:00:00,15.31,0.0,-0.0,0.0,2.76,60.5 +2020-05-08 20:00:00,14.52,0.0,-0.0,0.0,2.9,58.25 +2020-05-08 21:00:00,13.93,0.0,-0.0,0.0,3.1,60.3 +2020-05-08 22:00:00,13.47,0.0,-0.0,0.0,3.17,62.45 +2020-05-08 23:00:00,13.02,0.0,-0.0,0.0,3.24,64.65 +2020-05-09 00:00:00,12.67,0.0,-0.0,0.0,3.38,66.95 +2020-05-09 01:00:00,12.44,0.0,-0.0,0.0,3.52,69.45 +2020-05-09 02:00:00,12.16,0.0,-0.0,0.0,3.59,69.35 +2020-05-09 03:00:00,11.88,0.0,-0.0,0.0,3.59,71.85 +2020-05-09 04:00:00,11.64,9.0,0.0,9.0,3.45,71.85 +2020-05-09 05:00:00,12.29,79.0,3.84,78.0,3.17,71.95 +2020-05-09 06:00:00,13.91,250.0,152.03,187.0,2.83,69.65 +2020-05-09 07:00:00,15.79,363.0,183.36,261.0,2.83,70.0 +2020-05-09 08:00:00,17.19,217.0,1.48,216.0,2.62,65.45 +2020-05-09 09:00:00,18.61,244.0,1.3,243.0,2.28,63.4 +2020-05-09 10:00:00,20.14,384.0,29.26,360.0,2.48,59.35 +2020-05-09 11:00:00,21.14,396.0,31.17,370.0,3.24,55.55 +2020-05-09 12:00:00,21.53,562.0,206.86,395.0,3.59,53.75 +2020-05-09 13:00:00,21.76,462.0,133.51,363.0,3.52,53.75 +2020-05-09 14:00:00,21.6,277.0,21.83,263.0,3.03,51.9 +2020-05-09 15:00:00,21.72,42.0,0.0,42.0,3.17,51.9 +2020-05-09 16:00:00,21.3,222.0,163.71,162.0,2.21,57.55 +2020-05-09 17:00:00,20.51,111.0,128.13,84.0,1.45,63.8 +2020-05-09 18:00:00,19.22,18.0,52.97,15.0,1.66,70.55 +2020-05-09 19:00:00,16.82,0.0,-0.0,0.0,2.0,83.65 +2020-05-09 20:00:00,15.53,0.0,-0.0,0.0,2.07,86.5 +2020-05-09 21:00:00,14.81,0.0,-0.0,0.0,2.07,89.5 +2020-05-09 22:00:00,14.12,0.0,-0.0,0.0,2.07,89.5 +2020-05-09 23:00:00,13.5,0.0,-0.0,0.0,2.07,89.45 +2020-05-10 00:00:00,13.51,0.0,-0.0,0.0,2.14,89.45 +2020-05-10 01:00:00,13.27,0.0,-0.0,0.0,2.21,92.65 +2020-05-10 02:00:00,12.52,0.0,-0.0,0.0,2.07,92.6 +2020-05-10 03:00:00,11.79,0.0,-0.0,0.0,2.07,95.9 +2020-05-10 04:00:00,11.41,63.0,249.64,36.0,2.0,95.9 +2020-05-10 05:00:00,12.85,204.0,466.42,81.0,1.66,95.95 +2020-05-10 06:00:00,16.55,295.0,287.38,175.0,1.52,83.65 +2020-05-10 07:00:00,19.12,514.0,668.75,140.0,1.86,78.3 +2020-05-10 08:00:00,20.77,652.0,739.22,150.0,2.21,68.4 +2020-05-10 09:00:00,22.14,752.0,760.78,167.0,2.55,57.75 +2020-05-10 10:00:00,22.86,759.0,610.21,257.0,2.55,55.9 +2020-05-10 11:00:00,23.88,726.0,487.69,318.0,2.97,50.55 +2020-05-10 12:00:00,24.8,564.0,211.16,393.0,3.79,44.15 +2020-05-10 13:00:00,24.96,449.0,118.27,361.0,3.72,44.15 +2020-05-10 14:00:00,24.71,477.0,293.5,288.0,3.1,45.75 +2020-05-10 15:00:00,24.59,304.0,125.92,239.0,2.48,49.05 +2020-05-10 16:00:00,24.21,108.0,2.71,107.0,2.0,54.35 +2020-05-10 17:00:00,23.26,107.0,102.8,85.0,1.72,62.15 +2020-05-10 18:00:00,21.92,21.0,66.5,17.0,2.14,64.0 +2020-05-10 19:00:00,20.12,0.0,-0.0,0.0,2.34,68.3 +2020-05-10 20:00:00,18.97,0.0,-0.0,0.0,2.48,72.95 +2020-05-10 21:00:00,18.2,0.0,-0.0,0.0,2.62,75.45 +2020-05-10 22:00:00,17.75,0.0,-0.0,0.0,2.76,78.1 +2020-05-10 23:00:00,17.62,0.0,-0.0,0.0,2.9,78.1 +2020-05-11 00:00:00,17.77,0.0,-0.0,0.0,3.03,78.1 +2020-05-11 01:00:00,17.89,0.0,-0.0,0.0,3.17,75.4 +2020-05-11 02:00:00,18.0,0.0,-0.0,0.0,3.1,72.9 +2020-05-11 03:00:00,17.84,0.0,-0.0,0.0,3.1,75.4 +2020-05-11 04:00:00,17.77,9.0,0.0,9.0,3.03,75.4 +2020-05-11 05:00:00,18.39,145.0,127.33,111.0,2.9,75.45 +2020-05-11 06:00:00,20.21,128.0,2.38,127.0,2.48,68.3 +2020-05-11 07:00:00,22.18,197.0,5.34,194.0,2.76,66.35 +2020-05-11 08:00:00,24.05,439.0,167.21,325.0,3.1,60.25 +2020-05-11 09:00:00,25.6,544.0,230.72,366.0,3.1,54.7 +2020-05-11 10:00:00,26.8,648.0,345.41,363.0,3.17,53.1 +2020-05-11 11:00:00,27.54,821.0,761.61,182.0,3.17,48.0 +2020-05-11 12:00:00,28.08,803.0,794.11,158.0,3.1,43.4 +2020-05-11 13:00:00,28.29,738.0,800.96,140.0,2.97,43.4 +2020-05-11 14:00:00,28.55,313.0,44.85,284.0,3.17,41.95 +2020-05-11 15:00:00,28.46,178.0,5.78,175.0,3.38,43.4 +2020-05-11 16:00:00,26.7,28.0,0.0,28.0,2.28,53.1 +2020-05-11 17:00:00,25.8,14.0,0.0,14.0,2.55,56.6 +2020-05-11 18:00:00,24.36,2.0,0.0,2.0,2.48,60.25 +2020-05-11 19:00:00,21.48,0.0,-0.0,0.0,2.21,78.55 +2020-05-11 20:00:00,19.82,0.0,-0.0,0.0,0.76,89.85 +2020-05-11 21:00:00,19.06,0.0,-0.0,0.0,2.34,86.8 +2020-05-11 22:00:00,18.11,0.0,-0.0,0.0,4.0,96.05 +2020-05-11 23:00:00,16.43,0.0,-0.0,0.0,4.14,96.05 +2020-05-12 00:00:00,15.19,0.0,-0.0,0.0,3.66,92.75 +2020-05-12 01:00:00,14.13,0.0,-0.0,0.0,3.38,96.0 +2020-05-12 02:00:00,13.28,0.0,-0.0,0.0,3.79,92.65 +2020-05-12 03:00:00,12.51,0.0,-0.0,0.0,3.59,92.6 +2020-05-12 04:00:00,11.8,6.0,0.0,6.0,3.72,92.55 +2020-05-12 05:00:00,11.36,29.0,0.0,29.0,3.59,89.3 +2020-05-12 06:00:00,10.94,63.0,0.0,63.0,4.14,80.1 +2020-05-12 07:00:00,10.79,100.0,0.0,100.0,3.79,86.05 +2020-05-12 08:00:00,10.42,156.0,0.0,156.0,4.21,77.15 +2020-05-12 09:00:00,10.21,360.0,28.43,338.0,4.41,71.6 +2020-05-12 10:00:00,10.72,167.0,0.0,167.0,4.48,66.55 +2020-05-12 11:00:00,11.72,673.0,349.43,379.0,4.97,57.55 +2020-05-12 12:00:00,12.65,506.0,125.21,404.0,5.24,51.65 +2020-05-12 13:00:00,13.26,512.0,196.24,365.0,5.31,48.0 +2020-05-12 14:00:00,13.58,465.0,249.54,303.0,5.31,42.95 +2020-05-12 15:00:00,13.33,418.0,412.04,203.0,5.24,41.2 +2020-05-12 16:00:00,12.73,213.0,122.48,167.0,5.17,42.7 +2020-05-12 17:00:00,11.88,105.0,77.12,88.0,4.41,47.6 +2020-05-12 18:00:00,11.12,26.0,89.5,20.0,3.72,49.3 +2020-05-12 19:00:00,10.02,0.0,-0.0,0.0,3.72,55.05 +2020-05-12 20:00:00,9.52,0.0,-0.0,0.0,3.59,57.05 +2020-05-12 21:00:00,8.69,0.0,-0.0,0.0,3.38,61.3 +2020-05-12 22:00:00,8.15,0.0,-0.0,0.0,3.24,66.05 +2020-05-12 23:00:00,7.52,0.0,-0.0,0.0,3.24,68.5 +2020-05-13 00:00:00,6.91,0.0,-0.0,0.0,3.24,68.4 +2020-05-13 01:00:00,6.51,0.0,-0.0,0.0,3.17,68.4 +2020-05-13 02:00:00,5.99,0.0,-0.0,0.0,3.1,70.95 +2020-05-13 03:00:00,5.56,0.0,-0.0,0.0,3.03,73.65 +2020-05-13 04:00:00,5.27,64.0,168.83,44.0,2.97,76.4 +2020-05-13 05:00:00,5.39,110.0,25.61,103.0,3.45,73.65 +2020-05-13 06:00:00,6.04,140.0,2.34,139.0,4.28,68.3 +2020-05-13 07:00:00,6.64,170.0,0.0,170.0,3.93,71.05 +2020-05-13 08:00:00,7.36,300.0,18.93,287.0,3.72,68.5 +2020-05-13 09:00:00,7.88,264.0,1.29,263.0,3.72,66.05 +2020-05-13 10:00:00,8.99,224.0,0.0,224.0,3.86,61.45 +2020-05-13 11:00:00,9.66,224.0,0.0,224.0,3.86,59.25 +2020-05-13 12:00:00,10.12,211.0,0.0,211.0,3.79,55.05 +2020-05-13 13:00:00,10.53,229.0,0.0,229.0,3.66,55.15 +2020-05-13 14:00:00,10.88,305.0,30.69,285.0,3.38,53.1 +2020-05-13 15:00:00,11.03,256.0,45.76,232.0,3.03,51.25 +2020-05-13 16:00:00,10.69,151.0,18.49,144.0,2.62,53.1 +2020-05-13 17:00:00,10.52,79.0,13.42,76.0,2.41,53.1 +2020-05-13 18:00:00,10.04,18.0,14.2,17.0,1.86,55.05 +2020-05-13 19:00:00,8.65,0.0,-0.0,0.0,1.31,66.15 +2020-05-13 20:00:00,7.49,0.0,-0.0,0.0,0.97,76.7 +2020-05-13 21:00:00,7.67,0.0,-0.0,0.0,0.76,68.5 +2020-05-13 22:00:00,7.53,0.0,-0.0,0.0,0.55,68.5 +2020-05-13 23:00:00,7.14,0.0,-0.0,0.0,0.69,71.05 +2020-05-14 00:00:00,6.77,0.0,-0.0,0.0,0.76,71.05 +2020-05-14 01:00:00,6.51,0.0,-0.0,0.0,0.83,71.05 +2020-05-14 02:00:00,6.18,0.0,-0.0,0.0,0.83,73.7 +2020-05-14 03:00:00,5.63,0.0,-0.0,0.0,0.9,76.5 +2020-05-14 04:00:00,4.31,80.0,345.11,38.0,1.17,82.3 +2020-05-14 05:00:00,5.53,239.0,597.02,74.0,0.76,82.5 +2020-05-14 06:00:00,8.38,407.0,715.03,100.0,0.9,71.35 +2020-05-14 07:00:00,9.45,570.0,792.69,118.0,0.83,68.9 +2020-05-14 08:00:00,11.04,710.0,839.92,131.0,1.31,59.6 +2020-05-14 09:00:00,12.11,763.0,699.92,218.0,1.52,55.5 +2020-05-14 10:00:00,12.63,442.0,52.88,398.0,1.52,51.65 +2020-05-14 11:00:00,13.2,617.0,226.98,425.0,1.66,49.85 +2020-05-14 12:00:00,13.29,487.0,93.99,410.0,1.72,49.85 +2020-05-14 13:00:00,13.81,377.0,41.12,346.0,1.52,48.15 +2020-05-14 14:00:00,13.93,289.0,21.4,275.0,1.59,46.5 +2020-05-14 15:00:00,14.47,373.0,240.9,246.0,1.79,44.9 +2020-05-14 16:00:00,14.22,304.0,422.15,143.0,1.72,46.5 +2020-05-14 17:00:00,13.79,120.0,110.27,95.0,1.24,53.85 +2020-05-14 18:00:00,12.88,38.0,217.01,22.0,1.24,62.2 +2020-05-14 19:00:00,10.71,0.0,-0.0,0.0,1.93,66.55 +2020-05-14 20:00:00,8.78,0.0,-0.0,0.0,1.93,76.85 +2020-05-14 21:00:00,7.52,0.0,-0.0,0.0,1.93,79.65 +2020-05-14 22:00:00,6.88,0.0,-0.0,0.0,1.93,79.55 +2020-05-14 23:00:00,6.46,0.0,-0.0,0.0,1.93,79.55 +2020-05-15 00:00:00,5.74,0.0,-0.0,0.0,2.0,82.5 +2020-05-15 01:00:00,5.13,0.0,-0.0,0.0,2.0,85.6 +2020-05-15 02:00:00,5.04,0.0,-0.0,0.0,2.0,85.6 +2020-05-15 03:00:00,4.81,0.0,-0.0,0.0,2.07,88.85 +2020-05-15 04:00:00,4.76,15.0,0.0,15.0,2.0,88.85 +2020-05-15 05:00:00,6.94,38.0,0.0,38.0,1.66,85.75 +2020-05-15 06:00:00,10.78,91.0,0.0,91.0,1.59,71.7 +2020-05-15 07:00:00,12.67,311.0,75.08,268.0,1.86,64.55 +2020-05-15 08:00:00,13.97,240.0,2.89,238.0,2.97,48.25 +2020-05-15 09:00:00,14.71,172.0,0.0,172.0,3.17,44.9 +2020-05-15 10:00:00,15.05,481.0,82.71,412.0,3.03,43.4 +2020-05-15 11:00:00,15.2,449.0,53.06,404.0,3.17,43.4 +2020-05-15 12:00:00,15.24,601.0,236.18,407.0,2.97,45.05 +2020-05-15 13:00:00,15.24,703.0,596.45,252.0,3.1,45.05 +2020-05-15 14:00:00,15.63,632.0,698.96,173.0,3.1,45.15 +2020-05-15 15:00:00,15.89,418.0,368.04,223.0,2.97,43.5 +2020-05-15 16:00:00,16.15,107.0,0.0,107.0,2.9,40.5 +2020-05-15 17:00:00,14.65,80.0,13.06,77.0,2.62,50.25 +2020-05-15 18:00:00,12.92,25.0,38.96,22.0,2.21,62.3 +2020-05-15 19:00:00,10.87,0.0,-0.0,0.0,2.07,80.05 +2020-05-15 20:00:00,10.08,0.0,-0.0,0.0,2.28,79.95 +2020-05-15 21:00:00,9.36,0.0,-0.0,0.0,2.41,85.95 +2020-05-15 22:00:00,9.15,0.0,-0.0,0.0,2.62,85.95 +2020-05-15 23:00:00,8.91,0.0,-0.0,0.0,2.69,82.85 +2020-05-16 00:00:00,8.48,0.0,-0.0,0.0,2.76,85.9 +2020-05-16 01:00:00,7.89,0.0,-0.0,0.0,3.1,85.85 +2020-05-16 02:00:00,7.38,0.0,-0.0,0.0,3.31,85.8 +2020-05-16 03:00:00,6.87,0.0,-0.0,0.0,3.17,85.75 +2020-05-16 04:00:00,6.71,7.0,0.0,7.0,3.24,85.75 +2020-05-16 05:00:00,6.96,80.0,0.0,80.0,3.66,89.0 +2020-05-16 06:00:00,7.36,74.0,0.0,74.0,3.72,89.0 +2020-05-16 07:00:00,7.7,202.0,3.48,200.0,4.48,82.65 +2020-05-16 08:00:00,8.32,191.0,0.0,191.0,4.76,76.8 +2020-05-16 09:00:00,9.08,208.0,0.0,208.0,5.31,66.25 +2020-05-16 10:00:00,9.95,173.0,0.0,173.0,5.45,61.65 +2020-05-16 11:00:00,10.14,229.0,0.0,229.0,5.17,59.35 +2020-05-16 12:00:00,10.91,206.0,0.0,206.0,5.17,55.3 +2020-05-16 13:00:00,11.61,370.0,35.6,343.0,5.45,53.35 +2020-05-16 14:00:00,11.85,480.0,253.38,313.0,5.1,55.4 +2020-05-16 15:00:00,12.06,431.0,403.82,216.0,5.24,51.5 +2020-05-16 16:00:00,11.79,355.0,671.82,95.0,5.31,51.35 +2020-05-16 17:00:00,10.82,155.0,270.65,92.0,4.97,59.5 +2020-05-16 18:00:00,9.97,14.0,0.0,14.0,3.86,61.65 +2020-05-16 19:00:00,8.79,0.0,-0.0,0.0,4.07,63.7 +2020-05-16 20:00:00,8.04,0.0,-0.0,0.0,4.07,68.6 +2020-05-16 21:00:00,7.46,0.0,-0.0,0.0,3.86,71.15 +2020-05-16 22:00:00,7.15,0.0,-0.0,0.0,3.72,76.65 +2020-05-16 23:00:00,6.69,0.0,-0.0,0.0,3.79,76.65 +2020-05-17 00:00:00,6.01,0.0,-0.0,0.0,3.66,76.55 +2020-05-17 01:00:00,5.6,0.0,-0.0,0.0,3.52,79.4 +2020-05-17 02:00:00,5.01,0.0,-0.0,0.0,3.31,79.35 +2020-05-17 03:00:00,4.64,0.0,-0.0,0.0,3.24,82.35 +2020-05-17 04:00:00,4.49,79.0,244.6,47.0,3.31,82.35 +2020-05-17 05:00:00,4.99,236.0,519.57,88.0,3.24,79.35 +2020-05-17 06:00:00,5.66,333.0,331.65,188.0,4.14,73.65 +2020-05-17 07:00:00,6.35,464.0,372.29,249.0,4.69,70.95 +2020-05-17 08:00:00,7.28,572.0,379.21,308.0,4.97,61.0 +2020-05-17 09:00:00,8.35,641.0,357.89,360.0,4.69,54.55 +2020-05-17 10:00:00,8.68,622.0,242.14,419.0,4.41,52.6 +2020-05-17 11:00:00,9.6,573.0,159.59,437.0,4.55,47.05 +2020-05-17 12:00:00,10.33,620.0,255.56,409.0,4.55,47.2 +2020-05-17 13:00:00,10.57,563.0,248.49,374.0,4.14,45.55 +2020-05-17 14:00:00,10.92,502.0,288.76,311.0,3.93,43.95 +2020-05-17 15:00:00,11.01,423.0,360.78,230.0,3.45,45.7 +2020-05-17 16:00:00,10.97,223.0,115.46,178.0,2.9,45.7 +2020-05-17 17:00:00,10.85,118.0,84.84,98.0,2.28,49.2 +2020-05-17 18:00:00,10.15,41.0,155.92,28.0,1.38,57.15 +2020-05-17 19:00:00,8.44,0.0,-0.0,0.0,1.17,68.7 +2020-05-17 20:00:00,9.33,0.0,-0.0,0.0,0.21,54.8 +2020-05-17 21:00:00,7.9,0.0,-0.0,0.0,1.1,56.7 +2020-05-17 22:00:00,4.35,0.0,-0.0,0.0,2.0,73.45 +2020-05-17 23:00:00,3.21,0.0,-0.0,0.0,2.14,79.1 +2020-05-18 00:00:00,2.73,0.0,-0.0,0.0,2.21,82.15 +2020-05-18 01:00:00,2.54,0.0,-0.0,0.0,2.34,79.0 +2020-05-18 02:00:00,2.65,0.0,-0.0,0.0,2.55,79.0 +2020-05-18 03:00:00,2.77,0.0,-0.0,0.0,2.62,79.0 +2020-05-18 04:00:00,3.37,75.0,187.03,50.0,2.69,73.3 +2020-05-18 05:00:00,5.74,213.0,358.28,110.0,2.9,68.2 +2020-05-18 06:00:00,8.01,355.0,420.8,170.0,3.38,63.6 +2020-05-18 07:00:00,9.01,447.0,324.28,259.0,3.79,61.45 +2020-05-18 08:00:00,10.35,606.0,472.58,276.0,4.21,55.05 +2020-05-18 09:00:00,11.41,539.0,182.94,395.0,4.34,49.45 +2020-05-18 10:00:00,12.53,578.0,180.88,426.0,4.28,46.1 +2020-05-18 11:00:00,13.29,687.0,336.01,400.0,4.34,44.5 +2020-05-18 12:00:00,14.04,572.0,184.85,419.0,4.55,41.5 +2020-05-18 13:00:00,14.55,430.0,76.04,372.0,4.48,41.65 +2020-05-18 14:00:00,14.58,441.0,171.75,327.0,4.07,41.65 +2020-05-18 15:00:00,14.27,353.0,180.48,256.0,3.93,44.75 +2020-05-18 16:00:00,14.27,369.0,713.47,89.0,3.45,46.5 +2020-05-18 17:00:00,13.87,147.0,201.14,99.0,2.62,50.0 +2020-05-18 18:00:00,12.74,41.0,150.32,28.0,2.41,51.65 +2020-05-18 19:00:00,11.35,0.0,-0.0,0.0,2.48,57.4 +2020-05-18 20:00:00,10.28,0.0,-0.0,0.0,2.62,59.35 +2020-05-18 21:00:00,9.7,0.0,-0.0,0.0,2.76,59.25 +2020-05-18 22:00:00,9.33,0.0,-0.0,0.0,2.9,59.15 +2020-05-18 23:00:00,9.06,0.0,-0.0,0.0,3.03,59.15 +2020-05-19 00:00:00,8.79,0.0,-0.0,0.0,3.03,63.7 +2020-05-19 01:00:00,8.81,0.0,-0.0,0.0,2.97,66.15 +2020-05-19 02:00:00,8.51,0.0,-0.0,0.0,2.9,66.15 +2020-05-19 03:00:00,8.25,0.0,-0.0,0.0,2.9,68.6 +2020-05-19 04:00:00,8.07,78.0,212.59,49.0,2.76,71.25 +2020-05-19 05:00:00,9.51,219.0,399.97,103.0,2.55,68.9 +2020-05-19 06:00:00,11.84,382.0,563.38,133.0,2.48,66.75 +2020-05-19 07:00:00,14.47,555.0,725.19,133.0,2.62,58.25 +2020-05-19 08:00:00,15.96,692.0,779.65,146.0,2.69,54.45 +2020-05-19 09:00:00,17.79,750.0,665.31,225.0,2.69,52.75 +2020-05-19 10:00:00,19.21,869.0,856.07,148.0,2.76,47.65 +2020-05-19 11:00:00,20.22,895.0,884.29,138.0,2.83,44.6 +2020-05-19 12:00:00,20.96,849.0,840.1,152.0,2.76,43.1 +2020-05-19 13:00:00,21.5,767.0,804.09,152.0,2.62,40.2 +2020-05-19 14:00:00,21.78,654.0,774.77,138.0,2.48,38.9 +2020-05-19 15:00:00,21.77,517.0,740.89,117.0,2.48,38.9 +2020-05-19 16:00:00,21.43,340.0,574.54,113.0,2.48,41.7 +2020-05-19 17:00:00,20.44,190.0,488.64,72.0,2.07,49.7 +2020-05-19 18:00:00,18.59,41.0,122.86,30.0,2.34,54.95 +2020-05-19 19:00:00,17.19,0.0,-0.0,0.0,2.76,54.7 +2020-05-19 20:00:00,15.93,0.0,-0.0,0.0,2.97,56.35 +2020-05-19 21:00:00,15.08,0.0,-0.0,0.0,3.03,54.25 +2020-05-19 22:00:00,14.36,0.0,-0.0,0.0,3.03,56.0 +2020-05-19 23:00:00,13.71,0.0,-0.0,0.0,3.03,58.0 +2020-05-20 00:00:00,13.12,0.0,-0.0,0.0,3.03,60.05 +2020-05-20 01:00:00,12.43,0.0,-0.0,0.0,3.03,62.2 +2020-05-20 02:00:00,11.92,0.0,-0.0,0.0,3.03,62.1 +2020-05-20 03:00:00,11.54,0.0,-0.0,0.0,3.1,64.35 +2020-05-20 04:00:00,11.38,89.0,309.24,46.0,3.03,69.15 +2020-05-20 05:00:00,12.54,240.0,526.58,86.0,2.97,66.95 +2020-05-20 06:00:00,14.67,404.0,655.08,113.0,2.97,64.95 +2020-05-20 07:00:00,17.0,568.0,756.86,126.0,3.59,56.75 +2020-05-20 08:00:00,18.52,707.0,811.66,137.0,3.86,54.95 +2020-05-20 09:00:00,20.02,818.0,858.45,139.0,4.0,51.5 +2020-05-20 10:00:00,21.43,882.0,877.91,141.0,4.0,48.2 +2020-05-20 11:00:00,22.64,908.0,905.69,131.0,4.0,43.65 +2020-05-20 12:00:00,23.62,880.0,907.91,125.0,3.93,42.35 +2020-05-20 13:00:00,24.28,796.0,871.06,128.0,3.93,41.0 +2020-05-20 14:00:00,24.65,676.0,827.59,123.0,3.93,39.7 +2020-05-20 15:00:00,24.68,527.0,761.6,114.0,3.93,39.7 +2020-05-20 16:00:00,24.22,350.0,611.03,107.0,3.38,44.05 +2020-05-20 17:00:00,23.13,122.0,85.96,101.0,3.1,48.7 +2020-05-20 18:00:00,21.38,19.0,0.0,19.0,2.97,53.6 +2020-05-20 19:00:00,19.84,0.0,-0.0,0.0,3.1,53.25 +2020-05-20 20:00:00,18.67,0.0,-0.0,0.0,3.1,54.95 +2020-05-20 21:00:00,17.87,0.0,-0.0,0.0,3.1,56.75 +2020-05-20 22:00:00,17.37,0.0,-0.0,0.0,3.24,54.7 +2020-05-20 23:00:00,17.06,0.0,-0.0,0.0,3.38,54.7 +2020-05-21 00:00:00,16.94,0.0,-0.0,0.0,3.52,56.6 +2020-05-21 01:00:00,16.8,0.0,-0.0,0.0,3.59,56.6 +2020-05-21 02:00:00,16.45,0.0,-0.0,0.0,3.66,58.6 +2020-05-21 03:00:00,16.11,0.0,-0.0,0.0,3.72,60.75 +2020-05-21 04:00:00,15.85,64.0,84.75,52.0,3.72,65.15 +2020-05-21 05:00:00,16.61,207.0,318.87,113.0,3.86,65.35 +2020-05-21 06:00:00,18.14,260.0,127.7,203.0,3.59,65.55 +2020-05-21 07:00:00,19.84,239.0,17.07,229.0,4.76,61.4 +2020-05-21 08:00:00,21.67,272.0,8.52,266.0,4.97,53.75 +2020-05-21 09:00:00,23.6,304.0,7.57,298.0,5.03,48.8 +2020-05-21 10:00:00,25.23,136.0,0.0,136.0,5.66,42.75 +2020-05-21 11:00:00,25.92,127.0,0.0,127.0,6.41,39.95 +2020-05-21 12:00:00,24.77,250.0,0.0,250.0,6.21,45.75 +2020-05-21 13:00:00,26.07,385.0,46.82,349.0,6.97,37.35 +2020-05-21 14:00:00,26.0,371.0,83.54,315.0,6.55,35.9 +2020-05-21 15:00:00,25.8,188.0,5.51,185.0,6.21,35.9 +2020-05-21 16:00:00,25.2,114.0,0.0,114.0,5.38,38.45 +2020-05-21 17:00:00,24.12,86.0,16.19,82.0,4.83,41.0 +2020-05-21 18:00:00,22.79,20.0,0.0,20.0,4.55,43.65 +2020-05-21 19:00:00,21.81,0.0,-0.0,0.0,4.41,44.95 +2020-05-21 20:00:00,21.01,0.0,-0.0,0.0,4.41,46.5 +2020-05-21 21:00:00,20.04,0.0,-0.0,0.0,4.21,51.5 +2020-05-21 22:00:00,18.83,0.0,-0.0,0.0,3.72,61.2 +2020-05-21 23:00:00,17.97,0.0,-0.0,0.0,3.38,65.55 +2020-05-22 00:00:00,17.48,0.0,-0.0,0.0,3.38,67.85 +2020-05-22 01:00:00,17.16,0.0,-0.0,0.0,3.24,70.3 +2020-05-22 02:00:00,16.9,0.0,-0.0,0.0,3.1,72.7 +2020-05-22 03:00:00,16.59,0.0,-0.0,0.0,3.03,72.7 +2020-05-22 04:00:00,16.48,23.0,0.0,23.0,2.9,72.7 +2020-05-22 05:00:00,17.36,142.0,67.33,122.0,2.76,72.8 +2020-05-22 06:00:00,19.12,372.0,508.45,144.0,2.55,70.55 +2020-05-22 07:00:00,21.34,520.0,586.85,175.0,3.38,61.7 +2020-05-22 08:00:00,22.9,664.0,691.3,176.0,3.59,57.9 +2020-05-22 09:00:00,24.18,753.0,685.99,208.0,3.59,50.7 +2020-05-22 10:00:00,24.96,820.0,724.48,206.0,3.59,47.4 +2020-05-22 11:00:00,25.45,315.0,4.64,311.0,3.52,44.3 +2020-05-22 12:00:00,25.55,599.0,239.45,399.0,3.52,42.9 +2020-05-22 13:00:00,25.97,280.0,5.19,276.0,3.66,42.9 +2020-05-22 14:00:00,25.7,540.0,398.54,272.0,3.59,42.9 +2020-05-22 15:00:00,25.53,507.0,700.29,124.0,3.45,45.9 +2020-05-22 16:00:00,25.1,341.0,566.14,113.0,2.28,49.2 +2020-05-22 17:00:00,24.63,90.0,20.02,85.0,1.79,54.45 +2020-05-22 18:00:00,23.25,7.0,0.0,7.0,2.9,60.05 +2020-05-22 19:00:00,22.14,0.0,-0.0,0.0,1.79,57.75 +2020-05-22 20:00:00,20.19,0.0,-0.0,0.0,1.72,68.3 +2020-05-22 21:00:00,19.18,0.0,-0.0,0.0,1.66,70.55 +2020-05-22 22:00:00,19.17,0.0,-0.0,0.0,1.45,68.1 +2020-05-22 23:00:00,18.52,0.0,-0.0,0.0,1.52,70.45 +2020-05-23 00:00:00,17.37,0.0,-0.0,0.0,1.72,75.4 +2020-05-23 01:00:00,16.36,0.0,-0.0,0.0,1.86,77.95 +2020-05-23 02:00:00,15.58,0.0,-0.0,0.0,1.79,80.65 +2020-05-23 03:00:00,14.93,0.0,0.0,0.0,1.79,83.45 +2020-05-23 04:00:00,14.45,85.0,211.8,54.0,1.72,83.45 +2020-05-23 05:00:00,16.2,239.0,491.35,92.0,1.38,83.6 +2020-05-23 06:00:00,20.05,405.0,648.33,113.0,1.38,65.95 +2020-05-23 07:00:00,21.91,568.0,751.23,125.0,1.86,68.6 +2020-05-23 08:00:00,23.4,709.0,816.82,131.0,2.48,62.15 +2020-05-23 09:00:00,24.78,819.0,865.46,130.0,3.17,50.8 +2020-05-23 10:00:00,25.51,645.0,289.71,399.0,3.66,42.75 +2020-05-23 11:00:00,25.76,862.0,786.71,183.0,3.66,39.95 +2020-05-23 12:00:00,26.07,779.0,620.06,260.0,3.59,38.7 +2020-05-23 13:00:00,26.49,641.0,418.02,318.0,3.59,40.1 +2020-05-23 14:00:00,26.66,651.0,745.72,148.0,3.52,38.85 +2020-05-23 15:00:00,26.62,506.0,671.94,137.0,3.52,38.85 +2020-05-23 16:00:00,26.21,343.0,552.87,119.0,3.72,40.1 +2020-05-23 17:00:00,24.68,188.0,416.09,83.0,3.66,47.4 +2020-05-23 18:00:00,23.25,27.0,9.89,26.0,3.38,54.1 +2020-05-23 19:00:00,22.2,0.0,-0.0,0.0,2.48,52.0 +2020-05-23 20:00:00,20.82,0.0,-0.0,0.0,2.28,59.5 +2020-05-23 21:00:00,19.51,0.0,-0.0,0.0,2.28,65.85 +2020-05-23 22:00:00,18.64,0.0,-0.0,0.0,2.55,72.95 +2020-05-23 23:00:00,18.05,0.0,-0.0,0.0,2.55,75.45 +2020-05-24 00:00:00,16.86,0.0,-0.0,0.0,1.86,83.65 +2020-05-24 01:00:00,15.88,0.0,-0.0,0.0,1.86,86.5 +2020-05-24 02:00:00,15.12,0.0,-0.0,0.0,2.14,89.55 +2020-05-24 03:00:00,14.81,0.0,0.0,0.0,2.28,92.7 +2020-05-24 04:00:00,14.93,80.0,161.51,56.0,2.48,92.7 +2020-05-24 05:00:00,15.67,208.0,288.83,121.0,2.55,80.65 +2020-05-24 06:00:00,16.53,399.0,603.64,126.0,3.1,67.75 +2020-05-24 07:00:00,17.2,496.0,464.97,221.0,3.17,75.4 +2020-05-24 08:00:00,18.37,653.0,613.33,218.0,3.38,72.9 +2020-05-24 09:00:00,19.68,687.0,467.61,314.0,3.66,63.6 +2020-05-24 10:00:00,20.73,749.0,499.59,324.0,3.93,57.45 +2020-05-24 11:00:00,21.54,847.0,725.11,220.0,4.14,48.3 +2020-05-24 12:00:00,22.04,862.0,852.49,147.0,4.34,41.95 +2020-05-24 13:00:00,22.42,776.0,806.94,151.0,4.55,40.5 +2020-05-24 14:00:00,22.53,668.0,787.86,135.0,4.62,37.75 +2020-05-24 15:00:00,22.4,500.0,629.38,153.0,4.69,39.05 +2020-05-24 16:00:00,21.9,324.0,436.76,146.0,4.62,38.9 +2020-05-24 17:00:00,21.16,175.0,305.97,97.0,3.93,41.7 +2020-05-24 18:00:00,19.96,39.0,48.13,34.0,3.79,42.85 +2020-05-24 19:00:00,18.69,0.0,-0.0,0.0,3.24,42.6 +2020-05-24 20:00:00,17.49,0.0,-0.0,0.0,3.17,43.9 +2020-05-24 21:00:00,16.36,0.0,-0.0,0.0,2.83,43.65 +2020-05-24 22:00:00,15.22,0.0,-0.0,0.0,2.55,45.05 +2020-05-24 23:00:00,13.99,0.0,-0.0,0.0,2.28,48.25 +2020-05-25 00:00:00,12.44,0.0,-0.0,0.0,2.0,55.65 +2020-05-25 01:00:00,11.09,0.0,-0.0,0.0,1.93,61.85 +2020-05-25 02:00:00,9.98,0.0,-0.0,0.0,1.93,66.45 +2020-05-25 03:00:00,9.12,0.0,0.0,0.0,2.0,71.4 +2020-05-25 04:00:00,9.01,52.0,19.9,49.0,1.86,76.95 +2020-05-25 05:00:00,11.21,163.0,102.26,132.0,1.72,71.75 +2020-05-25 06:00:00,13.02,428.0,709.19,106.0,2.34,62.3 +2020-05-25 07:00:00,14.1,589.0,792.48,119.0,2.62,62.55 +2020-05-25 08:00:00,15.55,730.0,846.94,128.0,2.83,58.45 +2020-05-25 09:00:00,16.95,833.0,870.9,137.0,3.24,56.6 +2020-05-25 10:00:00,18.02,898.0,889.47,140.0,3.52,51.0 +2020-05-25 11:00:00,18.81,925.0,917.75,130.0,3.59,47.55 +2020-05-25 12:00:00,19.43,879.0,872.24,146.0,3.52,44.3 +2020-05-25 13:00:00,19.99,796.0,837.29,146.0,3.59,41.45 +2020-05-25 14:00:00,20.31,683.0,809.19,134.0,3.66,39.95 +2020-05-25 15:00:00,20.39,539.0,758.84,119.0,3.72,38.5 +2020-05-25 16:00:00,20.14,371.0,648.98,105.0,3.66,37.1 +2020-05-25 17:00:00,19.63,211.0,536.05,73.0,2.97,39.8 +2020-05-25 18:00:00,18.59,58.0,197.06,37.0,2.41,44.2 +2020-05-25 19:00:00,16.96,0.0,-0.0,0.0,2.0,43.9 +2020-05-25 20:00:00,15.36,0.0,-0.0,0.0,2.07,50.35 +2020-05-25 21:00:00,13.65,0.0,-0.0,0.0,2.0,60.2 +2020-05-25 22:00:00,12.65,0.0,-0.0,0.0,1.79,64.55 +2020-05-25 23:00:00,11.7,0.0,-0.0,0.0,1.86,64.35 +2020-05-26 00:00:00,11.03,0.0,-0.0,0.0,1.86,64.25 +2020-05-26 01:00:00,10.83,0.0,-0.0,0.0,1.72,66.55 +2020-05-26 02:00:00,10.75,0.0,-0.0,0.0,1.52,64.1 +2020-05-26 03:00:00,10.57,0.0,0.0,0.0,1.45,64.1 +2020-05-26 04:00:00,10.08,104.0,353.5,50.0,1.45,69.0 +2020-05-26 05:00:00,12.08,255.0,554.08,86.0,0.83,66.85 +2020-05-26 06:00:00,15.14,421.0,680.24,111.0,0.9,56.25 +2020-05-26 07:00:00,16.79,570.0,728.19,137.0,1.52,56.6 +2020-05-26 08:00:00,18.17,721.0,825.54,133.0,2.21,49.15 +2020-05-26 09:00:00,19.04,767.0,674.5,227.0,2.9,42.7 +2020-05-26 10:00:00,19.65,863.0,798.94,181.0,3.17,42.85 +2020-05-26 11:00:00,20.21,919.0,908.11,131.0,3.24,41.45 +2020-05-26 12:00:00,20.59,0.0,0.0,0.0,2.97,40.1 +2020-05-26 13:00:00,20.96,761.0,728.75,194.0,2.76,40.1 +2020-05-26 14:00:00,21.18,439.0,157.27,332.0,2.62,38.75 +2020-05-26 15:00:00,21.05,461.0,450.0,211.0,2.83,37.35 +2020-05-26 16:00:00,20.79,311.0,354.25,165.0,2.76,38.65 +2020-05-26 17:00:00,20.27,205.0,488.66,78.0,1.79,43.0 +2020-05-26 18:00:00,19.18,67.0,293.09,35.0,1.31,53.15 +2020-05-26 19:00:00,17.44,0.0,-0.0,0.0,2.0,49.05 +2020-05-26 20:00:00,16.3,0.0,-0.0,0.0,2.07,50.6 +2020-05-26 21:00:00,15.6,0.0,-0.0,0.0,2.34,52.4 +2020-05-26 22:00:00,14.8,0.0,-0.0,0.0,2.21,58.25 +2020-05-26 23:00:00,13.88,0.0,-0.0,0.0,1.79,69.65 +2020-05-27 00:00:00,13.69,0.0,-0.0,0.0,1.38,69.65 +2020-05-27 01:00:00,14.07,0.0,-0.0,0.0,1.1,64.85 +2020-05-27 02:00:00,14.88,0.0,-0.0,0.0,0.55,60.4 +2020-05-27 03:00:00,13.37,0.0,0.0,0.0,1.1,69.55 +2020-05-27 04:00:00,12.17,58.0,32.32,53.0,1.24,80.2 +2020-05-27 05:00:00,13.12,239.0,443.35,103.0,0.83,86.3 +2020-05-27 06:00:00,14.96,408.0,627.57,121.0,0.83,69.9 +2020-05-27 07:00:00,17.12,572.0,738.13,132.0,0.69,63.2 +2020-05-27 08:00:00,18.63,708.0,791.71,143.0,1.31,56.95 +2020-05-27 09:00:00,19.82,736.0,591.06,262.0,2.0,47.8 +2020-05-27 10:00:00,20.33,825.0,693.57,232.0,2.14,43.0 +2020-05-27 11:00:00,20.68,697.0,352.06,391.0,2.07,41.55 +2020-05-27 12:00:00,21.05,454.0,62.83,401.0,2.07,40.2 +2020-05-27 13:00:00,21.09,114.0,0.0,114.0,2.14,40.2 +2020-05-27 14:00:00,21.03,600.0,533.57,236.0,2.21,40.2 +2020-05-27 15:00:00,20.86,127.0,0.0,127.0,2.14,41.55 +2020-05-27 16:00:00,20.74,171.0,24.13,161.0,2.14,41.55 +2020-05-27 17:00:00,20.05,103.0,26.69,96.0,1.59,46.2 +2020-05-27 18:00:00,19.16,35.0,17.9,33.0,1.31,55.05 +2020-05-27 19:00:00,17.39,0.0,-0.0,0.0,1.24,67.85 +2020-05-27 20:00:00,17.18,0.0,-0.0,0.0,1.03,56.75 +2020-05-27 21:00:00,17.78,0.0,-0.0,0.0,0.83,52.75 +2020-05-27 22:00:00,17.58,0.0,-0.0,0.0,0.69,50.85 +2020-05-27 23:00:00,17.3,0.0,-0.0,0.0,0.9,50.85 +2020-05-28 00:00:00,15.11,0.0,-0.0,0.0,1.24,60.5 +2020-05-28 01:00:00,13.93,0.0,-0.0,0.0,1.45,67.25 +2020-05-28 02:00:00,13.16,0.0,-0.0,0.0,1.52,72.1 +2020-05-28 03:00:00,12.89,0.0,0.0,0.0,1.45,77.45 +2020-05-28 04:00:00,12.93,51.0,12.78,49.0,1.38,77.5 +2020-05-28 05:00:00,14.18,186.0,171.85,133.0,1.03,80.45 +2020-05-28 06:00:00,17.09,103.0,0.0,103.0,1.03,60.95 +2020-05-28 07:00:00,17.43,126.0,0.0,126.0,1.93,67.85 +2020-05-28 08:00:00,18.23,393.0,75.53,339.0,1.86,67.95 +2020-05-28 09:00:00,18.45,484.0,110.8,395.0,2.0,67.95 +2020-05-28 10:00:00,19.26,754.0,499.83,326.0,2.07,63.5 +2020-05-28 11:00:00,19.51,861.0,748.97,209.0,1.86,61.4 +2020-05-28 12:00:00,19.83,760.0,542.04,302.0,1.79,59.25 +2020-05-28 13:00:00,20.11,575.0,261.09,371.0,2.21,55.3 +2020-05-28 14:00:00,20.18,599.0,523.4,241.0,2.41,51.5 +2020-05-28 15:00:00,20.65,496.0,579.03,172.0,2.41,48.05 +2020-05-28 16:00:00,20.57,339.0,468.17,144.0,2.34,44.7 +2020-05-28 17:00:00,20.47,199.0,415.7,89.0,1.93,44.6 +2020-05-28 18:00:00,19.49,62.0,192.64,40.0,1.1,55.2 +2020-05-28 19:00:00,20.3,0.0,-0.0,0.0,0.21,41.45 +2020-05-28 20:00:00,19.52,0.0,-0.0,0.0,0.69,42.85 +2020-05-28 21:00:00,16.37,0.0,-0.0,0.0,1.45,54.45 +2020-05-28 22:00:00,13.79,0.0,-0.0,0.0,1.79,67.15 +2020-05-28 23:00:00,12.52,0.0,-0.0,0.0,1.79,74.7 +2020-05-29 00:00:00,11.53,0.0,-0.0,0.0,1.79,80.15 +2020-05-29 01:00:00,10.8,0.0,-0.0,0.0,1.79,86.05 +2020-05-29 02:00:00,10.52,0.0,-0.0,0.0,1.79,86.05 +2020-05-29 03:00:00,10.15,0.0,0.0,0.0,1.93,89.2 +2020-05-29 04:00:00,10.57,87.0,164.32,61.0,1.86,86.05 +2020-05-29 05:00:00,13.34,173.0,125.82,134.0,1.59,83.25 +2020-05-29 06:00:00,17.7,273.0,132.54,212.0,1.93,60.95 +2020-05-29 07:00:00,18.46,327.0,80.16,279.0,3.24,63.3 +2020-05-29 08:00:00,19.28,707.0,781.92,147.0,3.17,61.3 +2020-05-29 09:00:00,20.36,797.0,771.96,176.0,3.59,57.3 +2020-05-29 10:00:00,21.27,791.0,592.4,283.0,4.28,48.2 +2020-05-29 11:00:00,21.73,727.0,408.33,371.0,4.41,43.4 +2020-05-29 12:00:00,22.18,693.0,385.17,367.0,4.9,40.5 +2020-05-29 13:00:00,22.42,583.0,274.62,368.0,5.45,39.05 +2020-05-29 14:00:00,21.54,541.0,357.29,296.0,4.69,43.4 +2020-05-29 15:00:00,21.47,510.0,632.27,155.0,4.69,44.85 +2020-05-29 16:00:00,21.25,302.0,303.38,175.0,4.62,43.25 +2020-05-29 17:00:00,20.54,166.0,209.83,110.0,4.0,43.1 +2020-05-29 18:00:00,19.4,44.0,42.88,39.0,3.38,42.7 +2020-05-29 19:00:00,17.92,0.0,-0.0,0.0,3.03,47.3 +2020-05-29 20:00:00,16.8,0.0,-0.0,0.0,2.55,45.45 +2020-05-29 21:00:00,15.56,0.0,-0.0,0.0,2.28,46.9 +2020-05-29 22:00:00,14.47,0.0,-0.0,0.0,2.21,50.25 +2020-05-29 23:00:00,13.26,0.0,-0.0,0.0,2.0,57.9 +2020-05-30 00:00:00,12.03,0.0,-0.0,0.0,1.79,64.45 +2020-05-30 01:00:00,10.96,0.0,-0.0,0.0,1.72,69.15 +2020-05-30 02:00:00,10.25,0.0,-0.0,0.0,1.66,74.3 +2020-05-30 03:00:00,9.55,0.0,0.0,0.0,1.72,77.0 +2020-05-30 04:00:00,9.48,93.0,200.19,61.0,1.59,79.9 +2020-05-30 05:00:00,11.74,240.0,423.87,108.0,1.31,77.3 +2020-05-30 06:00:00,14.52,291.0,171.16,212.0,2.21,58.25 +2020-05-30 07:00:00,16.1,531.0,563.33,193.0,3.45,56.5 +2020-05-30 08:00:00,17.18,527.0,267.65,335.0,4.21,49.05 +2020-05-30 09:00:00,17.95,473.0,98.07,394.0,4.41,49.05 +2020-05-30 10:00:00,18.79,719.0,416.91,361.0,4.69,49.3 +2020-05-30 11:00:00,19.45,877.0,794.88,183.0,5.45,47.65 +2020-05-30 12:00:00,19.86,847.0,786.79,180.0,5.86,46.1 +2020-05-30 13:00:00,19.54,601.0,307.24,360.0,5.59,46.1 +2020-05-30 14:00:00,19.52,486.0,234.21,325.0,4.97,46.1 +2020-05-30 15:00:00,18.83,539.0,740.24,122.0,4.55,49.3 +2020-05-30 16:00:00,18.61,339.0,451.68,149.0,4.48,49.3 +2020-05-30 17:00:00,18.09,211.0,475.7,83.0,3.86,51.0 +2020-05-30 18:00:00,17.18,63.0,176.54,42.0,2.97,52.75 +2020-05-30 19:00:00,15.48,0.0,-0.0,0.0,2.62,58.45 +2020-05-30 20:00:00,14.43,0.0,-0.0,0.0,2.0,67.25 +2020-05-30 21:00:00,13.57,0.0,-0.0,0.0,1.38,69.65 +2020-05-30 22:00:00,12.31,0.0,-0.0,0.0,1.24,80.2 +2020-05-30 23:00:00,11.47,0.0,-0.0,0.0,1.31,83.1 +2020-05-31 00:00:00,10.81,0.0,-0.0,0.0,1.38,86.05 +2020-05-31 01:00:00,10.42,0.0,-0.0,0.0,1.45,86.05 +2020-05-31 02:00:00,9.82,0.0,-0.0,0.0,1.52,92.45 +2020-05-31 03:00:00,9.56,0.0,0.0,0.0,1.59,89.15 +2020-05-31 04:00:00,10.23,13.0,0.0,13.0,1.38,89.2 +2020-05-31 05:00:00,12.1,34.0,0.0,34.0,0.83,86.2 +2020-05-31 06:00:00,14.51,240.0,75.63,205.0,0.97,69.85 +2020-05-31 07:00:00,15.35,368.0,139.74,284.0,1.17,77.8 +2020-05-31 08:00:00,17.27,182.0,0.0,182.0,2.07,67.85 +2020-05-31 09:00:00,18.35,279.0,2.48,277.0,3.59,63.3 +2020-05-31 10:00:00,18.58,419.0,38.38,386.0,4.0,63.4 +2020-05-31 11:00:00,19.04,450.0,50.33,406.0,3.72,63.5 +2020-05-31 12:00:00,19.51,514.0,115.42,416.0,3.38,63.6 +2020-05-31 13:00:00,20.39,449.0,91.62,377.0,3.31,63.7 +2020-05-31 14:00:00,20.66,100.0,0.0,100.0,2.9,61.6 +2020-05-31 15:00:00,20.48,190.0,3.54,188.0,2.14,61.5 +2020-05-31 16:00:00,15.93,56.0,0.0,56.0,1.72,81.06 +2020-05-31 17:00:00,15.97,59.0,0.0,59.0,1.73,82.32 +2020-05-31 18:00:00,16.02,20.0,0.0,20.0,1.74,83.59 +2020-05-31 19:00:00,16.07,0.0,-0.0,0.0,1.75,84.86 +2020-05-31 20:00:00,16.12,0.0,-0.0,0.0,1.76,86.12 +2020-05-31 21:00:00,16.17,0.0,-0.0,0.0,1.77,87.39 +2020-05-31 22:00:00,16.22,0.0,-0.0,0.0,1.78,88.66 +2020-05-31 23:00:00,16.27,0.0,-0.0,0.0,1.79,89.92 +2020-06-01 00:00:00,16.31,0.0,-0.0,0.0,1.8,91.19 +2020-06-01 01:00:00,16.36,0.0,-0.0,0.0,1.81,92.46 +2020-06-01 02:00:00,16.41,0.0,-0.0,0.0,1.82,93.72 +2020-06-01 03:00:00,16.46,0.0,0.0,0.0,1.83,94.99 +2020-06-01 04:00:00,16.51,32.0,0.0,32.0,1.85,96.26 +2020-06-01 05:00:00,16.56,176.0,136.92,133.0,1.86,97.52 +2020-06-01 06:00:00,16.61,326.0,284.54,194.0,1.87,98.79 +2020-06-01 07:00:00,16.66,460.0,353.71,247.0,1.88,100.0 +2020-06-01 08:00:00,19.94,616.0,507.35,251.0,2.14,86.85 +2020-06-01 09:00:00,20.99,641.0,370.21,342.0,1.93,78.55 +2020-06-01 10:00:00,20.94,643.0,286.92,396.0,1.52,81.2 +2020-06-01 11:00:00,21.09,687.0,346.13,384.0,1.03,78.55 +2020-06-01 12:00:00,22.1,716.0,455.13,329.0,1.03,76.1 +2020-06-01 13:00:00,22.95,705.0,580.48,248.0,0.9,71.15 +2020-06-01 14:00:00,23.35,436.0,159.28,326.0,0.76,71.25 +2020-06-01 15:00:00,23.67,354.0,165.82,260.0,0.97,68.95 +2020-06-01 16:00:00,23.64,50.0,0.0,50.0,1.1,68.95 +2020-06-01 17:00:00,23.27,22.0,0.0,22.0,1.03,71.25 +2020-06-01 18:00:00,21.4,33.0,8.1,32.0,1.86,81.3 +2020-06-01 19:00:00,20.9,0.0,-0.0,0.0,1.52,81.2 +2020-06-01 20:00:00,19.62,0.0,-0.0,0.0,1.38,86.85 +2020-06-01 21:00:00,18.39,0.0,-0.0,0.0,1.66,92.85 +2020-06-01 22:00:00,17.62,0.0,-0.0,0.0,1.86,92.85 +2020-06-01 23:00:00,17.08,0.0,-0.0,0.0,1.79,89.7 +2020-06-02 00:00:00,16.39,0.0,-0.0,0.0,1.79,92.8 +2020-06-02 01:00:00,16.06,0.0,-0.0,0.0,1.79,92.8 +2020-06-02 02:00:00,15.49,0.0,-0.0,0.0,1.93,92.75 +2020-06-02 03:00:00,15.17,0.0,0.0,0.0,2.07,96.0 +2020-06-02 04:00:00,15.63,64.0,36.56,58.0,2.07,92.75 +2020-06-02 05:00:00,17.71,221.0,314.08,122.0,1.93,86.65 +2020-06-02 06:00:00,19.89,388.0,511.87,150.0,2.07,75.7 +2020-06-02 07:00:00,21.58,552.0,654.89,157.0,2.21,64.0 +2020-06-02 08:00:00,22.83,690.0,731.58,163.0,2.41,57.9 +2020-06-02 09:00:00,24.01,800.0,792.74,159.0,2.48,54.2 +2020-06-02 10:00:00,24.98,872.0,834.26,153.0,2.62,50.8 +2020-06-02 11:00:00,25.79,884.0,827.17,159.0,2.62,47.65 +2020-06-02 12:00:00,26.35,873.0,864.35,137.0,2.62,46.15 +2020-06-02 13:00:00,26.63,780.0,795.05,153.0,2.69,44.7 +2020-06-02 14:00:00,26.79,584.0,476.79,254.0,2.69,43.15 +2020-06-02 15:00:00,26.75,530.0,705.28,129.0,2.76,43.15 +2020-06-02 16:00:00,26.34,371.0,607.42,112.0,3.03,46.15 +2020-06-02 17:00:00,25.04,214.0,472.25,84.0,2.34,52.75 +2020-06-02 18:00:00,23.67,69.0,199.17,44.0,2.14,60.15 +2020-06-02 19:00:00,22.32,0.0,-0.0,0.0,2.34,61.95 +2020-06-02 20:00:00,20.84,0.0,-0.0,0.0,2.28,68.4 +2020-06-02 21:00:00,19.75,0.0,-0.0,0.0,2.28,73.15 +2020-06-02 22:00:00,18.88,0.0,-0.0,0.0,2.48,78.25 +2020-06-02 23:00:00,18.42,0.0,-0.0,0.0,2.62,78.15 +2020-06-03 00:00:00,17.93,0.0,-0.0,0.0,2.55,78.1 +2020-06-03 01:00:00,17.24,0.0,-0.0,0.0,2.48,75.4 +2020-06-03 02:00:00,16.65,0.0,-0.0,0.0,2.48,78.0 +2020-06-03 03:00:00,16.16,0.0,0.0,0.0,2.41,77.95 +2020-06-03 04:00:00,16.22,104.0,272.23,59.0,2.34,80.75 +2020-06-03 05:00:00,17.87,249.0,477.43,98.0,2.21,80.85 +2020-06-03 06:00:00,20.15,403.0,588.08,129.0,2.34,70.7 +2020-06-03 07:00:00,21.58,562.0,700.29,139.0,2.83,64.0 +2020-06-03 08:00:00,22.67,699.0,763.99,148.0,2.48,59.95 +2020-06-03 09:00:00,23.77,793.0,774.59,166.0,2.14,58.1 +2020-06-03 10:00:00,24.73,676.0,339.61,383.0,2.07,56.35 +2020-06-03 11:00:00,25.23,228.0,0.0,228.0,2.21,52.75 +2020-06-03 12:00:00,25.84,198.0,0.0,198.0,2.28,51.05 +2020-06-03 13:00:00,25.66,142.0,0.0,142.0,1.52,52.85 +2020-06-03 14:00:00,23.5,137.0,0.0,137.0,1.17,66.55 +2020-06-03 15:00:00,22.1,249.0,28.06,233.0,1.59,76.1 +2020-06-03 16:00:00,21.27,288.0,242.89,184.0,1.03,84.05 +2020-06-03 17:00:00,20.91,105.0,21.64,99.0,1.1,86.9 +2020-06-03 18:00:00,20.34,76.0,258.69,43.0,1.38,92.95 +2020-06-03 19:00:00,18.42,0.0,-0.0,0.0,1.86,96.05 +2020-06-03 20:00:00,17.71,0.0,-0.0,0.0,1.1,96.05 +2020-06-03 21:00:00,17.3,0.0,-0.0,0.0,0.69,96.05 +2020-06-03 22:00:00,16.91,0.0,-0.0,0.0,0.9,96.05 +2020-06-03 23:00:00,16.49,0.0,-0.0,0.0,0.9,96.05 +2020-06-04 00:00:00,16.04,0.0,-0.0,0.0,1.1,96.05 +2020-06-04 01:00:00,15.88,0.0,-0.0,0.0,1.03,96.0 +2020-06-04 02:00:00,16.11,0.0,-0.0,0.0,0.76,96.05 +2020-06-04 03:00:00,16.07,0.0,0.0,0.0,0.62,96.05 +2020-06-04 04:00:00,16.37,12.0,0.0,12.0,0.62,99.4 +2020-06-04 05:00:00,16.5,34.0,0.0,34.0,0.9,96.05 +2020-06-04 06:00:00,16.85,87.0,0.0,87.0,1.59,96.05 +2020-06-04 07:00:00,16.97,146.0,0.0,146.0,1.59,96.05 +2020-06-04 08:00:00,17.58,276.0,8.31,270.0,1.93,96.05 +2020-06-04 09:00:00,18.07,469.0,98.73,389.0,2.48,89.75 +2020-06-04 10:00:00,18.81,511.0,105.37,420.0,3.03,83.8 +2020-06-04 11:00:00,19.8,544.0,128.64,431.0,3.79,75.7 +2020-06-04 12:00:00,20.92,622.0,258.86,401.0,3.72,68.4 +2020-06-04 13:00:00,21.73,368.0,31.6,343.0,3.86,57.65 +2020-06-04 14:00:00,21.97,394.0,96.4,327.0,3.52,55.65 +2020-06-04 15:00:00,22.34,287.0,59.46,253.0,3.52,52.0 +2020-06-04 16:00:00,22.51,178.0,25.59,167.0,3.1,50.3 +2020-06-04 17:00:00,21.76,192.0,315.34,104.0,2.48,55.65 +2020-06-04 18:00:00,20.67,63.0,123.53,47.0,2.07,59.5 +2020-06-04 19:00:00,19.34,0.0,-0.0,0.0,2.0,65.75 +2020-06-04 20:00:00,18.25,0.0,-0.0,0.0,2.28,65.55 +2020-06-04 21:00:00,17.75,0.0,-0.0,0.0,3.17,60.95 +2020-06-04 22:00:00,17.28,0.0,-0.0,0.0,3.59,54.7 +2020-06-04 23:00:00,16.52,0.0,-0.0,0.0,3.31,54.6 +2020-06-05 00:00:00,15.69,0.0,-0.0,0.0,2.9,58.45 +2020-06-05 01:00:00,14.94,0.0,-0.0,0.0,2.69,60.5 +2020-06-05 02:00:00,14.25,0.0,-0.0,0.0,2.48,64.85 +2020-06-05 03:00:00,13.61,0.0,0.0,0.0,2.48,69.65 +2020-06-05 04:00:00,13.47,117.0,364.35,56.0,2.48,72.2 +2020-06-05 05:00:00,14.47,267.0,550.08,92.0,2.41,72.4 +2020-06-05 06:00:00,15.86,426.0,652.3,121.0,3.17,67.55 +2020-06-05 07:00:00,17.05,578.0,713.38,146.0,3.45,67.85 +2020-06-05 08:00:00,18.36,720.0,791.48,148.0,3.79,67.95 +2020-06-05 09:00:00,19.34,820.0,810.13,163.0,4.41,61.3 +2020-06-05 10:00:00,19.81,884.0,828.3,168.0,4.62,57.2 +2020-06-05 11:00:00,20.25,906.0,847.22,161.0,4.55,53.35 +2020-06-05 12:00:00,20.4,886.0,862.22,149.0,4.55,51.5 +2020-06-05 13:00:00,20.58,806.0,827.92,150.0,4.55,48.05 +2020-06-05 14:00:00,20.77,591.0,456.68,273.0,4.48,46.35 +2020-06-05 15:00:00,20.68,561.0,772.75,118.0,4.34,46.35 +2020-06-05 16:00:00,20.33,394.0,665.02,107.0,4.0,47.9 +2020-06-05 17:00:00,19.89,235.0,555.45,79.0,3.03,51.4 +2020-06-05 18:00:00,18.98,81.0,273.95,45.0,2.55,53.15 +2020-06-05 19:00:00,17.39,0.0,-0.0,0.0,2.62,56.75 +2020-06-05 20:00:00,16.21,0.0,-0.0,0.0,2.34,60.75 +2020-06-05 21:00:00,15.13,0.0,-0.0,0.0,2.34,62.75 +2020-06-05 22:00:00,14.38,0.0,-0.0,0.0,2.55,64.85 +2020-06-05 23:00:00,13.97,0.0,-0.0,0.0,2.55,62.55 +2020-06-06 00:00:00,13.76,0.0,-0.0,0.0,2.41,67.15 +2020-06-06 01:00:00,12.72,0.0,-0.0,0.0,2.07,72.05 +2020-06-06 02:00:00,11.63,0.0,-0.0,0.0,1.93,77.3 +2020-06-06 03:00:00,10.81,1.0,0.0,1.0,2.0,83.0 +2020-06-06 04:00:00,10.95,113.0,326.74,58.0,2.0,83.05 +2020-06-06 05:00:00,13.4,269.0,561.27,90.0,2.0,77.5 +2020-06-06 06:00:00,15.43,432.0,676.95,115.0,2.69,69.9 +2020-06-06 07:00:00,16.76,589.0,755.51,131.0,3.03,72.7 +2020-06-06 08:00:00,18.05,680.0,653.92,207.0,4.14,63.3 +2020-06-06 09:00:00,18.79,641.0,336.35,368.0,4.41,59.05 +2020-06-06 10:00:00,19.48,709.0,380.28,380.0,4.41,53.25 +2020-06-06 11:00:00,19.6,658.0,263.58,426.0,4.07,53.25 +2020-06-06 12:00:00,19.9,604.0,212.68,422.0,3.93,53.25 +2020-06-06 13:00:00,20.22,445.0,78.14,383.0,3.93,51.5 +2020-06-06 14:00:00,20.57,400.0,93.17,335.0,4.14,49.8 +2020-06-06 15:00:00,20.39,491.0,499.37,204.0,3.79,51.5 +2020-06-06 16:00:00,19.88,376.0,577.15,126.0,2.97,57.2 +2020-06-06 17:00:00,19.79,201.0,332.66,107.0,2.69,57.2 +2020-06-06 18:00:00,19.01,63.0,105.09,49.0,2.14,59.15 +2020-06-06 19:00:00,18.19,0.0,-0.0,0.0,2.14,65.55 +2020-06-06 20:00:00,16.82,0.0,-0.0,0.0,1.93,67.75 +2020-06-06 21:00:00,15.57,0.0,-0.0,0.0,1.79,75.15 +2020-06-06 22:00:00,14.4,0.0,-0.0,0.0,1.79,80.45 +2020-06-06 23:00:00,14.18,0.0,-0.0,0.0,1.59,77.65 +2020-06-07 00:00:00,13.39,0.0,-0.0,0.0,1.72,80.35 +2020-06-07 01:00:00,12.61,0.0,-0.0,0.0,1.79,80.3 +2020-06-07 02:00:00,12.15,0.0,-0.0,0.0,1.86,83.15 +2020-06-07 03:00:00,11.68,2.0,0.0,2.0,1.93,83.1 +2020-06-07 04:00:00,12.09,86.0,118.25,66.0,1.72,83.15 +2020-06-07 05:00:00,14.08,272.0,578.83,87.0,1.45,83.4 +2020-06-07 06:00:00,17.58,413.0,592.9,135.0,1.59,70.3 +2020-06-07 07:00:00,19.07,570.0,688.89,152.0,1.52,70.55 +2020-06-07 08:00:00,20.7,711.0,766.69,156.0,2.28,63.8 +2020-06-07 09:00:00,21.72,643.0,342.26,365.0,3.31,55.65 +2020-06-07 10:00:00,22.07,286.0,1.15,285.0,3.72,52.0 +2020-06-07 11:00:00,22.24,307.0,2.27,305.0,3.72,52.0 +2020-06-07 12:00:00,22.54,193.0,0.0,193.0,3.66,48.55 +2020-06-07 13:00:00,22.81,626.0,339.82,356.0,3.45,46.85 +2020-06-07 14:00:00,22.9,555.0,366.31,299.0,3.38,43.65 +2020-06-07 15:00:00,22.77,252.0,26.04,237.0,3.31,43.65 +2020-06-07 16:00:00,22.6,297.0,243.85,191.0,3.31,43.65 +2020-06-07 17:00:00,21.97,188.0,256.85,115.0,2.62,50.05 +2020-06-07 18:00:00,20.64,47.0,29.64,43.0,1.79,57.45 +2020-06-07 19:00:00,18.95,0.0,-0.0,0.0,1.66,65.65 +2020-06-07 20:00:00,18.62,0.0,-0.0,0.0,1.31,59.05 +2020-06-07 21:00:00,18.46,0.0,-0.0,0.0,1.31,56.85 +2020-06-07 22:00:00,18.7,0.0,-0.0,0.0,1.17,53.0 +2020-06-07 23:00:00,19.21,0.0,-0.0,0.0,0.9,51.25 +2020-06-08 00:00:00,18.66,0.0,-0.0,0.0,0.76,53.0 +2020-06-08 01:00:00,18.07,0.0,-0.0,0.0,1.03,52.9 +2020-06-08 02:00:00,17.62,0.0,-0.0,0.0,1.03,56.75 +2020-06-08 03:00:00,17.05,1.0,0.0,1.0,1.1,54.7 +2020-06-08 04:00:00,14.86,88.0,129.53,66.0,1.45,67.35 +2020-06-08 05:00:00,15.42,202.0,202.99,137.0,1.24,75.1 +2020-06-08 06:00:00,18.33,401.0,538.98,148.0,0.83,67.95 +2020-06-08 07:00:00,19.58,580.0,732.8,135.0,1.24,63.6 +2020-06-08 08:00:00,21.01,716.0,785.5,147.0,2.14,57.55 +2020-06-08 09:00:00,21.92,815.0,808.34,158.0,2.55,55.65 +2020-06-08 10:00:00,21.71,854.0,752.5,202.0,3.31,55.65 +2020-06-08 11:00:00,20.82,476.0,63.51,420.0,3.59,59.5 +2020-06-08 12:00:00,21.32,425.0,40.81,390.0,3.72,57.55 +2020-06-08 13:00:00,21.73,275.0,2.51,273.0,3.93,51.9 +2020-06-08 14:00:00,21.85,543.0,338.56,306.0,4.34,50.05 +2020-06-08 15:00:00,21.28,533.0,668.47,147.0,4.0,49.95 +2020-06-08 16:00:00,20.93,311.0,288.9,185.0,3.59,51.65 +2020-06-08 17:00:00,20.36,43.0,0.0,43.0,2.69,57.3 +2020-06-08 18:00:00,19.49,41.0,14.64,39.0,2.14,59.25 +2020-06-08 19:00:00,18.44,0.0,-0.0,0.0,1.59,70.35 +2020-06-08 20:00:00,16.93,0.0,-0.0,0.0,1.45,75.3 +2020-06-08 21:00:00,16.18,0.0,-0.0,0.0,1.45,75.25 +2020-06-08 22:00:00,14.75,0.0,-0.0,0.0,1.72,83.45 +2020-06-08 23:00:00,13.76,0.0,-0.0,0.0,1.79,89.45 +2020-06-09 00:00:00,12.94,0.0,-0.0,0.0,1.72,89.4 +2020-06-09 01:00:00,12.51,0.0,-0.0,0.0,1.72,92.6 +2020-06-09 02:00:00,12.36,0.0,-0.0,0.0,1.66,92.6 +2020-06-09 03:00:00,12.11,1.0,0.0,1.0,1.59,92.6 +2020-06-09 04:00:00,12.83,83.0,99.74,66.0,1.24,92.6 +2020-06-09 05:00:00,15.04,186.0,146.55,139.0,0.76,89.55 +2020-06-09 06:00:00,18.23,401.0,542.74,146.0,0.9,78.15 +2020-06-09 07:00:00,20.19,568.0,692.81,147.0,1.24,68.3 +2020-06-09 08:00:00,21.45,708.0,769.87,150.0,1.66,61.7 +2020-06-09 09:00:00,22.57,811.0,802.95,158.0,1.93,55.9 +2020-06-09 10:00:00,23.49,884.0,844.29,152.0,1.93,52.25 +2020-06-09 11:00:00,24.04,892.0,826.18,163.0,1.79,45.6 +2020-06-09 12:00:00,24.24,840.0,744.46,201.0,1.59,44.05 +2020-06-09 13:00:00,24.69,760.0,696.78,205.0,1.31,41.15 +2020-06-09 14:00:00,24.99,534.0,319.48,310.0,1.17,41.15 +2020-06-09 15:00:00,25.03,549.0,730.95,126.0,1.38,38.45 +2020-06-09 16:00:00,24.82,372.0,550.82,131.0,1.45,39.7 +2020-06-09 17:00:00,23.98,218.0,424.69,96.0,1.1,54.2 +2020-06-09 18:00:00,22.63,69.0,130.3,51.0,1.31,62.05 +2020-06-09 19:00:00,20.89,0.0,0.0,0.0,1.86,61.6 +2020-06-09 20:00:00,18.78,0.0,-0.0,0.0,2.07,68.05 +2020-06-09 21:00:00,17.33,0.0,-0.0,0.0,2.14,70.3 +2020-06-09 22:00:00,16.14,0.0,-0.0,0.0,2.28,72.65 +2020-06-09 23:00:00,15.51,0.0,-0.0,0.0,2.28,72.55 +2020-06-10 00:00:00,14.62,0.0,-0.0,0.0,2.14,75.0 +2020-06-10 01:00:00,14.33,0.0,-0.0,0.0,1.93,77.65 +2020-06-10 02:00:00,13.85,0.0,-0.0,0.0,1.86,80.4 +2020-06-10 03:00:00,13.09,2.0,0.0,2.0,1.86,83.25 +2020-06-10 04:00:00,13.05,118.0,356.82,57.0,1.66,86.3 +2020-06-10 05:00:00,15.41,44.0,0.0,44.0,1.1,86.5 +2020-06-10 06:00:00,19.25,70.0,0.0,70.0,1.24,70.55 +2020-06-10 07:00:00,20.79,585.0,741.78,134.0,1.45,63.8 +2020-06-10 08:00:00,22.54,118.0,0.0,118.0,1.72,55.9 +2020-06-10 09:00:00,23.93,0.0,0.0,0.0,2.0,48.8 +2020-06-10 10:00:00,24.82,839.0,693.96,237.0,2.34,44.15 +2020-06-10 11:00:00,25.36,148.0,0.0,148.0,2.48,41.25 +2020-06-10 12:00:00,25.45,144.0,0.0,144.0,2.48,41.25 +2020-06-10 13:00:00,25.68,814.0,842.71,142.0,2.41,39.95 +2020-06-10 14:00:00,25.63,689.0,771.86,147.0,2.48,39.95 +2020-06-10 15:00:00,25.86,90.0,0.0,90.0,2.69,39.95 +2020-06-10 16:00:00,25.41,390.0,622.08,117.0,2.76,42.75 +2020-06-10 17:00:00,24.52,228.0,474.57,91.0,2.07,50.8 +2020-06-10 18:00:00,23.24,15.0,0.0,15.0,1.86,58.0 +2020-06-10 19:00:00,21.61,0.0,0.0,0.0,1.93,59.7 +2020-06-10 20:00:00,19.73,0.0,-0.0,0.0,2.14,68.2 +2020-06-10 21:00:00,18.41,0.0,-0.0,0.0,2.21,72.9 +2020-06-10 22:00:00,17.46,0.0,-0.0,0.0,2.21,72.8 +2020-06-10 23:00:00,16.38,0.0,-0.0,0.0,2.21,77.95 +2020-06-11 00:00:00,15.94,0.0,-0.0,0.0,2.21,75.25 +2020-06-11 01:00:00,15.36,0.0,-0.0,0.0,2.34,77.8 +2020-06-11 02:00:00,14.83,0.0,-0.0,0.0,2.34,80.55 +2020-06-11 03:00:00,14.23,1.0,0.0,1.0,2.21,86.4 +2020-06-11 04:00:00,14.33,11.0,0.0,11.0,2.0,89.5 +2020-06-11 05:00:00,15.84,44.0,0.0,44.0,2.0,83.55 +2020-06-11 06:00:00,16.37,74.0,0.0,74.0,2.9,75.25 +2020-06-11 07:00:00,16.67,94.0,0.0,94.0,2.97,75.3 +2020-06-11 08:00:00,16.64,150.0,0.0,150.0,3.17,72.7 +2020-06-11 09:00:00,17.32,220.0,0.0,220.0,3.31,67.85 +2020-06-11 10:00:00,17.65,185.0,0.0,185.0,3.59,65.45 +2020-06-11 11:00:00,17.64,442.0,43.01,404.0,3.66,63.2 +2020-06-11 12:00:00,18.47,314.0,4.65,310.0,4.07,59.05 +2020-06-11 13:00:00,18.47,245.0,0.0,245.0,3.93,56.95 +2020-06-11 14:00:00,18.82,248.0,4.27,245.0,4.28,53.0 +2020-06-11 15:00:00,18.88,246.0,22.37,233.0,4.34,49.3 +2020-06-11 16:00:00,18.45,176.0,18.18,168.0,4.48,49.15 +2020-06-11 17:00:00,17.45,84.0,3.45,83.0,4.14,49.05 +2020-06-11 18:00:00,16.66,81.0,205.67,52.0,3.31,50.75 +2020-06-11 19:00:00,15.68,0.0,0.0,0.0,2.55,56.35 +2020-06-11 20:00:00,14.97,0.0,-0.0,0.0,2.48,58.35 +2020-06-11 21:00:00,14.18,0.0,-0.0,0.0,2.48,62.55 +2020-06-11 22:00:00,13.68,0.0,-0.0,0.0,2.55,64.75 +2020-06-11 23:00:00,13.01,0.0,-0.0,0.0,2.41,67.05 +2020-06-12 00:00:00,12.57,0.0,-0.0,0.0,2.34,72.05 +2020-06-12 01:00:00,12.13,0.0,-0.0,0.0,2.34,74.6 +2020-06-12 02:00:00,12.1,0.0,-0.0,0.0,2.41,74.6 +2020-06-12 03:00:00,11.64,1.0,0.0,1.0,2.34,77.3 +2020-06-12 04:00:00,11.99,11.0,0.0,11.0,2.21,77.35 +2020-06-12 05:00:00,12.3,43.0,0.0,43.0,2.69,74.6 +2020-06-12 06:00:00,12.88,140.0,2.12,139.0,2.9,72.05 +2020-06-12 07:00:00,13.35,371.0,133.13,290.0,2.48,74.75 +2020-06-12 08:00:00,14.28,469.0,158.48,354.0,2.76,67.25 +2020-06-12 09:00:00,15.15,610.0,277.55,384.0,2.9,58.35 +2020-06-12 10:00:00,15.87,138.0,0.0,138.0,3.03,54.35 +2020-06-12 11:00:00,16.53,127.0,0.0,127.0,3.03,50.75 +2020-06-12 12:00:00,16.8,149.0,0.0,149.0,3.17,47.15 +2020-06-12 13:00:00,16.83,90.0,0.0,90.0,2.62,47.15 +2020-06-12 14:00:00,16.74,121.0,0.0,121.0,1.93,48.9 +2020-06-12 15:00:00,16.61,110.0,0.0,110.0,1.38,48.9 +2020-06-12 16:00:00,16.27,58.0,0.0,58.0,1.03,52.5 +2020-06-12 17:00:00,15.83,68.0,0.0,68.0,0.97,56.35 +2020-06-12 18:00:00,15.24,41.0,7.03,40.0,1.03,62.75 +2020-06-12 19:00:00,14.27,0.0,0.0,0.0,0.28,72.3 +2020-06-12 20:00:00,13.45,0.0,-0.0,0.0,0.48,80.4 +2020-06-12 21:00:00,13.15,0.0,-0.0,0.0,0.34,80.35 +2020-06-12 22:00:00,12.82,0.0,-0.0,0.0,0.62,86.25 +2020-06-12 23:00:00,12.16,0.0,-0.0,0.0,0.76,89.35 +2020-06-13 00:00:00,11.73,0.0,-0.0,0.0,1.17,92.55 +2020-06-13 01:00:00,11.34,0.0,-0.0,0.0,1.17,95.9 +2020-06-13 02:00:00,11.32,0.0,-0.0,0.0,1.1,95.9 +2020-06-13 03:00:00,11.18,6.0,0.0,6.0,0.83,95.9 +2020-06-13 04:00:00,11.24,16.0,0.0,16.0,0.83,95.9 +2020-06-13 05:00:00,11.3,117.0,12.43,113.0,1.1,99.35 +2020-06-13 06:00:00,11.44,191.0,19.12,182.0,1.86,95.9 +2020-06-13 07:00:00,11.72,194.0,1.64,193.0,4.07,83.1 +2020-06-13 08:00:00,12.04,259.0,4.13,256.0,4.07,86.2 +2020-06-13 09:00:00,11.94,315.0,7.37,309.0,4.28,89.35 +2020-06-13 10:00:00,12.84,391.0,21.87,372.0,4.07,89.4 +2020-06-13 11:00:00,13.49,293.0,1.13,292.0,4.21,80.4 +2020-06-13 12:00:00,14.42,184.0,0.0,184.0,4.21,77.65 +2020-06-13 13:00:00,14.78,355.0,21.26,338.0,4.0,75.0 +2020-06-13 14:00:00,15.12,582.0,421.27,285.0,3.66,69.9 +2020-06-13 15:00:00,15.29,166.0,0.0,166.0,3.52,67.45 +2020-06-13 16:00:00,15.15,375.0,535.73,138.0,3.31,67.45 +2020-06-13 17:00:00,14.56,46.0,0.0,46.0,3.52,64.95 +2020-06-13 18:00:00,13.85,47.0,20.9,44.0,3.03,67.15 +2020-06-13 19:00:00,12.6,0.0,0.0,0.0,2.9,69.45 +2020-06-13 20:00:00,11.74,0.0,-0.0,0.0,2.9,74.55 +2020-06-13 21:00:00,11.09,0.0,-0.0,0.0,2.55,77.2 +2020-06-13 22:00:00,10.4,0.0,-0.0,0.0,2.55,80.05 +2020-06-13 23:00:00,9.88,0.0,-0.0,0.0,2.69,85.95 +2020-06-14 00:00:00,9.4,0.0,-0.0,0.0,2.62,82.9 +2020-06-14 01:00:00,9.11,0.0,-0.0,0.0,2.62,85.95 +2020-06-14 02:00:00,8.71,0.0,-0.0,0.0,2.55,89.1 +2020-06-14 03:00:00,8.46,1.0,0.0,1.0,2.41,89.1 +2020-06-14 04:00:00,8.65,112.0,284.87,63.0,2.14,89.1 +2020-06-14 05:00:00,9.82,255.0,453.6,109.0,2.07,89.15 +2020-06-14 06:00:00,11.14,415.0,582.03,141.0,3.03,77.2 +2020-06-14 07:00:00,12.21,473.0,343.44,264.0,2.28,83.15 +2020-06-14 08:00:00,13.1,432.0,107.46,354.0,2.07,77.5 +2020-06-14 09:00:00,14.09,568.0,203.77,402.0,2.14,72.3 +2020-06-14 10:00:00,14.85,441.0,43.73,403.0,2.55,58.25 +2020-06-14 11:00:00,15.57,573.0,144.65,445.0,2.9,48.65 +2020-06-14 12:00:00,16.2,578.0,170.62,431.0,3.17,45.3 +2020-06-14 13:00:00,16.67,683.0,448.49,324.0,3.17,43.8 +2020-06-14 14:00:00,16.5,414.0,102.01,342.0,2.9,45.45 +2020-06-14 15:00:00,16.56,447.0,328.75,255.0,2.76,45.45 +2020-06-14 16:00:00,16.2,210.0,47.36,189.0,2.34,47.0 +2020-06-14 17:00:00,15.88,225.0,422.41,101.0,1.86,50.5 +2020-06-14 18:00:00,15.24,53.0,34.57,48.0,1.03,58.35 +2020-06-14 19:00:00,13.5,0.0,0.0,0.0,1.17,69.65 +2020-06-14 20:00:00,12.23,0.0,-0.0,0.0,1.31,71.95 +2020-06-14 21:00:00,10.93,0.0,-0.0,0.0,1.45,74.45 +2020-06-14 22:00:00,9.55,0.0,-0.0,0.0,1.72,82.9 +2020-06-14 23:00:00,8.7,0.0,-0.0,0.0,1.79,85.9 +2020-06-15 00:00:00,8.17,0.0,-0.0,0.0,1.72,89.05 +2020-06-15 01:00:00,7.67,0.0,-0.0,0.0,1.72,92.35 +2020-06-15 02:00:00,7.41,0.0,-0.0,0.0,1.66,92.35 +2020-06-15 03:00:00,7.08,6.0,0.0,6.0,1.66,95.8 +2020-06-15 04:00:00,8.02,104.0,226.71,65.0,1.38,92.4 +2020-06-15 05:00:00,10.34,60.0,0.0,60.0,0.97,92.5 +2020-06-15 06:00:00,13.41,202.0,27.62,189.0,1.17,72.2 +2020-06-15 07:00:00,14.41,383.0,152.83,290.0,1.38,69.75 +2020-06-15 08:00:00,15.14,472.0,163.95,353.0,1.79,65.05 +2020-06-15 09:00:00,15.84,622.0,300.72,377.0,2.21,60.65 +2020-06-15 10:00:00,15.96,634.0,246.24,420.0,2.07,62.95 +2020-06-15 11:00:00,16.37,428.0,33.89,398.0,1.59,60.75 +2020-06-15 12:00:00,15.98,742.0,464.03,342.0,1.17,65.25 +2020-06-15 13:00:00,16.44,247.0,0.0,247.0,0.76,67.65 +2020-06-15 14:00:00,16.42,62.0,0.0,62.0,0.62,70.1 +2020-06-15 15:00:00,16.46,524.0,594.98,176.0,0.55,70.2 +2020-06-15 16:00:00,16.14,220.0,60.76,193.0,0.55,72.65 +2020-06-15 17:00:00,14.73,163.0,125.61,126.0,1.31,77.75 +2020-06-15 18:00:00,14.6,42.0,6.86,41.0,1.52,80.55 +2020-06-15 19:00:00,13.07,0.0,0.0,0.0,1.79,83.25 +2020-06-15 20:00:00,12.35,0.0,-0.0,0.0,1.93,89.35 +2020-06-15 21:00:00,12.16,0.0,-0.0,0.0,1.79,89.35 +2020-06-15 22:00:00,11.68,0.0,-0.0,0.0,1.79,89.3 +2020-06-15 23:00:00,11.48,0.0,-0.0,0.0,1.66,89.3 +2020-06-16 00:00:00,11.17,0.0,-0.0,0.0,1.66,92.55 +2020-06-16 01:00:00,10.89,0.0,-0.0,0.0,1.59,92.55 +2020-06-16 02:00:00,10.57,0.0,-0.0,0.0,1.52,95.9 +2020-06-16 03:00:00,10.3,1.0,0.0,1.0,1.45,95.9 +2020-06-16 04:00:00,10.45,11.0,0.0,11.0,1.1,95.9 +2020-06-16 05:00:00,11.91,27.0,0.0,27.0,0.83,92.6 +2020-06-16 06:00:00,12.53,47.0,0.0,47.0,0.83,89.4 +2020-06-16 07:00:00,12.45,143.0,0.0,143.0,0.62,86.25 +2020-06-16 08:00:00,12.67,158.0,0.0,158.0,0.9,86.25 +2020-06-16 09:00:00,13.0,373.0,27.0,351.0,0.97,83.25 +2020-06-16 10:00:00,13.96,457.0,56.37,408.0,0.97,77.65 +2020-06-16 11:00:00,14.83,272.0,0.0,272.0,1.03,75.0 +2020-06-16 12:00:00,15.76,340.0,8.12,333.0,0.9,70.0 +2020-06-16 13:00:00,16.47,403.0,46.16,366.0,0.76,65.35 +2020-06-16 14:00:00,16.5,130.0,0.0,130.0,1.17,65.35 +2020-06-16 15:00:00,16.61,478.0,433.67,224.0,1.31,65.35 +2020-06-16 16:00:00,16.57,297.0,224.59,197.0,1.31,65.35 +2020-06-16 17:00:00,16.13,74.0,0.0,74.0,1.03,65.25 +2020-06-16 18:00:00,15.25,49.0,20.46,46.0,1.52,72.45 +2020-06-16 19:00:00,13.91,0.0,0.0,0.0,1.79,77.6 +2020-06-16 20:00:00,12.84,0.0,-0.0,0.0,1.17,86.25 +2020-06-16 21:00:00,11.48,0.0,-0.0,0.0,1.24,92.55 +2020-06-16 22:00:00,10.72,0.0,-0.0,0.0,1.31,92.5 +2020-06-16 23:00:00,9.91,0.0,-0.0,0.0,1.45,92.5 +2020-06-17 00:00:00,9.19,0.0,-0.0,0.0,1.59,95.85 +2020-06-17 01:00:00,8.99,0.0,-0.0,0.0,1.52,95.85 +2020-06-17 02:00:00,8.61,0.0,-0.0,0.0,1.52,99.4 +2020-06-17 03:00:00,8.15,1.0,0.0,1.0,1.59,99.4 +2020-06-17 04:00:00,8.46,95.0,157.18,68.0,1.45,95.85 +2020-06-17 05:00:00,10.42,239.0,360.82,123.0,1.03,95.9 +2020-06-17 06:00:00,12.99,421.0,616.57,131.0,1.1,80.35 +2020-06-17 07:00:00,13.61,559.0,628.09,177.0,0.76,80.4 +2020-06-17 08:00:00,15.1,719.0,777.23,155.0,1.03,65.05 +2020-06-17 09:00:00,16.23,793.0,711.91,213.0,1.45,54.45 +2020-06-17 10:00:00,16.89,808.0,598.18,288.0,1.38,50.75 +2020-06-17 11:00:00,17.8,821.0,589.36,299.0,0.97,49.05 +2020-06-17 12:00:00,18.13,860.0,763.8,201.0,0.83,44.05 +2020-06-17 13:00:00,18.09,785.0,730.54,199.0,1.45,44.05 +2020-06-17 14:00:00,18.13,605.0,469.01,273.0,2.0,45.7 +2020-06-17 15:00:00,18.07,557.0,716.2,137.0,2.21,45.7 +2020-06-17 16:00:00,18.05,394.0,600.81,126.0,2.21,45.7 +2020-06-17 17:00:00,17.7,244.0,529.77,87.0,1.93,49.05 +2020-06-17 18:00:00,16.93,66.0,74.58,55.0,1.59,56.6 +2020-06-17 19:00:00,16.16,0.0,0.0,0.0,1.52,58.6 +2020-06-17 20:00:00,14.88,0.0,-0.0,0.0,1.45,69.85 +2020-06-17 21:00:00,14.43,0.0,-0.0,0.0,1.38,64.95 +2020-06-17 22:00:00,13.93,0.0,-0.0,0.0,1.45,67.25 +2020-06-17 23:00:00,13.39,0.0,-0.0,0.0,1.59,72.1 +2020-06-18 00:00:00,13.01,0.0,-0.0,0.0,1.59,72.1 +2020-06-18 01:00:00,12.84,0.0,-0.0,0.0,1.59,74.7 +2020-06-18 02:00:00,12.77,0.0,-0.0,0.0,1.52,77.45 +2020-06-18 03:00:00,12.52,1.0,0.0,1.0,1.45,80.3 +2020-06-18 04:00:00,12.53,11.0,0.0,11.0,1.31,83.25 +2020-06-18 05:00:00,13.21,43.0,0.0,43.0,1.03,86.3 +2020-06-18 06:00:00,15.22,190.0,19.15,181.0,0.76,72.45 +2020-06-18 07:00:00,15.16,178.0,0.0,178.0,0.55,83.5 +2020-06-18 08:00:00,16.53,467.0,157.14,353.0,0.41,67.75 +2020-06-18 09:00:00,17.26,412.0,46.65,374.0,0.34,63.2 +2020-06-18 10:00:00,18.03,600.0,197.85,428.0,0.28,61.05 +2020-06-18 11:00:00,18.46,773.0,487.66,341.0,0.14,58.9 +2020-06-18 12:00:00,19.24,180.0,0.0,180.0,0.07,55.05 +2020-06-18 13:00:00,20.12,208.0,0.0,208.0,0.07,51.5 +2020-06-18 14:00:00,20.92,310.0,22.58,294.0,0.34,51.65 +2020-06-18 15:00:00,20.83,538.0,648.97,157.0,0.69,51.65 +2020-06-18 16:00:00,20.82,348.0,393.92,172.0,0.83,53.5 +2020-06-18 17:00:00,20.62,225.0,410.61,103.0,0.9,53.5 +2020-06-18 18:00:00,19.7,77.0,141.64,56.0,1.17,65.85 +2020-06-18 19:00:00,19.26,0.0,0.0,0.0,1.03,70.55 +2020-06-18 20:00:00,19.65,0.0,-0.0,0.0,0.55,59.25 +2020-06-18 21:00:00,18.38,0.0,-0.0,0.0,1.03,65.55 +2020-06-18 22:00:00,15.06,0.0,-0.0,0.0,1.93,80.6 +2020-06-18 23:00:00,13.7,0.0,-0.0,0.0,1.93,89.45 +2020-06-19 00:00:00,12.66,0.0,-0.0,0.0,1.86,92.6 +2020-06-19 01:00:00,11.84,0.0,-0.0,0.0,1.86,95.9 +2020-06-19 02:00:00,11.41,0.0,-0.0,0.0,2.0,95.9 +2020-06-19 03:00:00,11.41,3.0,0.0,3.0,2.14,95.9 +2020-06-19 04:00:00,12.43,14.0,0.0,14.0,2.07,92.6 +2020-06-19 05:00:00,14.99,173.0,109.1,138.0,1.86,89.55 +2020-06-19 06:00:00,17.66,360.0,374.74,184.0,1.93,83.7 +2020-06-19 07:00:00,19.55,518.0,506.92,210.0,1.79,73.15 +2020-06-19 08:00:00,20.9,636.0,533.64,249.0,2.07,68.4 +2020-06-19 09:00:00,22.41,540.0,174.35,398.0,2.9,52.0 +2020-06-19 10:00:00,22.92,424.0,37.96,391.0,3.31,45.25 +2020-06-19 11:00:00,23.2,315.0,3.39,312.0,3.03,43.75 +2020-06-19 12:00:00,23.43,497.0,90.35,419.0,3.03,43.75 +2020-06-19 13:00:00,23.49,315.0,9.96,307.0,2.76,47.0 +2020-06-19 14:00:00,23.86,205.0,0.0,205.0,3.31,45.5 +2020-06-19 15:00:00,23.82,570.0,772.53,116.0,3.66,45.5 +2020-06-19 16:00:00,23.65,403.0,657.06,109.0,3.03,47.15 +2020-06-19 17:00:00,23.08,230.0,446.61,97.0,2.14,54.1 +2020-06-19 18:00:00,21.92,38.0,6.71,37.0,1.79,61.85 +2020-06-19 19:00:00,20.3,0.0,0.0,0.0,2.0,61.5 +2020-06-19 20:00:00,18.56,0.0,-0.0,0.0,2.28,65.65 +2020-06-19 21:00:00,17.45,0.0,-0.0,0.0,2.41,67.85 +2020-06-19 22:00:00,16.48,0.0,-0.0,0.0,2.34,67.75 +2020-06-19 23:00:00,15.82,0.0,-0.0,0.0,2.21,72.55 +2020-06-20 00:00:00,15.49,0.0,-0.0,0.0,2.34,70.0 +2020-06-20 01:00:00,14.91,0.0,-0.0,0.0,2.28,75.0 +2020-06-20 02:00:00,14.39,0.0,-0.0,0.0,2.07,77.65 +2020-06-20 03:00:00,13.7,3.0,0.0,3.0,2.07,80.4 +2020-06-20 04:00:00,14.4,52.0,11.72,50.0,2.34,80.45 +2020-06-20 05:00:00,16.09,27.0,0.0,27.0,3.1,72.65 +2020-06-20 06:00:00,17.05,54.0,0.0,54.0,3.38,72.8 +2020-06-20 07:00:00,16.39,157.0,0.0,157.0,2.69,86.55 +2020-06-20 08:00:00,16.69,352.0,42.77,321.0,3.59,83.65 +2020-06-20 09:00:00,17.36,585.0,243.17,387.0,3.79,80.85 +2020-06-20 10:00:00,18.51,604.0,208.23,423.0,3.72,72.95 +2020-06-20 11:00:00,18.84,659.0,272.0,418.0,3.45,68.05 +2020-06-20 12:00:00,19.92,698.0,378.68,371.0,3.59,61.4 +2020-06-20 13:00:00,20.49,699.0,506.67,292.0,3.52,53.5 +2020-06-20 14:00:00,21.05,571.0,396.13,290.0,3.72,49.95 +2020-06-20 15:00:00,21.12,342.0,115.61,274.0,3.45,48.2 +2020-06-20 16:00:00,20.92,398.0,636.13,113.0,3.38,46.35 +2020-06-20 17:00:00,20.28,162.0,120.64,126.0,2.41,49.7 +2020-06-20 18:00:00,19.4,80.0,160.51,56.0,1.72,57.1 +2020-06-20 19:00:00,18.12,0.0,0.0,0.0,1.59,65.55 +2020-06-20 20:00:00,16.56,0.0,-0.0,0.0,1.66,70.2 +2020-06-20 21:00:00,15.39,0.0,-0.0,0.0,1.79,75.1 +2020-06-20 22:00:00,14.9,0.0,-0.0,0.0,1.59,77.75 +2020-06-20 23:00:00,14.45,0.0,-0.0,0.0,1.52,75.0 +2020-06-21 00:00:00,14.66,0.0,-0.0,0.0,1.31,77.75 +2020-06-21 01:00:00,13.9,0.0,-0.0,0.0,1.38,80.4 +2020-06-21 02:00:00,13.51,0.0,-0.0,0.0,1.38,80.4 +2020-06-21 03:00:00,13.35,1.0,0.0,1.0,1.24,86.3 +2020-06-21 04:00:00,13.21,10.0,0.0,10.0,1.17,89.4 +2020-06-21 05:00:00,14.31,39.0,0.0,39.0,0.83,89.5 +2020-06-21 06:00:00,15.83,83.0,0.0,83.0,0.76,77.85 +2020-06-21 07:00:00,17.2,205.0,3.3,203.0,0.9,72.8 +2020-06-21 08:00:00,16.93,144.0,0.0,144.0,1.38,72.7 +2020-06-21 09:00:00,16.63,152.0,0.0,152.0,1.72,75.3 +2020-06-21 10:00:00,16.26,188.0,0.0,188.0,1.66,77.95 +2020-06-21 11:00:00,16.51,159.0,0.0,159.0,1.38,75.3 +2020-06-21 12:00:00,16.48,416.0,37.05,384.0,1.52,75.3 +2020-06-21 13:00:00,17.08,265.0,1.24,264.0,1.86,72.8 +2020-06-21 14:00:00,17.58,407.0,101.45,335.0,2.34,72.8 +2020-06-21 15:00:00,18.94,303.0,66.25,264.0,3.52,68.05 +2020-06-21 16:00:00,18.76,205.0,44.59,185.0,2.97,65.65 +2020-06-21 17:00:00,18.27,239.0,505.17,88.0,2.28,67.95 +2020-06-21 18:00:00,17.78,97.0,306.65,51.0,1.59,70.3 +2020-06-21 19:00:00,17.02,0.0,0.0,0.0,1.17,75.4 +2020-06-21 20:00:00,17.26,0.0,-0.0,0.0,0.34,70.3 +2020-06-21 21:00:00,15.49,0.0,-0.0,0.0,1.17,77.85 +2020-06-21 22:00:00,13.47,0.0,-0.0,0.0,1.79,89.45 +2020-06-21 23:00:00,12.63,0.0,-0.0,0.0,2.0,92.6 +2020-06-22 00:00:00,12.21,0.0,-0.0,0.0,2.07,92.6 +2020-06-22 01:00:00,12.2,0.0,-0.0,0.0,2.28,92.6 +2020-06-22 02:00:00,12.43,0.0,-0.0,0.0,2.41,89.4 +2020-06-22 03:00:00,12.76,2.0,0.0,2.0,2.55,89.4 +2020-06-22 04:00:00,13.53,118.0,371.61,55.0,2.55,86.35 +2020-06-22 05:00:00,15.53,264.0,548.35,89.0,2.48,80.65 +2020-06-22 06:00:00,18.12,421.0,655.89,114.0,2.9,75.45 +2020-06-22 07:00:00,20.43,575.0,739.06,127.0,3.38,70.7 +2020-06-22 08:00:00,22.17,711.0,794.15,136.0,3.72,64.1 +2020-06-22 09:00:00,23.8,821.0,841.89,136.0,3.93,60.15 +2020-06-22 10:00:00,25.4,873.0,827.51,154.0,4.0,56.5 +2020-06-22 11:00:00,26.69,878.0,798.01,171.0,3.86,53.1 +2020-06-22 12:00:00,27.77,665.0,321.86,387.0,3.66,49.8 +2020-06-22 13:00:00,28.44,746.0,650.71,223.0,3.59,49.8 +2020-06-22 14:00:00,28.46,545.0,338.01,305.0,3.52,49.8 +2020-06-22 15:00:00,28.3,286.0,49.23,257.0,3.03,49.8 +2020-06-22 16:00:00,28.11,386.0,585.83,123.0,2.34,53.35 +2020-06-22 17:00:00,27.47,230.0,451.01,95.0,1.72,58.9 +2020-06-22 18:00:00,26.32,47.0,13.3,45.0,1.86,62.8 +2020-06-22 19:00:00,24.63,0.0,0.0,0.0,2.21,62.45 +2020-06-22 20:00:00,23.04,0.0,-0.0,0.0,2.14,64.3 +2020-06-22 21:00:00,21.76,0.0,-0.0,0.0,2.21,71.0 +2020-06-22 22:00:00,21.02,0.0,-0.0,0.0,2.41,70.9 +2020-06-22 23:00:00,21.72,0.0,-0.0,0.0,3.52,64.0 +2020-06-23 00:00:00,21.51,0.0,-0.0,0.0,2.76,64.0 +2020-06-23 01:00:00,20.67,0.0,-0.0,0.0,2.41,68.4 +2020-06-23 02:00:00,20.16,0.0,-0.0,0.0,2.55,70.7 +2020-06-23 03:00:00,19.85,6.0,0.0,6.0,2.62,73.15 +2020-06-23 04:00:00,19.85,10.0,0.0,10.0,2.48,75.7 +2020-06-23 05:00:00,20.4,194.0,185.29,135.0,3.45,78.45 +2020-06-23 06:00:00,19.44,366.0,408.66,175.0,2.62,89.8 +2020-06-23 07:00:00,19.77,209.0,4.95,206.0,3.31,86.85 +2020-06-23 08:00:00,20.39,481.0,186.59,346.0,3.45,83.95 +2020-06-23 09:00:00,22.23,660.0,394.71,339.0,4.21,71.05 +2020-06-23 10:00:00,23.28,786.0,575.63,286.0,4.76,60.05 +2020-06-23 11:00:00,24.09,887.0,816.17,164.0,5.1,50.7 +2020-06-23 12:00:00,24.35,885.0,864.82,138.0,5.03,44.05 +2020-06-23 13:00:00,24.65,817.0,854.61,130.0,4.97,36.95 +2020-06-23 14:00:00,24.6,605.0,491.36,256.0,4.83,33.15 +2020-06-23 15:00:00,24.13,561.0,744.93,122.0,4.48,31.85 +2020-06-23 16:00:00,23.47,402.0,647.72,111.0,3.93,34.0 +2020-06-23 17:00:00,22.44,247.0,540.61,85.0,3.31,39.05 +2020-06-23 18:00:00,21.11,99.0,318.52,51.0,2.69,44.85 +2020-06-23 19:00:00,19.63,0.0,0.0,0.0,2.41,49.55 +2020-06-23 20:00:00,17.98,0.0,-0.0,0.0,2.07,56.85 +2020-06-23 21:00:00,16.31,0.0,-0.0,0.0,2.0,62.95 +2020-06-23 22:00:00,14.83,0.0,-0.0,0.0,1.93,69.85 +2020-06-23 23:00:00,13.76,0.0,-0.0,0.0,1.79,74.85 +2020-06-24 00:00:00,13.06,0.0,-0.0,0.0,1.72,74.75 +2020-06-24 01:00:00,12.88,0.0,-0.0,0.0,1.52,77.45 +2020-06-24 02:00:00,12.37,0.0,-0.0,0.0,1.45,77.35 +2020-06-24 03:00:00,11.73,1.0,0.0,1.0,1.45,80.15 +2020-06-24 04:00:00,11.75,110.0,303.58,59.0,1.31,83.1 +2020-06-24 05:00:00,13.66,256.0,503.76,96.0,1.17,74.85 +2020-06-24 06:00:00,14.83,412.0,615.05,125.0,1.72,69.85 +2020-06-24 07:00:00,15.82,576.0,734.03,132.0,1.79,72.55 +2020-06-24 08:00:00,16.87,708.0,777.37,146.0,1.66,67.75 +2020-06-24 09:00:00,17.91,817.0,821.85,149.0,1.72,65.45 +2020-06-24 10:00:00,19.09,874.0,818.84,163.0,1.72,59.15 +2020-06-24 11:00:00,20.07,847.0,686.49,239.0,1.38,55.3 +2020-06-24 12:00:00,20.68,854.0,773.4,186.0,1.1,53.5 +2020-06-24 13:00:00,21.56,815.0,840.85,139.0,1.17,50.05 +2020-06-24 14:00:00,21.8,527.0,288.56,322.0,1.59,50.05 +2020-06-24 15:00:00,21.85,385.0,184.89,276.0,2.14,50.05 +2020-06-24 16:00:00,21.63,391.0,591.74,125.0,2.28,50.05 +2020-06-24 17:00:00,21.07,220.0,373.45,108.0,2.0,49.95 +2020-06-24 18:00:00,20.23,81.0,152.42,58.0,1.79,55.3 +2020-06-24 19:00:00,18.68,0.0,0.0,0.0,1.86,56.95 +2020-06-24 20:00:00,17.17,0.0,-0.0,0.0,1.59,60.95 +2020-06-24 21:00:00,15.84,0.0,-0.0,0.0,1.59,70.0 +2020-06-24 22:00:00,15.45,0.0,-0.0,0.0,1.79,65.15 +2020-06-24 23:00:00,15.04,0.0,-0.0,0.0,1.93,65.05 +2020-06-25 00:00:00,14.7,0.0,-0.0,0.0,2.14,62.65 +2020-06-25 01:00:00,14.77,0.0,-0.0,0.0,2.21,60.4 +2020-06-25 02:00:00,14.77,0.0,-0.0,0.0,2.21,58.25 +2020-06-25 03:00:00,14.36,3.0,0.0,3.0,2.14,62.55 +2020-06-25 04:00:00,14.46,27.0,0.0,27.0,2.0,67.35 +2020-06-25 05:00:00,15.88,248.0,479.9,96.0,1.79,67.55 +2020-06-25 06:00:00,18.46,388.0,530.28,141.0,2.07,65.55 +2020-06-25 07:00:00,20.66,410.0,226.77,273.0,2.34,63.8 +2020-06-25 08:00:00,22.7,426.0,116.29,342.0,2.34,59.95 +2020-06-25 09:00:00,24.47,723.0,572.45,258.0,2.48,58.2 +2020-06-25 10:00:00,25.66,675.0,342.19,378.0,2.83,56.6 +2020-06-25 11:00:00,26.41,407.0,28.23,382.0,2.97,56.7 +2020-06-25 12:00:00,26.9,108.0,0.0,108.0,2.76,56.85 +2020-06-25 13:00:00,26.36,475.0,113.19,384.0,2.34,60.7 +2020-06-25 14:00:00,24.59,386.0,80.22,329.0,1.86,69.15 +2020-06-25 15:00:00,22.23,49.0,0.0,49.0,1.66,87.05 +2020-06-25 16:00:00,21.88,63.0,0.0,63.0,2.14,89.95 +2020-06-25 17:00:00,21.74,21.0,0.0,21.0,1.79,89.95 +2020-06-25 18:00:00,21.63,28.0,0.0,28.0,2.34,89.95 +2020-06-25 19:00:00,19.96,0.0,0.0,0.0,2.9,96.1 +2020-06-25 20:00:00,19.53,0.0,-0.0,0.0,3.1,92.9 +2020-06-25 21:00:00,19.2,0.0,-0.0,0.0,2.97,92.9 +2020-06-25 22:00:00,18.98,0.0,-0.0,0.0,2.9,92.9 +2020-06-25 23:00:00,18.78,0.0,-0.0,0.0,2.97,96.1 +2020-06-26 00:00:00,18.27,0.0,-0.0,0.0,3.17,96.05 +2020-06-26 01:00:00,18.0,0.0,-0.0,0.0,3.66,92.85 +2020-06-26 02:00:00,17.6,0.0,-0.0,0.0,3.52,92.85 +2020-06-26 03:00:00,17.05,1.0,0.0,1.0,3.45,89.7 +2020-06-26 04:00:00,16.48,36.0,0.0,36.0,3.24,89.65 +2020-06-26 05:00:00,16.52,84.0,0.0,84.0,2.69,86.6 +2020-06-26 06:00:00,17.02,151.0,4.3,149.0,3.03,83.7 +2020-06-26 07:00:00,18.23,532.0,563.55,192.0,3.31,75.45 +2020-06-26 08:00:00,19.18,687.0,702.58,180.0,3.24,65.75 +2020-06-26 09:00:00,20.2,684.0,441.03,326.0,3.24,59.35 +2020-06-26 10:00:00,21.01,671.0,312.39,400.0,3.24,53.6 +2020-06-26 11:00:00,21.67,715.0,367.17,390.0,3.24,50.05 +2020-06-26 12:00:00,22.22,799.0,608.02,274.0,2.97,48.45 +2020-06-26 13:00:00,22.56,779.0,730.18,192.0,2.69,45.25 +2020-06-26 14:00:00,22.82,562.0,364.5,303.0,2.41,45.25 +2020-06-26 15:00:00,22.87,540.0,647.7,158.0,2.07,45.25 +2020-06-26 16:00:00,22.77,346.0,377.96,176.0,1.79,45.25 +2020-06-26 17:00:00,22.37,168.0,129.94,129.0,1.66,46.75 +2020-06-26 18:00:00,21.37,26.0,0.0,26.0,1.31,57.55 +2020-06-26 19:00:00,19.79,0.0,0.0,0.0,1.52,63.6 +2020-06-26 20:00:00,20.26,0.0,-0.0,0.0,0.76,53.35 +2020-06-26 21:00:00,18.82,0.0,-0.0,0.0,1.1,61.2 +2020-06-26 22:00:00,16.97,0.0,-0.0,0.0,1.45,65.45 +2020-06-26 23:00:00,15.44,0.0,-0.0,0.0,1.66,72.55 +2020-06-27 00:00:00,15.02,0.0,-0.0,0.0,1.52,77.8 +2020-06-27 01:00:00,14.77,0.0,-0.0,0.0,1.45,77.75 +2020-06-27 02:00:00,14.09,0.0,-0.0,0.0,1.52,80.45 +2020-06-27 03:00:00,13.61,2.0,0.0,2.0,1.52,83.35 +2020-06-27 04:00:00,13.74,119.0,405.97,52.0,1.38,89.45 +2020-06-27 05:00:00,15.65,186.0,158.87,136.0,1.03,86.5 +2020-06-27 06:00:00,19.48,350.0,347.04,189.0,1.17,65.85 +2020-06-27 07:00:00,20.3,223.0,8.3,218.0,2.48,65.95 +2020-06-27 08:00:00,20.88,143.0,0.0,143.0,3.66,59.5 +2020-06-27 09:00:00,20.45,229.0,0.0,229.0,4.21,59.35 +2020-06-27 10:00:00,19.54,126.0,0.0,126.0,5.24,61.4 +2020-06-27 11:00:00,19.2,760.0,457.72,355.0,4.62,61.3 +2020-06-27 12:00:00,19.81,337.0,8.11,330.0,4.48,53.25 +2020-06-27 13:00:00,21.16,540.0,181.64,394.0,6.48,46.5 +2020-06-27 14:00:00,20.9,518.0,263.18,331.0,6.69,48.05 +2020-06-27 15:00:00,20.77,514.0,540.88,195.0,6.28,49.8 +2020-06-27 16:00:00,20.48,306.0,235.66,200.0,5.66,51.5 +2020-06-27 17:00:00,20.03,214.0,326.55,116.0,4.34,51.5 +2020-06-27 18:00:00,19.47,98.0,304.81,52.0,3.79,53.15 +2020-06-27 19:00:00,18.1,0.0,0.0,0.0,3.59,56.85 +2020-06-27 20:00:00,17.11,0.0,-0.0,0.0,2.9,56.75 +2020-06-27 21:00:00,16.26,0.0,-0.0,0.0,2.28,60.75 +2020-06-27 22:00:00,15.23,0.0,-0.0,0.0,2.0,67.45 +2020-06-27 23:00:00,13.95,0.0,-0.0,0.0,1.86,72.3 +2020-06-28 00:00:00,13.09,0.0,-0.0,0.0,1.86,77.5 +2020-06-28 01:00:00,12.77,0.0,-0.0,0.0,1.86,80.3 +2020-06-28 02:00:00,12.76,0.0,-0.0,0.0,2.0,80.3 +2020-06-28 03:00:00,13.21,0.0,0.0,0.0,2.28,77.5 +2020-06-28 04:00:00,13.86,79.0,97.63,63.0,2.34,80.4 +2020-06-28 05:00:00,15.18,83.0,0.0,83.0,2.34,77.8 +2020-06-28 06:00:00,16.85,236.0,69.13,204.0,2.83,72.7 +2020-06-28 07:00:00,17.48,224.0,8.31,219.0,2.97,75.4 +2020-06-28 08:00:00,18.23,361.0,50.0,325.0,3.59,72.9 +2020-06-28 09:00:00,19.07,266.0,1.23,265.0,4.07,68.1 +2020-06-28 10:00:00,20.58,212.0,0.0,212.0,5.93,55.45 +2020-06-28 11:00:00,20.71,324.0,4.52,320.0,6.0,57.45 +2020-06-28 12:00:00,20.45,577.0,177.29,424.0,5.86,61.5 +2020-06-28 13:00:00,20.53,444.0,77.15,382.0,4.9,59.5 +2020-06-28 14:00:00,20.73,476.0,192.84,339.0,4.41,61.6 +2020-06-28 15:00:00,20.62,470.0,403.59,232.0,4.41,66.05 +2020-06-28 16:00:00,20.73,351.0,406.92,168.0,4.21,68.4 +2020-06-28 17:00:00,20.13,204.0,283.35,119.0,2.97,70.7 +2020-06-28 18:00:00,19.72,101.0,338.37,50.0,2.69,73.15 +2020-06-28 19:00:00,19.44,0.0,0.0,0.0,2.55,70.55 +2020-06-28 20:00:00,18.63,0.0,-0.0,0.0,2.41,72.95 +2020-06-28 21:00:00,17.89,0.0,-0.0,0.0,2.28,75.4 +2020-06-28 22:00:00,17.11,0.0,-0.0,0.0,2.28,75.4 +2020-06-28 23:00:00,16.5,0.0,-0.0,0.0,2.21,78.0 +2020-06-29 00:00:00,15.87,0.0,-0.0,0.0,2.28,83.55 +2020-06-29 01:00:00,15.55,0.0,-0.0,0.0,2.34,83.55 +2020-06-29 02:00:00,15.42,0.0,-0.0,0.0,2.28,89.55 +2020-06-29 03:00:00,15.15,0.0,0.0,0.0,2.21,89.55 +2020-06-29 04:00:00,14.9,67.0,49.19,59.0,2.14,92.7 +2020-06-29 05:00:00,16.33,244.0,451.33,103.0,1.93,89.65 +2020-06-29 06:00:00,17.84,432.0,714.66,102.0,2.48,83.7 +2020-06-29 07:00:00,20.15,566.0,714.41,137.0,2.83,70.7 +2020-06-29 08:00:00,20.94,591.0,419.93,289.0,3.24,63.8 +2020-06-29 09:00:00,21.96,672.0,419.91,332.0,3.38,53.75 +2020-06-29 10:00:00,22.9,727.0,429.61,355.0,3.93,48.55 +2020-06-29 11:00:00,23.43,647.0,252.27,424.0,3.86,47.0 +2020-06-29 12:00:00,23.44,610.0,221.41,419.0,3.38,50.45 +2020-06-29 13:00:00,23.99,545.0,191.69,391.0,3.31,50.55 +2020-06-29 14:00:00,24.65,564.0,371.7,300.0,3.45,45.75 +2020-06-29 15:00:00,24.68,541.0,658.12,153.0,3.1,44.15 +2020-06-29 16:00:00,24.76,399.0,627.29,117.0,2.97,42.65 +2020-06-29 17:00:00,24.24,247.0,537.07,86.0,2.14,47.25 +2020-06-29 18:00:00,22.91,100.0,325.73,51.0,1.45,59.95 +2020-06-29 19:00:00,21.21,0.0,0.0,0.0,1.24,66.15 +2020-06-29 20:00:00,22.23,0.0,-0.0,0.0,0.69,50.2 +2020-06-29 21:00:00,21.28,0.0,-0.0,0.0,1.03,53.6 +2020-06-29 22:00:00,17.94,0.0,-0.0,0.0,1.52,70.3 +2020-06-29 23:00:00,15.9,0.0,-0.0,0.0,1.79,77.85 +2020-06-30 00:00:00,14.71,0.0,-0.0,0.0,1.79,83.45 +2020-06-30 01:00:00,13.72,0.0,-0.0,0.0,1.79,86.35 +2020-06-30 02:00:00,13.12,0.0,-0.0,0.0,1.79,89.4 +2020-06-30 03:00:00,13.01,0.0,0.0,0.0,1.72,89.4 +2020-06-30 04:00:00,13.96,13.0,0.0,13.0,1.45,89.5 +2020-06-30 05:00:00,16.47,73.0,0.0,73.0,1.45,80.8 +2020-06-30 06:00:00,18.6,254.0,99.87,208.0,2.55,70.45 +2020-06-30 07:00:00,18.9,523.0,548.85,194.0,3.03,78.25 +2020-06-30 08:00:00,19.79,638.0,555.52,239.0,3.38,73.15 +2020-06-30 09:00:00,20.43,557.0,203.98,392.0,3.93,68.3 +2020-06-30 10:00:00,21.22,274.0,1.16,273.0,4.0,63.9 +2020-06-30 11:00:00,21.86,516.0,95.08,432.0,4.34,59.7 +2020-06-30 12:00:00,22.45,650.0,286.46,403.0,4.48,55.8 +2020-06-30 13:00:00,22.85,669.0,427.11,326.0,4.55,50.3 +2020-06-30 14:00:00,22.84,568.0,380.29,298.0,4.48,48.55 +2020-06-30 15:00:00,22.7,541.0,656.68,154.0,4.28,46.85 +2020-06-30 16:00:00,20.06,407.0,663.24,109.0,2.85,45.78 +2020-06-30 17:00:00,19.81,171.0,143.58,128.0,2.67,49.67 +2020-06-30 18:00:00,19.57,78.0,133.3,58.0,2.49,53.56 +2020-06-30 19:00:00,19.32,0.0,0.0,0.0,2.32,57.45 +2020-06-30 20:00:00,19.07,0.0,-0.0,0.0,2.14,61.34 +2020-06-30 21:00:00,18.83,0.0,-0.0,0.0,1.96,65.23 +2020-06-30 22:00:00,18.58,0.0,-0.0,0.0,1.78,69.12 +2020-06-30 23:00:00,18.34,0.0,-0.0,0.0,1.61,73.01 +2020-07-01 00:00:00,18.09,0.0,-0.0,0.0,1.43,76.89 +2020-07-01 01:00:00,17.84,0.0,-0.0,0.0,1.25,80.78 +2020-07-01 02:00:00,17.6,0.0,-0.0,0.0,1.07,84.67 +2020-07-01 03:00:00,17.35,0.0,0.0,0.0,0.9,88.56 +2020-07-01 04:00:00,17.11,9.0,0.0,9.0,0.72,92.45 +2020-07-01 05:00:00,16.86,113.0,16.07,108.0,0.54,96.34 +2020-07-01 06:00:00,16.62,283.0,171.52,204.0,0.36,100.0 +2020-07-01 07:00:00,16.37,402.0,216.87,272.0,0.19,100.0 +2020-07-01 08:00:00,22.86,627.0,549.95,232.0,1.31,78.75 +2020-07-01 09:00:00,23.87,779.0,751.63,171.0,1.79,73.75 +2020-07-01 10:00:00,24.5,672.0,338.63,379.0,2.28,73.85 +2020-07-01 11:00:00,24.76,648.0,269.4,410.0,2.62,73.95 +2020-07-01 12:00:00,24.79,555.0,158.89,418.0,2.69,73.95 +2020-07-01 13:00:00,24.86,502.0,144.44,386.0,2.21,73.95 +2020-07-01 14:00:00,23.02,76.0,0.0,76.0,1.59,78.75 +2020-07-01 15:00:00,22.0,48.0,0.0,48.0,0.14,84.1 +2020-07-01 16:00:00,22.14,84.0,0.0,84.0,0.97,81.4 +2020-07-01 17:00:00,22.04,72.0,0.0,72.0,0.76,87.0 +2020-07-01 18:00:00,21.74,33.0,0.0,33.0,1.1,89.95 +2020-07-01 19:00:00,21.25,0.0,0.0,0.0,1.45,89.95 +2020-07-01 20:00:00,20.15,0.0,-0.0,0.0,1.03,92.95 +2020-07-01 21:00:00,19.18,0.0,-0.0,0.0,0.97,96.1 +2020-07-01 22:00:00,18.22,0.0,-0.0,0.0,1.24,96.1 +2020-07-01 23:00:00,18.27,0.0,-0.0,0.0,1.03,96.1 +2020-07-02 00:00:00,17.74,0.0,-0.0,0.0,1.03,96.05 +2020-07-02 01:00:00,16.72,0.0,-0.0,0.0,1.17,99.4 +2020-07-02 02:00:00,16.94,0.0,-0.0,0.0,0.97,99.4 +2020-07-02 03:00:00,17.05,0.0,0.0,0.0,0.9,99.4 +2020-07-02 04:00:00,17.24,9.0,0.0,9.0,0.69,99.4 +2020-07-02 05:00:00,18.22,24.0,0.0,24.0,0.28,96.1 +2020-07-02 06:00:00,19.02,79.0,0.0,79.0,0.41,96.1 +2020-07-02 07:00:00,21.01,172.0,0.0,172.0,0.41,86.9 +2020-07-02 08:00:00,22.11,181.0,0.0,181.0,0.9,81.4 +2020-07-02 09:00:00,23.28,260.0,1.24,259.0,1.45,76.2 +2020-07-02 10:00:00,23.96,451.0,60.15,399.0,1.79,76.3 +2020-07-02 11:00:00,24.73,868.0,789.46,171.0,2.34,69.15 +2020-07-02 12:00:00,22.74,708.0,418.9,347.0,2.28,78.75 +2020-07-02 13:00:00,22.51,409.0,54.81,365.0,1.45,81.4 +2020-07-02 14:00:00,22.62,219.0,1.41,218.0,1.17,78.75 +2020-07-02 15:00:00,21.74,48.0,0.0,48.0,2.28,84.1 +2020-07-02 16:00:00,21.39,39.0,0.0,39.0,1.59,86.95 +2020-07-02 17:00:00,21.45,21.0,0.0,21.0,1.66,86.95 +2020-07-02 18:00:00,21.05,26.0,0.0,26.0,1.24,86.95 +2020-07-02 19:00:00,20.63,0.0,0.0,0.0,1.24,89.9 +2020-07-02 20:00:00,19.6,0.0,-0.0,0.0,1.45,92.9 +2020-07-02 21:00:00,18.95,0.0,-0.0,0.0,1.31,99.4 +2020-07-02 22:00:00,18.11,0.0,-0.0,0.0,1.24,96.1 +2020-07-02 23:00:00,17.57,0.0,-0.0,0.0,1.1,99.4 +2020-07-03 00:00:00,17.93,0.0,-0.0,0.0,0.69,99.4 +2020-07-03 01:00:00,17.91,0.0,-0.0,0.0,0.76,99.4 +2020-07-03 02:00:00,17.61,0.0,-0.0,0.0,0.76,99.4 +2020-07-03 03:00:00,17.56,0.0,0.0,0.0,0.55,99.4 +2020-07-03 04:00:00,17.72,17.0,0.0,17.0,0.62,99.4 +2020-07-03 05:00:00,17.8,80.0,0.0,80.0,0.48,99.4 +2020-07-03 06:00:00,17.91,194.0,28.38,181.0,0.62,99.4 +2020-07-03 07:00:00,19.33,362.0,139.0,279.0,0.69,92.9 +2020-07-03 08:00:00,21.28,683.0,730.24,160.0,1.03,84.05 +2020-07-03 09:00:00,22.85,809.0,821.39,146.0,1.31,76.15 +2020-07-03 10:00:00,24.09,843.0,755.95,190.0,1.59,66.75 +2020-07-03 11:00:00,24.6,677.0,312.83,401.0,1.93,64.6 +2020-07-03 12:00:00,25.33,654.0,303.04,393.0,1.93,62.55 +2020-07-03 13:00:00,25.53,798.0,808.95,149.0,2.07,62.55 +2020-07-03 14:00:00,25.66,667.0,699.3,171.0,2.28,60.6 +2020-07-03 15:00:00,25.54,548.0,701.62,135.0,2.14,62.55 +2020-07-03 16:00:00,24.84,392.0,608.62,119.0,1.66,64.6 +2020-07-03 17:00:00,24.5,233.0,472.22,92.0,1.24,66.75 +2020-07-03 18:00:00,23.64,91.0,255.11,53.0,0.76,76.3 +2020-07-03 19:00:00,23.84,0.0,0.0,0.0,0.62,68.95 +2020-07-03 20:00:00,22.51,0.0,-0.0,0.0,0.9,76.1 +2020-07-03 21:00:00,20.68,0.0,-0.0,0.0,1.24,81.2 +2020-07-03 22:00:00,18.83,0.0,-0.0,0.0,1.66,89.75 +2020-07-03 23:00:00,17.61,0.0,-0.0,0.0,1.72,89.75 +2020-07-04 00:00:00,16.66,0.0,-0.0,0.0,1.72,92.8 +2020-07-04 01:00:00,16.37,0.0,-0.0,0.0,1.59,96.05 +2020-07-04 02:00:00,15.62,0.0,-0.0,0.0,1.59,96.0 +2020-07-04 03:00:00,15.32,0.0,0.0,0.0,1.59,99.4 +2020-07-04 04:00:00,15.64,64.0,51.01,56.0,1.38,96.0 +2020-07-04 05:00:00,18.06,224.0,371.47,110.0,0.9,92.85 +2020-07-04 06:00:00,21.61,374.0,492.74,149.0,0.9,81.35 +2020-07-04 07:00:00,22.27,534.0,622.63,163.0,1.93,78.7 +2020-07-04 08:00:00,23.17,668.0,685.22,178.0,2.28,73.7 +2020-07-04 09:00:00,23.86,738.0,620.18,238.0,2.55,73.75 +2020-07-04 10:00:00,24.42,751.0,507.53,313.0,3.03,71.4 +2020-07-04 11:00:00,23.29,502.0,88.48,424.0,2.9,78.85 +2020-07-04 12:00:00,23.78,616.0,241.66,408.0,2.14,78.9 +2020-07-04 13:00:00,23.39,562.0,228.24,379.0,2.76,78.85 +2020-07-04 14:00:00,22.14,61.0,0.0,61.0,2.69,81.4 +2020-07-04 15:00:00,22.73,49.0,0.0,49.0,1.86,78.75 +2020-07-04 16:00:00,23.28,35.0,0.0,35.0,1.86,76.2 +2020-07-04 17:00:00,22.27,33.0,0.0,33.0,2.14,78.7 +2020-07-04 18:00:00,21.31,87.0,222.58,54.0,1.52,84.05 +2020-07-04 19:00:00,20.26,0.0,0.0,0.0,1.93,86.85 +2020-07-04 20:00:00,19.34,0.0,-0.0,0.0,2.0,92.9 +2020-07-04 21:00:00,18.94,0.0,-0.0,0.0,2.0,96.1 +2020-07-04 22:00:00,18.42,0.0,-0.0,0.0,2.07,92.85 +2020-07-04 23:00:00,18.33,0.0,-0.0,0.0,2.0,92.85 +2020-07-05 00:00:00,17.45,0.0,-0.0,0.0,2.0,92.85 +2020-07-05 01:00:00,17.27,0.0,-0.0,0.0,1.79,92.85 +2020-07-05 02:00:00,17.0,0.0,-0.0,0.0,1.93,92.85 +2020-07-05 03:00:00,16.5,0.0,0.0,0.0,1.93,92.8 +2020-07-05 04:00:00,16.78,27.0,0.0,27.0,1.86,96.05 +2020-07-05 05:00:00,17.61,84.0,0.0,84.0,1.93,92.85 +2020-07-05 06:00:00,18.16,132.0,0.0,132.0,2.62,89.75 +2020-07-05 07:00:00,19.3,248.0,20.18,236.0,1.79,92.9 +2020-07-05 08:00:00,20.14,602.0,467.83,268.0,2.21,86.85 +2020-07-05 09:00:00,21.16,802.0,806.0,153.0,2.76,75.95 +2020-07-05 10:00:00,21.61,600.0,211.1,418.0,3.03,66.25 +2020-07-05 11:00:00,22.05,744.0,443.89,353.0,2.97,61.95 +2020-07-05 12:00:00,22.69,876.0,847.61,147.0,2.9,57.9 +2020-07-05 13:00:00,23.13,793.0,792.56,158.0,3.24,54.1 +2020-07-05 14:00:00,23.3,678.0,732.76,159.0,3.45,52.25 +2020-07-05 15:00:00,23.35,530.0,627.96,161.0,3.38,52.25 +2020-07-05 16:00:00,22.86,398.0,632.47,115.0,3.31,54.0 +2020-07-05 17:00:00,22.31,243.0,528.05,86.0,2.55,55.8 +2020-07-05 18:00:00,21.31,95.0,298.37,51.0,1.86,61.7 +2020-07-05 19:00:00,19.95,0.0,0.0,0.0,1.59,70.65 +2020-07-05 20:00:00,18.96,0.0,-0.0,0.0,1.24,72.95 +2020-07-05 21:00:00,19.71,0.0,-0.0,0.0,0.9,61.4 +2020-07-05 22:00:00,19.74,0.0,-0.0,0.0,0.48,61.4 +2020-07-05 23:00:00,17.2,0.0,-0.0,0.0,1.38,70.3 +2020-07-06 00:00:00,15.25,0.0,-0.0,0.0,1.59,80.6 +2020-07-06 01:00:00,13.97,0.0,-0.0,0.0,1.79,86.35 +2020-07-06 02:00:00,13.34,0.0,-0.0,0.0,1.66,89.4 +2020-07-06 03:00:00,13.18,0.0,0.0,0.0,1.59,89.4 +2020-07-06 04:00:00,13.55,81.0,143.35,59.0,1.45,89.45 +2020-07-06 05:00:00,15.97,247.0,520.26,89.0,0.9,89.6 +2020-07-06 06:00:00,19.59,404.0,637.03,115.0,0.69,73.15 +2020-07-06 07:00:00,21.62,566.0,740.08,127.0,0.55,64.0 +2020-07-06 08:00:00,22.83,629.0,551.41,236.0,0.97,54.0 +2020-07-06 09:00:00,23.45,792.0,782.2,163.0,1.38,50.45 +2020-07-06 10:00:00,23.87,608.0,224.1,415.0,1.79,48.8 +2020-07-06 11:00:00,23.96,596.0,187.49,431.0,2.0,48.8 +2020-07-06 12:00:00,23.8,597.0,210.62,416.0,2.07,48.8 +2020-07-06 13:00:00,23.54,213.0,0.0,213.0,2.28,50.45 +2020-07-06 14:00:00,23.15,140.0,0.0,140.0,2.41,52.25 +2020-07-06 15:00:00,22.69,96.0,0.0,96.0,2.69,55.9 +2020-07-06 16:00:00,21.98,106.0,0.0,106.0,2.62,64.0 +2020-07-06 17:00:00,21.39,66.0,0.0,66.0,2.0,68.5 +2020-07-06 18:00:00,20.78,37.0,6.82,36.0,1.38,73.3 +2020-07-06 19:00:00,19.5,0.0,0.0,0.0,1.17,86.8 +2020-07-06 20:00:00,18.27,0.0,-0.0,0.0,1.24,89.75 +2020-07-06 21:00:00,17.37,0.0,-0.0,0.0,1.52,92.85 +2020-07-06 22:00:00,16.72,0.0,-0.0,0.0,1.66,92.8 +2020-07-06 23:00:00,16.38,0.0,-0.0,0.0,1.59,92.8 +2020-07-07 00:00:00,15.88,0.0,-0.0,0.0,1.66,96.0 +2020-07-07 01:00:00,15.28,0.0,-0.0,0.0,1.66,96.0 +2020-07-07 02:00:00,14.61,0.0,-0.0,0.0,1.79,92.7 +2020-07-07 03:00:00,14.16,0.0,0.0,0.0,1.86,92.7 +2020-07-07 04:00:00,14.23,79.0,138.46,58.0,1.79,92.7 +2020-07-07 05:00:00,16.46,250.0,543.07,86.0,1.66,89.65 +2020-07-07 06:00:00,18.48,398.0,621.55,117.0,2.28,81.0 +2020-07-07 07:00:00,19.16,296.0,57.46,262.0,2.48,78.3 +2020-07-07 08:00:00,20.15,522.0,281.12,322.0,2.28,73.2 +2020-07-07 09:00:00,21.02,680.0,465.74,306.0,2.14,70.8 +2020-07-07 10:00:00,21.64,167.0,0.0,167.0,1.93,68.6 +2020-07-07 11:00:00,22.35,871.0,790.49,176.0,1.59,66.35 +2020-07-07 12:00:00,22.82,723.0,446.07,340.0,1.45,64.2 +2020-07-07 13:00:00,23.21,225.0,0.0,225.0,1.59,62.15 +2020-07-07 14:00:00,23.49,176.0,0.0,176.0,2.07,58.0 +2020-07-07 15:00:00,21.83,377.0,179.09,272.0,3.03,61.85 +2020-07-07 16:00:00,19.79,70.0,0.0,70.0,2.69,73.15 +2020-07-07 17:00:00,19.39,37.0,0.0,37.0,1.86,73.05 +2020-07-07 18:00:00,19.17,18.0,0.0,18.0,1.59,73.05 +2020-07-07 19:00:00,18.09,0.0,0.0,0.0,2.07,72.95 +2020-07-07 20:00:00,17.64,0.0,-0.0,0.0,1.86,75.45 +2020-07-07 21:00:00,17.24,0.0,-0.0,0.0,2.14,75.4 +2020-07-07 22:00:00,16.77,0.0,-0.0,0.0,2.28,75.3 +2020-07-07 23:00:00,16.3,0.0,-0.0,0.0,2.41,75.25 +2020-07-08 00:00:00,15.84,0.0,-0.0,0.0,2.48,75.15 +2020-07-08 01:00:00,15.31,0.0,-0.0,0.0,2.76,77.8 +2020-07-08 02:00:00,15.06,0.0,-0.0,0.0,2.83,75.1 +2020-07-08 03:00:00,14.94,0.0,0.0,0.0,2.97,77.75 +2020-07-08 04:00:00,14.95,71.0,93.46,57.0,3.24,77.75 +2020-07-08 05:00:00,15.27,232.0,443.01,99.0,3.45,77.8 +2020-07-08 06:00:00,16.06,242.0,91.02,201.0,4.14,70.1 +2020-07-08 07:00:00,16.12,347.0,118.59,277.0,4.34,72.65 +2020-07-08 08:00:00,16.87,540.0,316.85,315.0,4.21,70.2 +2020-07-08 09:00:00,17.92,508.0,144.66,392.0,4.28,67.95 +2020-07-08 10:00:00,18.67,632.0,260.7,408.0,4.55,68.05 +2020-07-08 11:00:00,18.75,542.0,124.1,433.0,4.48,70.45 +2020-07-08 12:00:00,19.44,845.0,763.59,190.0,4.55,68.1 +2020-07-08 13:00:00,19.69,691.0,491.8,298.0,4.83,65.85 +2020-07-08 14:00:00,19.38,148.0,0.0,148.0,5.17,68.1 +2020-07-08 15:00:00,18.81,511.0,555.09,186.0,5.24,68.05 +2020-07-08 16:00:00,18.6,198.0,35.94,182.0,5.17,65.65 +2020-07-08 17:00:00,18.36,134.0,54.29,118.0,5.31,61.2 +2020-07-08 18:00:00,16.82,75.0,138.44,55.0,5.24,67.75 +2020-07-08 19:00:00,15.59,0.0,0.0,0.0,5.03,72.55 +2020-07-08 20:00:00,15.36,0.0,-0.0,0.0,4.62,69.9 +2020-07-08 21:00:00,15.03,0.0,-0.0,0.0,4.62,69.9 +2020-07-08 22:00:00,14.8,0.0,-0.0,0.0,4.76,69.85 +2020-07-08 23:00:00,14.57,0.0,-0.0,0.0,4.9,69.85 +2020-07-09 00:00:00,14.42,0.0,-0.0,0.0,4.9,72.3 +2020-07-09 01:00:00,14.32,0.0,-0.0,0.0,4.69,74.95 +2020-07-09 02:00:00,14.34,0.0,-0.0,0.0,4.55,74.95 +2020-07-09 03:00:00,14.26,0.0,0.0,0.0,4.34,74.95 +2020-07-09 04:00:00,14.07,10.0,0.0,10.0,4.41,74.95 +2020-07-09 05:00:00,13.88,105.0,13.41,101.0,3.86,80.4 +2020-07-09 06:00:00,14.31,127.0,0.0,127.0,4.69,77.65 +2020-07-09 07:00:00,14.93,111.0,0.0,111.0,4.34,80.55 +2020-07-09 08:00:00,15.42,432.0,125.57,343.0,4.14,77.8 +2020-07-09 09:00:00,16.11,0.0,0.0,0.0,4.07,72.65 +2020-07-09 10:00:00,16.95,0.0,0.0,0.0,4.07,70.2 +2020-07-09 11:00:00,17.8,811.0,597.27,287.0,4.21,65.55 +2020-07-09 12:00:00,18.38,653.0,294.08,401.0,4.34,63.4 +2020-07-09 13:00:00,18.52,580.0,249.29,381.0,4.41,61.2 +2020-07-09 14:00:00,18.52,479.0,199.89,338.0,4.62,56.95 +2020-07-09 15:00:00,18.01,478.0,431.05,226.0,4.14,53.0 +2020-07-09 16:00:00,18.08,197.0,33.77,182.0,3.79,51.15 +2020-07-09 17:00:00,17.63,0.0,0.0,0.0,3.31,51.0 +2020-07-09 18:00:00,16.97,0.0,0.0,0.0,2.62,54.6 +2020-07-09 19:00:00,16.13,0.0,0.0,0.0,2.48,56.5 +2020-07-09 20:00:00,15.08,0.0,-0.0,0.0,2.14,60.5 +2020-07-09 21:00:00,14.0,0.0,-0.0,0.0,2.0,64.85 +2020-07-09 22:00:00,12.95,0.0,-0.0,0.0,1.86,74.7 +2020-07-09 23:00:00,11.97,0.0,-0.0,0.0,1.86,77.35 +2020-07-10 00:00:00,11.0,0.0,-0.0,0.0,1.93,80.1 +2020-07-10 01:00:00,10.51,0.0,-0.0,0.0,2.07,83.0 +2020-07-10 02:00:00,10.43,0.0,-0.0,0.0,2.21,86.0 +2020-07-10 03:00:00,10.82,0.0,0.0,0.0,2.41,83.0 +2020-07-10 04:00:00,11.77,18.0,0.0,18.0,2.69,80.15 +2020-07-10 05:00:00,12.62,59.0,0.0,59.0,2.69,80.3 +2020-07-10 06:00:00,13.11,372.0,498.88,149.0,2.48,83.25 +2020-07-10 07:00:00,14.09,402.0,221.42,272.0,3.52,74.95 +2020-07-10 08:00:00,15.2,463.0,171.07,342.0,3.31,67.45 +2020-07-10 09:00:00,16.72,456.0,87.57,386.0,3.45,60.85 +2020-07-10 10:00:00,17.75,271.0,1.17,270.0,3.66,58.9 +2020-07-10 11:00:00,18.31,781.0,526.07,320.0,4.07,59.05 +2020-07-10 12:00:00,18.45,835.0,727.83,212.0,4.48,59.05 +2020-07-10 13:00:00,18.5,670.0,435.19,323.0,4.9,56.95 +2020-07-10 14:00:00,18.81,407.0,99.36,337.0,5.1,56.95 +2020-07-10 15:00:00,17.97,130.0,0.0,130.0,5.52,58.9 +2020-07-10 16:00:00,17.52,247.0,106.06,200.0,4.83,58.9 +2020-07-10 17:00:00,16.56,30.0,0.0,30.0,4.48,63.05 +2020-07-10 18:00:00,15.09,46.0,14.09,44.0,4.34,72.45 +2020-07-10 19:00:00,13.52,0.0,0.0,0.0,3.79,72.2 +2020-07-10 20:00:00,13.26,0.0,-0.0,0.0,3.66,77.5 +2020-07-10 21:00:00,12.99,0.0,-0.0,0.0,3.86,80.35 +2020-07-10 22:00:00,12.38,0.0,-0.0,0.0,4.07,89.35 +2020-07-10 23:00:00,12.31,0.0,-0.0,0.0,4.76,89.35 +2020-07-11 00:00:00,12.23,0.0,-0.0,0.0,4.69,92.6 +2020-07-11 01:00:00,12.13,0.0,-0.0,0.0,4.34,89.35 +2020-07-11 02:00:00,11.96,0.0,-0.0,0.0,4.14,89.35 +2020-07-11 03:00:00,11.76,0.0,0.0,0.0,4.0,89.3 +2020-07-11 04:00:00,11.89,43.0,6.96,42.0,3.72,89.3 +2020-07-11 05:00:00,12.1,82.0,0.0,82.0,3.52,86.2 +2020-07-11 06:00:00,12.49,138.0,2.25,137.0,3.93,83.25 +2020-07-11 07:00:00,12.96,202.0,5.12,199.0,3.59,83.25 +2020-07-11 08:00:00,13.4,413.0,106.26,338.0,3.79,77.5 +2020-07-11 09:00:00,13.85,503.0,139.09,392.0,3.72,72.2 +2020-07-11 10:00:00,14.57,303.0,3.51,300.0,3.86,64.95 +2020-07-11 11:00:00,16.07,349.0,9.14,341.0,4.48,58.6 +2020-07-11 12:00:00,16.84,372.0,18.71,356.0,4.0,58.7 +2020-07-11 13:00:00,17.37,235.0,0.0,235.0,3.72,60.95 +2020-07-11 14:00:00,17.81,586.0,442.08,275.0,3.45,58.9 +2020-07-11 15:00:00,18.02,499.0,516.63,198.0,3.1,56.95 +2020-07-11 16:00:00,18.17,112.0,0.0,112.0,2.97,54.95 +2020-07-11 17:00:00,17.75,129.0,48.07,115.0,2.62,56.85 +2020-07-11 18:00:00,16.98,42.0,14.23,40.0,2.34,63.05 +2020-07-11 19:00:00,15.26,0.0,0.0,0.0,2.14,72.45 +2020-07-11 20:00:00,14.39,0.0,-0.0,0.0,2.28,77.65 +2020-07-11 21:00:00,13.93,0.0,-0.0,0.0,2.48,83.35 +2020-07-11 22:00:00,13.41,0.0,-0.0,0.0,2.21,86.3 +2020-07-11 23:00:00,12.89,0.0,-0.0,0.0,1.93,89.4 +2020-07-12 00:00:00,12.48,0.0,-0.0,0.0,2.0,86.25 +2020-07-12 01:00:00,12.17,0.0,-0.0,0.0,1.93,89.35 +2020-07-12 02:00:00,11.71,0.0,-0.0,0.0,1.93,92.55 +2020-07-12 03:00:00,11.45,0.0,0.0,0.0,1.86,95.9 +2020-07-12 04:00:00,11.91,17.0,0.0,17.0,1.52,95.9 +2020-07-12 05:00:00,13.16,144.0,78.64,121.0,1.17,92.65 +2020-07-12 06:00:00,14.27,225.0,69.93,194.0,1.66,86.4 +2020-07-12 07:00:00,15.36,368.0,161.02,274.0,1.52,83.5 +2020-07-12 08:00:00,16.54,450.0,157.61,339.0,2.07,72.7 +2020-07-12 09:00:00,17.67,623.0,338.92,353.0,2.0,67.95 +2020-07-12 10:00:00,18.85,494.0,92.44,415.0,2.21,63.4 +2020-07-12 11:00:00,19.74,503.0,89.24,425.0,2.55,59.25 +2020-07-12 12:00:00,20.25,401.0,30.45,375.0,2.83,57.3 +2020-07-12 13:00:00,20.66,256.0,1.26,255.0,2.55,55.45 +2020-07-12 14:00:00,20.99,533.0,314.62,312.0,2.41,55.45 +2020-07-12 15:00:00,21.31,394.0,216.68,268.0,2.21,53.6 +2020-07-12 16:00:00,21.09,320.0,310.82,183.0,2.0,55.55 +2020-07-12 17:00:00,20.53,139.0,69.0,119.0,1.31,63.7 +2020-07-12 18:00:00,19.72,42.0,14.38,40.0,1.38,70.65 +2020-07-12 19:00:00,18.83,0.0,-0.0,0.0,1.72,70.45 +2020-07-12 20:00:00,17.77,0.0,-0.0,0.0,1.79,72.9 +2020-07-12 21:00:00,16.95,0.0,-0.0,0.0,1.86,78.0 +2020-07-12 22:00:00,16.27,0.0,-0.0,0.0,2.07,77.95 +2020-07-12 23:00:00,16.01,0.0,-0.0,0.0,2.07,77.95 +2020-07-13 00:00:00,16.13,0.0,-0.0,0.0,2.14,77.95 +2020-07-13 01:00:00,15.83,0.0,-0.0,0.0,2.14,80.65 +2020-07-13 02:00:00,15.81,0.0,-0.0,0.0,2.14,83.55 +2020-07-13 03:00:00,15.8,0.0,-0.0,0.0,2.07,83.55 +2020-07-13 04:00:00,15.81,17.0,0.0,17.0,2.34,86.5 +2020-07-13 05:00:00,16.69,222.0,427.02,98.0,2.69,86.6 +2020-07-13 06:00:00,17.32,379.0,566.41,129.0,3.45,86.65 +2020-07-13 07:00:00,18.86,542.0,692.44,139.0,3.52,81.0 +2020-07-13 08:00:00,19.78,640.0,611.93,210.0,2.9,73.15 +2020-07-13 09:00:00,21.0,733.0,616.19,243.0,2.34,63.8 +2020-07-13 10:00:00,22.05,774.0,571.92,286.0,2.07,55.65 +2020-07-13 11:00:00,23.01,809.0,612.91,274.0,2.0,48.55 +2020-07-13 12:00:00,23.72,840.0,763.41,189.0,1.93,45.5 +2020-07-13 13:00:00,24.3,372.0,31.48,347.0,1.86,44.05 +2020-07-13 14:00:00,24.44,681.0,758.6,149.0,1.24,45.6 +2020-07-13 15:00:00,24.39,526.0,630.72,160.0,0.41,45.6 +2020-07-13 16:00:00,24.43,358.0,468.76,152.0,0.48,47.25 +2020-07-13 17:00:00,23.37,215.0,388.34,103.0,0.41,71.25 +2020-07-13 18:00:00,23.0,89.0,298.36,48.0,0.48,64.2 +2020-07-13 19:00:00,21.23,0.0,-0.0,0.0,1.93,63.9 +2020-07-13 20:00:00,19.97,0.0,-0.0,0.0,1.93,70.65 +2020-07-13 21:00:00,18.53,0.0,-0.0,0.0,2.0,75.55 +2020-07-13 22:00:00,17.56,0.0,-0.0,0.0,1.93,78.15 +2020-07-13 23:00:00,16.82,0.0,-0.0,0.0,1.93,83.65 +2020-07-14 00:00:00,16.42,0.0,-0.0,0.0,1.93,89.65 +2020-07-14 01:00:00,16.56,0.0,-0.0,0.0,2.0,86.6 +2020-07-14 02:00:00,16.41,0.0,-0.0,0.0,2.07,89.65 +2020-07-14 03:00:00,16.5,0.0,-0.0,0.0,2.0,89.65 +2020-07-14 04:00:00,16.62,9.0,0.0,9.0,1.93,89.65 +2020-07-14 05:00:00,17.88,30.0,0.0,30.0,1.79,86.7 +2020-07-14 06:00:00,20.02,60.0,0.0,60.0,1.59,83.9 +2020-07-14 07:00:00,20.05,114.0,0.0,114.0,1.79,86.85 +2020-07-14 08:00:00,21.04,443.0,161.19,330.0,1.66,84.05 +2020-07-14 09:00:00,22.55,613.0,342.69,341.0,2.28,76.1 +2020-07-14 10:00:00,23.48,538.0,147.9,412.0,2.62,71.25 +2020-07-14 11:00:00,23.97,139.0,0.0,139.0,2.83,71.35 +2020-07-14 12:00:00,23.98,294.0,3.52,291.0,2.62,71.35 +2020-07-14 13:00:00,23.67,624.0,368.21,332.0,2.34,73.75 +2020-07-14 14:00:00,24.02,593.0,499.95,243.0,2.41,73.75 +2020-07-14 15:00:00,22.7,390.0,224.53,260.0,2.28,78.75 +2020-07-14 16:00:00,22.39,341.0,424.61,155.0,1.79,81.4 +2020-07-14 17:00:00,22.47,222.0,467.14,88.0,0.76,81.4 +2020-07-14 18:00:00,22.28,85.0,272.66,48.0,0.83,84.15 +2020-07-14 19:00:00,23.37,0.0,-0.0,0.0,0.62,73.7 +2020-07-14 20:00:00,20.21,0.0,-0.0,0.0,1.79,86.85 +2020-07-14 21:00:00,18.64,0.0,-0.0,0.0,1.79,92.85 +2020-07-14 22:00:00,17.51,0.0,-0.0,0.0,1.79,92.85 +2020-07-14 23:00:00,16.75,0.0,-0.0,0.0,1.72,96.05 +2020-07-15 00:00:00,16.28,0.0,-0.0,0.0,1.72,96.05 +2020-07-15 01:00:00,16.17,0.0,-0.0,0.0,1.86,96.05 +2020-07-15 02:00:00,16.16,0.0,-0.0,0.0,1.93,92.8 +2020-07-15 03:00:00,16.25,0.0,-0.0,0.0,2.0,92.8 +2020-07-15 04:00:00,16.73,7.0,0.0,7.0,1.79,92.8 +2020-07-15 05:00:00,18.27,20.0,0.0,20.0,1.31,92.85 +2020-07-15 06:00:00,20.81,56.0,0.0,56.0,1.1,84.05 +2020-07-15 07:00:00,20.58,70.0,0.0,70.0,2.0,84.05 +2020-07-15 08:00:00,21.3,92.0,0.0,92.0,1.45,81.3 +2020-07-15 09:00:00,21.93,279.0,3.79,276.0,1.52,78.65 +2020-07-15 10:00:00,22.94,715.0,457.38,326.0,1.45,73.6 +2020-07-15 11:00:00,23.77,826.0,702.04,215.0,2.0,68.95 +2020-07-15 12:00:00,23.67,650.0,319.89,378.0,1.72,68.95 +2020-07-15 13:00:00,23.16,411.0,60.62,363.0,1.86,71.25 +2020-07-15 14:00:00,22.57,334.0,41.5,305.0,0.97,78.75 +2020-07-15 15:00:00,23.14,72.0,0.0,72.0,1.66,76.2 +2020-07-15 16:00:00,23.53,200.0,45.81,180.0,1.79,73.7 +2020-07-15 17:00:00,23.63,216.0,434.79,92.0,1.52,71.35 +2020-07-15 18:00:00,23.2,79.0,231.56,48.0,1.38,76.2 +2020-07-15 19:00:00,21.58,0.0,-0.0,0.0,1.79,78.65 +2020-07-15 20:00:00,20.03,0.0,-0.0,0.0,2.0,86.85 +2020-07-15 21:00:00,18.98,0.0,-0.0,0.0,2.14,92.85 +2020-07-15 22:00:00,18.34,0.0,-0.0,0.0,2.21,89.75 +2020-07-15 23:00:00,17.72,0.0,-0.0,0.0,2.14,89.75 +2020-07-16 00:00:00,17.21,0.0,-0.0,0.0,2.21,89.7 +2020-07-16 01:00:00,16.76,0.0,-0.0,0.0,2.34,89.65 +2020-07-16 02:00:00,16.31,0.0,-0.0,0.0,2.28,89.65 +2020-07-16 03:00:00,15.79,0.0,-0.0,0.0,2.21,89.6 +2020-07-16 04:00:00,15.52,52.0,45.37,46.0,2.0,89.6 +2020-07-16 05:00:00,17.15,215.0,419.39,96.0,1.52,92.85 +2020-07-16 06:00:00,20.27,367.0,537.64,133.0,1.38,83.95 +2020-07-16 07:00:00,21.66,532.0,676.61,142.0,2.0,78.65 +2020-07-16 08:00:00,22.89,679.0,765.47,145.0,1.86,71.15 +2020-07-16 09:00:00,23.99,706.0,555.27,267.0,1.86,64.4 +2020-07-16 10:00:00,24.53,815.0,701.97,219.0,1.93,58.2 +2020-07-16 11:00:00,24.77,789.0,574.26,290.0,1.86,54.45 +2020-07-16 12:00:00,25.03,834.0,759.75,189.0,1.86,54.45 +2020-07-16 13:00:00,25.38,771.0,754.0,175.0,1.93,50.95 +2020-07-16 14:00:00,25.33,630.0,596.51,214.0,1.86,50.95 +2020-07-16 15:00:00,25.1,535.0,689.07,138.0,1.66,50.95 +2020-07-16 16:00:00,25.01,374.0,572.48,125.0,1.45,52.6 +2020-07-16 17:00:00,24.34,190.0,268.13,114.0,1.17,62.35 +2020-07-16 18:00:00,23.4,80.0,242.52,48.0,1.31,68.85 +2020-07-16 19:00:00,21.72,0.0,-0.0,0.0,1.72,66.25 +2020-07-16 20:00:00,19.89,0.0,-0.0,0.0,1.93,73.15 +2020-07-16 21:00:00,18.58,0.0,-0.0,0.0,2.14,72.95 +2020-07-16 22:00:00,17.86,0.0,-0.0,0.0,2.28,75.45 +2020-07-16 23:00:00,17.98,0.0,-0.0,0.0,2.28,72.9 +2020-07-17 00:00:00,17.73,0.0,-0.0,0.0,2.34,72.9 +2020-07-17 01:00:00,17.23,0.0,-0.0,0.0,2.34,72.8 +2020-07-17 02:00:00,17.23,0.0,-0.0,0.0,2.41,72.8 +2020-07-17 03:00:00,16.96,0.0,-0.0,0.0,2.41,78.0 +2020-07-17 04:00:00,17.3,46.0,30.83,42.0,2.55,78.1 +2020-07-17 05:00:00,18.82,140.0,88.84,115.0,2.55,78.25 +2020-07-17 06:00:00,20.96,371.0,586.49,117.0,2.62,75.85 +2020-07-17 07:00:00,23.26,533.0,703.31,129.0,2.76,68.85 +2020-07-17 08:00:00,24.79,671.0,764.57,139.0,3.31,64.6 +2020-07-17 09:00:00,26.0,780.0,803.59,146.0,3.79,60.6 +2020-07-17 10:00:00,27.15,867.0,871.95,128.0,4.21,55.05 +2020-07-17 11:00:00,27.93,831.0,719.3,207.0,4.28,53.35 +2020-07-17 12:00:00,28.82,194.0,0.0,194.0,4.21,50.05 +2020-07-17 13:00:00,28.22,91.0,0.0,91.0,3.59,51.65 +2020-07-17 14:00:00,26.57,60.0,0.0,60.0,2.21,60.7 +2020-07-17 15:00:00,26.18,47.0,0.0,47.0,0.97,62.8 +2020-07-17 16:00:00,26.31,33.0,0.0,33.0,0.28,62.8 +2020-07-17 17:00:00,26.1,142.0,92.33,116.0,0.48,64.95 +2020-07-17 18:00:00,23.08,73.0,200.13,47.0,1.72,78.85 +2020-07-17 19:00:00,20.49,0.0,-0.0,0.0,1.38,92.95 +2020-07-17 20:00:00,19.72,0.0,-0.0,0.0,0.76,96.1 +2020-07-17 21:00:00,19.19,0.0,-0.0,0.0,0.83,96.1 +2020-07-17 22:00:00,18.56,0.0,-0.0,0.0,1.24,96.1 +2020-07-17 23:00:00,18.6,0.0,-0.0,0.0,1.24,96.1 +2020-07-18 00:00:00,18.73,0.0,-0.0,0.0,1.24,99.4 +2020-07-18 01:00:00,18.33,0.0,-0.0,0.0,0.76,96.1 +2020-07-18 02:00:00,17.95,0.0,-0.0,0.0,0.9,99.4 +2020-07-18 03:00:00,17.92,0.0,-0.0,0.0,1.24,99.4 +2020-07-18 04:00:00,17.58,7.0,0.0,7.0,4.14,92.85 +2020-07-18 05:00:00,15.73,19.0,0.0,19.0,6.14,89.6 +2020-07-18 06:00:00,15.64,51.0,0.0,51.0,5.17,83.55 +2020-07-18 07:00:00,15.93,274.0,47.17,247.0,4.14,77.85 +2020-07-18 08:00:00,16.69,110.0,0.0,110.0,3.79,72.7 +2020-07-18 09:00:00,16.83,112.0,0.0,112.0,4.14,72.7 +2020-07-18 10:00:00,16.88,142.0,0.0,142.0,4.07,75.3 +2020-07-18 11:00:00,16.4,141.0,0.0,141.0,3.31,80.75 +2020-07-18 12:00:00,15.89,133.0,0.0,133.0,2.9,86.5 +2020-07-18 13:00:00,15.62,147.0,0.0,147.0,3.38,86.5 +2020-07-18 14:00:00,14.85,125.0,0.0,125.0,2.9,89.5 +2020-07-18 15:00:00,14.94,184.0,1.75,183.0,2.62,92.7 +2020-07-18 16:00:00,15.03,104.0,0.0,104.0,2.48,89.55 +2020-07-18 17:00:00,15.26,39.0,0.0,39.0,2.21,89.55 +2020-07-18 18:00:00,15.48,24.0,0.0,24.0,2.28,86.5 +2020-07-18 19:00:00,14.79,0.0,-0.0,0.0,2.62,83.45 +2020-07-18 20:00:00,14.61,0.0,-0.0,0.0,3.03,83.45 +2020-07-18 21:00:00,14.5,0.0,-0.0,0.0,3.24,83.45 +2020-07-18 22:00:00,14.55,0.0,-0.0,0.0,3.52,80.55 +2020-07-18 23:00:00,14.42,0.0,-0.0,0.0,3.79,83.4 +2020-07-19 00:00:00,14.2,0.0,-0.0,0.0,3.93,80.45 +2020-07-19 01:00:00,14.27,0.0,-0.0,0.0,4.0,80.45 +2020-07-19 02:00:00,14.02,0.0,-0.0,0.0,4.0,80.45 +2020-07-19 03:00:00,13.75,0.0,-0.0,0.0,4.07,86.35 +2020-07-19 04:00:00,13.45,29.0,0.0,29.0,3.86,92.65 +2020-07-19 05:00:00,13.52,67.0,0.0,67.0,3.66,89.45 +2020-07-19 06:00:00,14.0,286.0,228.64,188.0,3.52,86.4 +2020-07-19 07:00:00,14.94,446.0,375.22,232.0,2.97,89.5 +2020-07-19 08:00:00,16.61,663.0,731.14,157.0,3.52,78.0 +2020-07-19 09:00:00,17.95,753.0,711.63,194.0,3.72,70.35 +2020-07-19 10:00:00,19.03,654.0,320.97,383.0,4.07,61.3 +2020-07-19 11:00:00,19.79,105.0,0.0,105.0,4.0,61.4 +2020-07-19 12:00:00,19.68,279.0,1.18,278.0,4.14,59.25 +2020-07-19 13:00:00,18.91,303.0,7.63,297.0,4.34,61.2 +2020-07-19 14:00:00,18.63,448.0,167.44,332.0,4.34,63.4 +2020-07-19 15:00:00,18.95,469.0,448.17,213.0,4.41,63.4 +2020-07-19 16:00:00,19.23,268.0,172.25,194.0,4.21,63.5 +2020-07-19 17:00:00,18.02,135.0,75.65,114.0,4.76,63.4 +2020-07-19 18:00:00,17.19,74.0,223.02,46.0,3.66,70.3 +2020-07-19 19:00:00,16.73,0.0,-0.0,0.0,3.45,67.75 +2020-07-19 20:00:00,16.29,0.0,-0.0,0.0,3.66,67.65 +2020-07-19 21:00:00,15.89,0.0,-0.0,0.0,3.72,70.0 +2020-07-19 22:00:00,15.67,0.0,-0.0,0.0,3.66,70.0 +2020-07-19 23:00:00,15.18,0.0,-0.0,0.0,3.52,72.45 +2020-07-20 00:00:00,14.71,0.0,-0.0,0.0,3.31,75.0 +2020-07-20 01:00:00,14.35,0.0,-0.0,0.0,3.1,77.65 +2020-07-20 02:00:00,13.94,0.0,-0.0,0.0,2.83,80.4 +2020-07-20 03:00:00,13.5,0.0,-0.0,0.0,2.69,80.4 +2020-07-20 04:00:00,13.33,21.0,0.0,21.0,2.34,83.25 +2020-07-20 05:00:00,14.25,85.0,7.3,83.0,2.48,80.45 +2020-07-20 06:00:00,15.57,94.0,0.0,94.0,2.48,75.15 +2020-07-20 07:00:00,16.29,392.0,234.06,259.0,3.17,77.95 +2020-07-20 08:00:00,17.29,333.0,40.57,305.0,3.72,72.8 +2020-07-20 09:00:00,18.38,371.0,34.45,344.0,3.86,68.05 +2020-07-20 10:00:00,19.4,456.0,67.64,399.0,4.0,61.3 +2020-07-20 11:00:00,20.39,286.0,1.16,285.0,4.41,59.35 +2020-07-20 12:00:00,20.17,496.0,100.83,411.0,4.97,59.35 +2020-07-20 13:00:00,18.47,754.0,712.64,195.0,4.41,70.45 +2020-07-20 14:00:00,19.07,538.0,347.28,298.0,4.48,65.75 +2020-07-20 15:00:00,20.46,518.0,639.27,154.0,5.38,57.3 +2020-07-20 16:00:00,19.08,333.0,402.22,161.0,4.48,63.5 +2020-07-20 17:00:00,18.77,213.0,450.18,89.0,3.31,68.05 +2020-07-20 18:00:00,18.5,55.0,81.16,45.0,3.52,63.4 +2020-07-20 19:00:00,17.56,0.0,-0.0,0.0,3.17,67.95 +2020-07-20 20:00:00,16.89,0.0,-0.0,0.0,2.76,72.7 +2020-07-20 21:00:00,16.36,0.0,-0.0,0.0,2.41,72.65 +2020-07-20 22:00:00,15.68,0.0,-0.0,0.0,2.0,75.15 +2020-07-20 23:00:00,15.29,0.0,-0.0,0.0,2.0,77.8 +2020-07-21 00:00:00,14.64,0.0,-0.0,0.0,2.14,80.55 +2020-07-21 01:00:00,14.0,0.0,-0.0,0.0,2.07,83.4 +2020-07-21 02:00:00,13.73,0.0,-0.0,0.0,1.93,86.35 +2020-07-21 03:00:00,13.19,0.0,-0.0,0.0,1.93,86.3 +2020-07-21 04:00:00,12.7,33.0,8.39,32.0,1.86,89.4 +2020-07-21 05:00:00,14.24,63.0,0.0,63.0,1.52,86.4 +2020-07-21 06:00:00,16.62,170.0,21.23,161.0,1.38,78.0 +2020-07-21 07:00:00,18.73,261.0,37.1,240.0,1.59,75.55 +2020-07-21 08:00:00,20.07,388.0,91.55,325.0,1.79,68.3 +2020-07-21 09:00:00,21.11,270.0,2.56,268.0,2.83,59.6 +2020-07-21 10:00:00,21.74,535.0,146.27,412.0,3.24,53.75 +2020-07-21 11:00:00,22.16,609.0,224.11,416.0,2.76,52.0 +2020-07-21 12:00:00,23.06,0.0,0.0,0.0,2.62,50.3 +2020-07-21 13:00:00,23.86,0.0,0.0,0.0,2.28,47.15 +2020-07-21 14:00:00,24.37,0.0,0.0,0.0,1.93,47.25 +2020-07-21 15:00:00,24.86,182.0,1.76,181.0,2.07,47.4 +2020-07-21 16:00:00,24.67,246.0,129.24,191.0,2.0,49.05 +2020-07-21 17:00:00,23.4,185.0,285.51,107.0,0.9,71.25 +2020-07-21 18:00:00,23.3,74.0,264.97,42.0,0.48,68.85 +2020-07-21 19:00:00,21.72,0.0,-0.0,0.0,1.93,71.0 +2020-07-21 20:00:00,19.6,0.0,-0.0,0.0,2.07,78.35 +2020-07-21 21:00:00,18.65,0.0,-0.0,0.0,2.28,83.8 +2020-07-21 22:00:00,18.24,0.0,-0.0,0.0,2.41,81.0 +2020-07-21 23:00:00,18.18,0.0,-0.0,0.0,2.55,81.0 +2020-07-22 00:00:00,18.35,0.0,-0.0,0.0,2.55,83.8 +2020-07-22 01:00:00,18.25,0.0,-0.0,0.0,2.48,81.0 +2020-07-22 02:00:00,18.19,0.0,-0.0,0.0,2.07,83.8 +2020-07-22 03:00:00,17.99,0.0,-0.0,0.0,2.34,89.75 +2020-07-22 04:00:00,18.5,24.0,0.0,24.0,2.9,86.75 +2020-07-22 05:00:00,19.07,22.0,0.0,22.0,3.03,86.8 +2020-07-22 06:00:00,20.73,32.0,0.0,32.0,2.97,84.05 +2020-07-22 07:00:00,21.33,103.0,0.0,103.0,1.59,81.3 +2020-07-22 08:00:00,22.73,327.0,42.27,298.0,1.31,76.15 +2020-07-22 09:00:00,25.28,350.0,26.92,329.0,2.48,66.95 +2020-07-22 10:00:00,27.15,594.0,237.15,395.0,2.62,60.9 +2020-07-22 11:00:00,27.11,271.0,1.16,270.0,3.17,60.9 +2020-07-22 12:00:00,24.55,254.0,0.0,254.0,2.97,73.85 +2020-07-22 13:00:00,23.91,105.0,0.0,105.0,2.21,76.3 +2020-07-22 14:00:00,24.75,225.0,2.91,223.0,1.59,73.95 +2020-07-22 15:00:00,26.0,421.0,328.91,235.0,2.07,67.05 +2020-07-22 16:00:00,26.19,105.0,0.0,105.0,1.86,62.8 +2020-07-22 17:00:00,25.39,171.0,228.91,109.0,1.1,69.25 +2020-07-22 18:00:00,24.26,67.0,203.01,43.0,1.24,76.35 +2020-07-22 19:00:00,22.57,0.0,-0.0,0.0,2.0,84.25 +2020-07-22 20:00:00,21.34,0.0,-0.0,0.0,2.21,89.95 +2020-07-22 21:00:00,20.54,0.0,-0.0,0.0,2.28,86.9 +2020-07-22 22:00:00,20.07,0.0,-0.0,0.0,2.34,86.85 +2020-07-22 23:00:00,19.76,0.0,-0.0,0.0,2.41,86.85 +2020-07-23 00:00:00,19.54,0.0,-0.0,0.0,2.48,81.1 +2020-07-23 01:00:00,19.12,0.0,-0.0,0.0,2.55,81.05 +2020-07-23 02:00:00,18.94,0.0,-0.0,0.0,2.69,81.0 +2020-07-23 03:00:00,19.08,0.0,-0.0,0.0,2.83,78.3 +2020-07-23 04:00:00,19.56,52.0,96.9,41.0,2.97,75.7 +2020-07-23 05:00:00,20.77,51.0,0.0,51.0,2.9,75.85 +2020-07-23 06:00:00,22.44,222.0,93.06,183.0,2.48,73.55 +2020-07-23 07:00:00,24.29,433.0,377.51,221.0,3.1,69.05 +2020-07-23 08:00:00,26.16,340.0,51.17,305.0,3.31,62.8 +2020-07-23 09:00:00,27.85,112.0,0.0,112.0,3.31,57.05 +2020-07-23 10:00:00,28.78,129.0,0.0,129.0,3.52,53.55 +2020-07-23 11:00:00,29.46,453.0,62.96,399.0,3.38,55.4 +2020-07-23 12:00:00,29.68,645.0,328.23,370.0,3.03,55.5 +2020-07-23 13:00:00,28.82,366.0,34.65,339.0,1.1,63.3 +2020-07-23 14:00:00,30.81,411.0,126.91,324.0,2.28,53.9 +2020-07-23 15:00:00,27.31,46.0,0.0,46.0,3.24,63.0 +2020-07-23 16:00:00,26.24,265.0,189.95,185.0,2.41,67.15 +2020-07-23 17:00:00,26.09,165.0,208.64,109.0,3.72,64.8 +2020-07-23 18:00:00,24.32,18.0,0.0,18.0,4.62,64.5 +2020-07-23 19:00:00,23.57,0.0,-0.0,0.0,3.72,68.95 +2020-07-23 20:00:00,21.07,0.0,-0.0,0.0,3.86,81.3 +2020-07-23 21:00:00,20.21,0.0,-0.0,0.0,3.59,83.95 +2020-07-23 22:00:00,19.58,0.0,-0.0,0.0,3.45,86.85 +2020-07-23 23:00:00,19.09,0.0,-0.0,0.0,3.38,86.8 +2020-07-24 00:00:00,18.79,0.0,-0.0,0.0,3.45,86.75 +2020-07-24 01:00:00,18.27,0.0,-0.0,0.0,3.38,83.8 +2020-07-24 02:00:00,17.9,0.0,-0.0,0.0,3.38,86.7 +2020-07-24 03:00:00,17.61,0.0,-0.0,0.0,3.31,86.7 +2020-07-24 04:00:00,17.61,38.0,27.12,35.0,3.52,83.75 +2020-07-24 05:00:00,17.5,142.0,125.21,109.0,3.72,86.65 +2020-07-24 06:00:00,17.29,326.0,427.28,148.0,3.79,89.7 +2020-07-24 07:00:00,17.94,348.0,159.14,259.0,3.79,86.7 +2020-07-24 08:00:00,19.13,506.0,297.71,303.0,3.45,83.85 +2020-07-24 09:00:00,20.29,555.0,247.41,363.0,3.24,81.15 +2020-07-24 10:00:00,21.06,670.0,374.68,357.0,3.24,75.95 +2020-07-24 11:00:00,22.09,332.0,8.18,325.0,3.45,71.05 +2020-07-24 12:00:00,22.43,529.0,144.74,408.0,3.93,68.7 +2020-07-24 13:00:00,21.87,251.0,1.29,250.0,3.86,66.25 +2020-07-24 14:00:00,21.96,219.0,1.46,218.0,4.21,59.7 +2020-07-24 15:00:00,22.11,334.0,131.85,260.0,4.07,52.0 +2020-07-24 16:00:00,21.91,285.0,250.71,180.0,3.03,55.65 +2020-07-24 17:00:00,21.57,181.0,300.91,101.0,2.62,55.65 +2020-07-24 18:00:00,20.78,62.0,186.12,41.0,2.69,57.45 +2020-07-24 19:00:00,18.48,0.0,-0.0,0.0,2.83,70.45 +2020-07-24 20:00:00,17.63,0.0,-0.0,0.0,2.21,70.35 +2020-07-24 21:00:00,17.29,0.0,-0.0,0.0,2.07,72.8 +2020-07-24 22:00:00,16.93,0.0,-0.0,0.0,2.07,75.3 +2020-07-24 23:00:00,16.57,0.0,-0.0,0.0,2.21,75.3 +2020-07-25 00:00:00,16.08,0.0,-0.0,0.0,2.21,77.95 +2020-07-25 01:00:00,15.7,0.0,-0.0,0.0,2.0,83.55 +2020-07-25 02:00:00,14.99,0.0,-0.0,0.0,1.93,86.5 +2020-07-25 03:00:00,14.48,0.0,-0.0,0.0,2.07,86.45 +2020-07-25 04:00:00,14.62,6.0,0.0,6.0,2.41,86.45 +2020-07-25 05:00:00,15.37,21.0,0.0,21.0,2.76,86.5 +2020-07-25 06:00:00,15.13,93.0,0.0,93.0,3.59,83.5 +2020-07-25 07:00:00,14.1,226.0,16.16,217.0,4.41,89.5 +2020-07-25 08:00:00,14.15,382.0,89.75,321.0,4.41,89.5 +2020-07-25 09:00:00,15.31,637.0,409.56,320.0,3.86,86.5 +2020-07-25 10:00:00,17.61,391.0,31.2,365.0,4.69,67.95 +2020-07-25 11:00:00,18.64,590.0,200.26,419.0,5.17,61.2 +2020-07-25 12:00:00,18.83,591.0,224.21,404.0,5.52,61.2 +2020-07-25 13:00:00,18.78,767.0,783.02,160.0,5.79,59.05 +2020-07-25 14:00:00,18.59,59.0,0.0,59.0,5.93,56.95 +2020-07-25 15:00:00,18.33,295.0,73.35,254.0,5.59,54.95 +2020-07-25 16:00:00,18.21,73.0,0.0,73.0,5.24,54.95 +2020-07-25 17:00:00,18.0,154.0,163.37,111.0,4.9,56.85 +2020-07-25 18:00:00,17.07,69.0,281.87,38.0,4.48,63.2 +2020-07-25 19:00:00,15.85,0.0,-0.0,0.0,4.21,70.0 +2020-07-25 20:00:00,15.56,0.0,-0.0,0.0,3.93,72.55 +2020-07-25 21:00:00,15.2,0.0,-0.0,0.0,3.79,75.1 +2020-07-25 22:00:00,14.97,0.0,-0.0,0.0,3.86,77.75 +2020-07-25 23:00:00,14.64,0.0,-0.0,0.0,3.79,77.75 +2020-07-26 00:00:00,14.39,0.0,-0.0,0.0,3.59,80.45 +2020-07-26 01:00:00,14.17,0.0,-0.0,0.0,3.38,80.45 +2020-07-26 02:00:00,13.76,0.0,-0.0,0.0,3.17,80.4 +2020-07-26 03:00:00,13.38,0.0,-0.0,0.0,3.1,83.25 +2020-07-26 04:00:00,12.94,31.0,9.56,30.0,3.03,86.25 +2020-07-26 05:00:00,13.29,124.0,69.77,106.0,2.76,86.3 +2020-07-26 06:00:00,14.1,262.0,184.71,186.0,3.79,80.45 +2020-07-26 07:00:00,14.36,327.0,119.02,261.0,3.72,86.4 +2020-07-26 08:00:00,15.59,582.0,484.16,254.0,3.79,72.55 +2020-07-26 09:00:00,16.7,582.0,286.31,361.0,3.79,63.05 +2020-07-26 10:00:00,17.74,710.0,447.43,338.0,4.62,54.85 +2020-07-26 11:00:00,18.11,581.0,184.29,424.0,4.48,51.15 +2020-07-26 12:00:00,18.25,746.0,533.62,302.0,4.48,51.15 +2020-07-26 13:00:00,18.25,753.0,725.62,192.0,3.79,51.15 +2020-07-26 14:00:00,19.11,639.0,664.01,188.0,4.0,49.45 +2020-07-26 15:00:00,19.08,464.0,458.1,209.0,3.45,49.45 +2020-07-26 16:00:00,19.11,361.0,582.37,120.0,3.17,49.45 +2020-07-26 17:00:00,18.77,133.0,92.14,109.0,2.28,54.95 +2020-07-26 18:00:00,17.54,66.0,270.95,37.0,1.24,67.95 +2020-07-26 19:00:00,18.43,0.0,-0.0,0.0,0.62,54.95 +2020-07-26 20:00:00,16.84,0.0,-0.0,0.0,1.03,63.05 +2020-07-26 21:00:00,13.2,0.0,-0.0,0.0,1.93,80.35 +2020-07-26 22:00:00,12.41,0.0,-0.0,0.0,2.0,86.2 +2020-07-26 23:00:00,12.32,0.0,-0.0,0.0,2.0,86.2 +2020-07-27 00:00:00,12.53,0.0,-0.0,0.0,2.07,83.25 +2020-07-27 01:00:00,12.57,0.0,-0.0,0.0,2.14,83.25 +2020-07-27 02:00:00,12.29,0.0,-0.0,0.0,2.28,83.15 +2020-07-27 03:00:00,12.18,0.0,-0.0,0.0,2.34,83.15 +2020-07-27 04:00:00,12.22,59.0,236.24,35.0,2.41,80.2 +2020-07-27 05:00:00,13.66,203.0,493.81,77.0,2.28,77.6 +2020-07-27 06:00:00,16.11,366.0,638.44,105.0,2.14,70.1 +2020-07-27 07:00:00,18.35,507.0,661.15,142.0,2.55,65.65 +2020-07-27 08:00:00,20.0,557.0,426.55,269.0,3.03,61.4 +2020-07-27 09:00:00,21.36,762.0,783.36,159.0,3.31,57.55 +2020-07-27 10:00:00,22.45,842.0,828.36,155.0,3.72,53.85 +2020-07-27 11:00:00,23.25,897.0,913.06,121.0,3.86,48.7 +2020-07-27 12:00:00,23.92,873.0,906.04,121.0,3.86,48.8 +2020-07-27 13:00:00,24.44,801.0,881.98,121.0,3.86,48.95 +2020-07-27 14:00:00,24.8,684.0,831.69,121.0,3.72,47.4 +2020-07-27 15:00:00,24.99,536.0,756.03,117.0,3.52,49.05 +2020-07-27 16:00:00,24.9,370.0,642.03,106.0,3.1,49.05 +2020-07-27 17:00:00,24.17,205.0,485.19,80.0,2.41,56.25 +2020-07-27 18:00:00,22.64,61.0,240.43,36.0,2.41,62.05 +2020-07-27 19:00:00,20.95,0.0,-0.0,0.0,2.62,61.6 +2020-07-27 20:00:00,19.75,0.0,-0.0,0.0,2.76,63.6 +2020-07-27 21:00:00,18.99,0.0,-0.0,0.0,2.9,65.65 +2020-07-27 22:00:00,18.4,0.0,-0.0,0.0,2.83,65.65 +2020-07-27 23:00:00,17.84,0.0,-0.0,0.0,2.41,67.95 +2020-07-28 00:00:00,16.8,0.0,-0.0,0.0,2.07,75.3 +2020-07-28 01:00:00,16.64,0.0,-0.0,0.0,2.28,75.3 +2020-07-28 02:00:00,17.12,0.0,-0.0,0.0,2.83,75.4 +2020-07-28 03:00:00,17.02,0.0,-0.0,0.0,2.97,80.85 +2020-07-28 04:00:00,16.76,5.0,0.0,5.0,2.83,86.6 +2020-07-28 05:00:00,16.74,57.0,0.0,57.0,2.9,89.65 +2020-07-28 06:00:00,16.74,36.0,0.0,36.0,3.1,89.65 +2020-07-28 07:00:00,17.6,62.0,0.0,62.0,3.38,80.9 +2020-07-28 08:00:00,17.52,106.0,0.0,106.0,3.31,80.9 +2020-07-28 09:00:00,17.8,529.0,208.45,369.0,3.24,78.15 +2020-07-28 10:00:00,18.59,145.0,0.0,145.0,3.24,70.45 +2020-07-28 11:00:00,19.87,625.0,265.4,400.0,3.59,63.6 +2020-07-28 12:00:00,20.51,724.0,508.54,303.0,3.72,59.35 +2020-07-28 13:00:00,20.91,522.0,196.42,371.0,3.72,55.45 +2020-07-28 14:00:00,20.82,414.0,134.9,323.0,3.38,53.5 +2020-07-28 15:00:00,20.75,225.0,18.13,215.0,3.1,53.5 +2020-07-28 16:00:00,20.61,294.0,306.02,169.0,2.9,53.5 +2020-07-28 17:00:00,20.1,189.0,400.48,87.0,2.34,53.35 +2020-07-28 18:00:00,19.06,56.0,198.35,36.0,1.66,61.3 +2020-07-28 19:00:00,18.1,0.0,-0.0,0.0,1.1,68.05 +2020-07-28 20:00:00,18.89,0.0,-0.0,0.0,0.48,59.05 +2020-07-28 21:00:00,16.69,0.0,-0.0,0.0,1.31,65.35 +2020-07-28 22:00:00,14.2,0.0,-0.0,0.0,1.79,77.65 +2020-07-28 23:00:00,13.18,0.0,-0.0,0.0,1.86,83.25 +2020-07-29 00:00:00,12.55,0.0,-0.0,0.0,1.86,86.25 +2020-07-29 01:00:00,12.26,0.0,-0.0,0.0,1.86,89.35 +2020-07-29 02:00:00,11.81,0.0,-0.0,0.0,1.86,89.3 +2020-07-29 03:00:00,11.44,0.0,-0.0,0.0,1.93,92.55 +2020-07-29 04:00:00,11.24,38.0,62.94,32.0,1.86,92.55 +2020-07-29 05:00:00,13.19,190.0,433.17,82.0,1.52,89.4 +2020-07-29 06:00:00,17.42,346.0,565.2,118.0,1.24,78.1 +2020-07-29 07:00:00,19.65,503.0,661.73,141.0,1.38,70.65 +2020-07-29 08:00:00,21.13,639.0,706.94,165.0,1.93,57.55 +2020-07-29 09:00:00,21.93,716.0,641.57,225.0,2.21,53.75 +2020-07-29 10:00:00,22.62,769.0,621.78,256.0,2.28,50.3 +2020-07-29 11:00:00,23.29,860.0,826.6,161.0,2.21,47.0 +2020-07-29 12:00:00,23.55,827.0,795.73,170.0,2.28,47.0 +2020-07-29 13:00:00,24.25,741.0,716.26,192.0,2.34,44.05 +2020-07-29 14:00:00,24.4,612.0,599.58,209.0,2.34,44.05 +2020-07-29 15:00:00,24.59,518.0,708.47,129.0,2.48,42.65 +2020-07-29 16:00:00,24.39,350.0,569.46,119.0,2.34,45.6 +2020-07-29 17:00:00,23.61,177.0,321.87,96.0,1.86,54.2 +2020-07-29 18:00:00,22.09,43.0,92.23,34.0,2.07,59.8 +2020-07-29 19:00:00,20.41,0.0,-0.0,0.0,2.48,63.7 +2020-07-29 20:00:00,19.32,0.0,-0.0,0.0,2.55,68.1 +2020-07-29 21:00:00,18.57,0.0,-0.0,0.0,2.55,68.05 +2020-07-29 22:00:00,17.94,0.0,-0.0,0.0,2.62,70.35 +2020-07-29 23:00:00,17.44,0.0,-0.0,0.0,2.83,67.85 +2020-07-30 00:00:00,17.05,0.0,-0.0,0.0,2.97,65.45 +2020-07-30 01:00:00,16.73,0.0,-0.0,0.0,3.17,67.75 +2020-07-30 02:00:00,16.6,0.0,-0.0,0.0,3.38,67.75 +2020-07-30 03:00:00,16.45,0.0,-0.0,0.0,3.52,72.65 +2020-07-30 04:00:00,16.25,47.0,151.95,33.0,3.45,75.25 +2020-07-30 05:00:00,16.81,190.0,454.64,78.0,3.45,75.3 +2020-07-30 06:00:00,18.43,310.0,401.86,149.0,3.24,72.95 +2020-07-30 07:00:00,20.74,194.0,5.51,191.0,3.31,68.4 +2020-07-30 08:00:00,22.49,322.0,40.41,295.0,3.17,66.35 +2020-07-30 09:00:00,24.09,729.0,695.92,198.0,3.86,62.35 +2020-07-30 10:00:00,24.5,442.0,64.41,389.0,4.97,56.25 +2020-07-30 11:00:00,24.43,729.0,474.27,329.0,5.31,52.5 +2020-07-30 12:00:00,24.31,692.0,429.93,338.0,5.1,50.7 +2020-07-30 13:00:00,24.34,582.0,304.93,349.0,4.97,48.95 +2020-07-30 14:00:00,24.13,491.0,274.78,307.0,4.62,47.25 +2020-07-30 15:00:00,23.47,322.0,120.8,256.0,4.14,50.45 +2020-07-30 16:00:00,22.76,195.0,57.11,172.0,3.72,50.3 +2020-07-30 17:00:00,21.61,66.0,0.0,66.0,3.52,51.9 +2020-07-30 18:00:00,20.42,31.0,21.22,29.0,3.1,55.3 +2020-07-30 19:00:00,19.72,0.0,-0.0,0.0,2.41,53.25 +2020-07-30 20:00:00,18.37,0.0,-0.0,0.0,1.93,59.05 +2020-07-30 21:00:00,16.54,0.0,-0.0,0.0,1.93,65.35 +2020-07-30 22:00:00,15.46,0.0,-0.0,0.0,1.86,72.45 +2020-07-30 23:00:00,14.48,0.0,-0.0,0.0,1.79,75.0 +2020-07-31 00:00:00,13.61,0.0,-0.0,0.0,1.86,77.6 +2020-07-31 01:00:00,13.0,0.0,-0.0,0.0,1.79,80.35 +2020-07-31 02:00:00,12.42,0.0,-0.0,0.0,1.93,83.15 +2020-07-31 03:00:00,12.64,0.0,-0.0,0.0,2.21,80.3 +2020-07-31 04:00:00,12.96,9.0,0.0,9.0,2.34,77.5 +2020-07-31 05:00:00,13.86,100.0,36.99,91.0,2.0,80.4 +2020-07-31 06:00:00,15.68,118.0,2.51,117.0,2.0,70.0 +2020-07-31 07:00:00,16.31,228.0,22.15,216.0,2.14,72.65 +2020-07-31 08:00:00,17.28,439.0,178.78,320.0,2.07,67.85 +2020-07-31 09:00:00,18.2,712.0,641.54,224.0,2.14,61.2 +2020-07-31 10:00:00,18.97,710.0,474.08,321.0,2.07,59.05 +2020-07-31 11:00:00,19.26,686.0,379.25,367.0,1.86,55.05 +2020-07-31 12:00:00,20.0,737.0,539.56,294.0,1.79,53.25 +2020-07-31 13:00:00,20.33,615.0,375.5,329.0,1.86,49.7 +2020-07-31 14:00:00,20.53,391.0,106.44,320.0,1.66,47.9 +2020-07-31 15:00:00,20.37,322.0,121.42,256.0,1.79,47.9 +2020-07-31 16:00:00,16.45,306.0,375.26,156.0,1.59,57.83 +2020-07-31 17:00:00,16.68,180.0,375.06,88.0,1.64,59.33 +2020-07-31 18:00:00,16.9,35.0,55.07,30.0,1.69,60.82 +2020-07-31 19:00:00,17.13,0.0,-0.0,0.0,1.74,62.31 +2020-07-31 20:00:00,17.36,0.0,-0.0,0.0,1.8,63.8 +2020-07-31 21:00:00,17.58,0.0,-0.0,0.0,1.85,65.3 +2020-07-31 22:00:00,17.81,0.0,-0.0,0.0,1.9,66.79 +2020-07-31 23:00:00,18.04,0.0,-0.0,0.0,1.95,68.28 +2020-08-01 00:00:00,18.26,0.0,-0.0,0.0,2.0,69.78 +2020-08-01 01:00:00,18.49,0.0,-0.0,0.0,2.06,71.27 +2020-08-01 02:00:00,18.71,0.0,-0.0,0.0,2.11,72.76 +2020-08-01 03:00:00,18.94,0.0,-0.0,0.0,2.16,74.26 +2020-08-01 04:00:00,19.17,30.0,48.62,26.0,2.21,75.75 +2020-08-01 05:00:00,19.39,158.0,299.38,87.0,2.26,77.24 +2020-08-01 06:00:00,19.62,325.0,540.7,113.0,2.32,78.74 +2020-08-01 07:00:00,19.84,484.0,665.41,127.0,2.37,80.23 +2020-08-01 08:00:00,25.97,632.0,756.92,132.0,2.28,56.6 +2020-08-01 09:00:00,27.51,741.0,797.79,138.0,2.34,51.45 +2020-08-01 10:00:00,28.73,816.0,832.32,137.0,2.41,46.65 +2020-08-01 11:00:00,29.53,840.0,840.54,137.0,2.28,43.65 +2020-08-01 12:00:00,30.09,812.0,820.92,142.0,2.0,38.3 +2020-08-01 13:00:00,30.61,73.0,0.0,73.0,1.86,35.85 +2020-08-01 14:00:00,30.64,70.0,0.0,70.0,1.52,35.85 +2020-08-01 15:00:00,29.67,195.0,7.44,191.0,2.41,38.15 +2020-08-01 16:00:00,27.19,285.0,330.43,155.0,3.24,46.4 +2020-08-01 17:00:00,25.82,132.0,142.53,98.0,2.14,58.55 +2020-08-01 18:00:00,24.87,35.0,83.72,28.0,1.66,66.85 +2020-08-01 19:00:00,23.38,0.0,-0.0,0.0,2.55,73.7 +2020-08-01 20:00:00,21.48,0.0,-0.0,0.0,2.0,86.95 +2020-08-01 21:00:00,20.66,0.0,-0.0,0.0,1.45,89.9 +2020-08-01 22:00:00,20.39,0.0,-0.0,0.0,1.66,92.95 +2020-08-01 23:00:00,20.02,0.0,-0.0,0.0,1.79,89.85 +2020-08-02 00:00:00,19.8,0.0,-0.0,0.0,1.93,89.85 +2020-08-02 01:00:00,19.63,0.0,-0.0,0.0,2.34,89.85 +2020-08-02 02:00:00,19.26,0.0,-0.0,0.0,2.48,92.9 +2020-08-02 03:00:00,18.63,0.0,-0.0,0.0,2.07,92.85 +2020-08-02 04:00:00,18.38,9.0,0.0,9.0,1.93,96.05 +2020-08-02 05:00:00,18.43,27.0,0.0,27.0,1.93,99.4 +2020-08-02 06:00:00,18.84,32.0,0.0,32.0,2.9,96.1 +2020-08-02 07:00:00,18.72,96.0,0.0,96.0,1.93,96.1 +2020-08-02 08:00:00,18.6,135.0,0.0,135.0,1.79,96.1 +2020-08-02 09:00:00,18.53,241.0,1.33,240.0,2.21,96.1 +2020-08-02 10:00:00,18.67,345.0,18.44,330.0,2.0,96.1 +2020-08-02 11:00:00,19.01,196.0,0.0,196.0,1.93,92.9 +2020-08-02 12:00:00,20.44,247.0,0.0,247.0,1.24,92.95 +2020-08-02 13:00:00,20.69,162.0,0.0,162.0,1.45,89.9 +2020-08-02 14:00:00,20.86,282.0,22.77,267.0,1.52,86.9 +2020-08-02 15:00:00,21.02,305.0,114.09,244.0,1.17,84.05 +2020-08-02 16:00:00,21.05,271.0,287.08,159.0,0.9,81.3 +2020-08-02 17:00:00,20.97,137.0,178.7,95.0,0.55,78.5 +2020-08-02 18:00:00,20.68,38.0,137.7,27.0,0.76,78.5 +2020-08-02 19:00:00,19.23,0.0,-0.0,0.0,0.83,89.8 +2020-08-02 20:00:00,17.97,0.0,-0.0,0.0,1.31,92.85 +2020-08-02 21:00:00,17.18,0.0,-0.0,0.0,1.59,92.85 +2020-08-02 22:00:00,16.55,0.0,-0.0,0.0,1.79,92.8 +2020-08-02 23:00:00,16.58,0.0,-0.0,0.0,1.86,92.8 +2020-08-03 00:00:00,16.61,0.0,-0.0,0.0,1.93,92.8 +2020-08-03 01:00:00,17.17,0.0,-0.0,0.0,2.0,92.85 +2020-08-03 02:00:00,17.44,0.0,-0.0,0.0,2.0,92.85 +2020-08-03 03:00:00,17.94,0.0,-0.0,0.0,2.07,92.85 +2020-08-03 04:00:00,17.88,33.0,105.98,25.0,2.14,86.65 +2020-08-03 05:00:00,18.49,114.0,90.98,93.0,2.34,78.25 +2020-08-03 06:00:00,19.69,185.0,59.56,162.0,2.69,73.15 +2020-08-03 07:00:00,20.49,497.0,726.95,111.0,3.59,70.8 +2020-08-03 08:00:00,21.39,435.0,193.79,308.0,4.14,70.9 +2020-08-03 09:00:00,22.36,359.0,38.63,330.0,4.21,66.35 +2020-08-03 10:00:00,23.22,568.0,223.23,387.0,4.34,60.05 +2020-08-03 11:00:00,23.81,323.0,8.42,316.0,4.55,56.15 +2020-08-03 12:00:00,24.2,506.0,136.87,395.0,4.83,52.5 +2020-08-03 13:00:00,24.49,126.0,0.0,126.0,4.9,52.5 +2020-08-03 14:00:00,24.57,210.0,1.52,209.0,4.48,49.05 +2020-08-03 15:00:00,24.19,276.0,71.5,238.0,3.93,50.7 +2020-08-03 16:00:00,24.1,113.0,2.59,112.0,3.72,50.7 +2020-08-03 17:00:00,23.44,129.0,146.92,95.0,3.03,56.0 +2020-08-03 18:00:00,22.4,34.0,105.19,26.0,2.34,59.8 +2020-08-03 19:00:00,21.31,0.0,-0.0,0.0,1.86,63.9 +2020-08-03 20:00:00,19.59,0.0,-0.0,0.0,2.0,73.15 +2020-08-03 21:00:00,18.25,0.0,-0.0,0.0,2.07,78.15 +2020-08-03 22:00:00,17.51,0.0,-0.0,0.0,2.14,80.85 +2020-08-03 23:00:00,17.03,0.0,-0.0,0.0,2.21,80.85 +2020-08-04 00:00:00,16.88,0.0,-0.0,0.0,2.34,86.6 +2020-08-04 01:00:00,17.34,0.0,-0.0,0.0,2.55,83.7 +2020-08-04 02:00:00,17.56,0.0,-0.0,0.0,2.9,83.7 +2020-08-04 03:00:00,18.16,0.0,-0.0,0.0,3.31,80.9 +2020-08-04 04:00:00,18.72,4.0,0.0,4.0,3.72,78.25 +2020-08-04 05:00:00,19.77,25.0,0.0,25.0,4.34,75.7 +2020-08-04 06:00:00,21.15,121.0,2.61,120.0,3.93,70.9 +2020-08-04 07:00:00,21.98,81.0,0.0,81.0,3.79,73.45 +2020-08-04 08:00:00,20.87,332.0,56.69,295.0,5.59,73.3 +2020-08-04 09:00:00,19.71,130.0,0.0,130.0,4.21,86.85 +2020-08-04 10:00:00,18.22,444.0,74.23,384.0,4.48,89.75 +2020-08-04 11:00:00,18.59,695.0,438.0,332.0,4.21,78.25 +2020-08-04 12:00:00,20.82,554.0,200.42,392.0,5.59,63.8 +2020-08-04 13:00:00,22.09,674.0,584.02,237.0,6.28,53.85 +2020-08-04 14:00:00,22.06,522.0,393.72,265.0,5.59,52.0 +2020-08-04 15:00:00,21.71,474.0,628.48,142.0,5.45,53.75 +2020-08-04 16:00:00,21.12,334.0,615.72,98.0,4.69,55.55 +2020-08-04 17:00:00,20.54,160.0,338.14,83.0,4.34,57.45 +2020-08-04 18:00:00,20.05,34.0,138.63,24.0,4.28,53.35 +2020-08-04 19:00:00,19.31,0.0,-0.0,0.0,3.24,53.15 +2020-08-04 20:00:00,18.42,0.0,-0.0,0.0,2.76,54.85 +2020-08-04 21:00:00,17.52,0.0,-0.0,0.0,2.76,56.75 +2020-08-04 22:00:00,16.8,0.0,-0.0,0.0,2.9,58.7 +2020-08-04 23:00:00,16.39,0.0,-0.0,0.0,3.17,60.75 +2020-08-05 00:00:00,16.12,0.0,-0.0,0.0,3.38,62.95 +2020-08-05 01:00:00,15.68,0.0,-0.0,0.0,3.45,67.55 +2020-08-05 02:00:00,15.41,0.0,-0.0,0.0,3.52,69.9 +2020-08-05 03:00:00,15.07,0.0,-0.0,0.0,3.79,69.9 +2020-08-05 04:00:00,14.88,36.0,204.23,22.0,4.0,72.4 +2020-08-05 05:00:00,15.14,176.0,485.83,67.0,4.34,72.45 +2020-08-05 06:00:00,16.09,333.0,618.18,98.0,4.28,70.1 +2020-08-05 07:00:00,17.52,493.0,711.96,119.0,6.41,70.3 +2020-08-05 08:00:00,18.15,610.0,681.59,167.0,6.48,65.55 +2020-08-05 09:00:00,18.71,653.0,513.7,270.0,6.55,63.4 +2020-08-05 10:00:00,19.39,465.0,90.61,392.0,6.69,61.3 +2020-08-05 11:00:00,19.86,741.0,543.51,292.0,6.55,57.2 +2020-08-05 12:00:00,20.81,618.0,305.39,372.0,6.76,53.5 +2020-08-05 13:00:00,21.34,278.0,5.37,274.0,6.55,49.95 +2020-08-05 14:00:00,21.43,566.0,521.83,227.0,6.0,48.2 +2020-08-05 15:00:00,21.41,348.0,196.22,245.0,5.38,48.2 +2020-08-05 16:00:00,21.16,230.0,155.38,171.0,4.55,48.2 +2020-08-05 17:00:00,20.74,148.0,267.96,88.0,3.52,49.8 +2020-08-05 18:00:00,19.84,31.0,117.43,23.0,2.83,51.4 +2020-08-05 19:00:00,18.72,0.0,-0.0,0.0,2.83,54.95 +2020-08-05 20:00:00,17.75,0.0,-0.0,0.0,2.34,60.95 +2020-08-05 21:00:00,16.64,0.0,-0.0,0.0,2.21,63.05 +2020-08-05 22:00:00,15.74,0.0,-0.0,0.0,2.21,67.55 +2020-08-05 23:00:00,14.83,0.0,-0.0,0.0,2.07,72.4 +2020-08-06 00:00:00,13.82,0.0,-0.0,0.0,1.93,80.4 +2020-08-06 01:00:00,13.09,0.0,-0.0,0.0,1.86,80.35 +2020-08-06 02:00:00,12.51,0.0,-0.0,0.0,1.72,83.25 +2020-08-06 03:00:00,12.26,0.0,-0.0,0.0,1.66,86.2 +2020-08-06 04:00:00,11.93,3.0,0.0,3.0,1.59,86.2 +2020-08-06 05:00:00,12.89,148.0,298.55,82.0,1.24,89.4 +2020-08-06 06:00:00,16.31,296.0,437.58,131.0,0.9,77.95 +2020-08-06 07:00:00,18.59,263.0,61.25,231.0,1.17,70.45 +2020-08-06 08:00:00,20.17,242.0,7.73,237.0,0.97,61.5 +2020-08-06 09:00:00,21.22,555.0,290.76,339.0,0.48,55.55 +2020-08-06 10:00:00,22.16,746.0,636.38,235.0,0.28,52.0 +2020-08-06 11:00:00,22.81,829.0,829.51,146.0,0.55,48.55 +2020-08-06 12:00:00,23.93,813.0,844.64,135.0,0.34,45.5 +2020-08-06 13:00:00,24.07,746.0,833.83,127.0,0.28,44.05 +2020-08-06 14:00:00,24.06,621.0,750.26,136.0,1.24,45.6 +2020-08-06 15:00:00,24.07,462.0,602.09,148.0,1.86,45.6 +2020-08-06 16:00:00,23.79,313.0,531.82,113.0,2.21,48.8 +2020-08-06 17:00:00,23.14,150.0,309.05,82.0,1.93,54.1 +2020-08-06 18:00:00,21.83,24.0,62.48,20.0,2.07,59.7 +2020-08-06 19:00:00,20.92,0.0,-0.0,0.0,2.48,59.5 +2020-08-06 20:00:00,19.68,0.0,-0.0,0.0,2.41,63.6 +2020-08-06 21:00:00,18.65,0.0,-0.0,0.0,2.34,65.65 +2020-08-06 22:00:00,18.06,0.0,-0.0,0.0,2.34,67.95 +2020-08-06 23:00:00,17.57,0.0,-0.0,0.0,2.48,67.85 +2020-08-07 00:00:00,17.16,0.0,-0.0,0.0,2.55,65.45 +2020-08-07 01:00:00,16.87,0.0,-0.0,0.0,2.69,65.35 +2020-08-07 02:00:00,16.79,0.0,-0.0,0.0,2.76,63.05 +2020-08-07 03:00:00,16.72,0.0,-0.0,0.0,2.9,63.05 +2020-08-07 04:00:00,16.71,30.0,178.96,19.0,2.97,63.05 +2020-08-07 05:00:00,17.23,167.0,468.44,65.0,2.83,65.45 +2020-08-07 06:00:00,19.14,333.0,647.12,91.0,2.69,65.75 +2020-08-07 07:00:00,21.3,486.0,712.25,116.0,3.1,61.7 +2020-08-07 08:00:00,23.09,638.0,806.98,118.0,3.24,56.0 +2020-08-07 09:00:00,24.71,736.0,805.26,140.0,3.45,49.05 +2020-08-07 10:00:00,26.16,802.0,817.25,148.0,3.72,44.55 +2020-08-07 11:00:00,27.34,849.0,882.29,125.0,3.79,40.35 +2020-08-07 12:00:00,28.36,829.0,886.47,120.0,3.93,39.1 +2020-08-07 13:00:00,29.07,121.0,0.0,121.0,4.0,36.7 +2020-08-07 14:00:00,29.51,645.0,831.81,110.0,4.07,35.45 +2020-08-07 15:00:00,29.67,484.0,704.62,119.0,4.0,35.6 +2020-08-07 16:00:00,29.32,319.0,580.14,103.0,3.31,39.35 +2020-08-07 17:00:00,28.2,139.0,245.32,86.0,2.76,43.4 +2020-08-07 18:00:00,26.22,25.0,100.29,19.0,2.69,47.75 +2020-08-07 19:00:00,24.79,0.0,-0.0,0.0,3.17,49.05 +2020-08-07 20:00:00,24.13,0.0,-0.0,0.0,3.38,50.7 +2020-08-07 21:00:00,23.76,0.0,-0.0,0.0,3.17,56.15 +2020-08-07 22:00:00,23.0,0.0,-0.0,0.0,2.76,62.05 +2020-08-07 23:00:00,22.04,0.0,-0.0,0.0,2.21,66.35 +2020-08-08 00:00:00,21.19,0.0,-0.0,0.0,1.93,73.35 +2020-08-08 01:00:00,20.32,0.0,-0.0,0.0,1.93,78.45 +2020-08-08 02:00:00,19.16,0.0,-0.0,0.0,1.86,86.8 +2020-08-08 03:00:00,18.67,0.0,-0.0,0.0,1.52,89.75 +2020-08-08 04:00:00,18.34,2.0,0.0,2.0,1.38,92.85 +2020-08-08 05:00:00,18.27,35.0,0.0,35.0,1.31,96.05 +2020-08-08 06:00:00,19.52,256.0,283.16,151.0,1.66,86.85 +2020-08-08 07:00:00,19.47,453.0,606.0,140.0,2.34,86.8 +2020-08-08 08:00:00,20.27,588.0,665.6,161.0,3.31,81.15 +2020-08-08 09:00:00,21.33,640.0,524.85,253.0,3.38,75.95 +2020-08-08 10:00:00,22.55,641.0,387.48,332.0,3.1,68.75 +2020-08-08 11:00:00,23.62,363.0,23.23,344.0,3.45,60.15 +2020-08-08 12:00:00,24.24,722.0,599.88,244.0,3.79,54.35 +2020-08-08 13:00:00,24.49,225.0,0.0,225.0,4.14,50.7 +2020-08-08 14:00:00,23.73,379.0,120.35,302.0,4.34,48.8 +2020-08-08 15:00:00,22.93,484.0,734.81,106.0,4.83,52.1 +2020-08-08 16:00:00,21.94,299.0,499.34,115.0,4.07,57.65 +2020-08-08 17:00:00,21.24,113.0,127.37,86.0,3.31,61.7 +2020-08-08 18:00:00,20.46,20.0,54.01,17.0,2.9,68.3 +2020-08-08 19:00:00,19.8,0.0,-0.0,0.0,3.59,61.4 +2020-08-08 20:00:00,19.23,0.0,-0.0,0.0,3.59,63.5 +2020-08-08 21:00:00,18.57,0.0,-0.0,0.0,3.38,65.65 +2020-08-08 22:00:00,18.12,0.0,-0.0,0.0,3.45,67.95 +2020-08-08 23:00:00,17.71,0.0,-0.0,0.0,3.59,70.3 +2020-08-09 00:00:00,17.31,0.0,-0.0,0.0,3.52,70.3 +2020-08-09 01:00:00,16.78,0.0,-0.0,0.0,3.72,75.3 +2020-08-09 02:00:00,16.22,0.0,-0.0,0.0,3.72,77.95 +2020-08-09 03:00:00,15.35,0.0,-0.0,0.0,4.28,86.5 +2020-08-09 04:00:00,14.97,20.0,55.3,17.0,4.07,86.5 +2020-08-09 05:00:00,14.54,30.0,0.0,30.0,3.86,86.45 +2020-08-09 06:00:00,14.53,169.0,46.24,152.0,3.93,83.45 +2020-08-09 07:00:00,14.82,221.0,25.32,208.0,4.14,86.45 +2020-08-09 08:00:00,15.47,412.0,164.41,307.0,4.48,77.85 +2020-08-09 09:00:00,16.07,452.0,126.61,359.0,4.41,72.65 +2020-08-09 10:00:00,17.22,123.0,0.0,123.0,4.69,60.95 +2020-08-09 11:00:00,17.98,110.0,0.0,110.0,4.97,54.85 +2020-08-09 12:00:00,18.61,203.0,0.0,203.0,5.17,49.3 +2020-08-09 13:00:00,18.91,142.0,0.0,142.0,5.03,47.55 +2020-08-09 14:00:00,19.19,495.0,345.71,275.0,4.83,44.3 +2020-08-09 15:00:00,18.98,372.0,287.82,225.0,4.21,42.7 +2020-08-09 16:00:00,18.95,284.0,411.45,134.0,4.07,44.2 +2020-08-09 17:00:00,18.58,136.0,264.66,81.0,3.38,44.2 +2020-08-09 18:00:00,17.95,19.0,58.62,16.0,2.55,49.05 +2020-08-09 19:00:00,16.65,0.0,-0.0,0.0,1.1,60.85 +2020-08-09 20:00:00,15.31,0.0,-0.0,0.0,1.17,69.9 +2020-08-09 21:00:00,14.04,0.0,-0.0,0.0,1.52,72.3 +2020-08-09 22:00:00,12.75,0.0,-0.0,0.0,1.66,80.3 +2020-08-09 23:00:00,12.27,0.0,-0.0,0.0,1.72,83.15 +2020-08-10 00:00:00,11.87,0.0,-0.0,0.0,1.79,83.1 +2020-08-10 01:00:00,11.35,0.0,-0.0,0.0,1.93,86.1 +2020-08-10 02:00:00,11.33,0.0,-0.0,0.0,2.07,86.1 +2020-08-10 03:00:00,11.25,0.0,-0.0,0.0,2.21,83.05 +2020-08-10 04:00:00,11.28,8.0,0.0,8.0,2.21,83.05 +2020-08-10 05:00:00,12.58,58.0,4.82,57.0,2.21,80.3 +2020-08-10 06:00:00,15.62,144.0,21.95,136.0,2.14,70.0 +2020-08-10 07:00:00,18.2,345.0,229.21,228.0,2.83,70.35 +2020-08-10 08:00:00,19.79,508.0,402.71,252.0,3.17,68.2 +2020-08-10 09:00:00,21.23,619.0,475.65,271.0,3.59,59.6 +2020-08-10 10:00:00,21.93,351.0,22.74,333.0,3.45,55.65 +2020-08-10 11:00:00,22.62,311.0,7.39,305.0,2.9,52.1 +2020-08-10 12:00:00,23.94,272.0,2.53,270.0,3.1,48.8 +2020-08-10 13:00:00,23.88,224.0,0.0,224.0,2.41,50.55 +2020-08-10 14:00:00,23.65,170.0,0.0,170.0,1.72,54.2 +2020-08-10 15:00:00,23.07,160.0,1.97,159.0,1.79,58.0 +2020-08-10 16:00:00,22.07,132.0,13.87,127.0,2.41,59.8 +2020-08-10 17:00:00,21.2,83.0,39.3,75.0,1.45,66.15 +2020-08-10 18:00:00,20.97,12.0,21.4,11.0,0.28,68.4 +2020-08-10 19:00:00,18.63,0.0,-0.0,0.0,1.45,86.75 +2020-08-10 20:00:00,17.89,0.0,-0.0,0.0,1.66,89.7 +2020-08-10 21:00:00,17.19,0.0,-0.0,0.0,1.79,86.65 +2020-08-10 22:00:00,17.29,0.0,-0.0,0.0,1.72,83.7 +2020-08-10 23:00:00,17.75,0.0,-0.0,0.0,2.0,83.7 +2020-08-11 00:00:00,17.34,0.0,-0.0,0.0,1.79,86.65 +2020-08-11 01:00:00,17.08,0.0,-0.0,0.0,1.86,86.65 +2020-08-11 02:00:00,16.74,0.0,-0.0,0.0,1.93,86.6 +2020-08-11 03:00:00,16.26,0.0,-0.0,0.0,2.07,89.65 +2020-08-11 04:00:00,16.3,2.0,0.0,2.0,2.14,89.65 +2020-08-11 05:00:00,16.64,113.0,156.77,81.0,2.07,86.6 +2020-08-11 06:00:00,17.31,199.0,116.29,157.0,1.93,86.65 +2020-08-11 07:00:00,19.57,385.0,362.66,201.0,1.72,78.35 +2020-08-11 08:00:00,21.67,582.0,674.87,155.0,2.28,68.6 +2020-08-11 09:00:00,23.16,631.0,522.86,250.0,2.97,62.15 +2020-08-11 10:00:00,24.17,392.0,45.64,356.0,3.1,58.2 +2020-08-11 11:00:00,25.13,615.0,311.57,363.0,3.03,52.75 +2020-08-11 12:00:00,24.99,231.0,0.0,231.0,2.83,52.6 +2020-08-11 13:00:00,24.8,517.0,247.85,337.0,2.28,54.45 +2020-08-11 14:00:00,25.21,577.0,662.69,160.0,1.66,52.75 +2020-08-11 15:00:00,25.15,200.0,15.9,192.0,1.24,54.55 +2020-08-11 16:00:00,24.86,292.0,521.81,106.0,0.69,56.35 +2020-08-11 17:00:00,23.11,141.0,376.44,66.0,0.55,66.55 +2020-08-11 18:00:00,21.76,15.0,47.42,13.0,0.97,76.0 +2020-08-11 19:00:00,21.68,0.0,-0.0,0.0,1.72,64.0 +2020-08-11 20:00:00,20.46,0.0,-0.0,0.0,1.72,73.2 +2020-08-11 21:00:00,19.22,0.0,-0.0,0.0,1.17,83.85 +2020-08-11 22:00:00,18.61,0.0,-0.0,0.0,0.97,86.75 +2020-08-11 23:00:00,18.19,0.0,-0.0,0.0,0.83,89.75 +2020-08-12 00:00:00,18.42,0.0,-0.0,0.0,0.34,89.75 +2020-08-12 01:00:00,17.25,0.0,-0.0,0.0,0.83,92.85 +2020-08-12 02:00:00,17.22,0.0,-0.0,0.0,1.72,92.85 +2020-08-12 03:00:00,17.42,0.0,-0.0,0.0,2.14,92.85 +2020-08-12 04:00:00,17.25,1.0,0.0,1.0,2.41,89.7 +2020-08-12 05:00:00,17.6,21.0,0.0,21.0,2.48,92.85 +2020-08-12 06:00:00,19.06,251.0,310.14,140.0,2.41,89.8 +2020-08-12 07:00:00,19.67,408.0,468.03,172.0,3.17,83.9 +2020-08-12 08:00:00,21.32,509.0,436.72,234.0,3.93,75.95 +2020-08-12 09:00:00,23.08,528.0,278.35,326.0,4.83,64.3 +2020-08-12 10:00:00,24.33,258.0,1.27,257.0,5.1,58.2 +2020-08-12 11:00:00,25.45,133.0,0.0,133.0,4.69,54.55 +2020-08-12 12:00:00,24.8,140.0,0.0,140.0,3.86,60.35 +2020-08-12 13:00:00,23.81,117.0,0.0,117.0,2.62,71.35 +2020-08-12 14:00:00,23.59,65.0,0.0,65.0,2.14,73.75 +2020-08-12 15:00:00,23.67,83.0,0.0,83.0,3.03,71.35 +2020-08-12 16:00:00,22.61,54.0,0.0,54.0,2.9,76.15 +2020-08-12 17:00:00,21.8,12.0,0.0,12.0,3.52,78.65 +2020-08-12 18:00:00,21.27,10.0,0.0,10.0,4.07,75.95 +2020-08-12 19:00:00,20.22,0.0,-0.0,0.0,2.55,81.15 +2020-08-12 20:00:00,19.75,0.0,-0.0,0.0,1.93,81.1 +2020-08-12 21:00:00,19.45,0.0,-0.0,0.0,1.86,83.85 +2020-08-12 22:00:00,19.24,0.0,-0.0,0.0,2.76,78.3 +2020-08-12 23:00:00,18.6,0.0,-0.0,0.0,3.31,78.25 +2020-08-13 00:00:00,17.63,0.0,-0.0,0.0,3.1,83.7 +2020-08-13 01:00:00,17.34,0.0,-0.0,0.0,2.76,83.7 +2020-08-13 02:00:00,17.04,0.0,-0.0,0.0,2.97,83.7 +2020-08-13 03:00:00,16.77,0.0,-0.0,0.0,3.31,80.8 +2020-08-13 04:00:00,16.6,12.0,0.0,12.0,3.52,78.0 +2020-08-13 05:00:00,16.52,13.0,0.0,13.0,3.66,75.3 +2020-08-13 06:00:00,16.82,138.0,19.74,131.0,3.59,78.0 +2020-08-13 07:00:00,17.79,275.0,91.8,229.0,3.38,78.1 +2020-08-13 08:00:00,19.62,629.0,836.22,105.0,4.21,65.85 +2020-08-13 09:00:00,20.83,540.0,290.59,330.0,4.69,55.45 +2020-08-13 10:00:00,21.51,704.0,563.45,263.0,4.83,50.05 +2020-08-13 11:00:00,21.89,566.0,220.55,389.0,5.17,48.3 +2020-08-13 12:00:00,22.63,767.0,767.06,168.0,5.24,45.25 +2020-08-13 13:00:00,22.7,594.0,421.26,291.0,4.9,43.65 +2020-08-13 14:00:00,23.19,532.0,496.94,223.0,5.17,40.75 +2020-08-13 15:00:00,23.22,420.0,512.95,166.0,4.62,39.3 +2020-08-13 16:00:00,22.99,299.0,589.09,94.0,3.79,42.1 +2020-08-13 17:00:00,22.35,133.0,346.81,67.0,3.1,45.1 +2020-08-13 18:00:00,20.79,9.0,0.0,9.0,2.28,48.05 +2020-08-13 19:00:00,19.24,0.0,-0.0,0.0,2.0,59.15 +2020-08-13 20:00:00,17.95,0.0,-0.0,0.0,2.0,65.45 +2020-08-13 21:00:00,16.76,0.0,-0.0,0.0,2.14,67.75 +2020-08-13 22:00:00,15.86,0.0,-0.0,0.0,2.28,72.55 +2020-08-13 23:00:00,15.01,0.0,-0.0,0.0,2.21,75.1 +2020-08-14 00:00:00,14.38,0.0,-0.0,0.0,2.34,77.65 +2020-08-14 01:00:00,14.05,0.0,-0.0,0.0,2.48,77.65 +2020-08-14 02:00:00,13.87,0.0,-0.0,0.0,2.55,77.6 +2020-08-14 03:00:00,13.67,0.0,-0.0,0.0,2.55,77.6 +2020-08-14 04:00:00,13.44,9.0,0.0,9.0,2.48,77.6 +2020-08-14 05:00:00,14.03,132.0,330.59,68.0,2.41,77.65 +2020-08-14 06:00:00,16.74,114.0,5.69,112.0,2.21,70.2 +2020-08-14 07:00:00,18.81,272.0,90.38,227.0,2.83,70.45 +2020-08-14 08:00:00,20.06,588.0,697.65,153.0,2.9,61.5 +2020-08-14 09:00:00,21.13,556.0,325.18,322.0,2.83,55.55 +2020-08-14 10:00:00,22.13,566.0,248.86,372.0,2.83,50.2 +2020-08-14 11:00:00,22.9,667.0,421.62,330.0,3.24,46.85 +2020-08-14 12:00:00,23.73,626.0,370.41,338.0,3.59,42.35 +2020-08-14 13:00:00,24.11,654.0,598.05,226.0,3.03,41.0 +2020-08-14 14:00:00,23.66,422.0,210.37,292.0,2.21,43.9 +2020-08-14 15:00:00,23.45,284.0,112.0,229.0,1.66,47.0 +2020-08-14 16:00:00,23.01,93.0,0.0,93.0,1.03,54.1 +2020-08-14 17:00:00,22.26,77.0,43.08,69.0,0.69,66.35 +2020-08-14 18:00:00,21.27,8.0,0.0,8.0,1.24,63.9 +2020-08-14 19:00:00,20.03,0.0,-0.0,0.0,1.59,61.5 +2020-08-14 20:00:00,18.88,0.0,-0.0,0.0,1.59,68.05 +2020-08-14 21:00:00,17.93,0.0,-0.0,0.0,1.59,70.3 +2020-08-14 22:00:00,17.25,0.0,-0.0,0.0,1.72,67.85 +2020-08-14 23:00:00,16.98,0.0,-0.0,0.0,1.79,67.85 +2020-08-15 00:00:00,16.57,0.0,-0.0,0.0,1.59,72.7 +2020-08-15 01:00:00,16.0,0.0,-0.0,0.0,1.66,77.95 +2020-08-15 02:00:00,15.35,0.0,-0.0,0.0,2.21,86.5 +2020-08-15 03:00:00,14.71,0.0,-0.0,0.0,2.83,89.5 +2020-08-15 04:00:00,14.24,1.0,0.0,1.0,3.17,89.5 +2020-08-15 05:00:00,14.04,17.0,0.0,17.0,3.17,89.5 +2020-08-15 06:00:00,13.93,32.0,0.0,32.0,3.17,89.5 +2020-08-15 07:00:00,14.19,62.0,0.0,62.0,3.24,92.7 +2020-08-15 08:00:00,14.27,68.0,0.0,68.0,3.93,92.7 +2020-08-15 09:00:00,14.71,102.0,0.0,102.0,4.34,92.7 +2020-08-15 10:00:00,14.32,96.0,0.0,96.0,4.9,92.7 +2020-08-15 11:00:00,14.24,108.0,0.0,108.0,4.34,92.7 +2020-08-15 12:00:00,14.12,91.0,0.0,91.0,4.28,92.7 +2020-08-15 13:00:00,13.98,87.0,0.0,87.0,4.41,92.7 +2020-08-15 14:00:00,13.74,88.0,0.0,88.0,4.55,92.65 +2020-08-15 15:00:00,13.65,50.0,0.0,50.0,4.62,92.65 +2020-08-15 16:00:00,13.56,41.0,0.0,41.0,4.76,89.45 +2020-08-15 17:00:00,13.32,25.0,0.0,25.0,4.28,92.65 +2020-08-15 18:00:00,13.05,2.0,0.0,2.0,3.59,89.4 +2020-08-15 19:00:00,12.69,0.0,-0.0,0.0,3.59,92.6 +2020-08-15 20:00:00,12.48,0.0,-0.0,0.0,3.24,92.6 +2020-08-15 21:00:00,12.25,0.0,-0.0,0.0,3.03,92.6 +2020-08-15 22:00:00,12.1,0.0,-0.0,0.0,2.76,92.6 +2020-08-15 23:00:00,11.9,0.0,-0.0,0.0,2.69,92.6 +2020-08-16 00:00:00,11.64,0.0,-0.0,0.0,2.62,95.9 +2020-08-16 01:00:00,11.57,0.0,-0.0,0.0,2.34,99.4 +2020-08-16 02:00:00,11.49,0.0,-0.0,0.0,2.07,99.4 +2020-08-16 03:00:00,11.48,0.0,-0.0,0.0,2.21,99.4 +2020-08-16 04:00:00,11.63,1.0,0.0,1.0,2.41,99.4 +2020-08-16 05:00:00,11.87,71.0,32.18,65.0,2.9,99.4 +2020-08-16 06:00:00,12.26,108.0,2.9,107.0,3.52,99.4 +2020-08-16 07:00:00,12.71,89.0,0.0,89.0,2.55,99.4 +2020-08-16 08:00:00,13.15,95.0,0.0,95.0,2.69,99.4 +2020-08-16 09:00:00,13.56,138.0,0.0,138.0,3.79,95.95 +2020-08-16 10:00:00,14.09,162.0,0.0,162.0,3.31,92.7 +2020-08-16 11:00:00,14.46,91.0,0.0,91.0,3.17,89.5 +2020-08-16 12:00:00,14.42,69.0,0.0,69.0,2.97,92.7 +2020-08-16 13:00:00,14.63,86.0,0.0,86.0,2.9,92.7 +2020-08-16 14:00:00,14.81,97.0,0.0,97.0,2.76,89.5 +2020-08-16 15:00:00,14.97,109.0,0.0,109.0,2.83,86.5 +2020-08-16 16:00:00,14.87,168.0,74.7,143.0,2.69,89.5 +2020-08-16 17:00:00,14.67,95.0,141.81,70.0,2.41,89.5 +2020-08-16 18:00:00,14.43,0.0,0.0,0.0,2.21,89.5 +2020-08-16 19:00:00,13.93,0.0,-0.0,0.0,2.48,89.5 +2020-08-16 20:00:00,13.74,0.0,-0.0,0.0,2.28,92.65 +2020-08-16 21:00:00,13.45,0.0,-0.0,0.0,2.21,92.65 +2020-08-16 22:00:00,13.28,0.0,-0.0,0.0,2.21,95.95 +2020-08-16 23:00:00,13.05,0.0,-0.0,0.0,2.21,92.65 +2020-08-17 00:00:00,12.33,0.0,-0.0,0.0,2.28,92.6 +2020-08-17 01:00:00,12.06,0.0,-0.0,0.0,2.28,89.35 +2020-08-17 02:00:00,11.71,0.0,-0.0,0.0,2.41,92.55 +2020-08-17 03:00:00,11.42,0.0,-0.0,0.0,2.41,89.3 +2020-08-17 04:00:00,11.13,1.0,0.0,1.0,2.34,92.55 +2020-08-17 05:00:00,11.67,65.0,21.88,61.0,2.28,89.3 +2020-08-17 06:00:00,13.39,189.0,114.36,150.0,2.0,89.4 +2020-08-17 07:00:00,15.46,455.0,702.73,112.0,2.83,83.55 +2020-08-17 08:00:00,16.97,599.0,770.41,126.0,3.24,75.4 +2020-08-17 09:00:00,18.33,696.0,764.72,153.0,3.72,65.55 +2020-08-17 10:00:00,19.28,757.0,757.3,174.0,3.66,57.1 +2020-08-17 11:00:00,19.79,784.0,779.24,169.0,3.31,53.25 +2020-08-17 12:00:00,20.3,719.0,640.24,228.0,3.03,51.5 +2020-08-17 13:00:00,20.71,672.0,692.8,184.0,2.97,48.05 +2020-08-17 14:00:00,20.91,563.0,656.83,165.0,2.9,46.35 +2020-08-17 15:00:00,20.71,403.0,495.54,166.0,2.48,46.35 +2020-08-17 16:00:00,20.42,265.0,463.53,112.0,1.79,51.5 +2020-08-17 17:00:00,19.56,64.0,23.33,60.0,1.24,63.6 +2020-08-17 18:00:00,18.95,0.0,0.0,0.0,0.83,63.4 +2020-08-17 19:00:00,17.79,0.0,-0.0,0.0,1.24,67.85 +2020-08-17 20:00:00,15.83,0.0,-0.0,0.0,1.86,75.15 +2020-08-17 21:00:00,14.66,0.0,-0.0,0.0,1.86,80.55 +2020-08-17 22:00:00,13.85,0.0,-0.0,0.0,1.93,83.35 +2020-08-17 23:00:00,13.42,0.0,-0.0,0.0,2.0,83.35 +2020-08-18 00:00:00,12.79,0.0,-0.0,0.0,2.14,86.25 +2020-08-18 01:00:00,12.5,0.0,-0.0,0.0,2.14,86.25 +2020-08-18 02:00:00,12.47,0.0,-0.0,0.0,2.28,83.25 +2020-08-18 03:00:00,12.42,0.0,-0.0,0.0,2.34,83.25 +2020-08-18 04:00:00,12.04,0.0,0.0,0.0,2.48,83.15 +2020-08-18 05:00:00,12.57,91.0,117.21,70.0,2.41,80.3 +2020-08-18 06:00:00,15.58,210.0,189.59,146.0,2.21,75.15 +2020-08-18 07:00:00,18.49,389.0,433.2,179.0,3.03,70.45 +2020-08-18 08:00:00,20.15,498.0,429.02,236.0,3.17,68.3 +2020-08-18 09:00:00,21.64,629.0,561.7,232.0,3.17,61.85 +2020-08-18 10:00:00,22.46,716.0,652.32,216.0,4.0,57.75 +2020-08-18 11:00:00,22.92,562.0,237.99,375.0,4.14,54.0 +2020-08-18 12:00:00,23.3,642.0,448.1,300.0,4.0,52.25 +2020-08-18 13:00:00,23.69,264.0,7.14,259.0,3.86,48.8 +2020-08-18 14:00:00,24.0,575.0,742.8,128.0,3.45,50.55 +2020-08-18 15:00:00,23.65,451.0,744.97,98.0,2.48,54.2 +2020-08-18 16:00:00,23.55,272.0,556.24,91.0,2.28,56.15 +2020-08-18 17:00:00,22.62,51.0,12.01,49.0,1.79,59.95 +2020-08-18 18:00:00,20.99,0.0,0.0,0.0,1.86,66.15 +2020-08-18 19:00:00,19.11,0.0,-0.0,0.0,2.0,73.05 +2020-08-18 20:00:00,17.75,0.0,-0.0,0.0,2.07,80.85 +2020-08-18 21:00:00,16.76,0.0,-0.0,0.0,2.14,80.8 +2020-08-18 22:00:00,16.36,0.0,-0.0,0.0,2.14,83.6 +2020-08-18 23:00:00,15.8,0.0,-0.0,0.0,2.28,83.55 +2020-08-19 00:00:00,15.6,0.0,-0.0,0.0,2.34,83.55 +2020-08-19 01:00:00,15.61,0.0,-0.0,0.0,2.41,80.65 +2020-08-19 02:00:00,15.57,0.0,-0.0,0.0,2.48,80.65 +2020-08-19 03:00:00,15.65,0.0,-0.0,0.0,2.62,77.85 +2020-08-19 04:00:00,15.89,0.0,0.0,0.0,2.69,77.85 +2020-08-19 05:00:00,16.37,38.0,0.0,38.0,2.69,77.95 +2020-08-19 06:00:00,18.24,98.0,2.99,97.0,2.55,75.45 +2020-08-19 07:00:00,20.88,420.0,608.65,127.0,2.9,70.8 +2020-08-19 08:00:00,22.84,461.0,339.16,255.0,3.38,64.2 +2020-08-19 09:00:00,24.33,451.0,163.48,336.0,3.59,60.25 +2020-08-19 10:00:00,25.61,659.0,509.77,270.0,3.72,56.6 +2020-08-19 11:00:00,26.56,647.0,432.12,309.0,3.79,54.95 +2020-08-19 12:00:00,27.34,610.0,389.74,314.0,3.66,51.45 +2020-08-19 13:00:00,27.53,139.0,0.0,139.0,3.24,53.2 +2020-08-19 14:00:00,27.53,68.0,0.0,68.0,2.83,53.2 +2020-08-19 15:00:00,27.11,205.0,31.96,190.0,2.48,53.2 +2020-08-19 16:00:00,24.97,274.0,592.57,84.0,1.45,71.5 +2020-08-19 17:00:00,23.84,24.0,0.0,24.0,0.62,76.3 +2020-08-19 18:00:00,21.91,0.0,0.0,0.0,1.66,84.1 +2020-08-19 19:00:00,20.33,0.0,-0.0,0.0,1.52,92.95 +2020-08-19 20:00:00,19.35,0.0,-0.0,0.0,0.97,96.1 +2020-08-19 21:00:00,18.75,0.0,-0.0,0.0,0.48,99.4 +2020-08-19 22:00:00,18.55,0.0,-0.0,0.0,0.28,96.1 +2020-08-19 23:00:00,17.36,0.0,-0.0,0.0,1.03,96.05 +2020-08-20 00:00:00,17.84,0.0,-0.0,0.0,1.72,99.4 +2020-08-20 01:00:00,18.06,0.0,-0.0,0.0,2.55,92.85 +2020-08-20 02:00:00,17.46,0.0,-0.0,0.0,2.97,89.7 +2020-08-20 03:00:00,16.74,0.0,-0.0,0.0,2.97,89.65 +2020-08-20 04:00:00,16.06,0.0,0.0,0.0,2.9,89.65 +2020-08-20 05:00:00,15.75,24.0,0.0,24.0,2.97,92.75 +2020-08-20 06:00:00,16.46,267.0,502.17,101.0,2.62,86.6 +2020-08-20 07:00:00,17.55,423.0,612.98,130.0,3.17,83.7 +2020-08-20 08:00:00,19.07,560.0,677.12,151.0,3.79,73.05 +2020-08-20 09:00:00,20.25,579.0,432.8,276.0,4.62,63.7 +2020-08-20 10:00:00,20.98,513.0,190.88,368.0,4.69,59.5 +2020-08-20 11:00:00,21.49,597.0,313.39,353.0,4.83,55.65 +2020-08-20 12:00:00,21.66,102.0,0.0,102.0,4.9,53.75 +2020-08-20 13:00:00,21.55,198.0,0.0,198.0,4.69,53.75 +2020-08-20 14:00:00,21.51,217.0,6.74,213.0,4.34,53.75 +2020-08-20 15:00:00,21.33,418.0,647.66,117.0,3.93,55.55 +2020-08-20 16:00:00,21.0,196.0,186.83,137.0,3.59,55.55 +2020-08-20 17:00:00,20.36,92.0,198.07,61.0,2.97,59.35 +2020-08-20 18:00:00,19.45,0.0,-0.0,0.0,2.83,61.3 +2020-08-20 19:00:00,18.51,0.0,-0.0,0.0,2.28,63.4 +2020-08-20 20:00:00,17.58,0.0,-0.0,0.0,2.41,67.85 +2020-08-20 21:00:00,17.18,0.0,-0.0,0.0,2.55,67.85 +2020-08-20 22:00:00,16.91,0.0,-0.0,0.0,2.76,70.2 +2020-08-20 23:00:00,16.81,0.0,-0.0,0.0,2.97,70.2 +2020-08-21 00:00:00,16.35,0.0,-0.0,0.0,2.9,72.65 +2020-08-21 01:00:00,16.09,0.0,-0.0,0.0,2.9,72.65 +2020-08-21 02:00:00,15.59,0.0,-0.0,0.0,2.83,75.15 +2020-08-21 03:00:00,15.31,0.0,-0.0,0.0,2.69,77.8 +2020-08-21 04:00:00,14.87,0.0,0.0,0.0,2.41,80.55 +2020-08-21 05:00:00,14.75,115.0,351.02,56.0,2.21,83.45 +2020-08-21 06:00:00,15.85,282.0,599.36,86.0,2.07,80.65 +2020-08-21 07:00:00,17.1,431.0,659.57,118.0,2.83,75.4 +2020-08-21 08:00:00,18.31,572.0,727.56,135.0,3.1,70.35 +2020-08-21 09:00:00,19.36,589.0,457.89,270.0,3.17,63.5 +2020-08-21 10:00:00,20.21,660.0,503.89,279.0,3.1,59.35 +2020-08-21 11:00:00,20.7,741.0,690.4,206.0,3.03,55.45 +2020-08-21 12:00:00,21.19,633.0,441.61,301.0,2.62,53.6 +2020-08-21 13:00:00,21.61,659.0,711.88,169.0,2.62,51.9 +2020-08-21 14:00:00,21.77,526.0,579.12,185.0,2.55,50.05 +2020-08-21 15:00:00,21.65,368.0,417.31,176.0,2.0,51.9 +2020-08-21 16:00:00,21.39,144.0,51.47,128.0,1.45,57.55 +2020-08-21 17:00:00,20.42,82.0,145.33,60.0,0.83,75.75 +2020-08-21 18:00:00,20.2,0.0,-0.0,0.0,0.76,65.95 +2020-08-21 19:00:00,20.32,0.0,-0.0,0.0,0.62,59.35 +2020-08-21 20:00:00,17.62,0.0,-0.0,0.0,1.31,75.4 +2020-08-21 21:00:00,15.98,0.0,-0.0,0.0,1.59,77.95 +2020-08-21 22:00:00,14.69,0.0,-0.0,0.0,1.72,86.45 +2020-08-21 23:00:00,13.78,0.0,-0.0,0.0,1.86,89.45 +2020-08-22 00:00:00,13.18,0.0,-0.0,0.0,1.93,89.4 +2020-08-22 01:00:00,13.03,0.0,-0.0,0.0,1.93,86.3 +2020-08-22 02:00:00,12.75,0.0,-0.0,0.0,2.0,86.25 +2020-08-22 03:00:00,12.51,0.0,-0.0,0.0,1.93,86.25 +2020-08-22 04:00:00,12.17,0.0,0.0,0.0,1.86,86.2 +2020-08-22 05:00:00,12.27,52.0,12.17,50.0,1.66,89.35 +2020-08-22 06:00:00,15.52,258.0,479.22,103.0,1.17,83.55 +2020-08-22 07:00:00,18.6,383.0,458.52,167.0,0.97,75.55 +2020-08-22 08:00:00,20.12,378.0,157.4,284.0,1.03,65.95 +2020-08-22 09:00:00,21.33,609.0,536.64,237.0,1.45,63.9 +2020-08-22 10:00:00,22.27,648.0,487.68,281.0,1.72,57.75 +2020-08-22 11:00:00,22.91,759.0,775.44,161.0,1.66,55.9 +2020-08-22 12:00:00,23.4,713.0,696.67,192.0,1.72,52.25 +2020-08-22 13:00:00,23.89,660.0,744.02,151.0,2.14,48.8 +2020-08-22 14:00:00,23.97,505.0,527.07,197.0,2.28,47.15 +2020-08-22 15:00:00,23.95,287.0,171.3,209.0,2.28,47.15 +2020-08-22 16:00:00,23.39,52.0,0.0,52.0,1.66,54.1 +2020-08-22 17:00:00,22.43,64.0,61.57,55.0,1.66,61.95 +2020-08-22 18:00:00,20.95,0.0,-0.0,0.0,1.79,66.05 +2020-08-22 19:00:00,19.59,0.0,-0.0,0.0,1.93,73.15 +2020-08-22 20:00:00,18.2,0.0,-0.0,0.0,1.93,80.9 +2020-08-22 21:00:00,17.23,0.0,-0.0,0.0,1.86,86.65 +2020-08-22 22:00:00,16.89,0.0,-0.0,0.0,1.59,89.65 +2020-08-22 23:00:00,16.56,0.0,-0.0,0.0,1.31,89.65 +2020-08-23 00:00:00,16.49,0.0,-0.0,0.0,1.66,89.65 +2020-08-23 01:00:00,16.53,0.0,-0.0,0.0,1.93,86.6 +2020-08-23 02:00:00,16.45,0.0,-0.0,0.0,2.48,86.6 +2020-08-23 03:00:00,15.89,0.0,-0.0,0.0,3.03,92.75 +2020-08-23 04:00:00,15.62,0.0,0.0,0.0,3.31,89.6 +2020-08-23 05:00:00,15.5,35.0,0.0,35.0,4.07,89.6 +2020-08-23 06:00:00,15.09,87.0,0.0,87.0,4.62,89.55 +2020-08-23 07:00:00,14.78,376.0,434.15,173.0,5.1,86.45 +2020-08-23 08:00:00,15.09,351.0,116.22,282.0,4.9,83.5 +2020-08-23 09:00:00,15.78,641.0,648.12,194.0,4.97,80.65 +2020-08-23 10:00:00,16.95,610.0,391.23,317.0,4.97,67.85 +2020-08-23 11:00:00,17.77,676.0,517.34,279.0,4.9,63.2 +2020-08-23 12:00:00,18.26,668.0,557.93,253.0,4.55,58.9 +2020-08-23 13:00:00,18.68,660.0,748.7,151.0,4.28,54.95 +2020-08-23 14:00:00,18.96,484.0,457.04,219.0,4.14,53.0 +2020-08-23 15:00:00,18.94,322.0,277.44,197.0,4.0,51.15 +2020-08-23 16:00:00,18.52,237.0,452.07,101.0,3.45,49.3 +2020-08-23 17:00:00,17.89,87.0,241.3,53.0,2.48,52.75 +2020-08-23 18:00:00,16.59,0.0,-0.0,0.0,1.93,52.65 +2020-08-23 19:00:00,14.82,0.0,-0.0,0.0,2.0,62.65 +2020-08-23 20:00:00,13.44,0.0,-0.0,0.0,1.93,69.65 +2020-08-23 21:00:00,12.41,0.0,-0.0,0.0,2.0,72.05 +2020-08-23 22:00:00,11.88,0.0,-0.0,0.0,2.14,77.3 +2020-08-23 23:00:00,12.13,0.0,-0.0,0.0,2.34,74.6 +2020-08-24 00:00:00,11.98,0.0,-0.0,0.0,2.41,74.6 +2020-08-24 01:00:00,11.59,0.0,-0.0,0.0,2.48,77.3 +2020-08-24 02:00:00,11.69,0.0,-0.0,0.0,2.76,77.3 +2020-08-24 03:00:00,11.93,0.0,-0.0,0.0,3.1,74.6 +2020-08-24 04:00:00,12.34,0.0,-0.0,0.0,3.59,77.35 +2020-08-24 05:00:00,12.44,10.0,0.0,10.0,3.59,80.3 +2020-08-24 06:00:00,12.48,53.0,0.0,53.0,3.52,86.25 +2020-08-24 07:00:00,12.57,94.0,0.0,94.0,5.31,89.4 +2020-08-24 08:00:00,12.87,140.0,0.0,140.0,4.97,92.6 +2020-08-24 09:00:00,13.69,153.0,0.0,153.0,6.14,83.35 +2020-08-24 10:00:00,14.26,246.0,1.34,245.0,6.21,80.45 +2020-08-24 11:00:00,14.9,566.0,268.49,361.0,4.76,77.75 +2020-08-24 12:00:00,15.66,216.0,0.0,216.0,5.1,67.55 +2020-08-24 13:00:00,16.62,432.0,152.48,329.0,4.0,63.05 +2020-08-24 14:00:00,17.56,432.0,307.72,255.0,4.62,58.8 +2020-08-24 15:00:00,17.82,352.0,403.9,172.0,4.48,58.8 +2020-08-24 16:00:00,17.47,220.0,368.61,111.0,4.14,60.95 +2020-08-24 17:00:00,17.16,90.0,317.19,47.0,3.86,58.8 +2020-08-24 18:00:00,16.43,0.0,-0.0,0.0,3.31,62.95 +2020-08-24 19:00:00,15.29,0.0,-0.0,0.0,2.41,69.9 +2020-08-24 20:00:00,14.62,0.0,-0.0,0.0,2.14,72.4 +2020-08-24 21:00:00,13.77,0.0,-0.0,0.0,2.0,77.6 +2020-08-24 22:00:00,12.81,0.0,-0.0,0.0,1.86,83.25 +2020-08-24 23:00:00,11.69,0.0,-0.0,0.0,1.86,86.15 +2020-08-25 00:00:00,10.82,0.0,-0.0,0.0,1.86,89.25 +2020-08-25 01:00:00,10.42,0.0,-0.0,0.0,1.79,86.05 +2020-08-25 02:00:00,10.1,0.0,-0.0,0.0,1.72,89.2 +2020-08-25 03:00:00,9.83,0.0,-0.0,0.0,1.66,92.45 +2020-08-25 04:00:00,9.54,0.0,-0.0,0.0,1.66,92.45 +2020-08-25 05:00:00,9.92,62.0,45.76,55.0,1.52,92.5 +2020-08-25 06:00:00,12.52,204.0,233.56,131.0,1.24,89.4 +2020-08-25 07:00:00,16.13,368.0,427.83,171.0,1.24,80.75 +2020-08-25 08:00:00,17.07,385.0,185.8,276.0,1.38,78.1 +2020-08-25 09:00:00,17.43,253.0,5.86,249.0,1.45,75.4 +2020-08-25 10:00:00,17.49,215.0,0.0,215.0,1.31,75.4 +2020-08-25 11:00:00,17.59,279.0,5.27,275.0,1.45,75.4 +2020-08-25 12:00:00,17.68,433.0,103.32,357.0,1.72,75.4 +2020-08-25 13:00:00,17.2,338.0,50.66,304.0,1.17,78.1 +2020-08-25 14:00:00,16.91,202.0,5.26,199.0,1.24,86.6 +2020-08-25 15:00:00,17.27,146.0,4.54,144.0,1.24,83.7 +2020-08-25 16:00:00,17.36,116.0,24.1,109.0,1.24,83.7 +2020-08-25 17:00:00,17.1,59.0,76.83,49.0,1.24,83.7 +2020-08-25 18:00:00,16.49,0.0,-0.0,0.0,1.45,83.65 +2020-08-25 19:00:00,15.89,0.0,-0.0,0.0,1.24,89.6 +2020-08-25 20:00:00,14.14,0.0,-0.0,0.0,1.66,92.7 +2020-08-25 21:00:00,13.29,0.0,-0.0,0.0,1.79,92.65 +2020-08-25 22:00:00,13.1,0.0,-0.0,0.0,1.86,92.65 +2020-08-25 23:00:00,13.54,0.0,-0.0,0.0,2.07,89.45 +2020-08-26 00:00:00,13.76,0.0,-0.0,0.0,2.28,89.45 +2020-08-26 01:00:00,14.07,0.0,-0.0,0.0,2.28,89.5 +2020-08-26 02:00:00,14.23,0.0,-0.0,0.0,2.14,92.7 +2020-08-26 03:00:00,14.41,0.0,-0.0,0.0,2.21,92.7 +2020-08-26 04:00:00,14.54,0.0,-0.0,0.0,2.21,92.7 +2020-08-26 05:00:00,14.52,43.0,6.7,42.0,2.21,92.7 +2020-08-26 06:00:00,15.44,174.0,132.74,133.0,2.07,89.6 +2020-08-26 07:00:00,16.35,343.0,345.84,185.0,2.21,89.65 +2020-08-26 08:00:00,17.79,444.0,334.44,249.0,2.55,83.7 +2020-08-26 09:00:00,18.46,528.0,346.18,293.0,2.48,80.9 +2020-08-26 10:00:00,19.69,538.0,261.62,345.0,3.1,68.2 +2020-08-26 11:00:00,20.36,589.0,334.82,336.0,3.17,63.7 +2020-08-26 12:00:00,21.05,575.0,348.66,320.0,2.97,57.55 +2020-08-26 13:00:00,21.19,579.0,519.04,233.0,2.62,57.55 +2020-08-26 14:00:00,21.35,424.0,316.39,245.0,2.41,57.55 +2020-08-26 15:00:00,21.41,345.0,417.74,163.0,2.14,57.55 +2020-08-26 16:00:00,21.28,217.0,413.69,99.0,1.66,59.6 +2020-08-26 17:00:00,20.24,68.0,168.42,47.0,0.97,73.2 +2020-08-26 18:00:00,20.53,0.0,-0.0,0.0,0.28,61.6 +2020-08-26 19:00:00,17.34,0.0,-0.0,0.0,1.31,80.85 +2020-08-26 20:00:00,16.59,0.0,-0.0,0.0,1.31,80.8 +2020-08-26 21:00:00,15.62,0.0,-0.0,0.0,1.52,83.55 +2020-08-26 22:00:00,14.7,0.0,-0.0,0.0,1.66,89.5 +2020-08-26 23:00:00,14.19,0.0,-0.0,0.0,1.66,89.5 +2020-08-27 00:00:00,14.15,0.0,-0.0,0.0,1.52,92.7 +2020-08-27 01:00:00,13.98,0.0,-0.0,0.0,1.52,92.7 +2020-08-27 02:00:00,14.01,0.0,-0.0,0.0,1.45,92.7 +2020-08-27 03:00:00,14.09,0.0,-0.0,0.0,1.45,92.7 +2020-08-27 04:00:00,14.0,0.0,-0.0,0.0,1.38,92.7 +2020-08-27 05:00:00,14.3,42.0,6.88,41.0,1.17,96.0 +2020-08-27 06:00:00,16.28,153.0,78.65,129.0,0.76,89.65 +2020-08-27 07:00:00,18.62,259.0,116.94,206.0,1.1,81.0 +2020-08-27 08:00:00,19.84,287.0,51.78,257.0,1.72,73.15 +2020-08-27 09:00:00,20.81,480.0,245.88,314.0,2.48,68.4 +2020-08-27 10:00:00,21.35,545.0,282.07,338.0,2.62,66.15 +2020-08-27 11:00:00,21.88,718.0,707.84,186.0,2.55,61.85 +2020-08-27 12:00:00,22.63,711.0,771.56,150.0,2.69,57.9 +2020-08-27 13:00:00,23.25,661.0,821.67,117.0,2.76,54.1 +2020-08-27 14:00:00,23.77,529.0,720.23,125.0,2.69,52.35 +2020-08-27 15:00:00,23.78,375.0,587.57,122.0,2.34,52.35 +2020-08-27 16:00:00,23.2,220.0,464.44,90.0,1.72,58.0 +2020-08-27 17:00:00,22.16,69.0,209.8,44.0,1.31,66.35 +2020-08-27 18:00:00,22.16,0.0,-0.0,0.0,0.55,59.8 +2020-08-27 19:00:00,21.24,0.0,-0.0,0.0,0.69,63.9 +2020-08-27 20:00:00,17.3,0.0,-0.0,0.0,1.79,83.7 +2020-08-27 21:00:00,16.04,0.0,-0.0,0.0,2.0,86.55 +2020-08-27 22:00:00,15.17,0.0,-0.0,0.0,1.86,89.55 +2020-08-27 23:00:00,14.85,0.0,-0.0,0.0,1.72,92.7 +2020-08-28 00:00:00,15.26,0.0,-0.0,0.0,2.21,89.55 +2020-08-28 01:00:00,14.88,0.0,-0.0,0.0,2.0,92.7 +2020-08-28 02:00:00,14.44,0.0,-0.0,0.0,1.93,92.7 +2020-08-28 03:00:00,14.22,0.0,-0.0,0.0,1.66,96.0 +2020-08-28 04:00:00,14.53,0.0,-0.0,0.0,1.66,96.0 +2020-08-28 05:00:00,14.63,17.0,0.0,17.0,1.52,96.0 +2020-08-28 06:00:00,15.68,152.0,86.26,126.0,2.07,92.75 +2020-08-28 07:00:00,16.71,81.0,0.0,81.0,2.55,86.6 +2020-08-28 08:00:00,17.63,90.0,0.0,90.0,2.97,83.7 +2020-08-28 09:00:00,18.14,161.0,0.0,161.0,3.1,80.9 +2020-08-28 10:00:00,18.26,190.0,0.0,190.0,3.24,80.9 +2020-08-28 11:00:00,18.95,137.0,0.0,137.0,3.52,78.25 +2020-08-28 12:00:00,19.19,223.0,0.0,223.0,3.72,75.65 +2020-08-28 13:00:00,19.17,301.0,30.42,281.0,3.66,75.65 +2020-08-28 14:00:00,19.49,316.0,102.51,259.0,4.0,73.15 +2020-08-28 15:00:00,19.31,120.0,0.0,120.0,3.38,75.65 +2020-08-28 16:00:00,19.15,111.0,29.14,103.0,3.03,73.05 +2020-08-28 17:00:00,18.76,47.0,61.64,40.0,2.55,75.55 +2020-08-28 18:00:00,18.05,0.0,-0.0,0.0,2.14,75.45 +2020-08-28 19:00:00,17.1,0.0,-0.0,0.0,2.34,75.4 +2020-08-28 20:00:00,16.76,0.0,-0.0,0.0,2.34,78.0 +2020-08-28 21:00:00,16.44,0.0,-0.0,0.0,2.34,78.0 +2020-08-28 22:00:00,15.98,0.0,-0.0,0.0,2.41,80.75 +2020-08-28 23:00:00,15.92,0.0,-0.0,0.0,2.41,83.55 +2020-08-29 00:00:00,15.92,0.0,-0.0,0.0,2.62,83.55 +2020-08-29 01:00:00,15.99,0.0,-0.0,0.0,2.83,80.75 +2020-08-29 02:00:00,15.79,0.0,-0.0,0.0,2.9,83.55 +2020-08-29 03:00:00,15.72,0.0,-0.0,0.0,3.03,83.55 +2020-08-29 04:00:00,15.85,0.0,-0.0,0.0,3.17,86.5 +2020-08-29 05:00:00,15.68,49.0,29.08,45.0,3.17,86.5 +2020-08-29 06:00:00,16.09,49.0,0.0,49.0,3.66,86.55 +2020-08-29 07:00:00,16.33,120.0,0.0,120.0,4.14,89.65 +2020-08-29 08:00:00,16.53,157.0,0.0,157.0,3.86,86.6 +2020-08-29 09:00:00,16.64,175.0,0.0,175.0,4.69,86.6 +2020-08-29 10:00:00,17.12,216.0,0.0,216.0,4.55,83.7 +2020-08-29 11:00:00,17.66,337.0,26.91,317.0,4.62,83.7 +2020-08-29 12:00:00,18.4,202.0,0.0,202.0,5.38,80.9 +2020-08-29 13:00:00,19.12,469.0,255.83,302.0,5.86,75.65 +2020-08-29 14:00:00,18.8,148.0,0.0,148.0,5.52,78.25 +2020-08-29 15:00:00,18.72,135.0,2.38,134.0,5.45,78.25 +2020-08-29 16:00:00,18.37,79.0,3.72,78.0,4.76,80.9 +2020-08-29 17:00:00,17.98,39.0,27.8,36.0,3.86,80.9 +2020-08-29 18:00:00,17.47,0.0,-0.0,0.0,3.52,78.1 +2020-08-29 19:00:00,16.65,0.0,-0.0,0.0,2.48,80.8 +2020-08-29 20:00:00,16.39,0.0,-0.0,0.0,2.28,83.6 +2020-08-29 21:00:00,15.91,0.0,-0.0,0.0,2.07,83.55 +2020-08-29 22:00:00,15.3,0.0,-0.0,0.0,1.93,86.5 +2020-08-29 23:00:00,14.92,0.0,-0.0,0.0,1.72,89.5 +2020-08-30 00:00:00,14.64,0.0,-0.0,0.0,1.52,89.5 +2020-08-30 01:00:00,14.06,0.0,-0.0,0.0,1.52,92.7 +2020-08-30 02:00:00,13.88,0.0,-0.0,0.0,1.38,95.95 +2020-08-30 03:00:00,13.65,0.0,-0.0,0.0,1.24,95.95 +2020-08-30 04:00:00,13.05,0.0,-0.0,0.0,1.24,95.95 +2020-08-30 05:00:00,13.05,59.0,74.83,49.0,1.03,95.95 +2020-08-30 06:00:00,14.28,148.0,78.27,125.0,1.24,96.0 +2020-08-30 07:00:00,15.0,342.0,375.47,176.0,1.59,86.5 +2020-08-30 08:00:00,16.02,457.0,399.46,230.0,1.45,77.95 +2020-08-30 09:00:00,17.06,577.0,506.23,241.0,1.59,70.3 +2020-08-30 10:00:00,18.11,668.0,616.34,223.0,1.45,65.55 +2020-08-30 11:00:00,18.92,699.0,658.91,212.0,1.45,59.05 +2020-08-30 12:00:00,19.47,675.0,663.99,201.0,1.45,55.05 +2020-08-30 13:00:00,19.81,640.0,782.38,133.0,1.52,53.25 +2020-08-30 14:00:00,20.07,517.0,710.55,129.0,1.59,49.7 +2020-08-30 15:00:00,19.88,364.0,588.08,120.0,1.66,51.4 +2020-08-30 16:00:00,19.53,208.0,462.95,86.0,1.72,51.4 +2020-08-30 17:00:00,18.57,57.0,176.09,39.0,1.59,59.05 +2020-08-30 18:00:00,16.91,0.0,-0.0,0.0,1.93,63.05 +2020-08-30 19:00:00,15.55,0.0,-0.0,0.0,2.21,65.15 +2020-08-30 20:00:00,14.41,0.0,-0.0,0.0,2.34,72.3 +2020-08-30 21:00:00,13.45,0.0,-0.0,0.0,2.28,72.2 +2020-08-30 22:00:00,12.51,0.0,-0.0,0.0,2.21,77.45 +2020-08-30 23:00:00,11.8,0.0,-0.0,0.0,2.21,83.1 +2020-08-31 00:00:00,11.34,0.0,-0.0,0.0,2.28,83.05 +2020-08-31 01:00:00,10.92,0.0,-0.0,0.0,2.34,80.1 +2020-08-31 02:00:00,10.61,0.0,-0.0,0.0,2.34,83.0 +2020-08-31 03:00:00,10.39,0.0,-0.0,0.0,2.41,83.0 +2020-08-31 04:00:00,10.24,0.0,-0.0,0.0,2.41,82.95 +2020-08-31 05:00:00,10.44,84.0,308.4,44.0,2.48,83.0 +2020-08-31 06:00:00,12.38,247.0,572.31,81.0,2.28,80.2 +2020-08-31 07:00:00,14.96,405.0,677.55,108.0,2.55,72.45 +2020-08-31 08:00:00,16.76,548.0,752.92,123.0,2.69,67.75 +2020-08-31 09:00:00,18.22,645.0,745.64,153.0,2.69,63.3 +2020-08-31 10:00:00,19.46,725.0,812.05,142.0,2.97,59.15 +2020-08-31 11:00:00,20.35,750.0,831.49,139.0,3.1,55.3 +2020-08-31 12:00:00,21.07,712.0,796.52,147.0,3.31,49.95 +2020-08-31 13:00:00,21.6,656.0,837.98,117.0,3.38,48.3 +2020-08-31 14:00:00,21.84,524.0,744.96,121.0,3.31,46.6 +2020-08-31 15:00:00,21.79,370.0,632.39,111.0,3.03,46.6 +2020-08-31 16:00:00,19.11,210.0,500.1,81.0,2.81,51.13 +2020-08-31 17:00:00,18.55,63.0,321.36,32.0,2.71,53.15 +2020-08-31 18:00:00,18.0,0.0,-0.0,0.0,2.61,55.17 +2020-08-31 19:00:00,17.45,0.0,-0.0,0.0,2.51,57.19 +2020-08-31 20:00:00,16.89,0.0,-0.0,0.0,2.42,59.21 +2020-08-31 21:00:00,16.34,0.0,-0.0,0.0,2.32,61.22 +2020-08-31 22:00:00,15.78,0.0,-0.0,0.0,2.22,63.24 +2020-08-31 23:00:00,15.23,0.0,-0.0,0.0,2.12,65.26 +2020-09-01 00:00:00,14.67,0.0,-0.0,0.0,2.02,67.28 +2020-09-01 01:00:00,14.12,0.0,-0.0,0.0,1.93,69.3 +2020-09-01 02:00:00,13.57,0.0,-0.0,0.0,1.83,71.32 +2020-09-01 03:00:00,13.01,0.0,-0.0,0.0,1.73,73.34 +2020-09-01 04:00:00,12.46,0.0,-0.0,0.0,1.63,75.36 +2020-09-01 05:00:00,11.9,35.0,7.71,34.0,1.53,77.38 +2020-09-01 06:00:00,11.35,117.0,27.58,109.0,1.44,79.39 +2020-09-01 07:00:00,10.79,271.0,166.54,198.0,1.34,81.41 +2020-09-01 08:00:00,13.74,192.0,3.54,190.0,2.0,74.85 +2020-09-01 09:00:00,14.47,231.0,4.55,228.0,2.14,74.95 +2020-09-01 10:00:00,14.99,118.0,0.0,118.0,2.48,69.9 +2020-09-01 11:00:00,15.32,137.0,0.0,137.0,2.55,72.45 +2020-09-01 12:00:00,15.57,129.0,0.0,129.0,2.9,70.0 +2020-09-01 13:00:00,15.62,89.0,0.0,89.0,2.83,72.55 +2020-09-01 14:00:00,15.26,61.0,0.0,61.0,2.48,77.8 +2020-09-01 15:00:00,14.88,141.0,7.32,138.0,2.21,83.45 +2020-09-01 16:00:00,14.33,107.0,34.89,98.0,2.34,86.4 +2020-09-01 17:00:00,14.0,26.0,10.37,25.0,2.21,86.4 +2020-09-01 18:00:00,13.75,0.0,-0.0,0.0,2.0,89.45 +2020-09-01 19:00:00,14.09,0.0,-0.0,0.0,2.48,83.4 +2020-09-01 20:00:00,13.79,0.0,-0.0,0.0,2.34,86.35 +2020-09-01 21:00:00,13.51,0.0,-0.0,0.0,2.21,89.45 +2020-09-01 22:00:00,13.4,0.0,-0.0,0.0,2.14,92.65 +2020-09-01 23:00:00,13.27,0.0,-0.0,0.0,2.28,92.65 +2020-09-02 00:00:00,13.24,0.0,-0.0,0.0,2.55,92.65 +2020-09-02 01:00:00,12.99,0.0,-0.0,0.0,2.76,92.65 +2020-09-02 02:00:00,12.98,0.0,-0.0,0.0,2.83,92.65 +2020-09-02 03:00:00,12.94,0.0,-0.0,0.0,2.97,95.95 +2020-09-02 04:00:00,12.9,0.0,-0.0,0.0,3.17,95.95 +2020-09-02 05:00:00,12.9,7.0,0.0,7.0,3.31,95.95 +2020-09-02 06:00:00,12.93,40.0,0.0,40.0,3.24,99.4 +2020-09-02 07:00:00,13.07,58.0,0.0,58.0,3.24,92.65 +2020-09-02 08:00:00,13.43,95.0,0.0,95.0,3.38,92.65 +2020-09-02 09:00:00,14.15,92.0,0.0,92.0,3.31,89.5 +2020-09-02 10:00:00,14.49,119.0,0.0,119.0,3.24,92.7 +2020-09-02 11:00:00,15.22,151.0,0.0,151.0,3.24,86.5 +2020-09-02 12:00:00,16.31,160.0,0.0,160.0,2.9,83.6 +2020-09-02 13:00:00,17.49,142.0,0.0,142.0,2.34,80.85 +2020-09-02 14:00:00,18.16,118.0,0.0,118.0,2.0,78.25 +2020-09-02 15:00:00,18.98,107.0,0.0,107.0,2.14,75.65 +2020-09-02 16:00:00,18.82,99.0,27.74,92.0,2.28,81.05 +2020-09-02 17:00:00,18.31,32.0,33.09,29.0,2.07,83.8 +2020-09-02 18:00:00,17.61,0.0,-0.0,0.0,1.66,86.7 +2020-09-02 19:00:00,17.83,0.0,-0.0,0.0,1.38,89.75 +2020-09-02 20:00:00,17.19,0.0,-0.0,0.0,1.38,92.85 +2020-09-02 21:00:00,16.82,0.0,-0.0,0.0,1.66,92.8 +2020-09-02 22:00:00,16.8,0.0,-0.0,0.0,1.79,96.05 +2020-09-02 23:00:00,16.84,0.0,-0.0,0.0,1.66,96.05 +2020-09-03 00:00:00,16.88,0.0,-0.0,0.0,1.52,96.05 +2020-09-03 01:00:00,16.79,0.0,-0.0,0.0,1.59,96.05 +2020-09-03 02:00:00,16.57,0.0,-0.0,0.0,1.66,92.8 +2020-09-03 03:00:00,16.48,0.0,-0.0,0.0,1.86,96.05 +2020-09-03 04:00:00,16.29,0.0,-0.0,0.0,1.93,92.8 +2020-09-03 05:00:00,16.07,64.0,156.01,45.0,1.79,92.8 +2020-09-03 06:00:00,16.79,70.0,0.0,70.0,1.66,92.8 +2020-09-03 07:00:00,17.96,250.0,130.02,194.0,1.93,86.7 +2020-09-03 08:00:00,19.6,475.0,526.26,182.0,2.55,75.7 +2020-09-03 09:00:00,20.22,451.0,227.03,303.0,2.55,73.2 +2020-09-03 10:00:00,21.22,460.0,167.69,341.0,2.76,68.5 +2020-09-03 11:00:00,21.8,128.0,0.0,128.0,2.69,66.25 +2020-09-03 12:00:00,22.28,113.0,0.0,113.0,2.69,64.1 +2020-09-03 13:00:00,22.73,287.0,30.0,268.0,2.83,62.05 +2020-09-03 14:00:00,22.69,255.0,47.12,230.0,2.83,57.9 +2020-09-03 15:00:00,21.81,136.0,7.52,133.0,2.07,66.25 +2020-09-03 16:00:00,20.86,148.0,186.52,102.0,2.14,73.3 +2020-09-03 17:00:00,20.16,36.0,70.75,30.0,2.07,75.75 +2020-09-03 18:00:00,19.48,0.0,-0.0,0.0,1.79,78.3 +2020-09-03 19:00:00,19.24,0.0,-0.0,0.0,1.38,81.05 +2020-09-03 20:00:00,18.64,0.0,-0.0,0.0,1.59,78.3 +2020-09-03 21:00:00,18.3,0.0,-0.0,0.0,1.93,78.25 +2020-09-03 22:00:00,17.67,0.0,-0.0,0.0,2.0,78.15 +2020-09-03 23:00:00,16.67,0.0,-0.0,0.0,1.86,83.65 +2020-09-04 00:00:00,16.39,0.0,-0.0,0.0,1.52,86.55 +2020-09-04 01:00:00,16.46,0.0,-0.0,0.0,1.31,86.55 +2020-09-04 02:00:00,17.33,0.0,-0.0,0.0,1.03,80.85 +2020-09-04 03:00:00,17.62,0.0,-0.0,0.0,0.76,80.9 +2020-09-04 04:00:00,17.08,0.0,-0.0,0.0,0.83,83.7 +2020-09-04 05:00:00,15.12,56.0,110.35,43.0,1.24,89.55 +2020-09-04 06:00:00,16.35,169.0,183.15,118.0,0.83,89.65 +2020-09-04 07:00:00,18.7,320.0,349.07,171.0,1.66,83.85 +2020-09-04 08:00:00,19.73,498.0,622.24,154.0,1.86,81.1 +2020-09-04 09:00:00,21.23,600.0,656.0,175.0,2.07,73.35 +2020-09-04 10:00:00,22.06,639.0,591.12,222.0,2.62,66.35 +2020-09-04 11:00:00,22.67,259.0,4.16,256.0,2.83,62.05 +2020-09-04 12:00:00,23.14,252.0,4.31,249.0,2.62,60.05 +2020-09-04 13:00:00,23.62,486.0,342.13,271.0,2.41,56.15 +2020-09-04 14:00:00,23.48,455.0,540.61,171.0,2.48,56.0 +2020-09-04 15:00:00,23.11,199.0,68.68,172.0,2.55,56.0 +2020-09-04 16:00:00,22.49,121.0,87.18,100.0,2.41,61.95 +2020-09-04 17:00:00,21.53,21.0,12.67,20.0,1.93,66.15 +2020-09-04 18:00:00,20.45,0.0,-0.0,0.0,1.66,70.7 +2020-09-04 19:00:00,19.62,0.0,-0.0,0.0,1.66,70.65 +2020-09-04 20:00:00,18.51,0.0,-0.0,0.0,1.59,72.95 +2020-09-04 21:00:00,17.67,0.0,-0.0,0.0,1.66,72.9 +2020-09-04 22:00:00,17.27,0.0,-0.0,0.0,1.52,75.4 +2020-09-04 23:00:00,18.25,0.0,-0.0,0.0,1.1,68.05 +2020-09-05 00:00:00,18.8,0.0,-0.0,0.0,0.41,65.75 +2020-09-05 01:00:00,18.39,0.0,-0.0,0.0,0.34,68.05 +2020-09-05 02:00:00,17.75,0.0,-0.0,0.0,0.83,70.35 +2020-09-05 03:00:00,17.11,0.0,-0.0,0.0,0.97,72.8 +2020-09-05 04:00:00,16.64,0.0,-0.0,0.0,0.97,75.3 +2020-09-05 05:00:00,16.35,49.0,70.3,41.0,0.76,77.95 +2020-09-05 06:00:00,16.65,226.0,539.06,78.0,0.14,78.0 +2020-09-05 07:00:00,17.68,359.0,529.61,135.0,0.21,80.9 +2020-09-05 08:00:00,19.36,478.0,537.45,183.0,0.62,75.65 +2020-09-05 09:00:00,20.82,610.0,689.67,166.0,1.17,70.8 +2020-09-05 10:00:00,22.02,717.0,852.86,119.0,1.72,64.0 +2020-09-05 11:00:00,23.05,721.0,815.74,136.0,1.93,55.9 +2020-09-05 12:00:00,23.4,619.0,553.14,237.0,1.93,50.45 +2020-09-05 13:00:00,23.6,457.0,267.9,290.0,1.93,47.15 +2020-09-05 14:00:00,23.53,425.0,428.84,202.0,2.0,48.7 +2020-09-05 15:00:00,23.2,283.0,307.04,164.0,2.14,48.7 +2020-09-05 16:00:00,22.76,158.0,267.98,95.0,2.07,50.3 +2020-09-05 17:00:00,21.7,33.0,95.95,26.0,1.72,53.75 +2020-09-05 18:00:00,20.11,0.0,-0.0,0.0,1.86,59.35 +2020-09-05 19:00:00,18.67,0.0,-0.0,0.0,1.72,65.75 +2020-09-05 20:00:00,17.16,0.0,-0.0,0.0,1.79,72.8 +2020-09-05 21:00:00,15.93,0.0,-0.0,0.0,2.0,77.85 +2020-09-05 22:00:00,15.26,0.0,-0.0,0.0,2.0,80.6 +2020-09-05 23:00:00,14.72,0.0,-0.0,0.0,1.93,83.45 +2020-09-06 00:00:00,14.38,0.0,-0.0,0.0,1.79,86.4 +2020-09-06 01:00:00,14.19,0.0,-0.0,0.0,1.59,86.4 +2020-09-06 02:00:00,13.61,0.0,-0.0,0.0,1.66,89.45 +2020-09-06 03:00:00,13.26,0.0,-0.0,0.0,1.59,92.65 +2020-09-06 04:00:00,13.07,0.0,-0.0,0.0,1.59,92.65 +2020-09-06 05:00:00,12.94,53.0,118.41,40.0,1.59,95.95 +2020-09-06 06:00:00,15.34,171.0,214.33,113.0,1.1,86.5 +2020-09-06 07:00:00,18.93,316.0,355.59,167.0,1.52,70.55 +2020-09-06 08:00:00,20.54,509.0,701.05,127.0,1.93,63.7 +2020-09-06 09:00:00,21.77,641.0,825.44,113.0,2.14,57.65 +2020-09-06 10:00:00,22.68,713.0,863.88,111.0,2.28,52.1 +2020-09-06 11:00:00,23.52,739.0,891.13,104.0,2.0,50.45 +2020-09-06 12:00:00,23.81,675.0,783.06,138.0,1.45,47.15 +2020-09-06 13:00:00,24.06,504.0,412.45,249.0,1.24,47.15 +2020-09-06 14:00:00,24.11,401.0,367.26,212.0,1.24,45.6 +2020-09-06 15:00:00,23.97,344.0,649.32,96.0,1.38,47.15 +2020-09-06 16:00:00,23.47,165.0,357.68,83.0,1.52,50.45 +2020-09-06 17:00:00,22.23,9.0,0.0,9.0,1.86,53.85 +2020-09-06 18:00:00,20.93,0.0,-0.0,0.0,2.07,59.5 +2020-09-06 19:00:00,20.41,0.0,-0.0,0.0,2.21,59.35 +2020-09-06 20:00:00,19.21,0.0,-0.0,0.0,2.21,65.75 +2020-09-06 21:00:00,18.43,0.0,-0.0,0.0,2.14,70.45 +2020-09-06 22:00:00,18.06,0.0,-0.0,0.0,2.07,70.45 +2020-09-06 23:00:00,17.56,0.0,-0.0,0.0,2.14,70.35 +2020-09-07 00:00:00,17.31,0.0,-0.0,0.0,2.21,70.3 +2020-09-07 01:00:00,16.93,0.0,-0.0,0.0,2.28,72.7 +2020-09-07 02:00:00,16.61,0.0,-0.0,0.0,2.21,70.2 +2020-09-07 03:00:00,16.06,0.0,-0.0,0.0,2.21,72.65 +2020-09-07 04:00:00,15.83,0.0,-0.0,0.0,2.07,75.15 +2020-09-07 05:00:00,15.21,6.0,0.0,6.0,1.72,80.6 +2020-09-07 06:00:00,16.13,19.0,0.0,19.0,1.31,80.75 +2020-09-07 07:00:00,16.91,318.0,387.9,157.0,1.03,83.65 +2020-09-07 08:00:00,17.73,166.0,1.85,165.0,1.24,80.9 +2020-09-07 09:00:00,17.65,330.0,62.94,290.0,1.24,86.7 +2020-09-07 10:00:00,18.18,112.0,0.0,112.0,1.31,86.75 +2020-09-07 11:00:00,18.62,88.0,0.0,88.0,1.03,83.85 +2020-09-07 12:00:00,19.67,294.0,20.56,280.0,1.31,81.1 +2020-09-07 13:00:00,19.49,104.0,0.0,104.0,1.31,83.85 +2020-09-07 14:00:00,20.12,428.0,496.88,175.0,1.1,78.45 +2020-09-07 15:00:00,20.82,278.0,342.85,149.0,1.59,68.4 +2020-09-07 16:00:00,20.93,169.0,438.73,71.0,1.52,63.8 +2020-09-07 17:00:00,20.37,27.0,98.45,21.0,0.69,70.7 +2020-09-07 18:00:00,19.14,0.0,-0.0,0.0,0.9,73.05 +2020-09-07 19:00:00,17.13,0.0,-0.0,0.0,1.52,80.85 +2020-09-07 20:00:00,16.43,0.0,-0.0,0.0,1.52,86.55 +2020-09-07 21:00:00,16.59,0.0,-0.0,0.0,1.79,78.0 +2020-09-07 22:00:00,16.81,0.0,-0.0,0.0,2.14,75.3 +2020-09-07 23:00:00,16.68,0.0,-0.0,0.0,2.41,72.7 +2020-09-08 00:00:00,16.36,0.0,-0.0,0.0,2.48,75.25 +2020-09-08 01:00:00,15.89,0.0,-0.0,0.0,2.21,77.85 +2020-09-08 02:00:00,15.13,0.0,-0.0,0.0,1.93,80.6 +2020-09-08 03:00:00,14.53,0.0,-0.0,0.0,1.86,80.55 +2020-09-08 04:00:00,13.54,0.0,-0.0,0.0,2.0,86.35 +2020-09-08 05:00:00,12.87,33.0,19.67,31.0,1.86,89.4 +2020-09-08 06:00:00,13.78,154.0,156.09,113.0,1.59,86.35 +2020-09-08 07:00:00,15.14,374.0,664.13,101.0,1.79,75.1 +2020-09-08 08:00:00,16.02,499.0,672.52,138.0,1.72,65.25 +2020-09-08 09:00:00,16.65,493.0,340.58,278.0,1.66,63.05 +2020-09-08 10:00:00,18.17,650.0,662.73,194.0,2.34,54.95 +2020-09-08 11:00:00,19.1,685.0,732.26,170.0,2.48,51.25 +2020-09-08 12:00:00,19.74,688.0,837.34,122.0,2.28,47.8 +2020-09-08 13:00:00,20.21,610.0,817.62,113.0,1.93,44.6 +2020-09-08 14:00:00,20.87,476.0,710.76,118.0,1.59,43.1 +2020-09-08 15:00:00,20.96,290.0,399.43,142.0,1.24,43.1 +2020-09-08 16:00:00,20.74,167.0,441.49,71.0,1.1,43.1 +2020-09-08 17:00:00,19.49,29.0,182.2,19.0,1.59,51.25 +2020-09-08 18:00:00,17.49,0.0,-0.0,0.0,1.72,56.75 +2020-09-08 19:00:00,15.52,0.0,-0.0,0.0,1.86,62.85 +2020-09-08 20:00:00,14.38,0.0,-0.0,0.0,1.72,69.75 +2020-09-08 21:00:00,13.7,0.0,-0.0,0.0,2.0,69.65 +2020-09-08 22:00:00,13.2,0.0,-0.0,0.0,2.14,72.1 +2020-09-08 23:00:00,12.84,0.0,-0.0,0.0,2.07,77.45 +2020-09-09 00:00:00,12.45,0.0,-0.0,0.0,1.93,80.2 +2020-09-09 01:00:00,12.01,0.0,-0.0,0.0,1.86,83.15 +2020-09-09 02:00:00,11.8,0.0,-0.0,0.0,1.79,86.15 +2020-09-09 03:00:00,11.29,0.0,-0.0,0.0,1.86,89.3 +2020-09-09 04:00:00,10.91,0.0,-0.0,0.0,1.86,89.25 +2020-09-09 05:00:00,10.87,33.0,30.73,30.0,1.79,89.25 +2020-09-09 06:00:00,12.71,114.0,46.39,102.0,1.31,86.25 +2020-09-09 07:00:00,16.22,171.0,24.57,161.0,1.1,72.65 +2020-09-09 08:00:00,18.4,326.0,137.04,253.0,0.97,63.4 +2020-09-09 09:00:00,20.05,367.0,103.67,302.0,1.03,53.35 +2020-09-09 10:00:00,21.15,657.0,708.02,173.0,0.97,48.2 +2020-09-09 11:00:00,22.18,625.0,545.39,244.0,1.17,45.1 +2020-09-09 12:00:00,22.99,561.0,432.22,271.0,1.59,43.65 +2020-09-09 13:00:00,23.43,412.0,205.78,288.0,1.66,40.75 +2020-09-09 14:00:00,23.43,491.0,792.94,96.0,1.72,40.75 +2020-09-09 15:00:00,23.07,324.0,625.09,96.0,1.86,42.1 +2020-09-09 16:00:00,22.32,173.0,524.87,62.0,1.72,46.75 +2020-09-09 17:00:00,20.99,20.0,81.98,16.0,1.66,53.5 +2020-09-09 18:00:00,18.95,0.0,-0.0,0.0,1.38,63.5 +2020-09-09 19:00:00,18.77,0.0,-0.0,0.0,1.1,57.1 +2020-09-09 20:00:00,19.65,0.0,-0.0,0.0,0.34,51.4 +2020-09-09 21:00:00,18.61,0.0,-0.0,0.0,1.1,53.15 +2020-09-09 22:00:00,16.24,0.0,-0.0,0.0,1.45,65.25 +2020-09-09 23:00:00,14.73,0.0,-0.0,0.0,1.72,72.4 +2020-09-10 00:00:00,13.86,0.0,-0.0,0.0,1.79,77.6 +2020-09-10 01:00:00,13.1,0.0,-0.0,0.0,1.86,80.35 +2020-09-10 02:00:00,12.41,0.0,-0.0,0.0,1.86,86.2 +2020-09-10 03:00:00,11.85,0.0,-0.0,0.0,1.86,89.3 +2020-09-10 04:00:00,11.46,0.0,-0.0,0.0,1.86,89.3 +2020-09-10 05:00:00,11.13,53.0,224.5,32.0,1.86,89.3 +2020-09-10 06:00:00,13.24,203.0,498.78,76.0,1.38,86.3 +2020-09-10 07:00:00,17.87,367.0,667.51,98.0,1.17,67.95 +2020-09-10 08:00:00,21.04,517.0,783.3,103.0,0.97,57.45 +2020-09-10 09:00:00,22.82,610.0,772.44,129.0,1.31,46.85 +2020-09-10 10:00:00,23.8,504.0,275.37,317.0,1.59,43.9 +2020-09-10 11:00:00,24.54,679.0,750.92,158.0,1.79,42.5 +2020-09-10 12:00:00,25.07,510.0,313.86,301.0,2.07,39.7 +2020-09-10 13:00:00,25.44,563.0,684.79,154.0,2.28,35.8 +2020-09-10 14:00:00,25.45,442.0,602.97,145.0,2.48,34.5 +2020-09-10 15:00:00,25.2,141.0,16.72,135.0,2.34,34.5 +2020-09-10 16:00:00,24.22,47.0,0.0,47.0,1.38,42.5 +2020-09-10 17:00:00,22.98,10.0,0.0,10.0,0.76,52.1 +2020-09-10 18:00:00,22.71,0.0,-0.0,0.0,0.69,43.65 +2020-09-10 19:00:00,19.68,0.0,-0.0,0.0,1.52,63.6 +2020-09-10 20:00:00,19.04,0.0,-0.0,0.0,1.93,63.5 +2020-09-10 21:00:00,18.0,0.0,-0.0,0.0,1.52,67.95 +2020-09-10 22:00:00,17.36,0.0,-0.0,0.0,1.45,72.8 +2020-09-10 23:00:00,17.16,0.0,-0.0,0.0,1.79,70.3 +2020-09-11 00:00:00,17.35,0.0,-0.0,0.0,2.0,67.85 +2020-09-11 01:00:00,16.95,0.0,-0.0,0.0,1.72,70.2 +2020-09-11 02:00:00,16.57,0.0,-0.0,0.0,1.38,72.7 +2020-09-11 03:00:00,16.19,0.0,-0.0,0.0,1.31,75.25 +2020-09-11 04:00:00,15.76,0.0,-0.0,0.0,1.31,77.85 +2020-09-11 05:00:00,15.68,15.0,0.0,15.0,1.31,80.65 +2020-09-11 06:00:00,16.38,40.0,0.0,40.0,1.24,80.75 +2020-09-11 07:00:00,18.04,272.0,245.68,174.0,0.83,68.05 +2020-09-11 08:00:00,19.21,496.0,726.62,115.0,1.31,63.5 +2020-09-11 09:00:00,21.48,585.0,706.72,148.0,2.62,55.55 +2020-09-11 10:00:00,23.04,546.0,395.84,279.0,3.66,48.55 +2020-09-11 11:00:00,23.9,649.0,674.9,184.0,3.86,43.9 +2020-09-11 12:00:00,24.76,551.0,440.37,260.0,4.0,38.3 +2020-09-11 13:00:00,25.28,413.0,231.46,276.0,4.07,34.5 +2020-09-11 14:00:00,25.2,372.0,349.13,202.0,4.0,33.3 +2020-09-11 15:00:00,24.94,266.0,368.21,136.0,3.66,34.4 +2020-09-11 16:00:00,24.25,131.0,265.75,78.0,2.69,36.8 +2020-09-11 17:00:00,22.94,9.0,0.0,9.0,2.34,42.1 +2020-09-11 18:00:00,21.52,0.0,-0.0,0.0,2.28,46.5 +2020-09-11 19:00:00,20.81,0.0,-0.0,0.0,2.41,44.7 +2020-09-11 20:00:00,19.79,0.0,-0.0,0.0,2.55,47.8 +2020-09-11 21:00:00,19.13,0.0,-0.0,0.0,2.69,51.25 +2020-09-11 22:00:00,18.6,0.0,-0.0,0.0,2.69,53.15 +2020-09-11 23:00:00,18.05,0.0,-0.0,0.0,2.62,54.95 +2020-09-12 00:00:00,17.31,0.0,-0.0,0.0,2.41,60.95 +2020-09-12 01:00:00,16.51,0.0,-0.0,0.0,2.41,63.05 +2020-09-12 02:00:00,15.75,0.0,-0.0,0.0,2.34,67.55 +2020-09-12 03:00:00,15.02,0.0,-0.0,0.0,2.28,69.9 +2020-09-12 04:00:00,14.37,0.0,-0.0,0.0,2.21,74.95 +2020-09-12 05:00:00,13.89,47.0,222.7,28.0,2.14,77.6 +2020-09-12 06:00:00,15.23,204.0,547.71,69.0,1.79,77.8 +2020-09-12 07:00:00,20.0,362.0,681.39,93.0,1.66,61.4 +2020-09-12 08:00:00,22.75,512.0,795.96,98.0,2.28,48.55 +2020-09-12 09:00:00,24.7,625.0,851.85,102.0,2.76,42.65 +2020-09-12 10:00:00,26.05,683.0,849.38,114.0,3.1,38.55 +2020-09-12 11:00:00,27.03,701.0,856.57,115.0,3.31,33.7 +2020-09-12 12:00:00,27.71,654.0,800.71,129.0,3.38,29.4 +2020-09-12 13:00:00,28.03,583.0,799.71,114.0,3.17,29.4 +2020-09-12 14:00:00,28.04,461.0,739.75,105.0,3.03,29.4 +2020-09-12 15:00:00,27.73,311.0,639.47,89.0,2.83,30.5 +2020-09-12 16:00:00,26.65,142.0,387.87,67.0,1.93,37.5 +2020-09-12 17:00:00,24.73,7.0,0.0,7.0,1.72,44.15 +2020-09-12 18:00:00,23.56,0.0,-0.0,0.0,1.1,48.7 +2020-09-12 19:00:00,22.03,0.0,-0.0,0.0,1.31,51.9 +2020-09-12 20:00:00,20.65,0.0,-0.0,0.0,3.38,55.45 +2020-09-12 21:00:00,18.71,0.0,-0.0,0.0,3.38,65.75 +2020-09-12 22:00:00,17.21,0.0,-0.0,0.0,2.41,78.1 +2020-09-12 23:00:00,16.28,0.0,-0.0,0.0,2.0,83.6 +2020-09-13 00:00:00,15.55,0.0,-0.0,0.0,1.79,86.5 +2020-09-13 01:00:00,14.54,0.0,-0.0,0.0,1.79,86.45 +2020-09-13 02:00:00,14.21,0.0,-0.0,0.0,1.66,89.5 +2020-09-13 03:00:00,13.83,0.0,-0.0,0.0,1.59,89.45 +2020-09-13 04:00:00,13.57,0.0,-0.0,0.0,1.45,89.45 +2020-09-13 05:00:00,13.51,6.0,0.0,6.0,1.38,89.45 +2020-09-13 06:00:00,14.24,60.0,0.0,60.0,1.72,86.4 +2020-09-13 07:00:00,14.67,166.0,30.72,154.0,2.55,83.45 +2020-09-13 08:00:00,15.56,184.0,5.82,181.0,2.14,77.85 +2020-09-13 09:00:00,16.2,349.0,100.08,288.0,2.0,77.95 +2020-09-13 10:00:00,17.08,175.0,0.0,175.0,2.0,75.4 +2020-09-13 11:00:00,17.63,128.0,0.0,128.0,2.0,75.45 +2020-09-13 12:00:00,18.25,91.0,0.0,91.0,2.0,75.55 +2020-09-13 13:00:00,18.36,101.0,0.0,101.0,1.86,75.55 +2020-09-13 14:00:00,18.14,195.0,18.93,186.0,1.93,75.55 +2020-09-13 15:00:00,17.78,109.0,2.93,108.0,2.07,78.15 +2020-09-13 16:00:00,17.25,39.0,0.0,39.0,2.0,80.85 +2020-09-13 17:00:00,16.64,4.0,0.0,4.0,1.86,83.65 +2020-09-13 18:00:00,16.0,0.0,-0.0,0.0,1.59,86.5 +2020-09-13 19:00:00,14.91,0.0,-0.0,0.0,1.72,92.7 +2020-09-13 20:00:00,14.55,0.0,-0.0,0.0,1.38,92.7 +2020-09-13 21:00:00,14.37,0.0,-0.0,0.0,1.24,96.0 +2020-09-13 22:00:00,14.12,0.0,-0.0,0.0,1.38,96.0 +2020-09-13 23:00:00,13.92,0.0,-0.0,0.0,1.31,95.95 +2020-09-14 00:00:00,13.78,0.0,-0.0,0.0,1.17,95.95 +2020-09-14 01:00:00,13.99,0.0,-0.0,0.0,1.17,92.7 +2020-09-14 02:00:00,13.73,0.0,-0.0,0.0,1.17,95.95 +2020-09-14 03:00:00,13.59,0.0,-0.0,0.0,1.24,95.95 +2020-09-14 04:00:00,13.55,0.0,-0.0,0.0,1.38,92.65 +2020-09-14 05:00:00,13.39,42.0,220.69,25.0,1.45,95.95 +2020-09-14 06:00:00,13.51,37.0,0.0,37.0,1.79,92.65 +2020-09-14 07:00:00,13.42,79.0,0.0,79.0,1.31,99.4 +2020-09-14 08:00:00,13.43,113.0,0.0,113.0,1.45,95.95 +2020-09-14 09:00:00,13.88,99.0,0.0,99.0,1.31,89.45 +2020-09-14 10:00:00,14.92,210.0,1.51,209.0,1.45,83.45 +2020-09-14 11:00:00,15.41,208.0,0.0,208.0,1.59,80.6 +2020-09-14 12:00:00,15.82,127.0,0.0,127.0,1.52,77.85 +2020-09-14 13:00:00,16.42,125.0,0.0,125.0,1.45,75.25 +2020-09-14 14:00:00,16.62,234.0,57.48,207.0,1.31,72.7 +2020-09-14 15:00:00,16.83,229.0,256.53,143.0,1.1,72.7 +2020-09-14 16:00:00,16.63,68.0,27.6,63.0,0.97,72.7 +2020-09-14 17:00:00,16.33,0.0,0.0,0.0,0.76,75.25 +2020-09-14 18:00:00,15.56,0.0,-0.0,0.0,0.83,80.65 +2020-09-14 19:00:00,15.15,0.0,-0.0,0.0,1.59,83.5 +2020-09-14 20:00:00,14.35,0.0,-0.0,0.0,1.72,86.4 +2020-09-14 21:00:00,13.85,0.0,-0.0,0.0,1.72,89.45 +2020-09-14 22:00:00,13.46,0.0,-0.0,0.0,1.72,92.65 +2020-09-14 23:00:00,13.46,0.0,-0.0,0.0,1.66,92.65 +2020-09-15 00:00:00,13.86,0.0,-0.0,0.0,1.52,92.65 +2020-09-15 01:00:00,14.08,0.0,-0.0,0.0,1.59,89.5 +2020-09-15 02:00:00,13.94,0.0,-0.0,0.0,1.72,92.65 +2020-09-15 03:00:00,13.92,0.0,-0.0,0.0,1.66,92.65 +2020-09-15 04:00:00,13.7,0.0,-0.0,0.0,1.79,92.65 +2020-09-15 05:00:00,13.13,22.0,13.72,21.0,1.93,92.65 +2020-09-15 06:00:00,13.81,58.0,0.0,58.0,1.79,92.65 +2020-09-15 07:00:00,15.45,212.0,104.64,172.0,2.48,86.5 +2020-09-15 08:00:00,16.5,414.0,425.84,198.0,3.1,72.65 +2020-09-15 09:00:00,17.43,538.0,562.83,200.0,3.38,58.8 +2020-09-15 10:00:00,18.38,373.0,86.92,316.0,3.72,49.3 +2020-09-15 11:00:00,19.18,242.0,4.48,239.0,4.14,44.3 +2020-09-15 12:00:00,19.66,588.0,601.62,203.0,4.07,41.3 +2020-09-15 13:00:00,19.95,207.0,5.26,204.0,4.0,39.8 +2020-09-15 14:00:00,19.69,255.0,86.22,215.0,3.79,39.8 +2020-09-15 15:00:00,19.29,271.0,485.97,111.0,3.17,44.3 +2020-09-15 16:00:00,18.72,126.0,371.49,61.0,2.21,47.65 +2020-09-15 17:00:00,17.77,0.0,0.0,0.0,1.59,52.9 +2020-09-15 18:00:00,16.1,0.0,-0.0,0.0,1.38,62.95 +2020-09-15 19:00:00,14.53,0.0,-0.0,0.0,1.59,67.35 +2020-09-15 20:00:00,13.45,0.0,-0.0,0.0,1.79,72.1 +2020-09-15 21:00:00,12.6,0.0,-0.0,0.0,1.93,72.05 +2020-09-15 22:00:00,12.08,0.0,-0.0,0.0,1.93,74.6 +2020-09-15 23:00:00,11.94,0.0,-0.0,0.0,1.79,77.3 +2020-09-16 00:00:00,11.98,0.0,-0.0,0.0,1.59,77.35 +2020-09-16 01:00:00,11.73,0.0,-0.0,0.0,1.52,80.15 +2020-09-16 02:00:00,11.59,0.0,-0.0,0.0,1.38,83.1 +2020-09-16 03:00:00,11.32,0.0,-0.0,0.0,1.31,83.05 +2020-09-16 04:00:00,11.22,0.0,-0.0,0.0,1.24,83.05 +2020-09-16 05:00:00,10.64,31.0,116.48,23.0,1.38,86.05 +2020-09-16 06:00:00,12.06,179.0,469.73,71.0,0.83,83.15 +2020-09-16 07:00:00,14.68,321.0,542.32,116.0,0.69,75.0 +2020-09-16 08:00:00,16.59,453.0,608.52,147.0,1.03,65.35 +2020-09-16 09:00:00,18.22,569.0,713.12,144.0,1.45,59.05 +2020-09-16 10:00:00,19.65,586.0,565.29,218.0,1.86,53.25 +2020-09-16 11:00:00,20.83,580.0,502.88,246.0,2.28,46.35 +2020-09-16 12:00:00,21.5,622.0,762.67,138.0,2.14,43.25 +2020-09-16 13:00:00,21.86,545.0,742.47,126.0,2.0,41.85 +2020-09-16 14:00:00,22.02,418.0,646.13,122.0,1.79,40.35 +2020-09-16 15:00:00,21.95,271.0,532.17,99.0,1.72,40.35 +2020-09-16 16:00:00,21.36,108.0,242.91,67.0,1.45,43.25 +2020-09-16 17:00:00,19.77,0.0,0.0,0.0,1.93,49.55 +2020-09-16 18:00:00,17.89,0.0,-0.0,0.0,2.21,54.85 +2020-09-16 19:00:00,16.81,0.0,-0.0,0.0,2.21,56.6 +2020-09-16 20:00:00,15.68,0.0,-0.0,0.0,2.34,60.65 +2020-09-16 21:00:00,14.81,0.0,-0.0,0.0,2.41,64.95 +2020-09-16 22:00:00,14.27,0.0,-0.0,0.0,2.41,64.85 +2020-09-16 23:00:00,13.73,0.0,-0.0,0.0,2.41,67.15 +2020-09-17 00:00:00,13.29,0.0,-0.0,0.0,2.34,69.55 +2020-09-17 01:00:00,12.63,0.0,-0.0,0.0,2.28,74.7 +2020-09-17 02:00:00,12.08,0.0,-0.0,0.0,2.28,77.35 +2020-09-17 03:00:00,11.61,0.0,-0.0,0.0,2.28,80.15 +2020-09-17 04:00:00,11.37,0.0,-0.0,0.0,2.28,83.05 +2020-09-17 05:00:00,11.14,30.0,124.06,22.0,2.21,83.05 +2020-09-17 06:00:00,12.22,184.0,522.77,66.0,2.0,83.15 +2020-09-17 07:00:00,15.72,336.0,639.48,97.0,1.93,72.55 +2020-09-17 08:00:00,18.97,484.0,754.33,108.0,1.93,61.3 +2020-09-17 09:00:00,21.27,599.0,828.58,109.0,2.0,55.55 +2020-09-17 10:00:00,22.79,649.0,801.66,131.0,1.79,50.3 +2020-09-17 11:00:00,23.84,574.0,488.57,252.0,1.59,45.5 +2020-09-17 12:00:00,24.52,616.0,743.75,148.0,1.17,42.5 +2020-09-17 13:00:00,24.97,536.0,712.37,138.0,0.83,39.7 +2020-09-17 14:00:00,25.1,417.0,654.52,121.0,0.55,38.45 +2020-09-17 15:00:00,25.03,227.0,296.4,133.0,0.62,39.7 +2020-09-17 16:00:00,24.27,113.0,332.13,59.0,1.1,45.6 +2020-09-17 17:00:00,22.27,0.0,-0.0,0.0,2.0,52.0 +2020-09-17 18:00:00,20.11,0.0,-0.0,0.0,2.21,59.35 +2020-09-17 19:00:00,19.06,0.0,-0.0,0.0,2.34,57.1 +2020-09-17 20:00:00,17.99,0.0,-0.0,0.0,2.41,58.9 +2020-09-17 21:00:00,17.21,0.0,-0.0,0.0,2.55,60.95 +2020-09-17 22:00:00,16.68,0.0,-0.0,0.0,2.55,63.05 +2020-09-17 23:00:00,16.22,0.0,-0.0,0.0,2.55,67.65 +2020-09-18 00:00:00,15.74,0.0,-0.0,0.0,2.55,70.0 +2020-09-18 01:00:00,15.28,0.0,-0.0,0.0,2.48,72.45 +2020-09-18 02:00:00,14.88,0.0,-0.0,0.0,2.55,75.0 +2020-09-18 03:00:00,14.52,0.0,-0.0,0.0,2.48,75.0 +2020-09-18 04:00:00,14.08,0.0,-0.0,0.0,2.48,77.65 +2020-09-18 05:00:00,13.89,20.0,33.18,18.0,2.55,83.35 +2020-09-18 06:00:00,15.09,157.0,338.6,82.0,2.41,77.8 +2020-09-18 07:00:00,18.16,326.0,614.44,99.0,2.28,65.65 +2020-09-18 08:00:00,20.9,471.0,730.74,110.0,2.76,61.6 +2020-09-18 09:00:00,22.82,579.0,784.0,119.0,2.83,57.9 +2020-09-18 10:00:00,24.36,627.0,751.62,145.0,2.62,54.35 +2020-09-18 11:00:00,25.66,663.0,828.83,121.0,2.48,49.3 +2020-09-18 12:00:00,26.6,618.0,782.27,130.0,2.28,46.15 +2020-09-18 13:00:00,27.27,542.0,768.5,117.0,2.21,41.8 +2020-09-18 14:00:00,27.57,418.0,696.78,107.0,2.14,40.35 +2020-09-18 15:00:00,27.57,269.0,581.9,88.0,2.0,38.95 +2020-09-18 16:00:00,26.6,106.0,306.96,58.0,1.66,44.55 +2020-09-18 17:00:00,24.41,0.0,-0.0,0.0,2.28,50.7 +2020-09-18 18:00:00,22.4,0.0,-0.0,0.0,2.41,57.75 +2020-09-18 19:00:00,21.79,0.0,-0.0,0.0,2.62,55.65 +2020-09-18 20:00:00,20.76,0.0,-0.0,0.0,2.48,61.6 +2020-09-18 21:00:00,19.59,0.0,-0.0,0.0,2.34,65.85 +2020-09-18 22:00:00,18.49,0.0,-0.0,0.0,2.28,70.45 +2020-09-18 23:00:00,17.53,0.0,-0.0,0.0,2.21,72.9 +2020-09-19 00:00:00,16.6,0.0,-0.0,0.0,2.21,78.0 +2020-09-19 01:00:00,15.92,0.0,-0.0,0.0,2.21,80.65 +2020-09-19 02:00:00,15.43,0.0,-0.0,0.0,2.07,83.5 +2020-09-19 03:00:00,14.89,0.0,-0.0,0.0,1.86,86.45 +2020-09-19 04:00:00,14.57,0.0,-0.0,0.0,1.79,83.45 +2020-09-19 05:00:00,14.71,18.0,35.68,16.0,1.72,83.45 +2020-09-19 06:00:00,16.05,153.0,349.81,77.0,1.31,77.95 +2020-09-19 07:00:00,18.8,227.0,175.29,163.0,1.17,70.55 +2020-09-19 08:00:00,19.85,426.0,557.66,153.0,1.31,68.2 +2020-09-19 09:00:00,21.26,510.0,542.9,194.0,0.97,61.7 +2020-09-19 10:00:00,22.6,606.0,707.15,156.0,1.1,57.9 +2020-09-19 11:00:00,24.38,634.0,767.63,136.0,1.1,52.5 +2020-09-19 12:00:00,25.58,600.0,755.2,133.0,0.83,49.2 +2020-09-19 13:00:00,26.37,522.0,729.01,123.0,0.9,46.15 +2020-09-19 14:00:00,27.0,401.0,656.21,112.0,0.97,43.15 +2020-09-19 15:00:00,27.13,256.0,550.93,88.0,1.24,41.8 +2020-09-19 16:00:00,26.42,95.0,246.44,58.0,1.45,46.15 +2020-09-19 17:00:00,24.5,0.0,-0.0,0.0,2.14,52.5 +2020-09-19 18:00:00,22.48,0.0,-0.0,0.0,2.21,57.75 +2020-09-19 19:00:00,21.59,0.0,-0.0,0.0,2.41,57.65 +2020-09-19 20:00:00,20.75,0.0,-0.0,0.0,2.41,59.5 +2020-09-19 21:00:00,19.72,0.0,-0.0,0.0,2.34,61.4 +2020-09-19 22:00:00,18.85,0.0,-0.0,0.0,2.21,63.5 +2020-09-19 23:00:00,18.07,0.0,-0.0,0.0,2.28,65.65 +2020-09-20 00:00:00,17.78,0.0,-0.0,0.0,2.28,67.95 +2020-09-20 01:00:00,17.25,0.0,-0.0,0.0,2.28,70.3 +2020-09-20 02:00:00,16.73,0.0,-0.0,0.0,2.28,75.3 +2020-09-20 03:00:00,16.21,0.0,-0.0,0.0,2.21,77.95 +2020-09-20 04:00:00,15.62,0.0,-0.0,0.0,2.14,80.65 +2020-09-20 05:00:00,15.26,16.0,38.61,14.0,2.07,83.5 +2020-09-20 06:00:00,16.15,155.0,394.38,71.0,1.79,83.6 +2020-09-20 07:00:00,20.19,298.0,512.81,113.0,1.1,73.2 +2020-09-20 08:00:00,22.99,436.0,626.76,132.0,1.1,62.05 +2020-09-20 09:00:00,24.54,541.0,689.36,143.0,1.45,56.25 +2020-09-20 10:00:00,26.18,446.0,237.57,296.0,1.86,49.45 +2020-09-20 11:00:00,27.31,477.0,281.26,296.0,2.62,46.4 +2020-09-20 12:00:00,28.08,499.0,409.54,248.0,3.03,40.5 +2020-09-20 13:00:00,28.56,402.0,300.97,239.0,2.9,37.9 +2020-09-20 14:00:00,28.85,331.0,356.78,176.0,2.76,35.45 +2020-09-20 15:00:00,28.63,214.0,317.93,119.0,2.34,34.2 +2020-09-20 16:00:00,27.46,85.0,201.54,56.0,1.52,41.8 +2020-09-20 17:00:00,25.5,0.0,-0.0,0.0,1.86,45.9 +2020-09-20 18:00:00,23.29,0.0,-0.0,0.0,2.21,52.25 +2020-09-20 19:00:00,22.04,0.0,-0.0,0.0,2.41,53.75 +2020-09-20 20:00:00,21.16,0.0,-0.0,0.0,2.48,55.55 +2020-09-20 21:00:00,20.32,0.0,-0.0,0.0,2.48,59.35 +2020-09-20 22:00:00,19.61,0.0,-0.0,0.0,2.48,61.4 +2020-09-20 23:00:00,19.07,0.0,-0.0,0.0,2.55,65.75 +2020-09-21 00:00:00,18.73,0.0,-0.0,0.0,2.62,65.75 +2020-09-21 01:00:00,18.44,0.0,-0.0,0.0,2.62,68.05 +2020-09-21 02:00:00,18.1,0.0,-0.0,0.0,2.69,68.05 +2020-09-21 03:00:00,17.83,0.0,-0.0,0.0,2.76,70.35 +2020-09-21 04:00:00,17.73,0.0,-0.0,0.0,3.03,70.35 +2020-09-21 05:00:00,17.68,7.0,0.0,7.0,3.24,70.35 +2020-09-21 06:00:00,18.5,131.0,229.99,83.0,3.52,70.45 +2020-09-21 07:00:00,20.42,277.0,415.29,129.0,3.31,65.95 +2020-09-21 08:00:00,22.6,363.0,337.16,201.0,3.93,57.9 +2020-09-21 09:00:00,24.46,508.0,571.09,181.0,4.34,50.7 +2020-09-21 10:00:00,26.11,600.0,727.96,144.0,4.76,43.05 +2020-09-21 11:00:00,27.51,440.0,211.51,305.0,4.76,38.95 +2020-09-21 12:00:00,28.62,573.0,693.16,152.0,4.76,35.3 +2020-09-21 13:00:00,29.2,266.0,52.26,238.0,4.97,30.9 +2020-09-21 14:00:00,28.77,322.0,338.42,177.0,5.17,30.75 +2020-09-21 15:00:00,27.49,66.0,0.0,66.0,4.62,36.3 +2020-09-21 16:00:00,26.25,19.0,0.0,19.0,5.03,38.7 +2020-09-21 17:00:00,24.02,0.0,-0.0,0.0,5.66,45.5 +2020-09-21 18:00:00,19.72,0.0,-0.0,0.0,5.17,68.2 +2020-09-21 19:00:00,15.6,0.0,-0.0,0.0,4.9,89.6 +2020-09-21 20:00:00,13.89,0.0,-0.0,0.0,3.72,89.45 +2020-09-21 21:00:00,13.36,0.0,-0.0,0.0,3.86,80.35 +2020-09-21 22:00:00,13.29,0.0,-0.0,0.0,3.93,77.5 +2020-09-21 23:00:00,13.34,0.0,-0.0,0.0,3.93,74.75 +2020-09-22 00:00:00,13.33,0.0,-0.0,0.0,3.86,69.55 +2020-09-22 01:00:00,13.26,0.0,-0.0,0.0,3.93,62.3 +2020-09-22 02:00:00,12.92,0.0,-0.0,0.0,4.07,59.95 +2020-09-22 03:00:00,12.77,0.0,-0.0,0.0,4.21,57.75 +2020-09-22 04:00:00,12.62,0.0,-0.0,0.0,4.28,57.75 +2020-09-22 05:00:00,12.46,13.0,46.21,11.0,4.34,59.85 +2020-09-22 06:00:00,12.94,160.0,474.56,63.0,4.0,57.75 +2020-09-22 07:00:00,13.42,285.0,443.23,129.0,6.07,62.3 +2020-09-22 08:00:00,13.96,440.0,649.28,131.0,6.21,58.0 +2020-09-22 09:00:00,14.85,512.0,565.34,191.0,6.28,50.25 +2020-09-22 10:00:00,15.64,407.0,160.93,307.0,5.72,45.15 +2020-09-22 11:00:00,16.35,470.0,263.83,303.0,5.31,42.05 +2020-09-22 12:00:00,16.91,393.0,159.52,297.0,5.1,37.65 +2020-09-22 13:00:00,17.19,306.0,98.11,254.0,4.69,35.05 +2020-09-22 14:00:00,17.62,199.0,40.24,182.0,4.48,32.65 +2020-09-22 15:00:00,17.95,80.0,0.0,80.0,4.48,29.1 +2020-09-22 16:00:00,17.53,46.0,15.22,44.0,3.24,29.1 +2020-09-22 17:00:00,16.89,0.0,-0.0,0.0,2.9,31.15 +2020-09-22 18:00:00,16.47,0.0,-0.0,0.0,3.1,32.2 +2020-09-22 19:00:00,15.35,0.0,-0.0,0.0,2.28,40.2 +2020-09-22 20:00:00,15.05,0.0,-0.0,0.0,2.9,41.75 +2020-09-22 21:00:00,14.66,0.0,-0.0,0.0,3.17,44.9 +2020-09-22 22:00:00,14.3,0.0,-0.0,0.0,3.24,50.1 +2020-09-22 23:00:00,13.89,0.0,-0.0,0.0,3.38,55.9 +2020-09-23 00:00:00,13.34,0.0,-0.0,0.0,3.31,64.65 +2020-09-23 01:00:00,12.45,0.0,-0.0,0.0,2.97,69.35 +2020-09-23 02:00:00,12.43,0.0,-0.0,0.0,2.83,71.95 +2020-09-23 03:00:00,12.32,0.0,-0.0,0.0,2.76,71.95 +2020-09-23 04:00:00,12.08,0.0,-0.0,0.0,2.55,71.95 +2020-09-23 05:00:00,12.1,1.0,0.0,1.0,2.41,71.95 +2020-09-23 06:00:00,12.13,65.0,10.0,63.0,1.66,71.95 +2020-09-23 07:00:00,12.25,113.0,5.76,111.0,1.52,74.6 +2020-09-23 08:00:00,12.27,117.0,0.0,117.0,1.52,77.35 +2020-09-23 09:00:00,12.51,180.0,1.78,179.0,1.72,77.45 +2020-09-23 10:00:00,12.65,191.0,0.0,191.0,1.72,77.45 +2020-09-23 11:00:00,12.78,83.0,0.0,83.0,1.93,80.3 +2020-09-23 12:00:00,12.7,134.0,0.0,134.0,2.28,83.25 +2020-09-23 13:00:00,12.62,118.0,0.0,118.0,2.83,86.25 +2020-09-23 14:00:00,12.68,34.0,0.0,34.0,3.66,86.25 +2020-09-23 15:00:00,12.96,28.0,0.0,28.0,4.0,89.4 +2020-09-23 16:00:00,13.89,22.0,0.0,22.0,4.28,89.45 +2020-09-23 17:00:00,16.39,0.0,-0.0,0.0,4.28,86.55 +2020-09-23 18:00:00,17.26,0.0,-0.0,0.0,4.21,86.65 +2020-09-23 19:00:00,10.57,0.0,-0.0,0.0,2.9,95.9 +2020-09-23 20:00:00,9.96,0.0,-0.0,0.0,6.0,95.9 +2020-09-23 21:00:00,8.78,0.0,-0.0,0.0,6.69,99.4 +2020-09-23 22:00:00,7.92,0.0,-0.0,0.0,5.45,95.8 +2020-09-23 23:00:00,7.81,0.0,-0.0,0.0,5.59,92.4 +2020-09-24 00:00:00,7.92,0.0,-0.0,0.0,5.52,89.1 +2020-09-24 01:00:00,8.33,0.0,-0.0,0.0,5.45,89.1 +2020-09-24 02:00:00,8.05,0.0,-0.0,0.0,5.93,89.1 +2020-09-24 03:00:00,7.95,0.0,-0.0,0.0,6.14,89.1 +2020-09-24 04:00:00,7.71,0.0,-0.0,0.0,5.79,89.05 +2020-09-24 05:00:00,7.63,4.0,0.0,4.0,5.79,89.05 +2020-09-24 06:00:00,7.69,121.0,204.37,81.0,6.07,85.85 +2020-09-24 07:00:00,8.21,208.0,145.75,158.0,4.83,89.1 +2020-09-24 08:00:00,9.02,296.0,156.43,223.0,5.1,85.95 +2020-09-24 09:00:00,10.1,327.0,100.34,271.0,5.72,79.95 +2020-09-24 10:00:00,11.01,444.0,242.15,296.0,6.07,74.45 +2020-09-24 11:00:00,11.87,244.0,8.04,239.0,6.55,66.75 +2020-09-24 12:00:00,12.28,214.0,5.08,211.0,6.69,59.85 +2020-09-24 13:00:00,12.57,418.0,374.28,224.0,6.69,53.6 +2020-09-24 14:00:00,12.66,284.0,226.61,191.0,6.83,47.85 +2020-09-24 15:00:00,12.29,161.0,138.62,123.0,6.41,47.75 +2020-09-24 16:00:00,11.87,18.0,0.0,18.0,5.66,47.6 +2020-09-24 17:00:00,11.15,0.0,-0.0,0.0,5.52,49.3 +2020-09-24 18:00:00,10.38,0.0,-0.0,0.0,5.66,57.15 +2020-09-24 19:00:00,8.93,0.0,-0.0,0.0,4.97,74.15 +2020-09-24 20:00:00,8.14,0.0,-0.0,0.0,4.69,74.05 +2020-09-24 21:00:00,7.43,0.0,-0.0,0.0,4.28,79.7 +2020-09-24 22:00:00,7.05,0.0,-0.0,0.0,5.03,82.65 +2020-09-24 23:00:00,6.63,0.0,-0.0,0.0,4.21,85.75 +2020-09-25 00:00:00,6.45,0.0,-0.0,0.0,4.0,82.6 +2020-09-25 01:00:00,6.37,0.0,-0.0,0.0,3.86,85.7 +2020-09-25 02:00:00,6.36,0.0,-0.0,0.0,3.72,82.55 +2020-09-25 03:00:00,6.11,0.0,-0.0,0.0,3.59,82.55 +2020-09-25 04:00:00,5.92,0.0,-0.0,0.0,3.59,82.55 +2020-09-25 05:00:00,5.99,6.0,0.0,6.0,3.52,82.55 +2020-09-25 06:00:00,6.21,147.0,433.76,64.0,3.31,85.7 +2020-09-25 07:00:00,6.91,250.0,307.18,146.0,4.76,82.6 +2020-09-25 08:00:00,8.08,321.0,216.45,221.0,4.55,71.35 +2020-09-25 09:00:00,9.27,351.0,139.18,274.0,4.21,63.8 +2020-09-25 10:00:00,10.16,449.0,254.1,295.0,4.07,55.05 +2020-09-25 11:00:00,11.16,327.0,56.74,292.0,4.07,49.3 +2020-09-25 12:00:00,11.64,407.0,198.31,291.0,3.79,44.1 +2020-09-25 13:00:00,12.1,389.0,290.76,240.0,3.66,40.95 +2020-09-25 14:00:00,12.27,324.0,383.35,169.0,3.38,39.4 +2020-09-25 15:00:00,12.27,199.0,328.43,111.0,3.17,37.9 +2020-09-25 16:00:00,11.82,67.0,204.29,44.0,2.34,39.25 +2020-09-25 17:00:00,10.55,0.0,-0.0,0.0,1.52,45.55 +2020-09-25 18:00:00,8.64,0.0,-0.0,0.0,1.45,61.3 +2020-09-25 19:00:00,7.05,0.0,-0.0,0.0,1.72,63.5 +2020-09-25 20:00:00,6.21,0.0,-0.0,0.0,1.72,68.3 +2020-09-25 21:00:00,5.12,0.0,-0.0,0.0,1.86,73.55 +2020-09-25 22:00:00,4.31,0.0,-0.0,0.0,1.86,79.2 +2020-09-25 23:00:00,3.93,0.0,-0.0,0.0,1.79,79.2 +2020-09-26 00:00:00,3.61,0.0,-0.0,0.0,1.86,82.25 +2020-09-26 01:00:00,2.98,0.0,-0.0,0.0,1.86,82.2 +2020-09-26 02:00:00,2.65,0.0,-0.0,0.0,1.93,85.35 +2020-09-26 03:00:00,2.68,0.0,-0.0,0.0,2.14,85.35 +2020-09-26 04:00:00,2.91,0.0,-0.0,0.0,2.21,82.2 +2020-09-26 05:00:00,3.25,6.0,0.0,6.0,2.28,79.1 +2020-09-26 06:00:00,4.62,77.0,37.44,70.0,2.21,73.45 +2020-09-26 07:00:00,7.31,262.0,401.15,128.0,2.28,68.5 +2020-09-26 08:00:00,9.81,381.0,450.48,175.0,3.1,57.05 +2020-09-26 09:00:00,11.27,476.0,494.25,205.0,3.38,49.3 +2020-09-26 10:00:00,12.68,588.0,728.92,150.0,3.66,42.7 +2020-09-26 11:00:00,14.11,530.0,474.28,240.0,3.45,38.45 +2020-09-26 12:00:00,15.34,516.0,528.24,210.0,3.31,34.5 +2020-09-26 13:00:00,16.1,448.0,534.97,177.0,3.24,31.0 +2020-09-26 14:00:00,16.3,200.0,55.24,178.0,3.17,29.8 +2020-09-26 15:00:00,16.05,159.0,164.28,116.0,2.55,33.5 +2020-09-26 16:00:00,15.03,43.0,47.03,38.0,2.41,35.85 +2020-09-26 17:00:00,13.57,0.0,-0.0,0.0,2.48,39.8 +2020-09-26 18:00:00,12.58,0.0,-0.0,0.0,2.69,41.1 +2020-09-26 19:00:00,12.09,0.0,-0.0,0.0,2.9,45.95 +2020-09-26 20:00:00,11.85,0.0,-0.0,0.0,3.24,45.8 +2020-09-26 21:00:00,11.7,0.0,-0.0,0.0,3.38,42.4 +2020-09-26 22:00:00,11.59,0.0,-0.0,0.0,3.38,40.8 +2020-09-26 23:00:00,11.5,0.0,-0.0,0.0,3.38,40.8 +2020-09-27 00:00:00,11.58,0.0,-0.0,0.0,3.38,40.8 +2020-09-27 01:00:00,11.62,0.0,-0.0,0.0,3.31,40.8 +2020-09-27 02:00:00,11.67,0.0,-0.0,0.0,3.31,40.8 +2020-09-27 03:00:00,11.74,0.0,-0.0,0.0,3.17,42.4 +2020-09-27 04:00:00,11.72,0.0,-0.0,0.0,2.97,42.4 +2020-09-27 05:00:00,11.63,0.0,0.0,0.0,2.83,44.1 +2020-09-27 06:00:00,11.82,93.0,109.56,73.0,2.62,45.8 +2020-09-27 07:00:00,13.38,108.0,6.07,106.0,2.76,49.85 +2020-09-27 08:00:00,13.98,186.0,19.89,177.0,2.48,51.9 +2020-09-27 09:00:00,14.95,188.0,3.68,186.0,2.69,50.25 +2020-09-27 10:00:00,15.84,310.0,60.43,274.0,2.76,50.5 +2020-09-27 11:00:00,17.29,466.0,325.08,269.0,2.76,52.75 +2020-09-27 12:00:00,18.37,524.0,615.42,171.0,2.76,59.05 +2020-09-27 13:00:00,19.3,465.0,675.11,127.0,2.83,63.5 +2020-09-27 14:00:00,19.88,351.0,629.81,104.0,2.69,61.4 +2020-09-27 15:00:00,20.12,207.0,504.8,78.0,2.21,59.35 +2020-09-27 16:00:00,19.58,55.0,169.88,38.0,1.17,63.6 +2020-09-27 17:00:00,17.91,0.0,-0.0,0.0,1.45,70.35 +2020-09-27 18:00:00,15.65,0.0,-0.0,0.0,2.07,77.85 +2020-09-27 19:00:00,14.96,0.0,-0.0,0.0,2.34,80.55 +2020-09-27 20:00:00,14.23,0.0,-0.0,0.0,2.41,77.65 +2020-09-27 21:00:00,13.65,0.0,-0.0,0.0,2.41,74.85 +2020-09-27 22:00:00,13.07,0.0,-0.0,0.0,2.34,72.1 +2020-09-27 23:00:00,12.37,0.0,-0.0,0.0,2.34,69.35 +2020-09-28 00:00:00,11.64,0.0,-0.0,0.0,2.34,66.75 +2020-09-28 01:00:00,11.01,0.0,-0.0,0.0,2.41,66.65 +2020-09-28 02:00:00,10.56,0.0,-0.0,0.0,2.55,64.1 +2020-09-28 03:00:00,10.27,0.0,-0.0,0.0,2.69,66.45 +2020-09-28 04:00:00,9.88,0.0,-0.0,0.0,2.76,68.9 +2020-09-28 05:00:00,9.47,0.0,0.0,0.0,2.76,68.9 +2020-09-28 06:00:00,9.8,121.0,308.79,66.0,2.55,71.5 +2020-09-28 07:00:00,12.37,267.0,483.19,110.0,2.28,69.35 +2020-09-28 08:00:00,14.11,213.0,42.43,194.0,2.9,62.55 +2020-09-28 09:00:00,14.79,103.0,0.0,103.0,3.31,67.35 +2020-09-28 10:00:00,14.54,72.0,0.0,72.0,3.72,75.0 +2020-09-28 11:00:00,14.29,82.0,0.0,82.0,3.66,77.65 +2020-09-28 12:00:00,14.25,70.0,0.0,70.0,3.38,77.65 +2020-09-28 13:00:00,14.22,70.0,0.0,70.0,2.97,77.65 +2020-09-28 14:00:00,14.12,120.0,2.59,119.0,3.1,74.95 +2020-09-28 15:00:00,13.96,102.0,28.07,95.0,3.31,67.15 +2020-09-28 16:00:00,13.6,30.0,10.66,29.0,2.9,60.2 +2020-09-28 17:00:00,12.74,0.0,-0.0,0.0,2.9,55.65 +2020-09-28 18:00:00,11.98,0.0,-0.0,0.0,3.03,51.5 +2020-09-28 19:00:00,10.67,0.0,-0.0,0.0,2.97,55.15 +2020-09-28 20:00:00,9.97,0.0,-0.0,0.0,2.83,55.05 +2020-09-28 21:00:00,9.19,0.0,-0.0,0.0,2.34,59.15 +2020-09-28 22:00:00,8.12,0.0,-0.0,0.0,1.93,63.7 +2020-09-28 23:00:00,6.96,0.0,-0.0,0.0,1.93,68.5 +2020-09-29 00:00:00,5.92,0.0,-0.0,0.0,1.86,73.7 +2020-09-29 01:00:00,5.15,0.0,-0.0,0.0,1.79,79.35 +2020-09-29 02:00:00,4.72,0.0,-0.0,0.0,1.86,82.35 +2020-09-29 03:00:00,4.29,0.0,-0.0,0.0,1.86,82.3 +2020-09-29 04:00:00,4.16,0.0,-0.0,0.0,1.86,82.3 +2020-09-29 05:00:00,3.97,0.0,0.0,0.0,1.79,82.3 +2020-09-29 06:00:00,4.66,131.0,420.34,58.0,1.45,85.55 +2020-09-29 07:00:00,7.03,283.0,596.27,92.0,1.45,79.65 +2020-09-29 08:00:00,8.68,419.0,686.22,115.0,1.52,76.85 +2020-09-29 09:00:00,9.94,520.0,723.73,134.0,1.24,74.2 +2020-09-29 10:00:00,10.99,519.0,505.81,223.0,1.38,61.85 +2020-09-29 11:00:00,11.47,498.0,408.39,255.0,1.52,57.55 +2020-09-29 12:00:00,11.79,407.0,238.38,273.0,1.17,55.4 +2020-09-29 13:00:00,12.09,441.0,560.56,167.0,0.76,51.5 +2020-09-29 14:00:00,12.38,353.0,663.15,101.0,0.41,49.6 +2020-09-29 15:00:00,12.23,200.0,493.5,80.0,0.28,47.75 +2020-09-29 16:00:00,11.9,50.0,194.06,33.0,0.28,49.45 +2020-09-29 17:00:00,10.31,0.0,-0.0,0.0,1.03,66.45 +2020-09-29 18:00:00,8.86,0.0,-0.0,0.0,1.31,68.7 +2020-09-29 19:00:00,7.08,0.0,-0.0,0.0,1.79,71.15 +2020-09-29 20:00:00,5.94,0.0,-0.0,0.0,2.0,76.55 +2020-09-29 21:00:00,5.38,0.0,-0.0,0.0,2.14,82.4 +2020-09-29 22:00:00,5.08,0.0,-0.0,0.0,2.28,79.35 +2020-09-29 23:00:00,4.93,0.0,-0.0,0.0,2.34,79.35 +2020-09-30 00:00:00,4.8,0.0,-0.0,0.0,2.34,79.3 +2020-09-30 01:00:00,4.59,0.0,-0.0,0.0,2.34,79.3 +2020-09-30 02:00:00,4.51,0.0,-0.0,0.0,2.41,76.3 +2020-09-30 03:00:00,4.57,0.0,-0.0,0.0,2.55,76.3 +2020-09-30 04:00:00,4.74,0.0,-0.0,0.0,2.69,73.45 +2020-09-30 05:00:00,4.89,0.0,0.0,0.0,2.76,70.8 +2020-09-30 06:00:00,5.62,128.0,431.44,55.0,2.69,70.85 +2020-09-30 07:00:00,7.71,284.0,630.33,85.0,2.41,66.05 +2020-09-30 08:00:00,10.29,423.0,727.99,104.0,2.9,57.15 +2020-09-30 09:00:00,12.2,523.0,762.81,120.0,2.76,49.6 +2020-09-30 10:00:00,13.74,578.0,762.18,136.0,2.55,42.95 +2020-09-30 11:00:00,15.01,583.0,737.93,148.0,2.34,38.7 +2020-09-30 12:00:00,15.89,522.0,623.67,175.0,2.48,37.4 +2020-09-30 13:00:00,16.39,454.0,648.23,141.0,2.41,36.15 +2020-09-30 14:00:00,16.48,343.0,633.84,106.0,2.21,36.15 +2020-09-30 15:00:00,16.19,198.0,527.48,73.0,2.0,36.15 +2020-09-30 16:00:00,11.83,43.0,159.73,30.0,2.36,48.24 +2020-09-30 17:00:00,11.37,0.0,-0.0,0.0,2.31,51.71 +2020-09-30 18:00:00,10.92,0.0,-0.0,0.0,2.25,55.17 +2020-09-30 19:00:00,10.47,0.0,-0.0,0.0,2.19,58.64 +2020-09-30 20:00:00,10.02,0.0,-0.0,0.0,2.14,62.1 +2020-09-30 21:00:00,9.57,0.0,-0.0,0.0,2.08,65.56 +2020-09-30 22:00:00,9.12,0.0,-0.0,0.0,2.03,69.03 +2020-09-30 23:00:00,8.67,0.0,-0.0,0.0,1.97,72.49 +2020-10-01 00:00:00,8.22,0.0,-0.0,0.0,1.92,75.96 +2020-10-01 01:00:00,7.77,0.0,-0.0,0.0,1.86,79.42 +2020-10-01 02:00:00,7.32,0.0,-0.0,0.0,1.8,82.89 +2020-10-01 03:00:00,6.87,0.0,-0.0,0.0,1.75,86.35 +2020-10-01 04:00:00,6.42,0.0,-0.0,0.0,1.69,89.81 +2020-10-01 05:00:00,5.96,0.0,0.0,0.0,1.64,93.28 +2020-10-01 06:00:00,5.51,121.0,406.77,54.0,1.58,96.74 +2020-10-01 07:00:00,5.06,270.0,581.87,89.0,1.53,100.0 +2020-10-01 08:00:00,8.89,417.0,740.74,96.0,2.14,85.95 +2020-10-01 09:00:00,10.44,532.0,837.09,94.0,2.07,77.15 +2020-10-01 10:00:00,11.21,588.0,844.06,103.0,1.79,74.45 +2020-10-01 11:00:00,12.0,581.0,770.63,131.0,1.72,69.35 +2020-10-01 12:00:00,12.64,573.0,879.02,89.0,1.79,64.55 +2020-10-01 13:00:00,12.8,477.0,803.11,94.0,2.0,64.55 +2020-10-01 14:00:00,12.78,345.0,701.44,87.0,2.21,64.55 +2020-10-01 15:00:00,12.54,190.0,515.6,71.0,2.21,66.95 +2020-10-01 16:00:00,11.91,34.0,93.1,27.0,2.0,69.35 +2020-10-01 17:00:00,10.77,0.0,-0.0,0.0,2.0,77.15 +2020-10-01 18:00:00,9.62,0.0,-0.0,0.0,2.07,82.9 +2020-10-01 19:00:00,9.22,0.0,-0.0,0.0,2.34,85.95 +2020-10-01 20:00:00,8.47,0.0,-0.0,0.0,2.41,89.1 +2020-10-01 21:00:00,8.02,0.0,-0.0,0.0,2.48,89.05 +2020-10-01 22:00:00,7.82,0.0,-0.0,0.0,2.55,92.35 +2020-10-01 23:00:00,7.82,0.0,-0.0,0.0,2.62,89.0 +2020-10-02 00:00:00,7.81,0.0,-0.0,0.0,2.62,85.8 +2020-10-02 01:00:00,7.58,0.0,-0.0,0.0,2.69,82.65 +2020-10-02 02:00:00,7.83,0.0,-0.0,0.0,2.76,82.65 +2020-10-02 03:00:00,7.91,0.0,-0.0,0.0,2.83,79.7 +2020-10-02 04:00:00,8.09,0.0,-0.0,0.0,2.97,79.7 +2020-10-02 05:00:00,8.35,0.0,-0.0,0.0,3.03,79.7 +2020-10-02 06:00:00,8.67,57.0,18.73,54.0,3.03,76.85 +2020-10-02 07:00:00,8.62,255.0,528.72,93.0,3.1,89.1 +2020-10-02 08:00:00,9.73,263.0,147.03,200.0,3.31,82.9 +2020-10-02 09:00:00,10.63,385.0,293.35,233.0,3.45,80.05 +2020-10-02 10:00:00,11.85,469.0,418.08,231.0,3.66,77.3 +2020-10-02 11:00:00,12.27,321.0,79.53,275.0,3.66,74.6 +2020-10-02 12:00:00,12.81,523.0,728.67,126.0,3.66,74.7 +2020-10-02 13:00:00,13.23,208.0,25.48,196.0,3.79,72.1 +2020-10-02 14:00:00,13.24,126.0,5.53,124.0,3.45,74.75 +2020-10-02 15:00:00,13.18,79.0,13.35,76.0,3.03,74.75 +2020-10-02 16:00:00,12.68,11.0,0.0,11.0,2.69,77.45 +2020-10-02 17:00:00,12.24,0.0,-0.0,0.0,2.76,80.2 +2020-10-02 18:00:00,12.03,0.0,-0.0,0.0,2.97,77.35 +2020-10-02 19:00:00,11.7,0.0,-0.0,0.0,3.1,83.1 +2020-10-02 20:00:00,11.58,0.0,-0.0,0.0,3.17,83.1 +2020-10-02 21:00:00,11.44,0.0,-0.0,0.0,3.24,83.1 +2020-10-02 22:00:00,11.19,0.0,-0.0,0.0,3.31,86.1 +2020-10-02 23:00:00,10.62,0.0,-0.0,0.0,3.31,89.25 +2020-10-03 00:00:00,10.26,0.0,-0.0,0.0,3.31,92.5 +2020-10-03 01:00:00,9.22,0.0,-0.0,0.0,3.38,92.45 +2020-10-03 02:00:00,8.92,0.0,-0.0,0.0,3.45,92.45 +2020-10-03 03:00:00,8.71,0.0,-0.0,0.0,3.59,95.85 +2020-10-03 04:00:00,8.73,0.0,-0.0,0.0,3.66,95.85 +2020-10-03 05:00:00,8.7,0.0,-0.0,0.0,3.79,95.85 +2020-10-03 06:00:00,8.96,93.0,211.96,60.0,4.07,92.45 +2020-10-03 07:00:00,9.75,233.0,417.61,107.0,4.41,92.45 +2020-10-03 08:00:00,10.86,294.0,247.88,189.0,4.83,89.25 +2020-10-03 09:00:00,11.7,424.0,444.42,196.0,4.9,83.1 +2020-10-03 10:00:00,12.53,546.0,757.21,119.0,5.24,77.45 +2020-10-03 11:00:00,13.66,455.0,368.38,244.0,5.03,74.85 +2020-10-03 12:00:00,13.93,190.0,3.71,188.0,5.1,74.95 +2020-10-03 13:00:00,14.29,201.0,23.66,190.0,5.59,72.3 +2020-10-03 14:00:00,14.35,169.0,47.8,152.0,5.66,72.3 +2020-10-03 15:00:00,14.11,143.0,247.15,89.0,5.45,72.3 +2020-10-03 16:00:00,13.48,23.0,47.72,20.0,4.9,74.85 +2020-10-03 17:00:00,12.55,0.0,-0.0,0.0,5.17,77.45 +2020-10-03 18:00:00,11.91,0.0,-0.0,0.0,5.52,77.35 +2020-10-03 19:00:00,10.38,0.0,-0.0,0.0,5.59,77.15 +2020-10-03 20:00:00,10.1,0.0,-0.0,0.0,5.72,79.95 +2020-10-03 21:00:00,9.94,0.0,-0.0,0.0,5.79,79.95 +2020-10-03 22:00:00,9.89,0.0,-0.0,0.0,5.79,79.95 +2020-10-03 23:00:00,9.8,0.0,-0.0,0.0,5.66,82.9 +2020-10-04 00:00:00,9.84,0.0,-0.0,0.0,5.66,82.9 +2020-10-04 01:00:00,9.75,0.0,-0.0,0.0,5.59,82.9 +2020-10-04 02:00:00,9.66,0.0,-0.0,0.0,5.45,82.9 +2020-10-04 03:00:00,9.58,0.0,-0.0,0.0,5.38,79.9 +2020-10-04 04:00:00,9.48,0.0,-0.0,0.0,5.31,79.9 +2020-10-04 05:00:00,9.35,0.0,-0.0,0.0,5.45,79.85 +2020-10-04 06:00:00,9.43,112.0,443.25,45.0,5.52,77.0 +2020-10-04 07:00:00,9.94,257.0,606.04,77.0,4.9,79.95 +2020-10-04 08:00:00,10.98,399.0,745.19,87.0,5.59,77.2 +2020-10-04 09:00:00,12.07,454.0,576.9,161.0,5.79,74.6 +2020-10-04 10:00:00,13.51,243.0,21.48,231.0,6.41,69.65 +2020-10-04 11:00:00,14.12,118.0,0.0,118.0,6.14,69.75 +2020-10-04 12:00:00,14.44,78.0,0.0,78.0,6.28,69.85 +2020-10-04 13:00:00,14.41,78.0,0.0,78.0,6.14,72.3 +2020-10-04 14:00:00,14.07,51.0,0.0,51.0,5.86,72.3 +2020-10-04 15:00:00,13.59,56.0,0.0,56.0,5.52,72.2 +2020-10-04 16:00:00,12.91,17.0,17.62,16.0,5.72,72.1 +2020-10-04 17:00:00,12.37,0.0,-0.0,0.0,5.72,77.35 +2020-10-04 18:00:00,12.18,0.0,-0.0,0.0,5.66,77.35 +2020-10-04 19:00:00,11.12,0.0,-0.0,0.0,5.59,80.1 +2020-10-04 20:00:00,11.1,0.0,-0.0,0.0,5.66,83.05 +2020-10-04 21:00:00,11.15,0.0,-0.0,0.0,5.52,83.05 +2020-10-04 22:00:00,11.22,0.0,-0.0,0.0,5.31,83.05 +2020-10-04 23:00:00,11.24,0.0,-0.0,0.0,5.03,86.1 +2020-10-05 00:00:00,11.23,0.0,-0.0,0.0,4.83,86.1 +2020-10-05 01:00:00,11.22,0.0,-0.0,0.0,4.69,86.1 +2020-10-05 02:00:00,11.15,0.0,-0.0,0.0,4.55,86.1 +2020-10-05 03:00:00,11.2,0.0,-0.0,0.0,4.41,86.1 +2020-10-05 04:00:00,11.08,0.0,-0.0,0.0,4.34,89.3 +2020-10-05 05:00:00,11.06,0.0,-0.0,0.0,4.28,89.3 +2020-10-05 06:00:00,11.23,67.0,75.03,56.0,4.14,89.3 +2020-10-05 07:00:00,10.92,163.0,119.75,128.0,3.66,86.1 +2020-10-05 08:00:00,11.66,291.0,258.61,184.0,3.86,86.15 +2020-10-05 09:00:00,12.62,430.0,503.26,177.0,4.21,80.3 +2020-10-05 10:00:00,13.53,401.0,260.33,257.0,4.07,77.6 +2020-10-05 11:00:00,14.24,323.0,96.16,269.0,4.07,74.95 +2020-10-05 12:00:00,14.7,185.0,3.79,183.0,4.14,75.0 +2020-10-05 13:00:00,15.06,189.0,19.87,180.0,4.0,72.45 +2020-10-05 14:00:00,14.92,109.0,2.91,108.0,3.72,75.0 +2020-10-05 15:00:00,14.68,99.0,67.88,85.0,3.38,75.0 +2020-10-05 16:00:00,14.15,15.0,19.75,14.0,3.17,77.65 +2020-10-05 17:00:00,13.58,0.0,-0.0,0.0,3.17,80.4 +2020-10-05 18:00:00,13.18,0.0,-0.0,0.0,3.17,80.35 +2020-10-05 19:00:00,11.83,0.0,-0.0,0.0,3.03,86.15 +2020-10-05 20:00:00,11.52,0.0,-0.0,0.0,3.24,86.15 +2020-10-05 21:00:00,11.27,0.0,-0.0,0.0,3.03,89.3 +2020-10-05 22:00:00,10.92,0.0,-0.0,0.0,3.03,89.3 +2020-10-05 23:00:00,10.51,0.0,-0.0,0.0,3.03,92.5 +2020-10-06 00:00:00,10.17,0.0,-0.0,0.0,3.1,95.9 +2020-10-06 01:00:00,9.99,0.0,-0.0,0.0,3.17,95.9 +2020-10-06 02:00:00,9.95,0.0,-0.0,0.0,3.1,95.9 +2020-10-06 03:00:00,9.93,0.0,-0.0,0.0,3.17,95.9 +2020-10-06 04:00:00,10.05,0.0,-0.0,0.0,3.17,95.9 +2020-10-06 05:00:00,10.05,0.0,-0.0,0.0,3.17,95.9 +2020-10-06 06:00:00,10.27,79.0,176.01,54.0,3.31,95.9 +2020-10-06 07:00:00,10.81,136.0,59.12,119.0,3.52,95.9 +2020-10-06 08:00:00,11.7,248.0,149.22,187.0,3.38,89.3 +2020-10-06 09:00:00,12.33,339.0,219.08,230.0,3.31,89.35 +2020-10-06 10:00:00,13.24,542.0,812.44,97.0,3.79,83.25 +2020-10-06 11:00:00,14.14,553.0,818.43,98.0,4.14,77.65 +2020-10-06 12:00:00,14.0,510.0,787.93,99.0,4.21,77.65 +2020-10-06 13:00:00,14.25,297.0,205.77,205.0,4.34,77.65 +2020-10-06 14:00:00,14.14,152.0,38.52,139.0,4.28,77.65 +2020-10-06 15:00:00,13.84,91.0,59.95,79.0,3.86,80.4 +2020-10-06 16:00:00,13.33,11.0,0.0,11.0,3.59,83.25 +2020-10-06 17:00:00,12.76,0.0,-0.0,0.0,3.66,86.25 +2020-10-06 18:00:00,12.5,0.0,-0.0,0.0,3.45,83.25 +2020-10-06 19:00:00,12.13,0.0,-0.0,0.0,3.52,86.2 +2020-10-06 20:00:00,11.91,0.0,-0.0,0.0,3.59,83.15 +2020-10-06 21:00:00,11.74,0.0,-0.0,0.0,3.52,86.15 +2020-10-06 22:00:00,11.41,0.0,-0.0,0.0,3.24,86.15 +2020-10-06 23:00:00,11.13,0.0,-0.0,0.0,2.76,89.3 +2020-10-07 00:00:00,11.1,0.0,-0.0,0.0,2.55,89.3 +2020-10-07 01:00:00,11.05,0.0,-0.0,0.0,2.55,89.3 +2020-10-07 02:00:00,11.01,0.0,-0.0,0.0,2.62,89.3 +2020-10-07 03:00:00,11.0,0.0,-0.0,0.0,2.62,89.3 +2020-10-07 04:00:00,10.97,0.0,-0.0,0.0,2.69,89.3 +2020-10-07 05:00:00,10.94,0.0,-0.0,0.0,2.69,89.3 +2020-10-07 06:00:00,11.02,65.0,87.3,53.0,2.76,89.3 +2020-10-07 07:00:00,11.68,166.0,152.06,123.0,2.21,89.3 +2020-10-07 08:00:00,12.64,248.0,153.53,186.0,2.55,86.25 +2020-10-07 09:00:00,13.34,149.0,0.0,149.0,2.97,83.25 +2020-10-07 10:00:00,13.97,106.0,0.0,106.0,2.83,77.65 +2020-10-07 11:00:00,14.38,121.0,0.0,121.0,2.97,77.65 +2020-10-07 12:00:00,14.73,64.0,0.0,64.0,3.1,75.0 +2020-10-07 13:00:00,14.93,111.0,0.0,111.0,3.03,72.45 +2020-10-07 14:00:00,15.16,136.0,24.14,128.0,3.17,72.45 +2020-10-07 15:00:00,15.22,112.0,164.86,80.0,2.76,72.45 +2020-10-07 16:00:00,14.9,10.0,0.0,10.0,2.48,75.0 +2020-10-07 17:00:00,14.16,0.0,-0.0,0.0,2.48,77.65 +2020-10-07 18:00:00,13.48,0.0,-0.0,0.0,2.69,77.6 +2020-10-07 19:00:00,12.46,0.0,-0.0,0.0,2.62,80.3 +2020-10-07 20:00:00,11.95,0.0,-0.0,0.0,2.9,80.2 +2020-10-07 21:00:00,11.62,0.0,-0.0,0.0,3.1,80.15 +2020-10-07 22:00:00,11.28,0.0,-0.0,0.0,3.17,83.05 +2020-10-07 23:00:00,10.72,0.0,-0.0,0.0,3.17,83.0 +2020-10-08 00:00:00,10.27,0.0,-0.0,0.0,3.31,86.0 +2020-10-08 01:00:00,9.82,0.0,-0.0,0.0,3.38,85.95 +2020-10-08 02:00:00,9.63,0.0,-0.0,0.0,3.45,85.95 +2020-10-08 03:00:00,9.37,0.0,-0.0,0.0,3.52,85.95 +2020-10-08 04:00:00,9.15,0.0,-0.0,0.0,3.59,89.1 +2020-10-08 05:00:00,8.94,0.0,-0.0,0.0,3.59,89.1 +2020-10-08 06:00:00,9.07,63.0,82.79,52.0,3.66,89.1 +2020-10-08 07:00:00,10.14,172.0,165.47,126.0,3.1,89.2 +2020-10-08 08:00:00,11.25,313.0,366.07,167.0,4.0,86.1 +2020-10-08 09:00:00,12.22,489.0,804.73,97.0,4.34,80.2 +2020-10-08 10:00:00,13.33,543.0,812.12,107.0,4.28,74.75 +2020-10-08 11:00:00,14.46,556.0,828.03,105.0,4.55,67.35 +2020-10-08 12:00:00,15.32,509.0,788.25,107.0,4.55,65.05 +2020-10-08 13:00:00,15.81,430.0,776.65,92.0,4.48,60.65 +2020-10-08 14:00:00,15.76,290.0,596.19,96.0,4.28,60.65 +2020-10-08 15:00:00,15.17,153.0,515.77,56.0,3.72,62.75 +2020-10-08 16:00:00,13.99,9.0,0.0,9.0,3.72,64.85 +2020-10-08 17:00:00,12.79,0.0,-0.0,0.0,3.86,69.45 +2020-10-08 18:00:00,12.04,0.0,-0.0,0.0,4.07,71.95 +2020-10-08 19:00:00,11.54,0.0,-0.0,0.0,4.28,71.85 +2020-10-08 20:00:00,11.08,0.0,-0.0,0.0,4.34,71.75 +2020-10-08 21:00:00,10.6,0.0,-0.0,0.0,4.34,71.7 +2020-10-08 22:00:00,10.1,0.0,-0.0,0.0,4.28,74.3 +2020-10-08 23:00:00,9.59,0.0,-0.0,0.0,4.07,74.2 +2020-10-09 00:00:00,9.2,0.0,-0.0,0.0,3.86,76.95 +2020-10-09 01:00:00,8.92,0.0,-0.0,0.0,3.72,74.15 +2020-10-09 02:00:00,8.75,0.0,-0.0,0.0,3.66,76.85 +2020-10-09 03:00:00,8.51,0.0,-0.0,0.0,3.59,74.05 +2020-10-09 04:00:00,8.33,0.0,-0.0,0.0,3.59,76.8 +2020-10-09 05:00:00,8.19,0.0,-0.0,0.0,3.59,76.8 +2020-10-09 06:00:00,8.14,59.0,70.17,50.0,3.59,76.8 +2020-10-09 07:00:00,8.59,224.0,475.84,94.0,3.1,82.75 +2020-10-09 08:00:00,9.95,376.0,721.14,92.0,3.38,74.3 +2020-10-09 09:00:00,11.48,486.0,809.31,96.0,3.31,66.75 +2020-10-09 10:00:00,13.03,543.0,827.98,103.0,3.31,62.3 +2020-10-09 11:00:00,14.09,537.0,766.21,124.0,3.38,58.1 +2020-10-09 12:00:00,14.84,482.0,672.38,143.0,3.38,56.15 +2020-10-09 13:00:00,15.1,399.0,635.96,126.0,3.45,56.25 +2020-10-09 14:00:00,14.96,242.0,334.99,135.0,3.24,58.35 +2020-10-09 15:00:00,14.22,53.0,0.0,53.0,2.69,64.85 +2020-10-09 16:00:00,12.72,1.0,0.0,1.0,2.76,69.45 +2020-10-09 17:00:00,11.14,0.0,-0.0,0.0,2.83,74.45 +2020-10-09 18:00:00,9.91,0.0,-0.0,0.0,2.76,74.3 +2020-10-09 19:00:00,8.64,0.0,-0.0,0.0,2.76,71.35 +2020-10-09 20:00:00,7.79,0.0,-0.0,0.0,2.62,73.9 +2020-10-09 21:00:00,7.03,0.0,-0.0,0.0,2.41,76.65 +2020-10-09 22:00:00,6.3,0.0,-0.0,0.0,2.34,76.55 +2020-10-09 23:00:00,5.66,0.0,-0.0,0.0,2.28,76.5 +2020-10-10 00:00:00,5.06,0.0,-0.0,0.0,2.28,79.35 +2020-10-10 01:00:00,4.6,0.0,-0.0,0.0,2.21,79.3 +2020-10-10 02:00:00,4.04,0.0,-0.0,0.0,2.14,79.2 +2020-10-10 03:00:00,3.42,0.0,-0.0,0.0,2.07,82.25 +2020-10-10 04:00:00,2.84,0.0,-0.0,0.0,2.0,85.4 +2020-10-10 05:00:00,2.27,0.0,-0.0,0.0,2.0,88.65 +2020-10-10 06:00:00,2.06,89.0,396.31,40.0,1.86,88.65 +2020-10-10 07:00:00,4.5,241.0,633.41,71.0,1.31,85.55 +2020-10-10 08:00:00,7.72,387.0,787.05,81.0,1.38,76.7 +2020-10-10 09:00:00,9.72,487.0,824.51,94.0,1.86,66.35 +2020-10-10 10:00:00,11.19,541.0,830.87,104.0,2.34,59.6 +2020-10-10 11:00:00,12.27,548.0,823.07,109.0,2.62,55.5 +2020-10-10 12:00:00,12.87,506.0,806.62,104.0,2.69,53.6 +2020-10-10 13:00:00,13.07,427.0,805.46,86.0,2.83,53.7 +2020-10-10 14:00:00,12.77,295.0,692.32,78.0,2.83,55.65 +2020-10-10 15:00:00,12.02,145.0,516.75,54.0,2.34,62.1 +2020-10-10 16:00:00,10.53,5.0,0.0,5.0,2.28,66.55 +2020-10-10 17:00:00,8.88,0.0,-0.0,0.0,2.28,74.15 +2020-10-10 18:00:00,7.57,0.0,-0.0,0.0,2.21,79.65 +2020-10-10 19:00:00,6.52,0.0,-0.0,0.0,1.93,79.55 +2020-10-10 20:00:00,5.14,0.0,-0.0,0.0,1.79,85.6 +2020-10-10 21:00:00,4.06,0.0,-0.0,0.0,1.79,88.8 +2020-10-10 22:00:00,3.24,0.0,-0.0,0.0,1.93,88.7 +2020-10-10 23:00:00,2.67,0.0,-0.0,0.0,2.07,92.15 +2020-10-11 00:00:00,2.41,0.0,-0.0,0.0,1.66,88.65 +2020-10-11 01:00:00,2.57,0.0,-0.0,0.0,1.38,92.15 +2020-10-11 02:00:00,1.96,0.0,-0.0,0.0,1.45,92.1 +2020-10-11 03:00:00,2.58,0.0,-0.0,0.0,1.24,92.15 +2020-10-11 04:00:00,2.01,0.0,-0.0,0.0,1.45,92.1 +2020-10-11 05:00:00,1.52,0.0,-0.0,0.0,1.59,92.05 +2020-10-11 06:00:00,1.93,70.0,201.66,46.0,1.45,88.65 +2020-10-11 07:00:00,4.27,204.0,383.2,103.0,0.69,88.8 +2020-10-11 08:00:00,6.84,362.0,677.51,102.0,0.9,82.6 +2020-10-11 09:00:00,8.85,477.0,808.25,96.0,1.52,74.05 +2020-10-11 10:00:00,10.23,534.0,824.22,105.0,1.86,66.45 +2020-10-11 11:00:00,11.3,543.0,829.99,105.0,1.86,59.6 +2020-10-11 12:00:00,11.98,501.0,812.06,101.0,1.93,53.5 +2020-10-11 13:00:00,12.2,420.0,797.67,87.0,2.21,53.5 +2020-10-11 14:00:00,11.86,288.0,682.94,78.0,2.48,57.55 +2020-10-11 15:00:00,11.02,138.0,505.38,52.0,2.28,61.85 +2020-10-11 16:00:00,9.47,0.0,0.0,0.0,2.28,68.9 +2020-10-11 17:00:00,7.86,0.0,-0.0,0.0,2.07,76.8 +2020-10-11 18:00:00,6.57,0.0,-0.0,0.0,1.86,82.6 +2020-10-11 19:00:00,5.14,0.0,-0.0,0.0,1.86,88.85 +2020-10-11 20:00:00,3.98,0.0,-0.0,0.0,1.66,92.2 +2020-10-11 21:00:00,3.29,0.0,-0.0,0.0,1.45,95.7 +2020-10-11 22:00:00,3.21,0.0,-0.0,0.0,1.24,95.7 +2020-10-11 23:00:00,3.34,0.0,-0.0,0.0,1.1,95.7 +2020-10-12 00:00:00,3.61,0.0,-0.0,0.0,0.97,99.4 +2020-10-12 01:00:00,3.46,0.0,-0.0,0.0,0.9,95.7 +2020-10-12 02:00:00,2.95,0.0,-0.0,0.0,0.83,95.7 +2020-10-12 03:00:00,2.1,0.0,-0.0,0.0,0.55,99.4 +2020-10-12 04:00:00,2.32,0.0,-0.0,0.0,0.41,95.7 +2020-10-12 05:00:00,1.99,0.0,-0.0,0.0,0.76,95.65 +2020-10-12 06:00:00,2.13,40.0,26.23,37.0,0.83,99.4 +2020-10-12 07:00:00,3.25,83.0,3.87,82.0,0.28,99.4 +2020-10-12 08:00:00,3.79,96.0,0.0,96.0,0.69,99.4 +2020-10-12 09:00:00,4.88,70.0,0.0,70.0,1.17,92.25 +2020-10-12 10:00:00,5.98,86.0,0.0,86.0,1.38,88.95 +2020-10-12 11:00:00,7.24,83.0,0.0,83.0,1.52,85.75 +2020-10-12 12:00:00,8.07,70.0,0.0,70.0,1.59,79.7 +2020-10-12 13:00:00,8.68,77.0,0.0,77.0,1.66,76.85 +2020-10-12 14:00:00,8.79,140.0,39.79,128.0,1.66,76.85 +2020-10-12 15:00:00,8.57,124.0,401.76,58.0,1.66,74.05 +2020-10-12 16:00:00,7.77,0.0,0.0,0.0,1.52,79.65 +2020-10-12 17:00:00,6.39,0.0,-0.0,0.0,1.66,79.55 +2020-10-12 18:00:00,5.09,0.0,-0.0,0.0,1.59,88.85 +2020-10-12 19:00:00,3.78,0.0,-0.0,0.0,1.66,95.7 +2020-10-12 20:00:00,3.17,0.0,-0.0,0.0,1.66,95.7 +2020-10-12 21:00:00,2.54,0.0,-0.0,0.0,1.59,95.7 +2020-10-12 22:00:00,2.16,0.0,-0.0,0.0,1.59,95.65 +2020-10-12 23:00:00,1.87,0.0,-0.0,0.0,1.59,95.65 +2020-10-13 00:00:00,1.75,0.0,-0.0,0.0,1.59,95.65 +2020-10-13 01:00:00,1.88,0.0,-0.0,0.0,1.66,95.65 +2020-10-13 02:00:00,1.46,0.0,-0.0,0.0,1.59,95.65 +2020-10-13 03:00:00,1.48,0.0,-0.0,0.0,1.45,95.65 +2020-10-13 04:00:00,0.96,0.0,-0.0,0.0,1.45,99.35 +2020-10-13 05:00:00,0.69,0.0,-0.0,0.0,1.45,100.0 +2020-10-13 06:00:00,1.13,40.0,27.34,37.0,1.38,99.35 +2020-10-13 07:00:00,3.07,120.0,51.2,107.0,1.79,95.7 +2020-10-13 08:00:00,3.86,174.0,37.47,160.0,1.59,88.8 +2020-10-13 09:00:00,5.38,432.0,640.13,137.0,1.24,79.4 +2020-10-13 10:00:00,7.13,513.0,796.79,107.0,1.17,76.65 +2020-10-13 11:00:00,8.62,522.0,801.67,108.0,1.45,71.35 +2020-10-13 12:00:00,9.57,488.0,817.02,95.0,1.86,66.35 +2020-10-13 13:00:00,10.09,404.0,791.17,83.0,2.14,64.0 +2020-10-13 14:00:00,10.09,273.0,669.6,75.0,2.34,64.0 +2020-10-13 15:00:00,9.57,123.0,448.16,52.0,1.93,66.35 +2020-10-13 16:00:00,8.3,0.0,0.0,0.0,1.86,73.95 +2020-10-13 17:00:00,6.83,0.0,-0.0,0.0,1.93,79.55 +2020-10-13 18:00:00,5.57,0.0,-0.0,0.0,1.93,85.65 +2020-10-13 19:00:00,4.25,0.0,-0.0,0.0,1.72,92.2 +2020-10-13 20:00:00,3.14,0.0,-0.0,0.0,1.66,95.7 +2020-10-13 21:00:00,2.18,0.0,-0.0,0.0,1.66,95.65 +2020-10-13 22:00:00,1.44,0.0,-0.0,0.0,1.72,95.65 +2020-10-13 23:00:00,0.84,0.0,-0.0,0.0,1.72,99.35 +2020-10-14 00:00:00,0.33,0.0,-0.0,0.0,1.66,99.4 +2020-10-14 01:00:00,-0.02,0.0,-0.0,0.0,1.66,99.4 +2020-10-14 02:00:00,-0.35,0.0,-0.0,0.0,1.66,99.4 +2020-10-14 03:00:00,-0.63,0.0,-0.0,0.0,1.66,99.4 +2020-10-14 04:00:00,-0.88,0.0,-0.0,0.0,1.45,99.4 +2020-10-14 05:00:00,-0.94,0.0,-0.0,0.0,1.52,99.4 +2020-10-14 06:00:00,-0.65,39.0,28.55,36.0,1.38,99.4 +2020-10-14 07:00:00,1.65,101.0,24.09,95.0,1.45,95.65 +2020-10-14 08:00:00,3.19,149.0,16.28,143.0,1.66,99.4 +2020-10-14 09:00:00,4.46,82.0,0.0,82.0,2.28,92.25 +2020-10-14 10:00:00,5.42,97.0,0.0,97.0,2.41,88.9 +2020-10-14 11:00:00,6.3,72.0,0.0,72.0,2.62,85.7 +2020-10-14 12:00:00,6.87,152.0,0.0,152.0,2.83,82.6 +2020-10-14 13:00:00,7.23,191.0,40.01,175.0,2.97,82.6 +2020-10-14 14:00:00,7.27,243.0,500.25,98.0,2.97,82.6 +2020-10-14 15:00:00,7.06,56.0,19.66,53.0,2.69,82.6 +2020-10-14 16:00:00,6.48,0.0,-0.0,0.0,2.41,82.6 +2020-10-14 17:00:00,6.2,0.0,-0.0,0.0,2.48,88.95 +2020-10-14 18:00:00,6.45,0.0,-0.0,0.0,2.62,85.75 +2020-10-14 19:00:00,7.03,0.0,-0.0,0.0,2.62,89.0 +2020-10-14 20:00:00,7.3,0.0,-0.0,0.0,2.62,92.35 +2020-10-14 21:00:00,7.39,0.0,-0.0,0.0,2.55,89.0 +2020-10-14 22:00:00,7.41,0.0,-0.0,0.0,2.62,92.35 +2020-10-14 23:00:00,7.41,0.0,-0.0,0.0,2.62,92.35 +2020-10-15 00:00:00,7.39,0.0,-0.0,0.0,2.62,92.35 +2020-10-15 01:00:00,7.98,0.0,-0.0,0.0,2.55,95.8 +2020-10-15 02:00:00,7.95,0.0,-0.0,0.0,2.69,95.8 +2020-10-15 03:00:00,7.85,0.0,-0.0,0.0,2.69,99.4 +2020-10-15 04:00:00,7.78,0.0,-0.0,0.0,2.69,100.0 +2020-10-15 05:00:00,7.81,0.0,-0.0,0.0,2.62,100.0 +2020-10-15 06:00:00,7.75,34.0,19.92,32.0,2.48,100.0 +2020-10-15 07:00:00,8.06,77.0,4.1,76.0,2.28,99.4 +2020-10-15 08:00:00,8.37,94.0,0.0,94.0,2.83,92.4 +2020-10-15 09:00:00,8.41,80.0,0.0,80.0,2.62,92.4 +2020-10-15 10:00:00,9.07,98.0,0.0,98.0,2.55,85.95 +2020-10-15 11:00:00,9.31,100.0,0.0,100.0,2.28,85.95 +2020-10-15 12:00:00,9.54,163.0,4.26,161.0,2.48,82.9 +2020-10-15 13:00:00,9.71,112.0,0.0,112.0,2.55,79.9 +2020-10-15 14:00:00,9.69,59.0,0.0,59.0,2.41,79.9 +2020-10-15 15:00:00,9.49,36.0,0.0,36.0,1.52,79.9 +2020-10-15 16:00:00,9.0,0.0,-0.0,0.0,1.03,82.85 +2020-10-15 17:00:00,8.2,0.0,-0.0,0.0,1.24,89.05 +2020-10-15 18:00:00,7.28,0.0,-0.0,0.0,1.52,95.8 +2020-10-15 19:00:00,6.33,0.0,-0.0,0.0,1.31,95.8 +2020-10-15 20:00:00,5.67,0.0,-0.0,0.0,1.45,95.75 +2020-10-15 21:00:00,5.29,0.0,-0.0,0.0,1.52,95.75 +2020-10-15 22:00:00,5.14,0.0,-0.0,0.0,1.59,95.75 +2020-10-15 23:00:00,4.75,0.0,-0.0,0.0,1.72,95.75 +2020-10-16 00:00:00,4.78,0.0,-0.0,0.0,1.72,95.75 +2020-10-16 01:00:00,4.61,0.0,-0.0,0.0,1.79,95.75 +2020-10-16 02:00:00,4.77,0.0,-0.0,0.0,1.72,95.75 +2020-10-16 03:00:00,4.72,0.0,-0.0,0.0,1.66,95.75 +2020-10-16 04:00:00,4.88,0.0,-0.0,0.0,1.45,95.75 +2020-10-16 05:00:00,5.53,0.0,-0.0,0.0,1.31,95.75 +2020-10-16 06:00:00,5.96,57.0,240.22,34.0,1.24,95.8 +2020-10-16 07:00:00,6.28,121.0,79.4,102.0,1.17,95.8 +2020-10-16 08:00:00,7.73,190.0,78.12,162.0,1.1,95.8 +2020-10-16 09:00:00,8.7,317.0,249.45,206.0,1.03,89.1 +2020-10-16 10:00:00,9.52,349.0,231.18,235.0,1.03,82.9 +2020-10-16 11:00:00,9.95,326.0,166.15,243.0,0.69,79.95 +2020-10-16 12:00:00,10.34,150.0,2.16,149.0,0.48,82.95 +2020-10-16 13:00:00,10.31,229.0,118.46,183.0,1.03,86.0 +2020-10-16 14:00:00,10.26,127.0,43.12,115.0,1.38,89.2 +2020-10-16 15:00:00,9.77,56.0,35.42,51.0,1.31,92.45 +2020-10-16 16:00:00,8.89,0.0,-0.0,0.0,1.45,89.1 +2020-10-16 17:00:00,8.22,0.0,-0.0,0.0,1.45,95.8 +2020-10-16 18:00:00,7.87,0.0,-0.0,0.0,1.52,92.4 +2020-10-16 19:00:00,7.42,0.0,-0.0,0.0,1.72,95.8 +2020-10-16 20:00:00,6.78,0.0,-0.0,0.0,1.72,95.8 +2020-10-16 21:00:00,6.27,0.0,-0.0,0.0,1.72,95.8 +2020-10-16 22:00:00,5.95,0.0,-0.0,0.0,1.72,92.3 +2020-10-16 23:00:00,5.5,0.0,-0.0,0.0,1.79,92.3 +2020-10-17 00:00:00,4.84,0.0,-0.0,0.0,1.93,92.25 +2020-10-17 01:00:00,4.34,0.0,-0.0,0.0,2.0,92.25 +2020-10-17 02:00:00,4.04,0.0,-0.0,0.0,2.07,95.75 +2020-10-17 03:00:00,3.32,0.0,-0.0,0.0,2.07,92.15 +2020-10-17 04:00:00,2.76,0.0,-0.0,0.0,2.0,95.7 +2020-10-17 05:00:00,2.22,0.0,-0.0,0.0,2.0,95.65 +2020-10-17 06:00:00,2.08,48.0,164.7,33.0,2.0,95.65 +2020-10-17 07:00:00,3.29,181.0,405.27,86.0,2.0,95.7 +2020-10-17 08:00:00,4.6,329.0,684.94,87.0,2.97,85.55 +2020-10-17 09:00:00,5.72,430.0,757.34,97.0,2.97,82.5 +2020-10-17 10:00:00,6.65,481.0,762.83,109.0,3.03,76.65 +2020-10-17 11:00:00,7.49,489.0,767.32,110.0,3.38,71.15 +2020-10-17 12:00:00,7.93,450.0,759.67,102.0,3.45,63.6 +2020-10-17 13:00:00,8.16,370.0,739.71,87.0,3.45,61.2 +2020-10-17 14:00:00,7.95,242.0,620.08,73.0,3.17,58.9 +2020-10-17 15:00:00,7.33,101.0,420.7,44.0,2.62,63.35 +2020-10-17 16:00:00,6.03,0.0,-0.0,0.0,2.62,63.25 +2020-10-17 17:00:00,4.93,0.0,-0.0,0.0,2.48,65.55 +2020-10-17 18:00:00,4.03,0.0,-0.0,0.0,2.34,70.6 +2020-10-17 19:00:00,3.45,0.0,-0.0,0.0,2.28,70.5 +2020-10-17 20:00:00,2.47,0.0,-0.0,0.0,2.0,76.0 +2020-10-17 21:00:00,1.45,0.0,-0.0,0.0,1.93,78.9 +2020-10-17 22:00:00,0.5,0.0,-0.0,0.0,1.72,81.9 +2020-10-17 23:00:00,0.18,0.0,-0.0,0.0,1.52,81.8 +2020-10-18 00:00:00,0.56,0.0,-0.0,0.0,1.17,78.75 +2020-10-18 01:00:00,0.87,0.0,-0.0,0.0,0.97,75.75 +2020-10-18 02:00:00,1.07,0.0,-0.0,0.0,0.69,75.75 +2020-10-18 03:00:00,0.78,0.0,-0.0,0.0,0.48,78.75 +2020-10-18 04:00:00,0.61,0.0,-0.0,0.0,0.34,81.9 +2020-10-18 05:00:00,0.44,0.0,-0.0,0.0,0.48,78.75 +2020-10-18 06:00:00,0.35,59.0,381.94,26.0,0.55,78.75 +2020-10-18 07:00:00,0.99,197.0,579.45,64.0,0.0,88.55 +2020-10-18 08:00:00,2.68,329.0,703.57,84.0,0.55,82.15 +2020-10-18 09:00:00,4.32,426.0,755.06,98.0,0.97,76.3 +2020-10-18 10:00:00,5.6,480.0,771.47,108.0,1.24,73.65 +2020-10-18 11:00:00,6.67,495.0,810.91,99.0,1.24,71.05 +2020-10-18 12:00:00,7.39,453.0,789.1,96.0,0.97,71.15 +2020-10-18 13:00:00,7.78,367.0,748.26,85.0,0.76,71.15 +2020-10-18 14:00:00,7.85,232.0,569.58,80.0,0.55,71.15 +2020-10-18 15:00:00,7.54,95.0,400.36,43.0,0.76,71.15 +2020-10-18 16:00:00,6.32,0.0,-0.0,0.0,1.38,76.55 +2020-10-18 17:00:00,4.84,0.0,-0.0,0.0,0.76,82.4 +2020-10-18 18:00:00,4.41,0.0,-0.0,0.0,0.9,82.35 +2020-10-18 19:00:00,1.78,0.0,-0.0,0.0,1.79,95.65 +2020-10-18 20:00:00,0.53,0.0,-0.0,0.0,2.0,99.4 +2020-10-18 21:00:00,-0.03,0.0,-0.0,0.0,2.0,99.4 +2020-10-18 22:00:00,-0.15,0.0,-0.0,0.0,2.14,95.6 +2020-10-18 23:00:00,-0.02,0.0,-0.0,0.0,2.21,95.6 +2020-10-19 00:00:00,-0.01,0.0,-0.0,0.0,2.21,95.6 +2020-10-19 01:00:00,0.02,0.0,-0.0,0.0,2.21,95.6 +2020-10-19 02:00:00,0.09,0.0,-0.0,0.0,2.34,92.0 +2020-10-19 03:00:00,0.45,0.0,-0.0,0.0,2.55,92.0 +2020-10-19 04:00:00,0.76,0.0,-0.0,0.0,2.62,95.65 +2020-10-19 05:00:00,1.12,0.0,-0.0,0.0,2.69,92.05 +2020-10-19 06:00:00,2.05,13.0,0.0,13.0,2.97,88.65 +2020-10-19 07:00:00,3.66,28.0,0.0,28.0,3.17,82.25 +2020-10-19 08:00:00,4.97,94.0,0.0,94.0,3.17,76.4 +2020-10-19 09:00:00,6.3,241.0,88.55,203.0,3.38,76.55 +2020-10-19 10:00:00,7.03,142.0,0.0,142.0,3.52,73.8 +2020-10-19 11:00:00,7.5,230.0,35.21,213.0,3.45,76.7 +2020-10-19 12:00:00,7.74,141.0,0.0,141.0,3.52,79.65 +2020-10-19 13:00:00,8.17,71.0,0.0,71.0,2.9,79.7 +2020-10-19 14:00:00,7.97,20.0,0.0,20.0,3.31,76.8 +2020-10-19 15:00:00,7.56,42.0,16.09,40.0,3.17,76.7 +2020-10-19 16:00:00,6.91,0.0,-0.0,0.0,3.03,79.55 +2020-10-19 17:00:00,6.39,0.0,-0.0,0.0,3.17,79.55 +2020-10-19 18:00:00,5.82,0.0,-0.0,0.0,3.17,88.9 +2020-10-19 19:00:00,5.79,0.0,-0.0,0.0,2.76,88.9 +2020-10-19 20:00:00,5.7,0.0,-0.0,0.0,2.69,92.3 +2020-10-19 21:00:00,5.66,0.0,-0.0,0.0,2.48,92.3 +2020-10-19 22:00:00,5.55,0.0,-0.0,0.0,2.48,92.3 +2020-10-19 23:00:00,5.36,0.0,-0.0,0.0,2.69,88.9 +2020-10-20 00:00:00,4.96,0.0,-0.0,0.0,2.76,92.25 +2020-10-20 01:00:00,4.8,0.0,-0.0,0.0,2.9,92.25 +2020-10-20 02:00:00,4.7,0.0,-0.0,0.0,3.03,92.25 +2020-10-20 03:00:00,4.41,0.0,-0.0,0.0,3.03,88.85 +2020-10-20 04:00:00,4.23,0.0,-0.0,0.0,3.1,92.2 +2020-10-20 05:00:00,4.26,0.0,-0.0,0.0,3.17,92.2 +2020-10-20 06:00:00,4.8,30.0,51.91,26.0,3.31,92.25 +2020-10-20 07:00:00,5.21,19.0,0.0,19.0,3.31,88.85 +2020-10-20 08:00:00,5.99,234.0,236.66,154.0,3.03,85.7 +2020-10-20 09:00:00,6.67,92.0,0.0,92.0,4.55,85.75 +2020-10-20 10:00:00,7.41,283.0,112.45,230.0,3.79,85.8 +2020-10-20 11:00:00,7.74,342.0,236.79,229.0,3.45,89.0 +2020-10-20 12:00:00,7.85,79.0,0.0,79.0,3.93,89.0 +2020-10-20 13:00:00,6.81,78.0,0.0,78.0,4.41,89.0 +2020-10-20 14:00:00,6.44,137.0,93.88,113.0,4.34,85.75 +2020-10-20 15:00:00,5.94,21.0,0.0,21.0,3.66,88.95 +2020-10-20 16:00:00,5.6,0.0,-0.0,0.0,3.38,88.9 +2020-10-20 17:00:00,5.33,0.0,-0.0,0.0,3.79,85.65 +2020-10-20 18:00:00,4.89,0.0,-0.0,0.0,4.14,85.6 +2020-10-20 19:00:00,4.74,0.0,-0.0,0.0,4.14,88.85 +2020-10-20 20:00:00,4.42,0.0,-0.0,0.0,4.62,85.55 +2020-10-20 21:00:00,4.1,0.0,-0.0,0.0,4.83,85.5 +2020-10-20 22:00:00,3.77,0.0,-0.0,0.0,5.17,85.45 +2020-10-20 23:00:00,3.38,0.0,-0.0,0.0,5.59,82.25 +2020-10-21 00:00:00,3.21,0.0,-0.0,0.0,5.93,85.4 +2020-10-21 01:00:00,3.14,0.0,-0.0,0.0,5.86,85.4 +2020-10-21 02:00:00,3.16,0.0,-0.0,0.0,5.72,85.4 +2020-10-21 03:00:00,3.01,0.0,-0.0,0.0,5.59,85.4 +2020-10-21 04:00:00,2.88,0.0,-0.0,0.0,5.66,85.4 +2020-10-21 05:00:00,2.68,0.0,-0.0,0.0,5.45,85.35 +2020-10-21 06:00:00,2.51,37.0,165.8,25.0,4.83,85.35 +2020-10-21 07:00:00,2.7,122.0,125.66,95.0,5.52,88.65 +2020-10-21 08:00:00,3.35,292.0,549.64,109.0,6.69,82.25 +2020-10-21 09:00:00,3.99,323.0,322.53,188.0,6.48,76.25 +2020-10-21 10:00:00,5.08,202.0,19.32,193.0,6.62,65.55 +2020-10-21 11:00:00,6.3,225.0,33.92,209.0,6.97,56.3 +2020-10-21 12:00:00,7.04,355.0,390.3,185.0,6.41,54.3 +2020-10-21 13:00:00,7.17,133.0,8.33,130.0,5.86,56.45 +2020-10-21 14:00:00,6.48,218.0,579.74,73.0,4.9,63.35 +2020-10-21 15:00:00,6.3,41.0,26.45,38.0,5.1,63.25 +2020-10-21 16:00:00,6.2,0.0,-0.0,0.0,4.97,60.85 +2020-10-21 17:00:00,5.96,0.0,-0.0,0.0,4.83,58.55 +2020-10-21 18:00:00,5.77,0.0,-0.0,0.0,4.62,58.45 +2020-10-21 19:00:00,5.2,0.0,-0.0,0.0,4.41,65.55 +2020-10-21 20:00:00,4.75,0.0,-0.0,0.0,4.48,68.0 +2020-10-21 21:00:00,4.47,0.0,-0.0,0.0,4.55,65.4 +2020-10-21 22:00:00,4.32,0.0,-0.0,0.0,4.62,65.4 +2020-10-21 23:00:00,4.1,0.0,-0.0,0.0,4.55,67.9 +2020-10-22 00:00:00,3.9,0.0,-0.0,0.0,4.34,67.9 +2020-10-22 01:00:00,3.43,0.0,-0.0,0.0,4.21,67.8 +2020-10-22 02:00:00,3.21,0.0,-0.0,0.0,4.21,67.7 +2020-10-22 03:00:00,2.98,0.0,-0.0,0.0,4.28,67.7 +2020-10-22 04:00:00,2.82,0.0,-0.0,0.0,4.41,67.7 +2020-10-22 05:00:00,2.72,0.0,-0.0,0.0,4.48,67.6 +2020-10-22 06:00:00,2.68,31.0,118.17,23.0,4.48,67.6 +2020-10-22 07:00:00,3.42,140.0,242.88,89.0,4.41,67.8 +2020-10-22 08:00:00,4.76,262.0,402.61,130.0,3.72,68.0 +2020-10-22 09:00:00,6.1,376.0,590.37,132.0,4.34,58.55 +2020-10-22 10:00:00,7.18,310.0,184.59,225.0,4.41,58.65 +2020-10-22 11:00:00,8.19,334.0,235.96,224.0,4.41,58.9 +2020-10-22 12:00:00,8.9,310.0,248.83,203.0,4.21,56.9 +2020-10-22 13:00:00,9.14,205.0,107.21,167.0,3.86,59.15 +2020-10-22 14:00:00,8.89,115.0,49.05,103.0,3.1,59.15 +2020-10-22 15:00:00,8.41,39.0,27.77,36.0,2.62,61.3 +2020-10-22 16:00:00,7.55,0.0,-0.0,0.0,2.62,63.5 +2020-10-22 17:00:00,6.91,0.0,-0.0,0.0,2.55,65.85 +2020-10-22 18:00:00,6.2,0.0,-0.0,0.0,2.41,68.3 +2020-10-22 19:00:00,5.7,0.0,-0.0,0.0,2.21,65.65 +2020-10-22 20:00:00,5.05,0.0,-0.0,0.0,2.14,68.1 +2020-10-22 21:00:00,4.29,0.0,-0.0,0.0,2.14,73.4 +2020-10-22 22:00:00,3.33,0.0,-0.0,0.0,2.21,73.3 +2020-10-22 23:00:00,2.37,0.0,-0.0,0.0,2.07,79.0 +2020-10-23 00:00:00,1.42,0.0,-0.0,0.0,2.07,85.25 +2020-10-23 01:00:00,0.6,0.0,-0.0,0.0,2.07,88.5 +2020-10-23 02:00:00,0.07,0.0,-0.0,0.0,2.14,88.45 +2020-10-23 03:00:00,-0.23,0.0,-0.0,0.0,2.21,91.95 +2020-10-23 04:00:00,-0.41,0.0,-0.0,0.0,2.28,88.45 +2020-10-23 05:00:00,-0.35,0.0,-0.0,0.0,2.41,88.45 +2020-10-23 06:00:00,-0.35,34.0,206.24,21.0,2.34,88.45 +2020-10-23 07:00:00,0.94,166.0,487.56,66.0,2.28,88.55 +2020-10-23 08:00:00,3.77,299.0,672.28,82.0,2.41,82.25 +2020-10-23 09:00:00,6.1,382.0,656.79,114.0,3.03,70.95 +2020-10-23 10:00:00,7.77,435.0,685.58,123.0,3.24,71.15 +2020-10-23 11:00:00,9.04,442.0,688.09,125.0,3.38,66.25 +2020-10-23 12:00:00,10.0,415.0,749.12,97.0,3.79,64.0 +2020-10-23 13:00:00,10.85,316.0,607.53,104.0,4.0,61.75 +2020-10-23 14:00:00,11.39,188.0,413.86,89.0,3.79,61.85 +2020-10-23 15:00:00,10.85,50.0,97.32,40.0,3.45,64.1 +2020-10-23 16:00:00,9.78,0.0,-0.0,0.0,3.59,68.9 +2020-10-23 17:00:00,8.98,0.0,-0.0,0.0,3.66,71.4 +2020-10-23 18:00:00,8.4,0.0,-0.0,0.0,3.79,76.85 +2020-10-23 19:00:00,8.01,0.0,-0.0,0.0,4.0,82.7 +2020-10-23 20:00:00,7.89,0.0,-0.0,0.0,4.0,85.85 +2020-10-23 21:00:00,8.33,0.0,-0.0,0.0,4.0,85.85 +2020-10-23 22:00:00,8.55,0.0,-0.0,0.0,3.93,82.75 +2020-10-23 23:00:00,8.41,0.0,-0.0,0.0,3.86,79.75 +2020-10-24 00:00:00,8.4,0.0,-0.0,0.0,3.93,79.75 +2020-10-24 01:00:00,8.82,0.0,-0.0,0.0,4.0,79.75 +2020-10-24 02:00:00,8.69,0.0,-0.0,0.0,4.0,79.75 +2020-10-24 03:00:00,8.86,0.0,-0.0,0.0,3.86,82.75 +2020-10-24 04:00:00,9.02,0.0,-0.0,0.0,3.86,82.85 +2020-10-24 05:00:00,8.99,0.0,-0.0,0.0,3.86,82.85 +2020-10-24 06:00:00,8.67,25.0,85.66,20.0,3.93,82.75 +2020-10-24 07:00:00,8.4,172.0,589.33,54.0,4.21,76.85 +2020-10-24 08:00:00,9.01,191.0,138.49,147.0,3.66,76.95 +2020-10-24 09:00:00,9.85,127.0,2.48,126.0,6.62,68.9 +2020-10-24 10:00:00,10.26,189.0,17.79,181.0,5.17,69.0 +2020-10-24 11:00:00,10.74,113.0,0.0,113.0,5.45,64.1 +2020-10-24 12:00:00,11.13,254.0,124.1,202.0,5.52,59.6 +2020-10-24 13:00:00,11.15,304.0,576.41,106.0,5.1,57.4 +2020-10-24 14:00:00,10.67,125.0,94.08,103.0,4.62,57.3 +2020-10-24 15:00:00,9.84,69.0,389.67,31.0,4.28,61.55 +2020-10-24 16:00:00,8.97,0.0,-0.0,0.0,4.0,61.45 +2020-10-24 17:00:00,8.16,0.0,-0.0,0.0,3.79,63.6 +2020-10-24 18:00:00,7.31,0.0,-0.0,0.0,3.72,68.4 +2020-10-24 19:00:00,6.76,0.0,-0.0,0.0,3.24,71.05 +2020-10-24 20:00:00,6.13,0.0,-0.0,0.0,3.31,73.7 +2020-10-24 21:00:00,5.86,0.0,-0.0,0.0,3.24,73.7 +2020-10-24 22:00:00,5.37,0.0,-0.0,0.0,3.03,76.5 +2020-10-24 23:00:00,4.83,0.0,-0.0,0.0,2.9,79.35 +2020-10-25 00:00:00,4.59,0.0,-0.0,0.0,2.83,82.35 +2020-10-25 01:00:00,4.38,0.0,-0.0,0.0,2.62,82.35 +2020-10-25 02:00:00,4.0,0.0,-0.0,0.0,2.55,85.5 +2020-10-25 03:00:00,3.62,0.0,-0.0,0.0,2.41,88.75 +2020-10-25 04:00:00,3.38,0.0,-0.0,0.0,2.28,88.75 +2020-10-25 05:00:00,3.1,0.0,-0.0,0.0,2.07,92.15 +2020-10-25 06:00:00,2.91,15.0,18.62,14.0,1.93,88.7 +2020-10-25 07:00:00,3.98,88.0,46.07,79.0,2.14,92.2 +2020-10-25 08:00:00,4.81,194.0,150.33,147.0,3.31,88.85 +2020-10-25 09:00:00,5.45,106.0,0.0,106.0,3.45,79.4 +2020-10-25 10:00:00,5.94,215.0,38.26,198.0,3.59,76.55 +2020-10-25 11:00:00,6.41,211.0,31.12,197.0,3.45,71.05 +2020-10-25 12:00:00,6.36,212.0,53.19,190.0,3.72,71.05 +2020-10-25 13:00:00,6.58,182.0,76.9,156.0,3.86,71.05 +2020-10-25 14:00:00,6.59,147.0,196.9,102.0,3.24,68.4 +2020-10-25 15:00:00,6.44,45.0,97.45,36.0,2.83,68.4 +2020-10-25 16:00:00,5.96,0.0,-0.0,0.0,2.55,68.3 +2020-10-25 17:00:00,5.36,0.0,-0.0,0.0,2.41,70.85 +2020-10-25 18:00:00,4.67,0.0,-0.0,0.0,2.34,76.3 +2020-10-25 19:00:00,4.17,0.0,-0.0,0.0,2.14,82.3 +2020-10-25 20:00:00,3.63,0.0,-0.0,0.0,2.48,82.25 +2020-10-25 21:00:00,3.27,0.0,-0.0,0.0,2.69,88.7 +2020-10-25 22:00:00,3.08,0.0,-0.0,0.0,2.83,88.7 +2020-10-25 23:00:00,3.02,0.0,-0.0,0.0,3.03,88.7 +2020-10-26 00:00:00,3.28,0.0,-0.0,0.0,3.38,88.7 +2020-10-26 01:00:00,3.4,0.0,-0.0,0.0,3.72,85.45 +2020-10-26 02:00:00,3.48,0.0,-0.0,0.0,3.79,85.45 +2020-10-26 03:00:00,3.65,0.0,-0.0,0.0,3.72,85.45 +2020-10-26 04:00:00,3.25,0.0,-0.0,0.0,3.52,88.7 +2020-10-26 05:00:00,3.15,0.0,-0.0,0.0,3.31,88.7 +2020-10-26 06:00:00,3.3,22.0,122.3,16.0,3.17,88.7 +2020-10-26 07:00:00,3.7,116.0,178.48,82.0,2.9,88.75 +2020-10-26 08:00:00,4.5,132.0,26.01,124.0,3.03,85.55 +2020-10-26 09:00:00,5.15,125.0,2.55,124.0,4.28,82.4 +2020-10-26 10:00:00,5.78,227.0,54.66,203.0,4.34,79.4 +2020-10-26 11:00:00,6.16,102.0,0.0,102.0,4.14,73.7 +2020-10-26 12:00:00,6.59,57.0,0.0,57.0,3.86,68.4 +2020-10-26 13:00:00,6.89,223.0,198.33,157.0,3.79,68.4 +2020-10-26 14:00:00,6.76,163.0,322.42,91.0,3.59,65.85 +2020-10-26 15:00:00,6.26,61.0,378.14,28.0,2.41,68.3 +2020-10-26 16:00:00,5.14,0.0,-0.0,0.0,1.79,73.55 +2020-10-26 17:00:00,3.62,0.0,-0.0,0.0,1.72,82.25 +2020-10-26 18:00:00,2.08,0.0,-0.0,0.0,1.72,88.65 +2020-10-26 19:00:00,1.96,0.0,-0.0,0.0,1.66,92.1 +2020-10-26 20:00:00,1.51,0.0,-0.0,0.0,1.59,95.65 +2020-10-26 21:00:00,1.16,0.0,-0.0,0.0,1.59,99.35 +2020-10-26 22:00:00,0.53,0.0,-0.0,0.0,1.66,99.4 +2020-10-26 23:00:00,-0.43,0.0,-0.0,0.0,1.72,99.4 +2020-10-27 00:00:00,-1.05,0.0,-0.0,0.0,1.79,99.4 +2020-10-27 01:00:00,-1.56,0.0,-0.0,0.0,1.79,99.4 +2020-10-27 02:00:00,-1.55,0.0,-0.0,0.0,1.79,99.4 +2020-10-27 03:00:00,-1.41,0.0,-0.0,0.0,1.93,99.4 +2020-10-27 04:00:00,-0.9,0.0,-0.0,0.0,1.93,99.4 +2020-10-27 05:00:00,-0.54,0.0,-0.0,0.0,2.0,99.4 +2020-10-27 06:00:00,-0.15,19.0,112.56,14.0,2.21,99.4 +2020-10-27 07:00:00,1.0,147.0,484.78,57.0,2.41,99.35 +2020-10-27 08:00:00,2.48,267.0,591.61,88.0,2.28,92.15 +2020-10-27 09:00:00,4.47,353.0,619.85,113.0,2.41,85.55 +2020-10-27 10:00:00,6.37,401.0,627.07,129.0,2.97,76.65 +2020-10-27 11:00:00,7.4,405.0,619.44,133.0,2.97,76.7 +2020-10-27 12:00:00,8.25,371.0,630.49,117.0,3.03,73.95 +2020-10-27 13:00:00,8.68,243.0,308.4,142.0,2.97,68.7 +2020-10-27 14:00:00,8.57,110.0,77.93,93.0,2.83,68.7 +2020-10-27 15:00:00,7.9,33.0,48.63,29.0,2.48,71.25 +2020-10-27 16:00:00,6.87,0.0,-0.0,0.0,2.55,76.65 +2020-10-27 17:00:00,6.34,0.0,-0.0,0.0,2.62,76.55 +2020-10-27 18:00:00,6.23,0.0,-0.0,0.0,2.55,76.55 +2020-10-27 19:00:00,6.41,0.0,-0.0,0.0,2.55,73.8 +2020-10-27 20:00:00,6.33,0.0,-0.0,0.0,2.69,73.7 +2020-10-27 21:00:00,6.63,0.0,-0.0,0.0,2.76,71.05 +2020-10-27 22:00:00,6.62,0.0,-0.0,0.0,2.76,68.4 +2020-10-27 23:00:00,7.0,0.0,-0.0,0.0,2.83,68.4 +2020-10-28 00:00:00,7.11,0.0,-0.0,0.0,2.69,68.4 +2020-10-28 01:00:00,7.05,0.0,-0.0,0.0,2.62,68.4 +2020-10-28 02:00:00,7.02,0.0,-0.0,0.0,2.55,65.85 +2020-10-28 03:00:00,7.04,0.0,-0.0,0.0,2.55,65.85 +2020-10-28 04:00:00,7.03,0.0,-0.0,0.0,2.48,65.85 +2020-10-28 05:00:00,7.03,0.0,-0.0,0.0,2.55,65.85 +2020-10-28 06:00:00,7.09,10.0,0.0,10.0,2.62,65.85 +2020-10-28 07:00:00,7.16,62.0,11.06,60.0,2.83,73.8 +2020-10-28 08:00:00,8.11,179.0,144.52,136.0,2.76,71.25 +2020-10-28 09:00:00,9.07,73.0,0.0,73.0,2.9,66.25 +2020-10-28 10:00:00,10.24,360.0,455.07,165.0,2.97,61.65 +2020-10-28 11:00:00,11.15,163.0,6.92,160.0,2.97,55.3 +2020-10-28 12:00:00,11.59,395.0,794.83,79.0,2.83,53.35 +2020-10-28 13:00:00,11.89,248.0,363.05,131.0,2.9,55.4 +2020-10-28 14:00:00,11.86,164.0,413.07,76.0,2.76,57.55 +2020-10-28 15:00:00,11.19,31.0,64.67,26.0,2.69,59.6 +2020-10-28 16:00:00,9.83,0.0,-0.0,0.0,2.55,63.9 +2020-10-28 17:00:00,8.85,0.0,-0.0,0.0,2.69,66.15 +2020-10-28 18:00:00,8.21,0.0,-0.0,0.0,2.69,68.6 +2020-10-28 19:00:00,8.08,0.0,-0.0,0.0,2.55,68.6 +2020-10-28 20:00:00,7.42,0.0,-0.0,0.0,2.62,68.5 +2020-10-28 21:00:00,6.67,0.0,-0.0,0.0,2.62,71.05 +2020-10-28 22:00:00,6.24,0.0,-0.0,0.0,2.48,73.7 +2020-10-28 23:00:00,5.52,0.0,-0.0,0.0,2.48,76.5 +2020-10-29 00:00:00,4.83,0.0,-0.0,0.0,2.28,79.35 +2020-10-29 01:00:00,4.03,0.0,-0.0,0.0,2.14,85.5 +2020-10-29 02:00:00,3.19,0.0,-0.0,0.0,2.07,88.7 +2020-10-29 03:00:00,2.36,0.0,-0.0,0.0,2.0,88.65 +2020-10-29 04:00:00,1.76,0.0,-0.0,0.0,2.0,95.65 +2020-10-29 05:00:00,1.44,0.0,-0.0,0.0,2.07,95.65 +2020-10-29 06:00:00,1.24,10.0,0.0,10.0,2.14,99.35 +2020-10-29 07:00:00,2.6,142.0,511.38,52.0,2.21,92.15 +2020-10-29 08:00:00,5.87,257.0,594.78,83.0,2.07,82.55 +2020-10-29 09:00:00,8.79,346.0,636.79,106.0,2.28,76.85 +2020-10-29 10:00:00,10.55,395.0,654.42,118.0,2.83,69.1 +2020-10-29 11:00:00,11.78,369.0,492.37,158.0,3.03,64.35 +2020-10-29 12:00:00,12.5,348.0,560.75,128.0,3.03,59.95 +2020-10-29 13:00:00,12.72,273.0,532.94,104.0,2.97,59.95 +2020-10-29 14:00:00,12.3,148.0,307.68,84.0,2.76,64.45 +2020-10-29 15:00:00,10.95,35.0,110.41,27.0,3.03,69.15 +2020-10-29 16:00:00,9.41,0.0,-0.0,0.0,3.38,74.2 +2020-10-29 17:00:00,8.67,0.0,-0.0,0.0,3.52,76.85 +2020-10-29 18:00:00,8.29,0.0,-0.0,0.0,3.66,79.7 +2020-10-29 19:00:00,8.03,0.0,-0.0,0.0,3.93,71.25 +2020-10-29 20:00:00,7.66,0.0,-0.0,0.0,4.21,73.9 +2020-10-29 21:00:00,7.18,0.0,-0.0,0.0,4.41,76.65 +2020-10-29 22:00:00,6.63,0.0,-0.0,0.0,4.48,73.8 +2020-10-29 23:00:00,6.1,0.0,-0.0,0.0,4.48,76.55 +2020-10-30 00:00:00,5.63,0.0,-0.0,0.0,4.55,79.4 +2020-10-30 01:00:00,5.36,0.0,-0.0,0.0,4.62,76.5 +2020-10-30 02:00:00,5.06,0.0,-0.0,0.0,4.69,79.35 +2020-10-30 03:00:00,4.82,0.0,-0.0,0.0,4.76,79.3 +2020-10-30 04:00:00,4.58,0.0,-0.0,0.0,4.76,76.3 +2020-10-30 05:00:00,4.34,0.0,-0.0,0.0,4.83,76.3 +2020-10-30 06:00:00,4.12,7.0,0.0,7.0,4.83,76.25 +2020-10-30 07:00:00,4.21,121.0,327.12,65.0,4.55,79.2 +2020-10-30 08:00:00,5.42,251.0,573.78,86.0,4.34,73.65 +2020-10-30 09:00:00,7.17,330.0,562.17,121.0,4.41,73.8 +2020-10-30 10:00:00,8.95,375.0,566.88,138.0,4.28,68.8 +2020-10-30 11:00:00,10.45,386.0,592.93,135.0,4.07,66.55 +2020-10-30 12:00:00,11.54,360.0,650.92,108.0,3.79,64.35 +2020-10-30 13:00:00,12.08,281.0,621.77,87.0,3.52,64.45 +2020-10-30 14:00:00,11.82,176.0,595.92,55.0,3.1,66.75 +2020-10-30 15:00:00,10.64,44.0,339.85,21.0,2.83,69.1 +2020-10-30 16:00:00,8.94,0.0,-0.0,0.0,2.76,76.95 +2020-10-30 17:00:00,7.83,0.0,-0.0,0.0,2.83,82.65 +2020-10-30 18:00:00,6.98,0.0,-0.0,0.0,2.97,85.75 +2020-10-30 19:00:00,6.47,0.0,-0.0,0.0,3.24,76.65 +2020-10-30 20:00:00,6.05,0.0,-0.0,0.0,3.24,79.5 +2020-10-30 21:00:00,5.75,0.0,-0.0,0.0,3.17,82.5 +2020-10-30 22:00:00,5.47,0.0,-0.0,0.0,3.17,79.4 +2020-10-30 23:00:00,5.24,0.0,-0.0,0.0,3.24,82.4 +2020-10-31 00:00:00,5.02,0.0,-0.0,0.0,3.17,82.4 +2020-10-31 01:00:00,4.66,0.0,-0.0,0.0,3.17,82.35 +2020-10-31 02:00:00,4.56,0.0,-0.0,0.0,3.17,82.35 +2020-10-31 03:00:00,4.16,0.0,-0.0,0.0,3.24,82.3 +2020-10-31 04:00:00,3.99,0.0,-0.0,0.0,3.31,82.3 +2020-10-31 05:00:00,3.9,0.0,-0.0,0.0,3.31,85.5 +2020-10-31 06:00:00,3.74,5.0,0.0,5.0,3.45,88.75 +2020-10-31 07:00:00,4.21,62.0,24.04,58.0,3.38,88.8 +2020-10-31 08:00:00,5.03,75.0,0.0,75.0,3.93,85.6 +2020-10-31 09:00:00,6.04,99.0,0.0,99.0,4.07,82.55 +2020-10-31 10:00:00,7.43,76.0,0.0,76.0,3.93,79.65 +2020-10-31 11:00:00,8.7,285.0,208.06,198.0,3.66,76.85 +2020-10-31 12:00:00,10.09,345.0,609.93,112.0,3.45,74.3 +2020-10-31 13:00:00,10.73,196.0,182.42,140.0,3.31,71.7 +2020-10-31 14:00:00,10.83,93.0,60.56,81.0,3.03,74.4 +2020-10-31 15:00:00,10.06,20.0,15.88,19.0,3.03,77.1 +2020-10-31 16:00:00,8.22,0.0,-0.0,0.0,3.5,86.67 +2020-10-31 17:00:00,8.18,0.0,-0.0,0.0,3.53,86.64 +2020-10-31 18:00:00,8.13,0.0,-0.0,0.0,3.56,86.62 +2020-10-31 19:00:00,8.08,0.0,-0.0,0.0,3.59,86.59 +2020-10-31 20:00:00,8.03,0.0,-0.0,0.0,3.62,86.56 +2020-10-31 21:00:00,7.98,0.0,-0.0,0.0,3.65,86.53 +2020-10-31 22:00:00,7.94,0.0,-0.0,0.0,3.69,86.5 +2020-10-31 23:00:00,7.89,0.0,-0.0,0.0,3.72,86.48 +2020-11-01 00:00:00,7.84,0.0,-0.0,0.0,3.75,86.45 +2020-11-01 01:00:00,7.79,0.0,-0.0,0.0,3.78,86.42 +2020-11-01 02:00:00,7.75,0.0,-0.0,0.0,3.81,86.39 +2020-11-01 03:00:00,7.7,0.0,-0.0,0.0,3.84,86.36 +2020-11-01 04:00:00,7.65,0.0,-0.0,0.0,3.87,86.34 +2020-11-01 05:00:00,7.6,0.0,-0.0,0.0,3.9,86.31 +2020-11-01 06:00:00,7.55,0.0,0.0,0.0,3.94,86.28 +2020-11-01 07:00:00,7.51,78.0,80.43,65.0,3.97,86.25 +2020-11-01 08:00:00,8.35,40.0,0.0,40.0,2.69,82.75 +2020-11-01 09:00:00,9.47,46.0,0.0,46.0,2.48,82.9 +2020-11-01 10:00:00,10.15,76.0,0.0,76.0,2.14,82.95 +2020-11-01 11:00:00,10.1,79.0,0.0,79.0,1.66,86.0 +2020-11-01 12:00:00,10.17,74.0,0.0,74.0,1.24,92.5 +2020-11-01 13:00:00,10.18,24.0,0.0,24.0,1.1,92.5 +2020-11-01 14:00:00,10.26,71.0,15.52,68.0,1.17,89.25 +2020-11-01 15:00:00,10.18,25.0,85.66,20.0,1.38,92.5 +2020-11-01 16:00:00,9.79,0.0,-0.0,0.0,1.38,89.2 +2020-11-01 17:00:00,9.36,0.0,-0.0,0.0,1.31,89.15 +2020-11-01 18:00:00,8.99,0.0,-0.0,0.0,1.31,92.45 +2020-11-01 19:00:00,7.97,0.0,-0.0,0.0,1.24,95.8 +2020-11-01 20:00:00,7.94,0.0,-0.0,0.0,1.31,95.8 +2020-11-01 21:00:00,8.18,0.0,-0.0,0.0,1.52,95.8 +2020-11-01 22:00:00,8.19,0.0,-0.0,0.0,1.52,92.4 +2020-11-01 23:00:00,8.05,0.0,-0.0,0.0,1.52,92.4 +2020-11-02 00:00:00,7.74,0.0,-0.0,0.0,1.38,89.05 +2020-11-02 01:00:00,8.06,0.0,-0.0,0.0,0.69,89.05 +2020-11-02 02:00:00,7.51,0.0,-0.0,0.0,0.9,89.0 +2020-11-02 03:00:00,7.08,0.0,-0.0,0.0,1.1,92.35 +2020-11-02 04:00:00,7.04,0.0,-0.0,0.0,1.24,92.35 +2020-11-02 05:00:00,6.99,0.0,-0.0,0.0,1.52,92.35 +2020-11-02 06:00:00,6.93,0.0,0.0,0.0,1.52,92.35 +2020-11-02 07:00:00,7.53,19.0,0.0,19.0,1.24,95.8 +2020-11-02 08:00:00,8.48,70.0,0.0,70.0,1.24,92.4 +2020-11-02 09:00:00,8.96,149.0,19.63,142.0,2.69,89.1 +2020-11-02 10:00:00,9.58,263.0,176.3,192.0,3.1,82.9 +2020-11-02 11:00:00,10.09,278.0,205.91,194.0,2.9,77.1 +2020-11-02 12:00:00,10.56,212.0,99.48,175.0,2.83,71.7 +2020-11-02 13:00:00,10.77,43.0,0.0,43.0,2.41,66.65 +2020-11-02 14:00:00,10.7,15.0,0.0,15.0,1.86,71.7 +2020-11-02 15:00:00,10.07,3.0,0.0,3.0,1.24,74.3 +2020-11-02 16:00:00,8.55,0.0,-0.0,0.0,1.17,85.9 +2020-11-02 17:00:00,7.21,0.0,-0.0,0.0,1.45,85.8 +2020-11-02 18:00:00,5.84,0.0,-0.0,0.0,1.72,88.9 +2020-11-02 19:00:00,5.02,0.0,-0.0,0.0,1.72,95.75 +2020-11-02 20:00:00,4.81,0.0,-0.0,0.0,1.86,95.75 +2020-11-02 21:00:00,4.96,0.0,-0.0,0.0,1.86,95.75 +2020-11-02 22:00:00,4.83,0.0,-0.0,0.0,2.14,92.25 +2020-11-02 23:00:00,5.35,0.0,-0.0,0.0,2.34,92.25 +2020-11-03 00:00:00,6.14,0.0,-0.0,0.0,2.34,88.9 +2020-11-03 01:00:00,5.67,0.0,-0.0,0.0,1.93,92.25 +2020-11-03 02:00:00,5.17,0.0,-0.0,0.0,1.72,95.75 +2020-11-03 03:00:00,5.41,0.0,-0.0,0.0,1.52,92.25 +2020-11-03 04:00:00,5.82,0.0,-0.0,0.0,1.52,92.3 +2020-11-03 05:00:00,5.92,0.0,-0.0,0.0,1.72,92.3 +2020-11-03 06:00:00,5.76,0.0,0.0,0.0,1.72,92.3 +2020-11-03 07:00:00,4.55,34.0,0.0,34.0,1.66,99.4 +2020-11-03 08:00:00,5.97,192.0,287.42,115.0,1.59,92.3 +2020-11-03 09:00:00,7.05,331.0,699.57,85.0,2.07,89.0 +2020-11-03 10:00:00,8.22,351.0,538.09,137.0,2.41,79.75 +2020-11-03 11:00:00,8.96,381.0,680.01,107.0,2.34,74.15 +2020-11-03 12:00:00,9.4,341.0,659.42,99.0,2.21,71.5 +2020-11-03 13:00:00,9.78,270.0,680.79,71.0,2.28,69.0 +2020-11-03 14:00:00,9.69,101.0,119.57,79.0,2.21,74.2 +2020-11-03 15:00:00,9.14,10.0,0.0,10.0,2.21,76.95 +2020-11-03 16:00:00,8.2,0.0,-0.0,0.0,2.28,82.7 +2020-11-03 17:00:00,7.95,0.0,-0.0,0.0,2.62,82.7 +2020-11-03 18:00:00,7.8,0.0,-0.0,0.0,2.34,85.85 +2020-11-03 19:00:00,8.01,0.0,-0.0,0.0,2.48,85.85 +2020-11-03 20:00:00,7.82,0.0,-0.0,0.0,2.48,85.85 +2020-11-03 21:00:00,7.39,0.0,-0.0,0.0,1.86,89.0 +2020-11-03 22:00:00,6.7,0.0,-0.0,0.0,1.72,92.35 +2020-11-03 23:00:00,6.54,0.0,-0.0,0.0,1.66,95.8 +2020-11-04 00:00:00,6.27,0.0,-0.0,0.0,1.72,95.8 +2020-11-04 01:00:00,6.11,0.0,-0.0,0.0,1.31,99.4 +2020-11-04 02:00:00,5.89,0.0,-0.0,0.0,1.24,95.75 +2020-11-04 03:00:00,5.82,0.0,-0.0,0.0,1.1,95.75 +2020-11-04 04:00:00,5.69,0.0,-0.0,0.0,1.03,95.75 +2020-11-04 05:00:00,6.15,0.0,-0.0,0.0,0.69,99.4 +2020-11-04 06:00:00,6.58,0.0,0.0,0.0,0.48,95.8 +2020-11-04 07:00:00,6.24,34.0,0.0,34.0,1.38,99.4 +2020-11-04 08:00:00,6.3,20.0,0.0,20.0,1.66,99.4 +2020-11-04 09:00:00,6.31,33.0,0.0,33.0,2.34,99.4 +2020-11-04 10:00:00,6.15,53.0,0.0,53.0,3.31,99.4 +2020-11-04 11:00:00,6.03,34.0,0.0,34.0,4.83,99.4 +2020-11-04 12:00:00,5.78,99.0,0.0,99.0,4.9,95.75 +2020-11-04 13:00:00,5.51,80.0,0.0,80.0,3.93,99.4 +2020-11-04 14:00:00,5.11,76.0,39.01,69.0,3.93,99.4 +2020-11-04 15:00:00,4.84,15.0,44.39,13.0,6.41,95.75 +2020-11-04 16:00:00,4.85,0.0,-0.0,0.0,6.0,95.75 +2020-11-04 17:00:00,5.26,0.0,-0.0,0.0,6.21,92.25 +2020-11-04 18:00:00,5.62,0.0,-0.0,0.0,6.28,95.75 +2020-11-04 19:00:00,5.76,0.0,-0.0,0.0,6.21,92.3 +2020-11-04 20:00:00,5.92,0.0,-0.0,0.0,5.93,92.3 +2020-11-04 21:00:00,5.92,0.0,-0.0,0.0,5.52,92.3 +2020-11-04 22:00:00,5.89,0.0,-0.0,0.0,5.31,88.9 +2020-11-04 23:00:00,6.0,0.0,-0.0,0.0,5.1,88.9 +2020-11-05 00:00:00,6.03,0.0,-0.0,0.0,4.83,88.9 +2020-11-05 01:00:00,6.44,0.0,-0.0,0.0,4.62,85.7 +2020-11-05 02:00:00,6.51,0.0,-0.0,0.0,4.55,85.7 +2020-11-05 03:00:00,6.51,0.0,-0.0,0.0,4.34,85.7 +2020-11-05 04:00:00,6.49,0.0,-0.0,0.0,4.14,85.7 +2020-11-05 05:00:00,6.46,0.0,-0.0,0.0,4.0,88.95 +2020-11-05 06:00:00,6.32,0.0,0.0,0.0,3.72,88.95 +2020-11-05 07:00:00,6.37,19.0,0.0,19.0,3.66,92.3 +2020-11-05 08:00:00,6.33,41.0,0.0,41.0,3.45,92.3 +2020-11-05 09:00:00,6.3,86.0,0.0,86.0,3.31,95.8 +2020-11-05 10:00:00,6.58,71.0,0.0,71.0,2.69,95.8 +2020-11-05 11:00:00,6.92,100.0,0.0,100.0,2.83,95.8 +2020-11-05 12:00:00,7.34,120.0,2.8,119.0,2.9,92.35 +2020-11-05 13:00:00,7.52,115.0,21.21,109.0,3.1,92.35 +2020-11-05 14:00:00,7.43,72.0,34.3,66.0,3.1,92.35 +2020-11-05 15:00:00,7.23,6.0,0.0,6.0,3.1,89.0 +2020-11-05 16:00:00,6.96,0.0,-0.0,0.0,3.24,89.0 +2020-11-05 17:00:00,6.65,0.0,-0.0,0.0,3.31,88.95 +2020-11-05 18:00:00,6.56,0.0,-0.0,0.0,3.45,88.95 +2020-11-05 19:00:00,6.37,0.0,-0.0,0.0,2.97,88.95 +2020-11-05 20:00:00,5.77,0.0,-0.0,0.0,2.9,92.3 +2020-11-05 21:00:00,5.45,0.0,-0.0,0.0,2.97,92.25 +2020-11-05 22:00:00,5.29,0.0,-0.0,0.0,3.1,92.25 +2020-11-05 23:00:00,5.17,0.0,-0.0,0.0,3.1,95.75 +2020-11-06 00:00:00,5.04,0.0,-0.0,0.0,3.17,95.75 +2020-11-06 01:00:00,4.96,0.0,-0.0,0.0,3.17,95.75 +2020-11-06 02:00:00,4.88,0.0,-0.0,0.0,3.1,95.75 +2020-11-06 03:00:00,4.72,0.0,-0.0,0.0,3.03,92.25 +2020-11-06 04:00:00,4.45,0.0,-0.0,0.0,3.1,95.75 +2020-11-06 05:00:00,4.39,0.0,-0.0,0.0,3.1,95.75 +2020-11-06 06:00:00,4.5,0.0,-0.0,0.0,3.17,95.75 +2020-11-06 07:00:00,5.28,97.0,347.36,49.0,2.9,92.25 +2020-11-06 08:00:00,6.44,212.0,512.87,82.0,2.76,88.95 +2020-11-06 09:00:00,7.66,275.0,421.43,133.0,3.45,85.8 +2020-11-06 10:00:00,8.68,340.0,548.43,130.0,3.93,76.85 +2020-11-06 11:00:00,9.39,298.0,321.97,173.0,3.86,68.9 +2020-11-06 12:00:00,9.87,278.0,368.73,148.0,4.0,66.45 +2020-11-06 13:00:00,10.0,245.0,585.67,82.0,3.79,66.45 +2020-11-06 14:00:00,9.65,122.0,334.21,65.0,3.17,68.9 +2020-11-06 15:00:00,8.69,9.0,0.0,9.0,2.62,74.05 +2020-11-06 16:00:00,7.55,0.0,-0.0,0.0,2.69,76.7 +2020-11-06 17:00:00,6.65,0.0,-0.0,0.0,2.69,82.55 +2020-11-06 18:00:00,6.01,0.0,-0.0,0.0,2.9,82.5 +2020-11-06 19:00:00,5.83,0.0,-0.0,0.0,3.24,82.5 +2020-11-06 20:00:00,5.42,0.0,-0.0,0.0,3.17,82.4 +2020-11-06 21:00:00,5.16,0.0,-0.0,0.0,3.38,82.35 +2020-11-06 22:00:00,4.83,0.0,-0.0,0.0,3.45,82.35 +2020-11-06 23:00:00,4.62,0.0,-0.0,0.0,3.52,85.5 +2020-11-07 00:00:00,4.6,0.0,-0.0,0.0,3.59,82.3 +2020-11-07 01:00:00,4.43,0.0,-0.0,0.0,3.45,82.3 +2020-11-07 02:00:00,4.76,0.0,-0.0,0.0,3.59,76.3 +2020-11-07 03:00:00,5.06,0.0,-0.0,0.0,3.93,76.3 +2020-11-07 04:00:00,5.28,0.0,-0.0,0.0,4.07,73.55 +2020-11-07 05:00:00,5.08,0.0,-0.0,0.0,4.48,73.45 +2020-11-07 06:00:00,5.12,0.0,-0.0,0.0,4.62,76.3 +2020-11-07 07:00:00,5.66,67.0,97.32,54.0,4.9,82.4 +2020-11-07 08:00:00,6.2,56.0,0.0,56.0,5.1,79.5 +2020-11-07 09:00:00,6.48,58.0,0.0,58.0,5.31,82.55 +2020-11-07 10:00:00,6.57,310.0,425.83,149.0,5.38,82.55 +2020-11-07 11:00:00,7.11,62.0,0.0,62.0,4.41,82.6 +2020-11-07 12:00:00,7.37,76.0,0.0,76.0,5.24,79.65 +2020-11-07 13:00:00,7.71,22.0,0.0,22.0,5.1,79.7 +2020-11-07 14:00:00,7.64,13.0,0.0,13.0,5.1,82.65 +2020-11-07 15:00:00,7.55,2.0,0.0,2.0,4.62,82.65 +2020-11-07 16:00:00,7.45,0.0,-0.0,0.0,4.41,82.65 +2020-11-07 17:00:00,7.36,0.0,-0.0,0.0,4.48,82.65 +2020-11-07 18:00:00,7.13,0.0,-0.0,0.0,4.48,85.75 +2020-11-07 19:00:00,6.59,0.0,-0.0,0.0,4.34,88.95 +2020-11-07 20:00:00,6.24,0.0,-0.0,0.0,4.34,88.95 +2020-11-07 21:00:00,6.08,0.0,-0.0,0.0,4.41,88.9 +2020-11-07 22:00:00,6.19,0.0,-0.0,0.0,4.34,85.7 +2020-11-07 23:00:00,6.47,0.0,-0.0,0.0,4.41,88.95 +2020-11-08 00:00:00,6.51,0.0,-0.0,0.0,4.41,88.95 +2020-11-08 01:00:00,6.75,0.0,-0.0,0.0,4.41,85.75 +2020-11-08 02:00:00,6.66,0.0,-0.0,0.0,4.48,88.95 +2020-11-08 03:00:00,6.55,0.0,-0.0,0.0,4.48,88.95 +2020-11-08 04:00:00,6.42,0.0,-0.0,0.0,4.28,85.7 +2020-11-08 05:00:00,6.32,0.0,-0.0,0.0,4.0,85.7 +2020-11-08 06:00:00,6.36,0.0,-0.0,0.0,3.79,85.7 +2020-11-08 07:00:00,6.45,74.0,170.51,52.0,3.93,88.95 +2020-11-08 08:00:00,6.55,114.0,49.17,102.0,3.59,92.3 +2020-11-08 09:00:00,6.78,90.0,0.0,90.0,3.79,89.0 +2020-11-08 10:00:00,7.31,121.0,2.68,120.0,4.41,85.8 +2020-11-08 11:00:00,7.82,172.0,29.04,161.0,4.0,82.7 +2020-11-08 12:00:00,8.35,192.0,93.21,160.0,3.59,79.75 +2020-11-08 13:00:00,8.64,157.0,126.21,123.0,3.24,79.75 +2020-11-08 14:00:00,8.77,37.0,0.0,37.0,3.1,76.95 +2020-11-08 15:00:00,8.36,3.0,0.0,3.0,2.83,76.85 +2020-11-08 16:00:00,7.94,0.0,-0.0,0.0,2.55,79.7 +2020-11-08 17:00:00,7.7,0.0,-0.0,0.0,2.28,82.65 +2020-11-08 18:00:00,7.4,0.0,-0.0,0.0,2.14,85.8 +2020-11-08 19:00:00,6.67,0.0,-0.0,0.0,2.28,85.7 +2020-11-08 20:00:00,6.69,0.0,-0.0,0.0,2.34,82.6 +2020-11-08 21:00:00,6.69,0.0,-0.0,0.0,2.41,85.7 +2020-11-08 22:00:00,6.67,0.0,-0.0,0.0,2.34,85.7 +2020-11-08 23:00:00,6.71,0.0,-0.0,0.0,2.21,82.6 +2020-11-09 00:00:00,6.65,0.0,-0.0,0.0,2.0,85.7 +2020-11-09 01:00:00,6.4,0.0,-0.0,0.0,1.86,85.7 +2020-11-09 02:00:00,6.56,0.0,-0.0,0.0,1.79,85.7 +2020-11-09 03:00:00,6.59,0.0,-0.0,0.0,1.66,85.7 +2020-11-09 04:00:00,6.62,0.0,-0.0,0.0,1.45,85.7 +2020-11-09 05:00:00,6.59,0.0,-0.0,0.0,1.38,88.95 +2020-11-09 06:00:00,6.44,0.0,-0.0,0.0,1.31,88.95 +2020-11-09 07:00:00,4.71,82.0,289.17,46.0,1.31,95.75 +2020-11-09 08:00:00,6.37,187.0,413.54,88.0,0.83,88.95 +2020-11-09 09:00:00,7.99,264.0,440.09,122.0,0.83,79.7 +2020-11-09 10:00:00,8.77,357.0,746.05,82.0,0.76,74.15 +2020-11-09 11:00:00,9.67,353.0,689.64,95.0,1.86,71.5 +2020-11-09 12:00:00,9.93,307.0,613.96,99.0,2.41,66.45 +2020-11-09 13:00:00,9.98,227.0,550.85,81.0,2.55,66.45 +2020-11-09 14:00:00,9.58,110.0,316.61,60.0,2.07,68.9 +2020-11-09 15:00:00,8.74,5.0,0.0,5.0,2.07,71.4 +2020-11-09 16:00:00,7.58,0.0,-0.0,0.0,2.41,79.65 +2020-11-09 17:00:00,6.92,0.0,-0.0,0.0,2.76,82.6 +2020-11-09 18:00:00,6.37,0.0,-0.0,0.0,2.97,82.55 +2020-11-09 19:00:00,5.81,0.0,-0.0,0.0,2.55,85.65 +2020-11-09 20:00:00,5.17,0.0,-0.0,0.0,2.69,88.85 +2020-11-09 21:00:00,4.86,0.0,-0.0,0.0,2.97,85.55 +2020-11-09 22:00:00,4.79,0.0,-0.0,0.0,3.24,82.35 +2020-11-09 23:00:00,4.56,0.0,-0.0,0.0,3.03,85.5 +2020-11-10 00:00:00,4.15,0.0,-0.0,0.0,2.9,88.75 +2020-11-10 01:00:00,3.42,0.0,-0.0,0.0,2.83,85.45 +2020-11-10 02:00:00,3.11,0.0,-0.0,0.0,2.69,88.7 +2020-11-10 03:00:00,2.75,0.0,-0.0,0.0,2.83,88.7 +2020-11-10 04:00:00,2.57,0.0,-0.0,0.0,3.1,88.65 +2020-11-10 05:00:00,2.45,0.0,-0.0,0.0,3.1,88.65 +2020-11-10 06:00:00,2.29,0.0,-0.0,0.0,2.97,88.65 +2020-11-10 07:00:00,2.16,59.0,91.66,48.0,2.76,92.15 +2020-11-10 08:00:00,3.27,129.0,102.22,105.0,2.41,88.75 +2020-11-10 09:00:00,4.9,238.0,314.46,138.0,2.69,88.85 +2020-11-10 10:00:00,6.23,330.0,596.22,113.0,2.55,82.55 +2020-11-10 11:00:00,7.32,261.0,235.43,174.0,2.28,76.7 +2020-11-10 12:00:00,7.78,235.0,242.26,154.0,2.0,73.95 +2020-11-10 13:00:00,8.08,201.0,375.77,103.0,2.0,76.8 +2020-11-10 14:00:00,7.9,96.0,214.43,63.0,1.72,76.8 +2020-11-10 15:00:00,7.05,0.0,0.0,0.0,1.79,82.6 +2020-11-10 16:00:00,5.7,0.0,-0.0,0.0,2.14,85.65 +2020-11-10 17:00:00,4.85,0.0,-0.0,0.0,2.21,88.85 +2020-11-10 18:00:00,4.28,0.0,-0.0,0.0,2.14,92.2 +2020-11-10 19:00:00,3.85,0.0,-0.0,0.0,2.14,92.15 +2020-11-10 20:00:00,3.64,0.0,-0.0,0.0,2.07,92.15 +2020-11-10 21:00:00,3.79,0.0,-0.0,0.0,1.93,92.15 +2020-11-10 22:00:00,3.38,0.0,-0.0,0.0,1.72,92.15 +2020-11-10 23:00:00,2.57,0.0,-0.0,0.0,1.79,95.7 +2020-11-11 00:00:00,2.02,0.0,-0.0,0.0,1.79,95.65 +2020-11-11 01:00:00,1.61,0.0,-0.0,0.0,1.79,95.65 +2020-11-11 02:00:00,1.25,0.0,-0.0,0.0,1.79,95.65 +2020-11-11 03:00:00,0.97,0.0,-0.0,0.0,1.72,99.35 +2020-11-11 04:00:00,0.88,0.0,-0.0,0.0,1.86,99.35 +2020-11-11 05:00:00,1.09,0.0,-0.0,0.0,2.0,99.35 +2020-11-11 06:00:00,1.16,0.0,-0.0,0.0,1.86,95.65 +2020-11-11 07:00:00,1.88,75.0,294.22,41.0,1.79,95.65 +2020-11-11 08:00:00,3.14,184.0,451.71,80.0,1.72,95.7 +2020-11-11 09:00:00,4.53,252.0,421.19,120.0,1.66,92.2 +2020-11-11 10:00:00,5.49,303.0,475.83,132.0,1.45,88.85 +2020-11-11 11:00:00,6.81,341.0,679.38,93.0,1.24,82.6 +2020-11-11 12:00:00,7.77,303.0,651.53,88.0,1.24,76.8 +2020-11-11 13:00:00,8.39,219.0,553.31,77.0,1.17,74.05 +2020-11-11 14:00:00,8.31,107.0,360.08,53.0,0.76,74.05 +2020-11-11 15:00:00,7.46,0.0,0.0,0.0,0.62,82.65 +2020-11-11 16:00:00,6.16,0.0,-0.0,0.0,0.9,88.9 +2020-11-11 17:00:00,4.93,0.0,-0.0,0.0,1.03,92.25 +2020-11-11 18:00:00,4.8,0.0,-0.0,0.0,0.97,92.25 +2020-11-11 19:00:00,0.88,0.0,-0.0,0.0,1.72,95.65 +2020-11-11 20:00:00,0.2,0.0,-0.0,0.0,1.59,95.65 +2020-11-11 21:00:00,0.3,0.0,-0.0,0.0,1.24,99.4 +2020-11-11 22:00:00,0.36,0.0,-0.0,0.0,1.24,99.4 +2020-11-11 23:00:00,0.37,0.0,-0.0,0.0,1.31,99.4 +2020-11-12 00:00:00,0.35,0.0,-0.0,0.0,1.45,99.4 +2020-11-12 01:00:00,0.33,0.0,-0.0,0.0,1.31,99.4 +2020-11-12 02:00:00,0.45,0.0,-0.0,0.0,1.31,99.4 +2020-11-12 03:00:00,0.53,0.0,-0.0,0.0,1.38,99.4 +2020-11-12 04:00:00,0.68,0.0,-0.0,0.0,1.45,95.65 +2020-11-12 05:00:00,0.73,0.0,-0.0,0.0,1.59,95.65 +2020-11-12 06:00:00,0.71,0.0,-0.0,0.0,1.79,95.65 +2020-11-12 07:00:00,0.56,54.0,98.96,43.0,2.0,99.4 +2020-11-12 08:00:00,1.02,177.0,420.86,82.0,2.07,99.35 +2020-11-12 09:00:00,2.15,259.0,482.43,110.0,2.14,95.65 +2020-11-12 10:00:00,3.73,337.0,707.32,86.0,2.41,92.15 +2020-11-12 11:00:00,5.21,342.0,698.81,90.0,2.83,82.4 +2020-11-12 12:00:00,6.22,297.0,632.44,91.0,2.9,79.5 +2020-11-12 13:00:00,6.61,221.0,593.9,71.0,2.97,79.5 +2020-11-12 14:00:00,6.44,102.0,328.47,54.0,2.9,79.5 +2020-11-12 15:00:00,5.38,0.0,0.0,0.0,2.83,85.6 +2020-11-12 16:00:00,4.37,0.0,-0.0,0.0,2.9,92.2 +2020-11-12 17:00:00,3.84,0.0,-0.0,0.0,2.9,92.15 +2020-11-12 18:00:00,3.63,0.0,-0.0,0.0,2.76,92.15 +2020-11-12 19:00:00,3.19,0.0,-0.0,0.0,2.76,88.75 +2020-11-12 20:00:00,3.17,0.0,-0.0,0.0,2.76,88.75 +2020-11-12 21:00:00,3.27,0.0,-0.0,0.0,2.97,88.75 +2020-11-12 22:00:00,3.19,0.0,-0.0,0.0,3.24,88.75 +2020-11-12 23:00:00,3.22,0.0,-0.0,0.0,3.45,88.75 +2020-11-13 00:00:00,3.3,0.0,-0.0,0.0,3.38,88.75 +2020-11-13 01:00:00,3.78,0.0,-0.0,0.0,3.31,92.15 +2020-11-13 02:00:00,3.69,0.0,-0.0,0.0,3.03,92.15 +2020-11-13 03:00:00,3.57,0.0,-0.0,0.0,2.76,92.15 +2020-11-13 04:00:00,3.7,0.0,-0.0,0.0,2.76,95.7 +2020-11-13 05:00:00,3.71,0.0,-0.0,0.0,2.9,95.7 +2020-11-13 06:00:00,3.87,0.0,-0.0,0.0,3.03,95.7 +2020-11-13 07:00:00,3.87,39.0,28.09,36.0,2.69,88.75 +2020-11-13 08:00:00,4.28,98.0,40.67,89.0,2.28,88.8 +2020-11-13 09:00:00,5.24,233.0,364.69,122.0,2.21,85.6 +2020-11-13 10:00:00,6.23,146.0,19.98,139.0,1.79,82.55 +2020-11-13 11:00:00,7.34,260.0,286.3,158.0,1.72,79.65 +2020-11-13 12:00:00,8.21,167.0,71.53,144.0,1.86,74.05 +2020-11-13 13:00:00,8.33,73.0,0.0,73.0,1.86,74.05 +2020-11-13 14:00:00,8.01,21.0,0.0,21.0,1.72,79.7 +2020-11-13 15:00:00,7.28,0.0,0.0,0.0,1.79,79.65 +2020-11-13 16:00:00,6.34,0.0,-0.0,0.0,1.86,85.7 +2020-11-13 17:00:00,5.34,0.0,-0.0,0.0,1.86,92.25 +2020-11-13 18:00:00,4.85,0.0,-0.0,0.0,1.79,95.75 +2020-11-13 19:00:00,5.36,0.0,-0.0,0.0,1.59,92.25 +2020-11-13 20:00:00,4.92,0.0,-0.0,0.0,1.52,95.75 +2020-11-13 21:00:00,4.84,0.0,-0.0,0.0,1.45,95.75 +2020-11-13 22:00:00,4.63,0.0,-0.0,0.0,1.45,99.4 +2020-11-13 23:00:00,4.49,0.0,-0.0,0.0,1.38,99.4 +2020-11-14 00:00:00,4.51,0.0,-0.0,0.0,1.31,99.4 +2020-11-14 01:00:00,4.12,0.0,-0.0,0.0,1.24,99.4 +2020-11-14 02:00:00,3.96,0.0,-0.0,0.0,1.17,99.4 +2020-11-14 03:00:00,3.76,0.0,-0.0,0.0,1.24,99.4 +2020-11-14 04:00:00,3.92,0.0,-0.0,0.0,1.17,99.4 +2020-11-14 05:00:00,4.37,0.0,-0.0,0.0,1.03,95.75 +2020-11-14 06:00:00,4.32,0.0,-0.0,0.0,1.1,95.75 +2020-11-14 07:00:00,4.12,69.0,361.02,32.0,0.97,100.0 +2020-11-14 08:00:00,5.33,119.0,115.27,94.0,0.28,95.75 +2020-11-14 09:00:00,6.47,99.0,3.33,98.0,0.41,92.3 +2020-11-14 10:00:00,7.24,154.0,28.9,144.0,0.69,85.8 +2020-11-14 11:00:00,7.78,157.0,31.25,146.0,1.17,85.85 +2020-11-14 12:00:00,7.81,56.0,0.0,56.0,1.45,85.85 +2020-11-14 13:00:00,7.79,57.0,0.0,57.0,1.72,85.85 +2020-11-14 14:00:00,7.6,40.0,7.21,39.0,1.86,89.0 +2020-11-14 15:00:00,7.17,0.0,0.0,0.0,1.72,92.35 +2020-11-14 16:00:00,6.74,0.0,-0.0,0.0,1.79,92.35 +2020-11-14 17:00:00,6.41,0.0,-0.0,0.0,2.0,95.8 +2020-11-14 18:00:00,6.14,0.0,-0.0,0.0,2.14,99.4 +2020-11-14 19:00:00,5.51,0.0,-0.0,0.0,2.69,95.75 +2020-11-14 20:00:00,5.21,0.0,-0.0,0.0,2.34,92.25 +2020-11-14 21:00:00,4.97,0.0,-0.0,0.0,2.07,95.75 +2020-11-14 22:00:00,4.74,0.0,-0.0,0.0,1.93,95.75 +2020-11-14 23:00:00,4.46,0.0,-0.0,0.0,2.21,99.4 +2020-11-15 00:00:00,4.05,0.0,-0.0,0.0,2.69,99.4 +2020-11-15 01:00:00,3.49,0.0,-0.0,0.0,2.76,95.7 +2020-11-15 02:00:00,3.16,0.0,-0.0,0.0,2.48,99.4 +2020-11-15 03:00:00,2.99,0.0,-0.0,0.0,2.28,95.7 +2020-11-15 04:00:00,2.85,0.0,-0.0,0.0,2.28,95.7 +2020-11-15 05:00:00,2.76,0.0,-0.0,0.0,2.41,92.15 +2020-11-15 06:00:00,2.67,0.0,-0.0,0.0,2.55,92.15 +2020-11-15 07:00:00,3.15,25.0,0.0,25.0,2.48,92.15 +2020-11-15 08:00:00,3.49,58.0,0.0,58.0,2.41,88.75 +2020-11-15 09:00:00,3.93,49.0,0.0,49.0,2.28,92.15 +2020-11-15 10:00:00,4.7,62.0,0.0,62.0,2.21,85.55 +2020-11-15 11:00:00,5.43,71.0,0.0,71.0,2.14,82.4 +2020-11-15 12:00:00,6.16,61.0,0.0,61.0,2.07,82.5 +2020-11-15 13:00:00,6.66,66.0,0.0,66.0,2.0,79.5 +2020-11-15 14:00:00,6.72,37.0,0.0,37.0,1.93,76.65 +2020-11-15 15:00:00,6.01,0.0,0.0,0.0,1.86,82.5 +2020-11-15 16:00:00,5.02,0.0,-0.0,0.0,2.0,88.85 +2020-11-15 17:00:00,4.26,0.0,-0.0,0.0,2.14,92.2 +2020-11-15 18:00:00,3.69,0.0,-0.0,0.0,2.14,92.15 +2020-11-15 19:00:00,3.31,0.0,-0.0,0.0,1.93,92.15 +2020-11-15 20:00:00,2.91,0.0,-0.0,0.0,1.93,95.7 +2020-11-15 21:00:00,2.64,0.0,-0.0,0.0,2.07,95.7 +2020-11-15 22:00:00,2.47,0.0,-0.0,0.0,2.14,95.7 +2020-11-15 23:00:00,2.76,0.0,-0.0,0.0,2.14,92.15 +2020-11-16 00:00:00,3.14,0.0,-0.0,0.0,2.14,95.7 +2020-11-16 01:00:00,3.59,0.0,-0.0,0.0,2.07,95.7 +2020-11-16 02:00:00,3.97,0.0,-0.0,0.0,2.0,99.4 +2020-11-16 03:00:00,4.32,0.0,-0.0,0.0,1.93,99.4 +2020-11-16 04:00:00,4.63,0.0,-0.0,0.0,2.07,99.4 +2020-11-16 05:00:00,4.93,0.0,-0.0,0.0,2.34,95.75 +2020-11-16 06:00:00,4.98,0.0,-0.0,0.0,2.69,95.75 +2020-11-16 07:00:00,5.21,41.0,74.45,34.0,3.03,92.25 +2020-11-16 08:00:00,5.7,114.0,110.44,91.0,3.17,88.9 +2020-11-16 09:00:00,6.56,214.0,302.1,126.0,4.55,85.7 +2020-11-16 10:00:00,7.09,245.0,275.55,152.0,4.21,82.6 +2020-11-16 11:00:00,7.53,252.0,282.21,155.0,3.45,79.65 +2020-11-16 12:00:00,7.86,237.0,348.93,129.0,2.97,76.8 +2020-11-16 13:00:00,8.13,135.0,118.02,107.0,2.62,76.8 +2020-11-16 14:00:00,8.09,57.0,53.11,50.0,1.86,76.8 +2020-11-16 15:00:00,7.7,0.0,0.0,0.0,1.72,76.8 +2020-11-16 16:00:00,7.17,0.0,-0.0,0.0,1.79,82.6 +2020-11-16 17:00:00,6.76,0.0,-0.0,0.0,1.86,82.6 +2020-11-16 18:00:00,6.49,0.0,-0.0,0.0,1.86,85.7 +2020-11-16 19:00:00,6.07,0.0,-0.0,0.0,1.24,88.9 +2020-11-16 20:00:00,5.7,0.0,-0.0,0.0,1.38,88.9 +2020-11-16 21:00:00,5.26,0.0,-0.0,0.0,1.31,92.25 +2020-11-16 22:00:00,4.48,0.0,-0.0,0.0,1.31,95.75 +2020-11-16 23:00:00,4.2,0.0,-0.0,0.0,1.17,95.75 +2020-11-17 00:00:00,3.79,0.0,-0.0,0.0,1.17,99.4 +2020-11-17 01:00:00,3.74,0.0,-0.0,0.0,1.03,99.4 +2020-11-17 02:00:00,3.72,0.0,-0.0,0.0,0.9,99.4 +2020-11-17 03:00:00,3.91,0.0,-0.0,0.0,0.69,100.0 +2020-11-17 04:00:00,3.86,0.0,-0.0,0.0,0.76,99.4 +2020-11-17 05:00:00,3.91,0.0,-0.0,0.0,0.69,100.0 +2020-11-17 06:00:00,3.98,0.0,-0.0,0.0,0.76,100.0 +2020-11-17 07:00:00,4.44,8.0,0.0,8.0,1.1,99.4 +2020-11-17 08:00:00,4.92,82.0,24.51,77.0,1.79,95.75 +2020-11-17 09:00:00,5.26,98.0,6.97,96.0,2.14,88.85 +2020-11-17 10:00:00,5.4,30.0,0.0,30.0,2.28,88.85 +2020-11-17 11:00:00,5.57,57.0,0.0,57.0,2.97,85.6 +2020-11-17 12:00:00,5.72,37.0,0.0,37.0,3.1,82.5 +2020-11-17 13:00:00,5.81,18.0,0.0,18.0,3.17,79.4 +2020-11-17 14:00:00,5.6,13.0,0.0,13.0,2.83,82.4 +2020-11-17 15:00:00,5.44,0.0,-0.0,0.0,2.9,79.35 +2020-11-17 16:00:00,5.2,0.0,-0.0,0.0,2.83,79.35 +2020-11-17 17:00:00,4.95,0.0,-0.0,0.0,2.83,82.35 +2020-11-17 18:00:00,5.09,0.0,-0.0,0.0,2.9,85.55 +2020-11-17 19:00:00,5.29,0.0,-0.0,0.0,3.38,82.4 +2020-11-17 20:00:00,5.63,0.0,-0.0,0.0,3.79,85.6 +2020-11-17 21:00:00,5.64,0.0,-0.0,0.0,3.93,88.85 +2020-11-17 22:00:00,5.62,0.0,-0.0,0.0,4.0,88.85 +2020-11-17 23:00:00,5.47,0.0,-0.0,0.0,3.79,88.85 +2020-11-18 00:00:00,5.34,0.0,-0.0,0.0,3.72,88.85 +2020-11-18 01:00:00,5.64,0.0,-0.0,0.0,3.72,92.25 +2020-11-18 02:00:00,5.5,0.0,-0.0,0.0,3.79,92.25 +2020-11-18 03:00:00,5.38,0.0,-0.0,0.0,3.72,88.85 +2020-11-18 04:00:00,5.18,0.0,-0.0,0.0,3.72,88.85 +2020-11-18 05:00:00,4.82,0.0,-0.0,0.0,3.72,92.25 +2020-11-18 06:00:00,4.8,0.0,-0.0,0.0,3.79,92.25 +2020-11-18 07:00:00,5.51,47.0,174.87,32.0,3.59,92.25 +2020-11-18 08:00:00,6.13,166.0,545.35,57.0,3.72,88.9 +2020-11-18 09:00:00,7.05,92.0,3.53,91.0,3.59,89.0 +2020-11-18 10:00:00,7.57,77.0,0.0,77.0,3.86,85.8 +2020-11-18 11:00:00,7.64,140.0,20.85,133.0,3.79,85.8 +2020-11-18 12:00:00,8.38,189.0,162.26,140.0,5.59,76.85 +2020-11-18 13:00:00,8.62,103.0,39.1,94.0,4.76,76.85 +2020-11-18 14:00:00,8.26,69.0,151.67,50.0,4.07,76.85 +2020-11-18 15:00:00,7.49,0.0,-0.0,0.0,3.86,79.65 +2020-11-18 16:00:00,7.11,0.0,-0.0,0.0,3.93,79.55 +2020-11-18 17:00:00,6.92,0.0,-0.0,0.0,4.28,76.65 +2020-11-18 18:00:00,6.7,0.0,-0.0,0.0,4.48,76.65 +2020-11-18 19:00:00,6.37,0.0,-0.0,0.0,4.62,85.7 +2020-11-18 20:00:00,6.58,0.0,-0.0,0.0,4.83,85.7 +2020-11-18 21:00:00,6.79,0.0,-0.0,0.0,4.83,82.6 +2020-11-18 22:00:00,7.08,0.0,-0.0,0.0,5.1,82.6 +2020-11-18 23:00:00,7.39,0.0,-0.0,0.0,5.24,79.65 +2020-11-19 00:00:00,7.64,0.0,-0.0,0.0,5.38,82.65 +2020-11-19 01:00:00,7.2,0.0,-0.0,0.0,5.38,79.65 +2020-11-19 02:00:00,7.3,0.0,-0.0,0.0,5.24,82.65 +2020-11-19 03:00:00,7.47,0.0,-0.0,0.0,5.17,85.8 +2020-11-19 04:00:00,7.7,0.0,-0.0,0.0,5.1,89.0 +2020-11-19 05:00:00,7.87,0.0,-0.0,0.0,5.03,85.85 +2020-11-19 06:00:00,7.97,0.0,-0.0,0.0,5.17,89.05 +2020-11-19 07:00:00,7.75,25.0,12.23,24.0,5.17,85.85 +2020-11-19 08:00:00,7.93,74.0,20.43,70.0,5.24,89.05 +2020-11-19 09:00:00,8.17,45.0,0.0,45.0,5.45,89.05 +2020-11-19 10:00:00,8.39,109.0,3.07,108.0,4.97,85.9 +2020-11-19 11:00:00,8.56,67.0,0.0,67.0,5.79,89.1 +2020-11-19 12:00:00,8.73,104.0,6.7,102.0,6.41,85.95 +2020-11-19 13:00:00,8.92,38.0,0.0,38.0,5.66,89.1 +2020-11-19 14:00:00,8.97,31.0,0.0,31.0,5.03,89.1 +2020-11-19 15:00:00,8.78,0.0,-0.0,0.0,4.83,89.1 +2020-11-19 16:00:00,8.58,0.0,-0.0,0.0,4.83,92.4 +2020-11-19 17:00:00,8.42,0.0,-0.0,0.0,4.83,92.4 +2020-11-19 18:00:00,8.31,0.0,-0.0,0.0,4.69,92.4 +2020-11-19 19:00:00,7.91,0.0,-0.0,0.0,4.69,92.4 +2020-11-19 20:00:00,7.8,0.0,-0.0,0.0,4.69,92.4 +2020-11-19 21:00:00,7.87,0.0,-0.0,0.0,4.62,92.4 +2020-11-19 22:00:00,7.92,0.0,-0.0,0.0,4.48,92.4 +2020-11-19 23:00:00,7.91,0.0,-0.0,0.0,4.34,92.4 +2020-11-20 00:00:00,8.01,0.0,-0.0,0.0,4.34,95.8 +2020-11-20 01:00:00,8.27,0.0,-0.0,0.0,4.21,92.4 +2020-11-20 02:00:00,8.31,0.0,-0.0,0.0,4.14,92.4 +2020-11-20 03:00:00,8.3,0.0,-0.0,0.0,4.07,92.4 +2020-11-20 04:00:00,8.35,0.0,-0.0,0.0,4.0,95.85 +2020-11-20 05:00:00,8.35,0.0,-0.0,0.0,3.86,95.85 +2020-11-20 06:00:00,8.33,0.0,-0.0,0.0,3.72,95.85 +2020-11-20 07:00:00,8.48,16.0,0.0,16.0,3.79,95.85 +2020-11-20 08:00:00,8.52,50.0,0.0,50.0,3.59,95.85 +2020-11-20 09:00:00,8.7,30.0,0.0,30.0,3.1,95.85 +2020-11-20 10:00:00,8.86,33.0,0.0,33.0,3.03,92.45 +2020-11-20 11:00:00,9.22,52.0,0.0,52.0,3.52,92.45 +2020-11-20 12:00:00,9.34,48.0,0.0,48.0,3.31,92.45 +2020-11-20 13:00:00,9.39,38.0,0.0,38.0,2.9,89.15 +2020-11-20 14:00:00,9.28,13.0,0.0,13.0,2.97,89.15 +2020-11-20 15:00:00,9.02,0.0,-0.0,0.0,2.9,89.1 +2020-11-20 16:00:00,8.6,0.0,-0.0,0.0,2.9,89.1 +2020-11-20 17:00:00,8.17,0.0,-0.0,0.0,2.97,89.05 +2020-11-20 18:00:00,7.62,0.0,-0.0,0.0,2.97,89.0 +2020-11-20 19:00:00,7.04,0.0,-0.0,0.0,2.76,89.0 +2020-11-20 20:00:00,6.74,0.0,-0.0,0.0,2.83,85.75 +2020-11-20 21:00:00,6.46,0.0,-0.0,0.0,2.83,88.95 +2020-11-20 22:00:00,6.03,0.0,-0.0,0.0,2.76,88.9 +2020-11-20 23:00:00,5.54,0.0,-0.0,0.0,2.62,92.25 +2020-11-21 00:00:00,5.6,0.0,-0.0,0.0,2.62,92.25 +2020-11-21 01:00:00,5.41,0.0,-0.0,0.0,2.55,88.85 +2020-11-21 02:00:00,5.29,0.0,-0.0,0.0,2.41,88.85 +2020-11-21 03:00:00,5.06,0.0,-0.0,0.0,2.34,92.25 +2020-11-21 04:00:00,4.84,0.0,-0.0,0.0,2.34,88.85 +2020-11-21 05:00:00,4.82,0.0,-0.0,0.0,2.28,88.85 +2020-11-21 06:00:00,4.54,0.0,-0.0,0.0,2.34,88.8 +2020-11-21 07:00:00,4.29,4.0,0.0,4.0,2.41,92.2 +2020-11-21 08:00:00,4.76,14.0,0.0,14.0,2.41,88.85 +2020-11-21 09:00:00,5.55,45.0,0.0,45.0,3.1,85.6 +2020-11-21 10:00:00,6.09,71.0,0.0,71.0,3.52,85.65 +2020-11-21 11:00:00,6.33,98.0,0.0,98.0,3.1,85.7 +2020-11-21 12:00:00,6.43,55.0,0.0,55.0,2.97,85.7 +2020-11-21 13:00:00,6.47,68.0,4.54,67.0,2.83,85.7 +2020-11-21 14:00:00,6.45,33.0,8.6,32.0,2.83,85.7 +2020-11-21 15:00:00,6.32,0.0,-0.0,0.0,2.0,85.7 +2020-11-21 16:00:00,6.19,0.0,-0.0,0.0,1.86,85.7 +2020-11-21 17:00:00,6.07,0.0,-0.0,0.0,1.93,88.9 +2020-11-21 18:00:00,5.94,0.0,-0.0,0.0,1.93,88.9 +2020-11-21 19:00:00,5.75,0.0,-0.0,0.0,1.59,92.3 +2020-11-21 20:00:00,5.71,0.0,-0.0,0.0,1.52,95.75 +2020-11-21 21:00:00,5.61,0.0,-0.0,0.0,1.66,99.4 +2020-11-21 22:00:00,5.51,0.0,-0.0,0.0,1.86,99.4 +2020-11-21 23:00:00,5.42,0.0,-0.0,0.0,1.66,99.4 +2020-11-22 00:00:00,5.3,0.0,-0.0,0.0,1.72,99.4 +2020-11-22 01:00:00,5.51,0.0,-0.0,0.0,1.72,99.4 +2020-11-22 02:00:00,5.27,0.0,-0.0,0.0,1.72,99.4 +2020-11-22 03:00:00,5.04,0.0,-0.0,0.0,1.52,99.4 +2020-11-22 04:00:00,4.83,0.0,-0.0,0.0,1.52,99.4 +2020-11-22 05:00:00,4.66,0.0,-0.0,0.0,1.59,99.4 +2020-11-22 06:00:00,4.54,0.0,-0.0,0.0,1.72,99.4 +2020-11-22 07:00:00,4.28,21.0,14.28,20.0,1.79,99.4 +2020-11-22 08:00:00,4.28,46.0,0.0,46.0,2.07,99.4 +2020-11-22 09:00:00,4.31,43.0,0.0,43.0,2.41,99.4 +2020-11-22 10:00:00,4.32,134.0,22.3,127.0,2.48,99.4 +2020-11-22 11:00:00,4.46,57.0,0.0,57.0,2.28,99.4 +2020-11-22 12:00:00,4.28,74.0,0.0,74.0,2.83,95.75 +2020-11-22 13:00:00,4.19,110.0,73.63,94.0,3.38,92.2 +2020-11-22 14:00:00,3.9,32.0,0.0,32.0,3.45,95.7 +2020-11-22 15:00:00,3.42,0.0,-0.0,0.0,2.76,92.15 +2020-11-22 16:00:00,2.85,0.0,-0.0,0.0,2.76,92.15 +2020-11-22 17:00:00,2.21,0.0,-0.0,0.0,2.69,92.15 +2020-11-22 18:00:00,1.57,0.0,-0.0,0.0,2.41,95.65 +2020-11-22 19:00:00,1.74,0.0,-0.0,0.0,2.28,92.1 +2020-11-22 20:00:00,1.34,0.0,-0.0,0.0,1.93,92.05 +2020-11-22 21:00:00,1.07,0.0,-0.0,0.0,1.79,92.05 +2020-11-22 22:00:00,0.65,0.0,-0.0,0.0,1.72,92.05 +2020-11-22 23:00:00,-0.2,0.0,-0.0,0.0,1.86,92.0 +2020-11-23 00:00:00,-0.75,0.0,-0.0,0.0,1.93,91.95 +2020-11-23 01:00:00,-1.18,0.0,-0.0,0.0,2.0,91.95 +2020-11-23 02:00:00,-1.5,0.0,-0.0,0.0,2.14,91.9 +2020-11-23 03:00:00,-1.58,0.0,-0.0,0.0,2.21,91.9 +2020-11-23 04:00:00,-1.6,0.0,-0.0,0.0,2.34,88.35 +2020-11-23 05:00:00,-1.39,0.0,-0.0,0.0,2.55,88.35 +2020-11-23 06:00:00,-1.1,0.0,-0.0,0.0,2.62,88.4 +2020-11-23 07:00:00,-0.38,32.0,135.88,23.0,2.83,88.45 +2020-11-23 08:00:00,0.79,113.0,199.86,77.0,2.83,85.2 +2020-11-23 09:00:00,2.01,66.0,0.0,66.0,2.83,85.3 +2020-11-23 10:00:00,2.8,57.0,0.0,57.0,3.03,85.4 +2020-11-23 11:00:00,3.27,181.0,100.77,149.0,3.17,85.45 +2020-11-23 12:00:00,3.48,99.0,7.02,97.0,3.17,88.75 +2020-11-23 13:00:00,3.7,69.0,4.67,68.0,2.97,88.75 +2020-11-23 14:00:00,3.69,48.0,54.14,42.0,2.83,92.15 +2020-11-23 15:00:00,3.55,0.0,-0.0,0.0,2.62,92.15 +2020-11-23 16:00:00,3.38,0.0,-0.0,0.0,2.41,92.15 +2020-11-23 17:00:00,3.43,0.0,-0.0,0.0,2.28,95.7 +2020-11-23 18:00:00,3.55,0.0,-0.0,0.0,2.21,95.7 +2020-11-23 19:00:00,3.4,0.0,-0.0,0.0,2.21,95.7 +2020-11-23 20:00:00,3.47,0.0,-0.0,0.0,2.21,95.7 +2020-11-23 21:00:00,3.55,0.0,-0.0,0.0,2.28,95.7 +2020-11-23 22:00:00,3.64,0.0,-0.0,0.0,2.34,99.4 +2020-11-23 23:00:00,3.71,0.0,-0.0,0.0,2.34,99.4 +2020-11-24 00:00:00,3.8,0.0,-0.0,0.0,2.28,99.4 +2020-11-24 01:00:00,3.79,0.0,-0.0,0.0,2.28,99.4 +2020-11-24 02:00:00,3.81,0.0,-0.0,0.0,2.21,99.4 +2020-11-24 03:00:00,3.88,0.0,-0.0,0.0,2.21,99.4 +2020-11-24 04:00:00,3.86,0.0,-0.0,0.0,2.21,99.4 +2020-11-24 05:00:00,3.87,0.0,-0.0,0.0,2.14,99.4 +2020-11-24 06:00:00,3.91,0.0,-0.0,0.0,1.93,99.4 +2020-11-24 07:00:00,4.0,19.0,15.99,18.0,1.45,100.0 +2020-11-24 08:00:00,4.28,76.0,39.68,69.0,1.38,99.4 +2020-11-24 09:00:00,4.53,75.0,0.0,75.0,1.38,99.4 +2020-11-24 10:00:00,4.84,102.0,3.26,101.0,1.17,95.75 +2020-11-24 11:00:00,5.32,60.0,0.0,60.0,0.97,92.25 +2020-11-24 12:00:00,5.38,37.0,0.0,37.0,0.69,92.25 +2020-11-24 13:00:00,5.54,33.0,0.0,33.0,0.14,88.85 +2020-11-24 14:00:00,5.49,14.0,0.0,14.0,0.55,88.85 +2020-11-24 15:00:00,4.97,0.0,-0.0,0.0,1.24,92.25 +2020-11-24 16:00:00,4.28,0.0,-0.0,0.0,1.45,92.2 +2020-11-24 17:00:00,3.6,0.0,-0.0,0.0,1.66,95.7 +2020-11-24 18:00:00,3.37,0.0,-0.0,0.0,1.72,92.15 +2020-11-24 19:00:00,3.67,0.0,-0.0,0.0,1.93,92.15 +2020-11-24 20:00:00,3.03,0.0,-0.0,0.0,2.07,88.7 +2020-11-24 21:00:00,2.97,0.0,-0.0,0.0,2.21,85.4 +2020-11-24 22:00:00,2.67,0.0,-0.0,0.0,2.28,85.4 +2020-11-24 23:00:00,1.88,0.0,-0.0,0.0,2.34,88.65 +2020-11-25 00:00:00,1.19,0.0,-0.0,0.0,2.34,88.6 +2020-11-25 01:00:00,0.71,0.0,-0.0,0.0,2.34,88.55 +2020-11-25 02:00:00,0.3,0.0,-0.0,0.0,2.34,92.0 +2020-11-25 03:00:00,-0.31,0.0,-0.0,0.0,2.41,92.0 +2020-11-25 04:00:00,-0.7,0.0,-0.0,0.0,2.48,91.95 +2020-11-25 05:00:00,-0.87,0.0,-0.0,0.0,2.48,91.95 +2020-11-25 06:00:00,-0.87,0.0,-0.0,0.0,2.34,91.95 +2020-11-25 07:00:00,0.24,20.0,33.96,18.0,1.86,92.0 +2020-11-25 08:00:00,0.84,69.0,23.15,65.0,1.86,88.55 +2020-11-25 09:00:00,1.52,68.0,0.0,68.0,2.07,85.25 +2020-11-25 10:00:00,2.01,60.0,0.0,60.0,2.07,78.95 +2020-11-25 11:00:00,2.47,54.0,0.0,54.0,2.14,76.0 +2020-11-25 12:00:00,2.99,36.0,0.0,36.0,2.14,73.2 +2020-11-25 13:00:00,3.12,29.0,0.0,29.0,2.14,70.4 +2020-11-25 14:00:00,3.08,29.0,0.0,29.0,2.28,70.4 +2020-11-25 15:00:00,2.68,0.0,-0.0,0.0,1.93,73.2 +2020-11-25 16:00:00,2.32,0.0,-0.0,0.0,2.21,76.0 +2020-11-25 17:00:00,1.94,0.0,-0.0,0.0,2.21,78.95 +2020-11-25 18:00:00,1.69,0.0,-0.0,0.0,2.28,78.95 +2020-11-25 19:00:00,1.25,0.0,-0.0,0.0,2.48,82.0 +2020-11-25 20:00:00,1.44,0.0,-0.0,0.0,2.41,82.0 +2020-11-25 21:00:00,1.35,0.0,-0.0,0.0,2.48,82.0 +2020-11-25 22:00:00,1.46,0.0,-0.0,0.0,2.48,82.0 +2020-11-25 23:00:00,1.79,0.0,-0.0,0.0,2.48,78.95 +2020-11-26 00:00:00,2.02,0.0,-0.0,0.0,2.41,82.05 +2020-11-26 01:00:00,2.43,0.0,-0.0,0.0,2.28,82.15 +2020-11-26 02:00:00,2.48,0.0,-0.0,0.0,2.28,82.15 +2020-11-26 03:00:00,2.61,0.0,-0.0,0.0,2.28,85.35 +2020-11-26 04:00:00,2.59,0.0,-0.0,0.0,2.14,85.35 +2020-11-26 05:00:00,2.22,0.0,-0.0,0.0,1.86,85.35 +2020-11-26 06:00:00,1.72,0.0,-0.0,0.0,1.86,88.65 +2020-11-26 07:00:00,1.8,24.0,90.37,19.0,2.0,88.65 +2020-11-26 08:00:00,1.93,42.0,0.0,42.0,1.86,85.3 +2020-11-26 09:00:00,2.28,67.0,0.0,67.0,1.93,82.15 +2020-11-26 10:00:00,2.62,95.0,3.33,94.0,1.86,82.15 +2020-11-26 11:00:00,3.05,106.0,3.25,105.0,1.72,76.1 +2020-11-26 12:00:00,3.42,84.0,0.0,84.0,1.66,73.3 +2020-11-26 13:00:00,3.88,41.0,0.0,41.0,1.72,70.5 +2020-11-26 14:00:00,3.79,10.0,0.0,10.0,1.24,70.5 +2020-11-26 15:00:00,3.32,0.0,-0.0,0.0,0.76,76.15 +2020-11-26 16:00:00,2.8,0.0,-0.0,0.0,1.1,79.1 +2020-11-26 17:00:00,2.15,0.0,-0.0,0.0,1.31,82.15 +2020-11-26 18:00:00,0.96,0.0,-0.0,0.0,1.59,92.05 +2020-11-26 19:00:00,0.53,0.0,-0.0,0.0,1.79,92.0 +2020-11-26 20:00:00,0.01,0.0,-0.0,0.0,1.93,92.0 +2020-11-26 21:00:00,-0.72,0.0,-0.0,0.0,2.0,88.45 +2020-11-26 22:00:00,-1.24,0.0,-0.0,0.0,2.0,88.4 +2020-11-26 23:00:00,-1.5,0.0,-0.0,0.0,2.07,88.35 +2020-11-27 00:00:00,-1.83,0.0,-0.0,0.0,2.14,88.35 +2020-11-27 01:00:00,-1.75,0.0,-0.0,0.0,2.21,88.35 +2020-11-27 02:00:00,-1.94,0.0,-0.0,0.0,2.41,88.3 +2020-11-27 03:00:00,-1.73,0.0,-0.0,0.0,2.62,88.35 +2020-11-27 04:00:00,-1.42,0.0,-0.0,0.0,2.83,88.35 +2020-11-27 05:00:00,-1.16,0.0,-0.0,0.0,2.97,88.4 +2020-11-27 06:00:00,-0.89,0.0,-0.0,0.0,3.03,88.4 +2020-11-27 07:00:00,-0.39,20.0,57.86,17.0,2.9,88.45 +2020-11-27 08:00:00,0.54,131.0,476.74,52.0,2.97,88.5 +2020-11-27 09:00:00,1.65,83.0,4.01,82.0,2.9,82.05 +2020-11-27 10:00:00,2.48,139.0,40.45,127.0,2.97,82.15 +2020-11-27 11:00:00,3.09,135.0,29.53,126.0,3.03,79.1 +2020-11-27 12:00:00,3.57,113.0,21.97,107.0,3.03,79.15 +2020-11-27 13:00:00,3.66,125.0,171.86,90.0,3.03,79.15 +2020-11-27 14:00:00,3.82,18.0,0.0,18.0,2.83,82.25 +2020-11-27 15:00:00,3.42,0.0,-0.0,0.0,2.69,79.15 +2020-11-27 16:00:00,3.4,0.0,-0.0,0.0,2.55,79.15 +2020-11-27 17:00:00,2.79,0.0,-0.0,0.0,2.34,82.2 +2020-11-27 18:00:00,1.86,0.0,-0.0,0.0,2.34,85.3 +2020-11-27 19:00:00,0.62,0.0,-0.0,0.0,2.07,95.65 +2020-11-27 20:00:00,-0.02,0.0,-0.0,0.0,2.14,92.0 +2020-11-27 21:00:00,-0.09,0.0,-0.0,0.0,1.93,92.0 +2020-11-27 22:00:00,-0.03,0.0,-0.0,0.0,1.72,92.0 +2020-11-27 23:00:00,-0.8,0.0,-0.0,0.0,1.72,91.95 +2020-11-28 00:00:00,-1.26,0.0,-0.0,0.0,1.72,91.95 +2020-11-28 01:00:00,-1.16,0.0,-0.0,0.0,1.66,91.95 +2020-11-28 02:00:00,-1.9,0.0,-0.0,0.0,1.79,95.55 +2020-11-28 03:00:00,-2.25,0.0,-0.0,0.0,1.86,91.85 +2020-11-28 04:00:00,-2.41,0.0,-0.0,0.0,1.86,91.85 +2020-11-28 05:00:00,-2.31,0.0,-0.0,0.0,1.66,88.3 +2020-11-28 06:00:00,-2.13,0.0,-0.0,0.0,1.52,88.3 +2020-11-28 07:00:00,-2.41,9.0,0.0,9.0,1.38,95.55 +2020-11-28 08:00:00,-0.67,58.0,12.32,56.0,0.69,95.6 +2020-11-28 09:00:00,0.46,76.0,4.07,75.0,0.55,95.65 +2020-11-28 10:00:00,1.35,87.0,0.0,87.0,0.21,88.6 +2020-11-28 11:00:00,1.73,112.0,6.63,110.0,0.21,85.3 +2020-11-28 12:00:00,1.96,129.0,44.38,117.0,0.41,85.3 +2020-11-28 13:00:00,1.98,96.0,54.65,85.0,0.76,85.3 +2020-11-28 14:00:00,1.67,49.0,110.88,38.0,0.76,88.65 +2020-11-28 15:00:00,0.76,0.0,-0.0,0.0,0.9,92.05 +2020-11-28 16:00:00,0.0,0.0,-0.0,0.0,1.1,95.6 +2020-11-28 17:00:00,-0.88,0.0,-0.0,0.0,1.38,95.6 +2020-11-28 18:00:00,-1.18,0.0,-0.0,0.0,1.45,91.95 +2020-11-28 19:00:00,-0.76,0.0,-0.0,0.0,1.38,95.6 +2020-11-28 20:00:00,-0.61,0.0,-0.0,0.0,1.52,95.6 +2020-11-28 21:00:00,-0.69,0.0,-0.0,0.0,1.52,95.6 +2020-11-28 22:00:00,-1.36,0.0,-0.0,0.0,1.45,91.95 +2020-11-28 23:00:00,-1.4,0.0,-0.0,0.0,1.31,95.6 +2020-11-29 00:00:00,-1.94,0.0,-0.0,0.0,1.52,95.55 +2020-11-29 01:00:00,-1.79,0.0,-0.0,0.0,1.72,91.9 +2020-11-29 02:00:00,-1.57,0.0,-0.0,0.0,2.07,95.6 +2020-11-29 03:00:00,-1.55,0.0,-0.0,0.0,2.21,95.6 +2020-11-29 04:00:00,-1.24,0.0,-0.0,0.0,2.07,95.6 +2020-11-29 05:00:00,-0.5,0.0,-0.0,0.0,3.1,95.6 +2020-11-29 06:00:00,-0.17,0.0,-0.0,0.0,2.9,92.0 +2020-11-29 07:00:00,-1.14,8.0,0.0,8.0,2.0,95.6 +2020-11-29 08:00:00,-0.26,59.0,18.87,56.0,3.59,92.0 +2020-11-29 09:00:00,0.39,56.0,0.0,56.0,3.66,92.0 +2020-11-29 10:00:00,1.08,83.0,0.0,83.0,4.0,92.05 +2020-11-29 11:00:00,1.28,94.0,0.0,94.0,4.48,88.6 +2020-11-29 12:00:00,1.53,74.0,0.0,74.0,4.62,82.0 +2020-11-29 13:00:00,1.62,58.0,0.0,58.0,4.48,82.0 +2020-11-29 14:00:00,1.37,48.0,113.14,37.0,3.52,85.25 +2020-11-29 15:00:00,1.03,0.0,-0.0,0.0,3.72,92.05 +2020-11-29 16:00:00,0.94,0.0,-0.0,0.0,4.0,92.05 +2020-11-29 17:00:00,0.79,0.0,-0.0,0.0,4.28,92.05 +2020-11-29 18:00:00,0.73,0.0,-0.0,0.0,4.41,92.05 +2020-11-29 19:00:00,0.31,0.0,-0.0,0.0,3.72,92.0 +2020-11-29 20:00:00,0.17,0.0,-0.0,0.0,3.79,88.5 +2020-11-29 21:00:00,0.32,0.0,-0.0,0.0,3.79,92.0 +2020-11-29 22:00:00,0.5,0.0,-0.0,0.0,4.07,95.65 +2020-11-29 23:00:00,0.66,0.0,-0.0,0.0,4.34,92.05 +2020-11-30 00:00:00,0.69,0.0,-0.0,0.0,4.48,88.55 +2020-11-30 01:00:00,0.43,0.0,-0.0,0.0,4.28,88.5 +2020-11-30 02:00:00,0.33,0.0,-0.0,0.0,4.28,85.15 +2020-11-30 03:00:00,0.15,0.0,-0.0,0.0,4.34,81.9 +2020-11-30 04:00:00,0.34,0.0,-0.0,0.0,4.28,78.75 +2020-11-30 05:00:00,0.69,0.0,-0.0,0.0,4.34,78.8 +2020-11-30 06:00:00,1.17,0.0,-0.0,0.0,4.69,82.0 +2020-11-30 07:00:00,1.5,5.0,0.0,5.0,5.31,85.25 +2020-11-30 08:00:00,1.71,44.0,0.0,44.0,5.93,78.95 +2020-11-30 09:00:00,2.06,56.0,0.0,56.0,6.83,78.95 +2020-11-30 10:00:00,2.59,64.0,0.0,64.0,7.45,79.0 +2020-11-30 11:00:00,2.72,69.0,0.0,69.0,7.72,79.1 +2020-11-30 12:00:00,2.71,85.0,3.77,84.0,7.31,82.2 +2020-11-30 13:00:00,2.67,46.0,0.0,46.0,6.83,88.7 +2020-11-30 14:00:00,2.75,6.0,0.0,6.0,6.76,92.15 +2020-11-30 15:00:00,3.32,0.0,-0.0,0.0,6.55,92.15 +2020-11-30 16:00:00,5.02,0.0,-0.0,0.0,7.33,95.8 +2020-11-30 17:00:00,4.61,0.0,-0.0,0.0,6.85,95.6 +2020-11-30 18:00:00,4.2,0.0,-0.0,0.0,6.37,95.41 +2020-11-30 19:00:00,3.79,0.0,-0.0,0.0,5.89,95.21 +2020-11-30 20:00:00,3.37,0.0,-0.0,0.0,5.4,95.02 +2020-11-30 21:00:00,2.96,0.0,-0.0,0.0,4.92,94.82 +2020-11-30 22:00:00,2.55,0.0,-0.0,0.0,4.44,94.62 +2020-11-30 23:00:00,2.14,0.0,-0.0,0.0,3.96,94.43 +2020-12-01 00:00:00,1.73,0.0,-0.0,0.0,3.47,94.23 +2020-12-01 01:00:00,1.31,0.0,-0.0,0.0,2.99,94.04 +2020-12-01 02:00:00,0.9,0.0,-0.0,0.0,2.51,93.84 +2020-12-01 03:00:00,0.49,0.0,-0.0,0.0,2.03,93.65 +2020-12-01 04:00:00,0.08,0.0,-0.0,0.0,1.54,93.45 +2020-12-01 05:00:00,-0.33,0.0,-0.0,0.0,1.06,93.25 +2020-12-01 06:00:00,-0.75,0.0,-0.0,0.0,0.58,93.06 +2020-12-01 07:00:00,-1.16,2.0,0.0,2.0,0.1,92.86 +2020-12-01 08:00:00,0.83,73.0,65.5,63.0,1.31,92.05 +2020-12-01 09:00:00,2.35,150.0,173.32,109.0,1.31,85.35 +2020-12-01 10:00:00,2.75,168.0,112.42,136.0,2.48,73.2 +2020-12-01 11:00:00,2.97,247.0,459.66,112.0,2.62,73.2 +2020-12-01 12:00:00,3.1,250.0,722.3,60.0,2.28,70.4 +2020-12-01 13:00:00,3.07,166.0,559.51,57.0,1.93,70.4 +2020-12-01 14:00:00,2.87,58.0,256.31,34.0,1.79,67.7 +2020-12-01 15:00:00,2.19,0.0,-0.0,0.0,1.52,75.95 +2020-12-01 16:00:00,1.25,0.0,-0.0,0.0,1.66,78.9 +2020-12-01 17:00:00,0.55,0.0,-0.0,0.0,1.59,85.15 +2020-12-01 18:00:00,0.22,0.0,-0.0,0.0,1.31,85.15 +2020-12-01 19:00:00,-0.13,0.0,-0.0,0.0,1.24,88.45 +2020-12-01 20:00:00,-0.17,0.0,-0.0,0.0,1.1,88.45 +2020-12-01 21:00:00,-0.04,0.0,-0.0,0.0,0.97,88.45 +2020-12-01 22:00:00,0.84,0.0,-0.0,0.0,0.69,81.95 +2020-12-01 23:00:00,0.75,0.0,-0.0,0.0,0.48,85.2 +2020-12-02 00:00:00,-0.35,0.0,-0.0,0.0,0.97,91.95 +2020-12-02 01:00:00,-0.53,0.0,-0.0,0.0,1.1,95.6 +2020-12-02 02:00:00,-0.71,0.0,-0.0,0.0,1.24,95.6 +2020-12-02 03:00:00,-0.68,0.0,-0.0,0.0,1.45,95.6 +2020-12-02 04:00:00,-0.26,0.0,-0.0,0.0,1.59,95.6 +2020-12-02 05:00:00,0.39,0.0,-0.0,0.0,2.34,95.65 +2020-12-02 06:00:00,0.42,0.0,-0.0,0.0,2.07,99.4 +2020-12-02 07:00:00,0.46,5.0,0.0,5.0,2.41,99.4 +2020-12-02 08:00:00,0.74,68.0,53.46,60.0,4.0,92.05 +2020-12-02 09:00:00,1.57,71.0,0.0,71.0,4.14,88.6 +2020-12-02 10:00:00,2.51,97.0,3.55,96.0,4.83,82.15 +2020-12-02 11:00:00,2.89,78.0,0.0,78.0,5.38,82.2 +2020-12-02 12:00:00,3.05,75.0,0.0,75.0,5.45,82.2 +2020-12-02 13:00:00,2.97,44.0,0.0,44.0,5.31,82.2 +2020-12-02 14:00:00,3.06,32.0,21.73,30.0,4.41,82.2 +2020-12-02 15:00:00,2.89,0.0,-0.0,0.0,3.86,85.4 +2020-12-02 16:00:00,2.77,0.0,-0.0,0.0,3.79,85.4 +2020-12-02 17:00:00,2.69,0.0,-0.0,0.0,4.14,88.65 +2020-12-02 18:00:00,2.6,0.0,-0.0,0.0,4.28,92.15 +2020-12-02 19:00:00,2.78,0.0,-0.0,0.0,5.24,92.15 +2020-12-02 20:00:00,2.23,0.0,-0.0,0.0,4.21,92.15 +2020-12-02 21:00:00,2.4,0.0,-0.0,0.0,4.34,88.65 +2020-12-02 22:00:00,2.37,0.0,-0.0,0.0,4.48,95.7 +2020-12-02 23:00:00,2.08,0.0,-0.0,0.0,4.76,95.65 +2020-12-03 00:00:00,1.67,0.0,-0.0,0.0,4.41,99.4 +2020-12-03 01:00:00,1.45,0.0,-0.0,0.0,4.21,95.65 +2020-12-03 02:00:00,1.17,0.0,-0.0,0.0,3.38,99.35 +2020-12-03 03:00:00,1.01,0.0,-0.0,0.0,2.9,99.35 +2020-12-03 04:00:00,1.01,0.0,-0.0,0.0,2.55,99.35 +2020-12-03 05:00:00,0.8,0.0,-0.0,0.0,2.07,95.65 +2020-12-03 06:00:00,0.48,0.0,-0.0,0.0,1.66,99.4 +2020-12-03 07:00:00,0.77,2.0,0.0,2.0,1.24,99.35 +2020-12-03 08:00:00,1.57,69.0,61.35,60.0,1.59,95.65 +2020-12-03 09:00:00,2.3,50.0,0.0,50.0,1.66,88.65 +2020-12-03 10:00:00,3.16,135.0,46.54,122.0,1.86,85.4 +2020-12-03 11:00:00,3.67,152.0,69.24,132.0,1.93,82.25 +2020-12-03 12:00:00,3.85,191.0,309.18,111.0,2.21,82.3 +2020-12-03 13:00:00,4.04,0.0,0.0,0.0,2.41,85.5 +2020-12-03 14:00:00,3.85,48.0,154.65,34.0,1.93,88.8 +2020-12-03 15:00:00,3.61,0.0,-0.0,0.0,1.93,95.7 +2020-12-03 16:00:00,3.51,0.0,-0.0,0.0,1.93,95.7 +2020-12-03 17:00:00,3.45,0.0,-0.0,0.0,2.07,95.7 +2020-12-03 18:00:00,3.43,0.0,-0.0,0.0,2.28,95.7 +2020-12-03 19:00:00,3.8,0.0,-0.0,0.0,2.41,95.75 +2020-12-03 20:00:00,3.71,0.0,-0.0,0.0,2.62,92.2 +2020-12-03 21:00:00,3.72,0.0,-0.0,0.0,2.55,92.2 +2020-12-03 22:00:00,3.71,0.0,-0.0,0.0,2.41,92.2 +2020-12-03 23:00:00,3.46,0.0,-0.0,0.0,2.34,92.15 +2020-12-04 00:00:00,3.44,0.0,-0.0,0.0,2.21,92.15 +2020-12-04 01:00:00,2.73,0.0,-0.0,0.0,2.0,88.7 +2020-12-04 02:00:00,2.29,0.0,-0.0,0.0,1.86,88.65 +2020-12-04 03:00:00,2.04,0.0,-0.0,0.0,1.72,88.65 +2020-12-04 04:00:00,1.67,0.0,-0.0,0.0,1.66,92.05 +2020-12-04 05:00:00,1.05,0.0,-0.0,0.0,1.72,92.05 +2020-12-04 06:00:00,0.97,0.0,-0.0,0.0,1.66,92.05 +2020-12-04 07:00:00,0.9,4.0,0.0,4.0,1.93,92.05 +2020-12-04 08:00:00,0.98,113.0,500.5,41.0,1.72,92.05 +2020-12-04 09:00:00,2.1,205.0,666.3,53.0,1.86,88.65 +2020-12-04 10:00:00,3.22,254.0,679.18,66.0,2.14,79.15 +2020-12-04 11:00:00,4.15,247.0,537.37,93.0,2.41,76.25 +2020-12-04 12:00:00,4.83,233.0,669.83,61.0,2.48,73.45 +2020-12-04 13:00:00,4.98,158.0,554.26,53.0,2.34,73.45 +2020-12-04 14:00:00,4.43,64.0,448.68,24.0,2.14,73.45 +2020-12-04 15:00:00,3.19,0.0,-0.0,0.0,2.55,82.2 +2020-12-04 16:00:00,2.26,0.0,-0.0,0.0,2.9,82.15 +2020-12-04 17:00:00,1.81,0.0,-0.0,0.0,3.03,85.3 +2020-12-04 18:00:00,1.59,0.0,-0.0,0.0,3.17,85.25 +2020-12-04 19:00:00,1.28,0.0,-0.0,0.0,3.24,82.0 +2020-12-04 20:00:00,1.09,0.0,-0.0,0.0,3.31,81.95 +2020-12-04 21:00:00,0.67,0.0,-0.0,0.0,3.31,81.9 +2020-12-04 22:00:00,0.35,0.0,-0.0,0.0,3.31,78.75 +2020-12-04 23:00:00,0.08,0.0,-0.0,0.0,3.31,81.8 +2020-12-05 00:00:00,-0.17,0.0,-0.0,0.0,3.45,78.65 +2020-12-05 01:00:00,-0.48,0.0,-0.0,0.0,3.59,81.75 +2020-12-05 02:00:00,-0.54,0.0,-0.0,0.0,3.72,81.75 +2020-12-05 03:00:00,-0.55,0.0,-0.0,0.0,3.79,81.75 +2020-12-05 04:00:00,-0.72,0.0,-0.0,0.0,3.79,81.75 +2020-12-05 05:00:00,-0.92,0.0,-0.0,0.0,3.72,81.7 +2020-12-05 06:00:00,-1.07,0.0,-0.0,0.0,3.59,81.7 +2020-12-05 07:00:00,-1.18,3.0,0.0,3.0,3.66,81.7 +2020-12-05 08:00:00,-0.81,110.0,474.79,43.0,3.66,81.75 +2020-12-05 09:00:00,0.14,207.0,682.85,53.0,3.52,81.8 +2020-12-05 10:00:00,1.33,259.0,714.26,63.0,3.38,75.85 +2020-12-05 11:00:00,2.42,270.0,724.23,64.0,3.1,73.1 +2020-12-05 12:00:00,3.11,243.0,745.3,53.0,2.9,70.4 +2020-12-05 13:00:00,3.23,162.0,606.72,48.0,2.55,70.5 +2020-12-05 14:00:00,2.53,62.0,432.32,24.0,2.48,76.0 +2020-12-05 15:00:00,1.24,0.0,-0.0,0.0,2.69,78.9 +2020-12-05 16:00:00,0.5,0.0,-0.0,0.0,2.69,81.9 +2020-12-05 17:00:00,0.23,0.0,-0.0,0.0,2.83,81.9 +2020-12-05 18:00:00,0.1,0.0,-0.0,0.0,2.97,81.8 +2020-12-05 19:00:00,-1.02,0.0,-0.0,0.0,3.1,81.7 +2020-12-05 20:00:00,-1.09,0.0,-0.0,0.0,2.97,78.5 +2020-12-05 21:00:00,-1.22,0.0,-0.0,0.0,2.76,78.5 +2020-12-05 22:00:00,-1.19,0.0,-0.0,0.0,2.76,75.45 +2020-12-05 23:00:00,-1.05,0.0,-0.0,0.0,2.83,75.45 +2020-12-06 00:00:00,-1.0,0.0,-0.0,0.0,2.83,75.45 +2020-12-06 01:00:00,-0.8,0.0,-0.0,0.0,2.83,72.6 +2020-12-06 02:00:00,-0.47,0.0,-0.0,0.0,2.9,72.6 +2020-12-06 03:00:00,-0.44,0.0,-0.0,0.0,3.03,69.75 +2020-12-06 04:00:00,-0.03,0.0,-0.0,0.0,3.31,64.45 +2020-12-06 05:00:00,0.38,0.0,-0.0,0.0,3.52,59.6 +2020-12-06 06:00:00,0.57,0.0,-0.0,0.0,3.45,57.2 +2020-12-06 07:00:00,0.06,3.0,0.0,3.0,3.72,61.9 +2020-12-06 08:00:00,0.64,109.0,476.61,43.0,3.72,62.0 +2020-12-06 09:00:00,2.09,203.0,663.57,55.0,3.72,62.35 +2020-12-06 10:00:00,3.56,223.0,433.61,105.0,3.72,60.3 +2020-12-06 11:00:00,4.7,216.0,332.83,122.0,3.79,60.5 +2020-12-06 12:00:00,5.32,217.0,521.32,85.0,3.86,58.3 +2020-12-06 13:00:00,5.27,148.0,445.11,65.0,3.72,58.3 +2020-12-06 14:00:00,4.56,30.0,23.05,28.0,3.72,58.2 +2020-12-06 15:00:00,3.66,0.0,-0.0,0.0,4.0,62.7 +2020-12-06 16:00:00,3.29,0.0,-0.0,0.0,4.34,65.2 +2020-12-06 17:00:00,3.29,0.0,-0.0,0.0,4.62,67.8 +2020-12-06 18:00:00,3.56,0.0,-0.0,0.0,4.69,73.3 +2020-12-06 19:00:00,3.11,0.0,-0.0,0.0,4.76,70.4 +2020-12-06 20:00:00,3.65,0.0,-0.0,0.0,4.9,73.3 +2020-12-06 21:00:00,4.32,0.0,-0.0,0.0,5.03,70.7 +2020-12-06 22:00:00,4.73,0.0,-0.0,0.0,5.03,76.3 +2020-12-06 23:00:00,5.04,0.0,-0.0,0.0,5.17,82.35 +2020-12-07 00:00:00,5.12,0.0,-0.0,0.0,5.31,88.85 +2020-12-07 01:00:00,5.73,0.0,-0.0,0.0,5.45,85.65 +2020-12-07 02:00:00,5.59,0.0,-0.0,0.0,5.52,88.85 +2020-12-07 03:00:00,5.57,0.0,-0.0,0.0,5.45,88.85 +2020-12-07 04:00:00,5.79,0.0,-0.0,0.0,5.52,85.65 +2020-12-07 05:00:00,6.11,0.0,-0.0,0.0,5.59,85.65 +2020-12-07 06:00:00,6.38,0.0,-0.0,0.0,5.86,85.7 +2020-12-07 07:00:00,7.05,0.0,0.0,0.0,6.07,85.75 +2020-12-07 08:00:00,7.23,61.0,58.85,53.0,6.0,85.75 +2020-12-07 09:00:00,7.39,65.0,0.0,65.0,5.72,85.8 +2020-12-07 10:00:00,7.59,94.0,7.41,92.0,6.0,85.8 +2020-12-07 11:00:00,7.81,39.0,0.0,39.0,5.93,82.7 +2020-12-07 12:00:00,7.82,33.0,0.0,33.0,6.0,82.7 +2020-12-07 13:00:00,7.67,77.0,32.4,71.0,5.38,85.8 +2020-12-07 14:00:00,7.27,15.0,0.0,15.0,4.97,82.65 +2020-12-07 15:00:00,7.0,0.0,-0.0,0.0,4.83,85.75 +2020-12-07 16:00:00,6.8,0.0,-0.0,0.0,4.69,85.75 +2020-12-07 17:00:00,6.73,0.0,-0.0,0.0,4.62,85.7 +2020-12-07 18:00:00,6.7,0.0,-0.0,0.0,4.62,85.7 +2020-12-07 19:00:00,6.72,0.0,-0.0,0.0,4.62,88.95 +2020-12-07 20:00:00,6.66,0.0,-0.0,0.0,4.55,85.7 +2020-12-07 21:00:00,6.58,0.0,-0.0,0.0,4.41,85.7 +2020-12-07 22:00:00,6.46,0.0,-0.0,0.0,4.28,85.7 +2020-12-07 23:00:00,6.31,0.0,-0.0,0.0,4.21,82.55 +2020-12-08 00:00:00,6.27,0.0,-0.0,0.0,4.0,82.55 +2020-12-08 01:00:00,5.97,0.0,-0.0,0.0,3.86,85.65 +2020-12-08 02:00:00,5.94,0.0,-0.0,0.0,3.72,85.65 +2020-12-08 03:00:00,5.98,0.0,-0.0,0.0,3.72,85.65 +2020-12-08 04:00:00,5.95,0.0,-0.0,0.0,3.79,85.65 +2020-12-08 05:00:00,5.96,0.0,-0.0,0.0,4.0,85.65 +2020-12-08 06:00:00,5.73,0.0,-0.0,0.0,4.07,82.5 +2020-12-08 07:00:00,6.4,0.0,0.0,0.0,4.21,82.55 +2020-12-08 08:00:00,6.69,84.0,239.69,52.0,4.34,82.55 +2020-12-08 09:00:00,7.19,105.0,59.53,92.0,4.55,79.55 +2020-12-08 10:00:00,7.9,219.0,459.08,96.0,3.86,76.8 +2020-12-08 11:00:00,8.39,159.0,107.62,129.0,4.21,74.05 +2020-12-08 12:00:00,8.68,70.0,0.0,70.0,4.07,76.85 +2020-12-08 13:00:00,8.93,87.0,59.78,76.0,5.86,71.4 +2020-12-08 14:00:00,8.95,29.0,23.56,27.0,5.45,71.4 +2020-12-08 15:00:00,8.83,0.0,-0.0,0.0,5.31,71.4 +2020-12-08 16:00:00,8.98,0.0,-0.0,0.0,5.17,74.15 +2020-12-08 17:00:00,9.26,0.0,-0.0,0.0,5.03,76.95 +2020-12-08 18:00:00,9.45,0.0,-0.0,0.0,4.97,77.0 +2020-12-08 19:00:00,9.92,0.0,-0.0,0.0,4.83,74.3 +2020-12-08 20:00:00,9.97,0.0,-0.0,0.0,4.69,74.3 +2020-12-08 21:00:00,9.89,0.0,-0.0,0.0,4.41,77.1 +2020-12-08 22:00:00,9.72,0.0,-0.0,0.0,4.07,79.9 +2020-12-08 23:00:00,9.55,0.0,-0.0,0.0,3.66,79.9 +2020-12-09 00:00:00,9.32,0.0,-0.0,0.0,3.72,82.9 +2020-12-09 01:00:00,9.19,0.0,-0.0,0.0,3.86,89.1 +2020-12-09 02:00:00,8.72,0.0,-0.0,0.0,3.93,85.9 +2020-12-09 03:00:00,8.15,0.0,-0.0,0.0,4.41,85.85 +2020-12-09 04:00:00,7.43,0.0,-0.0,0.0,4.21,82.65 +2020-12-09 05:00:00,6.84,0.0,-0.0,0.0,3.93,82.6 +2020-12-09 06:00:00,6.13,0.0,-0.0,0.0,3.66,85.65 +2020-12-09 07:00:00,6.3,0.0,0.0,0.0,3.52,82.55 +2020-12-09 08:00:00,6.88,104.0,525.99,35.0,3.79,82.6 +2020-12-09 09:00:00,7.59,145.0,235.86,94.0,3.93,79.65 +2020-12-09 10:00:00,8.09,176.0,203.01,122.0,4.14,76.8 +2020-12-09 11:00:00,8.5,195.0,259.84,123.0,3.86,71.35 +2020-12-09 12:00:00,8.83,57.0,0.0,57.0,4.0,68.8 +2020-12-09 13:00:00,9.07,37.0,0.0,37.0,4.14,66.25 +2020-12-09 14:00:00,8.36,7.0,0.0,7.0,3.72,68.7 +2020-12-09 15:00:00,7.7,0.0,-0.0,0.0,3.52,71.15 +2020-12-09 16:00:00,7.03,0.0,-0.0,0.0,3.45,73.8 +2020-12-09 17:00:00,6.59,0.0,-0.0,0.0,3.59,79.5 +2020-12-09 18:00:00,6.27,0.0,-0.0,0.0,3.59,79.5 +2020-12-09 19:00:00,6.07,0.0,-0.0,0.0,3.59,85.65 +2020-12-09 20:00:00,6.13,0.0,-0.0,0.0,4.21,85.65 +2020-12-09 21:00:00,5.83,0.0,-0.0,0.0,4.69,85.65 +2020-12-09 22:00:00,5.47,0.0,-0.0,0.0,4.55,88.85 +2020-12-09 23:00:00,4.95,0.0,-0.0,0.0,4.55,92.25 +2020-12-10 00:00:00,4.48,0.0,-0.0,0.0,4.97,92.25 +2020-12-10 01:00:00,4.16,0.0,-0.0,0.0,4.9,95.75 +2020-12-10 02:00:00,3.55,0.0,-0.0,0.0,4.9,92.15 +2020-12-10 03:00:00,3.05,0.0,-0.0,0.0,4.9,92.15 +2020-12-10 04:00:00,2.68,0.0,-0.0,0.0,5.1,95.7 +2020-12-10 05:00:00,2.35,0.0,-0.0,0.0,4.69,92.15 +2020-12-10 06:00:00,2.27,0.0,-0.0,0.0,4.69,92.15 +2020-12-10 07:00:00,2.34,0.0,0.0,0.0,4.34,92.15 +2020-12-10 08:00:00,2.5,105.0,550.55,34.0,4.0,92.15 +2020-12-10 09:00:00,2.84,136.0,182.08,97.0,4.76,88.7 +2020-12-10 10:00:00,3.19,193.0,287.67,117.0,4.55,88.7 +2020-12-10 11:00:00,3.69,237.0,515.3,95.0,4.34,85.45 +2020-12-10 12:00:00,4.02,207.0,484.77,87.0,4.34,79.2 +2020-12-10 13:00:00,4.01,56.0,5.49,55.0,3.45,79.2 +2020-12-10 14:00:00,3.82,34.0,47.92,30.0,2.62,79.2 +2020-12-10 15:00:00,3.39,0.0,-0.0,0.0,2.07,85.45 +2020-12-10 16:00:00,2.71,0.0,-0.0,0.0,1.86,85.4 +2020-12-10 17:00:00,2.4,0.0,-0.0,0.0,1.66,88.65 +2020-12-10 18:00:00,1.63,0.0,-0.0,0.0,1.86,92.05 +2020-12-10 19:00:00,0.66,0.0,-0.0,0.0,2.14,92.0 +2020-12-10 20:00:00,0.11,0.0,-0.0,0.0,2.28,92.0 +2020-12-10 21:00:00,-0.23,0.0,-0.0,0.0,2.41,88.45 +2020-12-10 22:00:00,-0.36,0.0,-0.0,0.0,2.55,88.45 +2020-12-10 23:00:00,-0.28,0.0,-0.0,0.0,2.83,85.1 +2020-12-11 00:00:00,-0.18,0.0,-0.0,0.0,3.24,85.1 +2020-12-11 01:00:00,-0.57,0.0,-0.0,0.0,3.66,81.75 +2020-12-11 02:00:00,-0.6,0.0,-0.0,0.0,3.86,81.75 +2020-12-11 03:00:00,-0.53,0.0,-0.0,0.0,3.86,81.75 +2020-12-11 04:00:00,-0.63,0.0,-0.0,0.0,3.79,81.75 +2020-12-11 05:00:00,-0.76,0.0,-0.0,0.0,3.79,78.6 +2020-12-11 06:00:00,-0.82,0.0,-0.0,0.0,3.86,78.6 +2020-12-11 07:00:00,-0.44,0.0,0.0,0.0,4.21,78.6 +2020-12-11 08:00:00,-0.04,88.0,315.33,48.0,4.21,78.65 +2020-12-11 09:00:00,0.89,129.0,150.76,97.0,4.07,75.75 +2020-12-11 10:00:00,1.83,126.0,45.71,114.0,3.79,70.2 +2020-12-11 11:00:00,2.62,217.0,375.69,114.0,3.93,67.6 +2020-12-11 12:00:00,3.01,227.0,669.55,62.0,3.86,67.7 +2020-12-11 13:00:00,2.95,144.0,452.49,62.0,3.72,67.7 +2020-12-11 14:00:00,2.34,62.0,494.26,21.0,3.79,70.3 +2020-12-11 15:00:00,1.41,0.0,-0.0,0.0,3.72,72.95 +2020-12-11 16:00:00,0.78,0.0,-0.0,0.0,3.66,75.75 +2020-12-11 17:00:00,0.53,0.0,-0.0,0.0,3.66,75.7 +2020-12-11 18:00:00,0.36,0.0,-0.0,0.0,3.52,75.7 +2020-12-11 19:00:00,0.18,0.0,-0.0,0.0,3.59,75.7 +2020-12-11 20:00:00,0.24,0.0,-0.0,0.0,3.31,72.75 +2020-12-11 21:00:00,0.11,0.0,-0.0,0.0,3.1,75.6 +2020-12-11 22:00:00,0.13,0.0,-0.0,0.0,2.9,72.65 +2020-12-11 23:00:00,0.0,0.0,-0.0,0.0,2.83,72.65 +2020-12-12 00:00:00,-0.09,0.0,-0.0,0.0,2.69,72.65 +2020-12-12 01:00:00,-0.17,0.0,-0.0,0.0,2.55,75.6 +2020-12-12 02:00:00,-0.42,0.0,-0.0,0.0,2.55,78.6 +2020-12-12 03:00:00,-0.33,0.0,-0.0,0.0,2.48,78.6 +2020-12-12 04:00:00,0.05,0.0,-0.0,0.0,2.34,78.65 +2020-12-12 05:00:00,0.23,0.0,-0.0,0.0,2.21,78.75 +2020-12-12 06:00:00,0.34,0.0,-0.0,0.0,2.07,81.9 +2020-12-12 07:00:00,0.71,0.0,0.0,0.0,2.0,88.55 +2020-12-12 08:00:00,1.03,51.0,40.05,46.0,1.93,92.05 +2020-12-12 09:00:00,1.62,92.0,33.26,85.0,1.86,92.05 +2020-12-12 10:00:00,2.63,61.0,0.0,61.0,2.07,88.65 +2020-12-12 11:00:00,3.49,125.0,36.65,115.0,2.34,82.25 +2020-12-12 12:00:00,4.18,77.0,4.07,76.0,2.55,79.2 +2020-12-12 13:00:00,4.57,76.0,33.23,70.0,2.07,76.3 +2020-12-12 14:00:00,4.33,54.0,363.37,24.0,1.59,79.3 +2020-12-12 15:00:00,3.45,0.0,-0.0,0.0,1.79,82.25 +2020-12-12 16:00:00,2.66,0.0,-0.0,0.0,2.07,88.65 +2020-12-12 17:00:00,2.36,0.0,-0.0,0.0,2.28,85.35 +2020-12-12 18:00:00,2.08,0.0,-0.0,0.0,2.48,88.65 +2020-12-12 19:00:00,1.24,0.0,-0.0,0.0,2.62,88.6 +2020-12-12 20:00:00,0.78,0.0,-0.0,0.0,2.76,92.05 +2020-12-12 21:00:00,0.83,0.0,-0.0,0.0,2.76,92.05 +2020-12-12 22:00:00,0.62,0.0,-0.0,0.0,2.83,92.0 +2020-12-12 23:00:00,0.52,0.0,-0.0,0.0,2.97,92.0 +2020-12-13 00:00:00,0.4,0.0,-0.0,0.0,3.17,92.0 +2020-12-13 01:00:00,0.15,0.0,-0.0,0.0,3.38,92.0 +2020-12-13 02:00:00,-0.05,0.0,-0.0,0.0,3.52,92.0 +2020-12-13 03:00:00,-0.3,0.0,-0.0,0.0,3.79,88.45 +2020-12-13 04:00:00,-0.41,0.0,-0.0,0.0,4.14,91.95 +2020-12-13 05:00:00,-0.27,0.0,-0.0,0.0,4.34,88.45 +2020-12-13 06:00:00,-0.27,0.0,-0.0,0.0,4.62,88.45 +2020-12-13 07:00:00,-0.22,0.0,0.0,0.0,4.62,88.45 +2020-12-13 08:00:00,-0.06,49.0,32.53,45.0,4.41,88.45 +2020-12-13 09:00:00,0.14,108.0,76.64,92.0,5.1,88.45 +2020-12-13 10:00:00,0.55,143.0,92.49,119.0,5.59,85.15 +2020-12-13 11:00:00,1.36,184.0,217.13,125.0,5.59,75.85 +2020-12-13 12:00:00,1.57,159.0,196.23,111.0,5.31,78.9 +2020-12-13 13:00:00,1.84,50.0,0.0,50.0,4.41,75.95 +2020-12-13 14:00:00,1.72,44.0,170.13,30.0,3.72,75.95 +2020-12-13 15:00:00,1.43,0.0,-0.0,0.0,3.66,78.9 +2020-12-13 16:00:00,1.34,0.0,-0.0,0.0,3.66,78.9 +2020-12-13 17:00:00,1.26,0.0,-0.0,0.0,3.86,78.9 +2020-12-13 18:00:00,1.29,0.0,-0.0,0.0,3.93,82.0 +2020-12-13 19:00:00,2.01,0.0,-0.0,0.0,3.59,85.3 +2020-12-13 20:00:00,2.59,0.0,-0.0,0.0,3.45,85.35 +2020-12-13 21:00:00,3.23,0.0,-0.0,0.0,3.45,82.25 +2020-12-13 22:00:00,3.26,0.0,-0.0,0.0,3.66,82.25 +2020-12-13 23:00:00,3.33,0.0,-0.0,0.0,3.79,82.25 +2020-12-14 00:00:00,3.33,0.0,-0.0,0.0,4.0,82.25 +2020-12-14 01:00:00,3.67,0.0,-0.0,0.0,4.28,88.75 +2020-12-14 02:00:00,3.62,0.0,-0.0,0.0,4.41,92.15 +2020-12-14 03:00:00,3.93,0.0,-0.0,0.0,4.48,88.8 +2020-12-14 04:00:00,4.16,0.0,-0.0,0.0,4.41,88.8 +2020-12-14 05:00:00,4.14,0.0,-0.0,0.0,4.14,85.5 +2020-12-14 06:00:00,3.81,0.0,-0.0,0.0,3.59,85.5 +2020-12-14 07:00:00,3.29,0.0,0.0,0.0,3.66,85.45 +2020-12-14 08:00:00,3.37,33.0,0.0,33.0,3.86,85.45 +2020-12-14 09:00:00,3.68,110.0,91.71,91.0,4.48,85.45 +2020-12-14 10:00:00,3.08,32.0,0.0,32.0,5.03,92.15 +2020-12-14 11:00:00,3.33,22.0,0.0,22.0,5.1,88.75 +2020-12-14 12:00:00,4.81,34.0,0.0,34.0,6.28,88.85 +2020-12-14 13:00:00,4.55,34.0,0.0,34.0,8.14,88.85 +2020-12-14 14:00:00,4.46,52.0,328.69,25.0,6.34,85.55 +2020-12-14 15:00:00,4.57,0.0,-0.0,0.0,5.52,82.35 +2020-12-14 16:00:00,4.38,0.0,-0.0,0.0,5.66,79.3 +2020-12-14 17:00:00,4.2,0.0,-0.0,0.0,5.72,76.25 +2020-12-14 18:00:00,3.96,0.0,-0.0,0.0,5.1,70.6 +2020-12-14 19:00:00,4.17,0.0,-0.0,0.0,4.55,79.2 +2020-12-14 20:00:00,4.58,0.0,-0.0,0.0,5.59,79.3 +2020-12-14 21:00:00,4.97,0.0,-0.0,0.0,6.28,82.35 +2020-12-14 22:00:00,5.15,0.0,-0.0,0.0,6.41,82.35 +2020-12-14 23:00:00,5.1,0.0,-0.0,0.0,6.48,79.3 +2020-12-15 00:00:00,4.67,0.0,-0.0,0.0,6.07,73.45 +2020-12-15 01:00:00,4.22,0.0,-0.0,0.0,4.76,70.7 +2020-12-15 02:00:00,3.6,0.0,-0.0,0.0,3.52,76.15 +2020-12-15 03:00:00,3.09,0.0,-0.0,0.0,2.76,79.1 +2020-12-15 04:00:00,2.81,0.0,-0.0,0.0,2.9,79.1 +2020-12-15 05:00:00,2.88,0.0,-0.0,0.0,3.38,82.2 +2020-12-15 06:00:00,3.49,0.0,-0.0,0.0,4.14,82.25 +2020-12-15 07:00:00,4.76,0.0,0.0,0.0,4.48,88.85 +2020-12-15 08:00:00,5.86,19.0,0.0,19.0,5.24,88.9 +2020-12-15 09:00:00,7.08,41.0,0.0,41.0,5.31,89.0 +2020-12-15 10:00:00,8.48,122.0,46.7,110.0,5.59,85.9 +2020-12-15 11:00:00,10.32,141.0,74.13,121.0,5.72,80.05 +2020-12-15 12:00:00,10.87,83.0,4.11,82.0,5.66,80.1 +2020-12-15 13:00:00,10.37,20.0,0.0,20.0,5.1,83.0 +2020-12-15 14:00:00,9.22,38.0,97.42,30.0,4.14,85.95 +2020-12-15 15:00:00,8.6,0.0,-0.0,0.0,4.0,79.75 +2020-12-15 16:00:00,8.34,0.0,-0.0,0.0,4.07,76.85 +2020-12-15 17:00:00,7.88,0.0,-0.0,0.0,3.93,73.95 +2020-12-15 18:00:00,7.25,0.0,-0.0,0.0,3.79,76.7 +2020-12-15 19:00:00,6.67,0.0,-0.0,0.0,3.72,82.55 +2020-12-15 20:00:00,6.56,0.0,-0.0,0.0,3.79,82.55 +2020-12-15 21:00:00,6.42,0.0,-0.0,0.0,3.72,82.55 +2020-12-15 22:00:00,6.23,0.0,-0.0,0.0,3.52,82.55 +2020-12-15 23:00:00,5.94,0.0,-0.0,0.0,3.24,85.65 +2020-12-16 00:00:00,5.31,0.0,-0.0,0.0,2.9,85.6 +2020-12-16 01:00:00,4.62,0.0,-0.0,0.0,2.55,85.55 +2020-12-16 02:00:00,4.58,0.0,-0.0,0.0,2.34,88.85 +2020-12-16 03:00:00,4.27,0.0,-0.0,0.0,2.28,85.55 +2020-12-16 04:00:00,4.74,0.0,-0.0,0.0,2.34,88.85 +2020-12-16 05:00:00,5.21,0.0,-0.0,0.0,2.48,92.25 +2020-12-16 06:00:00,5.7,0.0,-0.0,0.0,2.62,88.85 +2020-12-16 07:00:00,5.69,0.0,0.0,0.0,2.83,92.25 +2020-12-16 08:00:00,6.38,42.0,16.95,40.0,2.9,88.95 +2020-12-16 09:00:00,7.21,29.0,0.0,29.0,2.83,92.35 +2020-12-16 10:00:00,7.69,162.0,175.84,117.0,2.97,89.0 +2020-12-16 11:00:00,8.31,220.0,457.2,97.0,3.17,85.9 +2020-12-16 12:00:00,8.7,137.0,111.19,110.0,3.52,89.1 +2020-12-16 13:00:00,9.05,93.0,94.91,76.0,3.52,85.95 +2020-12-16 14:00:00,8.69,34.0,60.81,29.0,3.31,85.9 +2020-12-16 15:00:00,7.72,0.0,-0.0,0.0,3.24,89.0 +2020-12-16 16:00:00,7.05,0.0,-0.0,0.0,3.24,89.0 +2020-12-16 17:00:00,6.78,0.0,-0.0,0.0,3.38,89.0 +2020-12-16 18:00:00,6.62,0.0,-0.0,0.0,3.45,88.95 +2020-12-16 19:00:00,6.23,0.0,-0.0,0.0,3.52,92.3 +2020-12-16 20:00:00,5.95,0.0,-0.0,0.0,3.59,92.3 +2020-12-16 21:00:00,5.56,0.0,-0.0,0.0,3.45,92.25 +2020-12-16 22:00:00,5.45,0.0,-0.0,0.0,3.52,92.25 +2020-12-16 23:00:00,5.17,0.0,-0.0,0.0,3.38,95.75 +2020-12-17 00:00:00,5.12,0.0,-0.0,0.0,3.52,92.25 +2020-12-17 01:00:00,5.31,0.0,-0.0,0.0,3.59,88.85 +2020-12-17 02:00:00,5.17,0.0,-0.0,0.0,3.66,92.25 +2020-12-17 03:00:00,5.33,0.0,-0.0,0.0,3.79,88.85 +2020-12-17 04:00:00,5.41,0.0,-0.0,0.0,4.0,85.6 +2020-12-17 05:00:00,5.48,0.0,-0.0,0.0,4.21,85.6 +2020-12-17 06:00:00,5.1,0.0,-0.0,0.0,4.28,88.85 +2020-12-17 07:00:00,4.4,0.0,0.0,0.0,4.34,85.55 +2020-12-17 08:00:00,4.66,72.0,240.27,44.0,4.14,85.55 +2020-12-17 09:00:00,5.35,167.0,521.7,61.0,3.45,85.6 +2020-12-17 10:00:00,6.69,206.0,451.02,91.0,3.52,82.55 +2020-12-17 11:00:00,7.77,161.0,141.59,123.0,3.45,73.95 +2020-12-17 12:00:00,8.64,175.0,309.28,100.0,3.1,74.05 +2020-12-17 13:00:00,9.0,91.0,83.76,76.0,2.97,74.15 +2020-12-17 14:00:00,8.52,52.0,339.63,24.0,2.9,76.85 +2020-12-17 15:00:00,7.22,0.0,-0.0,0.0,3.03,82.6 +2020-12-17 16:00:00,6.42,0.0,-0.0,0.0,3.03,82.55 +2020-12-17 17:00:00,5.66,0.0,-0.0,0.0,3.1,88.85 +2020-12-17 18:00:00,5.25,0.0,-0.0,0.0,3.17,88.85 +2020-12-17 19:00:00,4.88,0.0,-0.0,0.0,3.52,88.85 +2020-12-17 20:00:00,5.08,0.0,-0.0,0.0,3.52,88.85 +2020-12-17 21:00:00,5.62,0.0,-0.0,0.0,3.45,85.6 +2020-12-17 22:00:00,5.88,0.0,-0.0,0.0,3.52,82.5 +2020-12-17 23:00:00,6.1,0.0,-0.0,0.0,3.45,82.5 +2020-12-18 00:00:00,6.03,0.0,-0.0,0.0,3.31,82.5 +2020-12-18 01:00:00,5.82,0.0,-0.0,0.0,3.38,82.5 +2020-12-18 02:00:00,5.77,0.0,-0.0,0.0,3.52,85.65 +2020-12-18 03:00:00,6.42,0.0,-0.0,0.0,3.79,82.55 +2020-12-18 04:00:00,8.03,0.0,-0.0,0.0,3.93,73.95 +2020-12-18 05:00:00,9.11,0.0,-0.0,0.0,3.72,68.8 +2020-12-18 06:00:00,9.5,0.0,-0.0,0.0,3.59,68.9 +2020-12-18 07:00:00,9.49,0.0,-0.0,0.0,3.31,71.5 +2020-12-18 08:00:00,9.63,77.0,329.8,39.0,3.1,77.0 +2020-12-18 09:00:00,9.91,79.0,19.79,75.0,2.83,79.95 +2020-12-18 10:00:00,10.43,75.0,0.0,75.0,2.34,80.05 +2020-12-18 11:00:00,11.39,91.0,3.73,90.0,2.14,77.3 +2020-12-18 12:00:00,11.33,73.0,0.0,73.0,1.79,77.3 +2020-12-18 13:00:00,10.88,80.0,44.65,72.0,1.52,80.1 +2020-12-18 14:00:00,10.51,12.0,0.0,12.0,1.59,83.0 +2020-12-18 15:00:00,10.01,0.0,-0.0,0.0,1.45,86.0 +2020-12-18 16:00:00,9.1,0.0,-0.0,0.0,1.38,92.45 +2020-12-18 17:00:00,8.11,0.0,-0.0,0.0,1.45,95.8 +2020-12-18 18:00:00,6.89,0.0,-0.0,0.0,1.52,95.8 +2020-12-18 19:00:00,4.99,0.0,-0.0,0.0,1.93,99.4 +2020-12-18 20:00:00,4.09,0.0,-0.0,0.0,1.79,95.75 +2020-12-18 21:00:00,3.88,0.0,-0.0,0.0,1.93,95.75 +2020-12-18 22:00:00,4.19,0.0,-0.0,0.0,2.14,95.75 +2020-12-18 23:00:00,4.07,0.0,-0.0,0.0,2.21,95.75 +2020-12-19 00:00:00,4.33,0.0,-0.0,0.0,2.48,95.75 +2020-12-19 01:00:00,4.11,0.0,-0.0,0.0,2.62,95.75 +2020-12-19 02:00:00,4.37,0.0,-0.0,0.0,2.76,95.75 +2020-12-19 03:00:00,4.42,0.0,-0.0,0.0,2.69,95.75 +2020-12-19 04:00:00,4.32,0.0,-0.0,0.0,2.69,95.75 +2020-12-19 05:00:00,4.12,0.0,-0.0,0.0,2.83,95.75 +2020-12-19 06:00:00,4.3,0.0,-0.0,0.0,2.97,95.75 +2020-12-19 07:00:00,4.2,0.0,-0.0,0.0,3.1,95.75 +2020-12-19 08:00:00,4.56,89.0,517.44,30.0,3.52,92.25 +2020-12-19 09:00:00,5.36,157.0,432.55,70.0,3.38,92.25 +2020-12-19 10:00:00,6.39,224.0,607.56,70.0,3.72,92.3 +2020-12-19 11:00:00,6.93,208.0,381.33,106.0,3.93,92.35 +2020-12-19 12:00:00,7.17,139.0,119.73,110.0,4.14,92.35 +2020-12-19 13:00:00,7.15,56.0,5.57,55.0,3.86,92.35 +2020-12-19 14:00:00,6.9,25.0,12.01,24.0,4.0,92.35 +2020-12-19 15:00:00,6.67,0.0,-0.0,0.0,3.86,95.8 +2020-12-19 16:00:00,6.66,0.0,-0.0,0.0,3.79,95.8 +2020-12-19 17:00:00,6.62,0.0,-0.0,0.0,3.72,95.8 +2020-12-19 18:00:00,6.61,0.0,-0.0,0.0,3.72,92.3 +2020-12-19 19:00:00,6.26,0.0,-0.0,0.0,4.07,88.95 +2020-12-19 20:00:00,6.19,0.0,-0.0,0.0,4.14,92.3 +2020-12-19 21:00:00,5.79,0.0,-0.0,0.0,4.07,88.9 +2020-12-19 22:00:00,5.71,0.0,-0.0,0.0,4.07,95.75 +2020-12-19 23:00:00,5.65,0.0,-0.0,0.0,4.14,95.75 +2020-12-20 00:00:00,5.83,0.0,-0.0,0.0,4.14,92.3 +2020-12-20 01:00:00,6.27,0.0,-0.0,0.0,4.28,92.3 +2020-12-20 02:00:00,6.32,0.0,-0.0,0.0,4.34,92.3 +2020-12-20 03:00:00,6.47,0.0,-0.0,0.0,4.41,95.8 +2020-12-20 04:00:00,6.44,0.0,-0.0,0.0,4.34,95.8 +2020-12-20 05:00:00,6.31,0.0,-0.0,0.0,4.41,92.3 +2020-12-20 06:00:00,6.25,0.0,-0.0,0.0,4.55,92.3 +2020-12-20 07:00:00,5.88,0.0,-0.0,0.0,4.97,92.3 +2020-12-20 08:00:00,5.94,83.0,451.55,32.0,4.83,92.3 +2020-12-20 09:00:00,6.61,176.0,639.07,48.0,4.34,92.3 +2020-12-20 10:00:00,7.65,229.0,668.21,60.0,4.55,89.0 +2020-12-20 11:00:00,8.58,245.0,681.06,63.0,4.69,85.9 +2020-12-20 12:00:00,9.08,216.0,643.93,60.0,4.48,82.85 +2020-12-20 13:00:00,9.4,157.0,645.47,41.0,4.41,82.9 +2020-12-20 14:00:00,9.22,41.0,131.2,30.0,4.62,85.95 +2020-12-20 15:00:00,8.53,0.0,-0.0,0.0,4.62,89.1 +2020-12-20 16:00:00,7.99,0.0,-0.0,0.0,4.69,89.05 +2020-12-20 17:00:00,8.04,0.0,-0.0,0.0,4.55,89.05 +2020-12-20 18:00:00,7.87,0.0,-0.0,0.0,4.14,89.05 +2020-12-20 19:00:00,7.3,0.0,-0.0,0.0,3.66,89.0 +2020-12-20 20:00:00,7.02,0.0,-0.0,0.0,3.31,92.35 +2020-12-20 21:00:00,6.86,0.0,-0.0,0.0,2.76,92.35 +2020-12-20 22:00:00,6.65,0.0,-0.0,0.0,2.34,95.8 +2020-12-20 23:00:00,6.79,0.0,-0.0,0.0,2.0,92.35 +2020-12-21 00:00:00,7.07,0.0,-0.0,0.0,2.07,92.35 +2020-12-21 01:00:00,7.95,0.0,-0.0,0.0,2.97,89.05 +2020-12-21 02:00:00,8.38,0.0,-0.0,0.0,3.52,89.1 +2020-12-21 03:00:00,8.32,0.0,-0.0,0.0,3.59,85.9 +2020-12-21 04:00:00,7.97,0.0,-0.0,0.0,3.31,89.05 +2020-12-21 05:00:00,7.32,0.0,-0.0,0.0,2.55,89.0 +2020-12-21 06:00:00,6.42,0.0,-0.0,0.0,2.41,88.95 +2020-12-21 07:00:00,5.79,0.0,-0.0,0.0,2.41,88.9 +2020-12-21 08:00:00,5.25,84.0,473.28,31.0,2.21,92.25 +2020-12-21 09:00:00,6.39,177.0,651.38,47.0,2.28,88.95 +2020-12-21 10:00:00,7.31,233.0,701.03,56.0,2.0,85.8 +2020-12-21 11:00:00,7.96,241.0,647.68,68.0,1.79,82.7 +2020-12-21 12:00:00,8.31,196.0,457.84,85.0,1.38,76.85 +2020-12-21 13:00:00,8.29,129.0,338.55,68.0,1.17,76.85 +2020-12-21 14:00:00,7.62,49.0,236.53,29.0,1.52,82.65 +2020-12-21 15:00:00,6.64,0.0,-0.0,0.0,1.72,88.95 +2020-12-21 16:00:00,5.61,0.0,-0.0,0.0,1.52,92.25 +2020-12-21 17:00:00,4.59,0.0,-0.0,0.0,1.59,92.25 +2020-12-21 18:00:00,4.14,0.0,-0.0,0.0,1.79,95.75 +2020-12-21 19:00:00,4.01,0.0,-0.0,0.0,1.93,95.75 +2020-12-21 20:00:00,3.69,0.0,-0.0,0.0,2.0,99.4 +2020-12-21 21:00:00,3.4,0.0,-0.0,0.0,2.07,95.7 +2020-12-21 22:00:00,3.21,0.0,-0.0,0.0,2.0,95.7 +2020-12-21 23:00:00,3.06,0.0,-0.0,0.0,2.07,99.4 +2020-12-22 00:00:00,2.9,0.0,-0.0,0.0,2.14,99.4 +2020-12-22 01:00:00,3.03,0.0,-0.0,0.0,2.28,99.4 +2020-12-22 02:00:00,3.3,0.0,-0.0,0.0,2.48,95.7 +2020-12-22 03:00:00,3.68,0.0,-0.0,0.0,2.83,99.4 +2020-12-22 04:00:00,3.64,0.0,-0.0,0.0,2.97,95.7 +2020-12-22 05:00:00,3.39,0.0,-0.0,0.0,2.83,95.7 +2020-12-22 06:00:00,3.44,0.0,-0.0,0.0,2.9,95.7 +2020-12-22 07:00:00,3.98,0.0,-0.0,0.0,2.97,95.75 +2020-12-22 08:00:00,4.22,74.0,314.91,39.0,3.03,92.25 +2020-12-22 09:00:00,4.74,110.0,110.56,88.0,3.17,92.25 +2020-12-22 10:00:00,5.45,174.0,249.82,111.0,3.1,88.85 +2020-12-22 11:00:00,6.06,112.0,22.46,106.0,3.03,85.65 +2020-12-22 12:00:00,6.39,166.0,243.04,107.0,2.69,82.55 +2020-12-22 13:00:00,6.4,116.0,221.27,76.0,2.41,82.55 +2020-12-22 14:00:00,6.08,42.0,117.12,32.0,2.34,85.65 +2020-12-22 15:00:00,5.56,0.0,-0.0,0.0,2.48,88.85 +2020-12-22 16:00:00,5.14,0.0,-0.0,0.0,2.48,92.25 +2020-12-22 17:00:00,4.97,0.0,-0.0,0.0,2.48,92.25 +2020-12-22 18:00:00,4.79,0.0,-0.0,0.0,2.41,92.25 +2020-12-22 19:00:00,4.4,0.0,-0.0,0.0,2.55,92.25 +2020-12-22 20:00:00,4.6,0.0,-0.0,0.0,2.28,92.25 +2020-12-22 21:00:00,4.49,0.0,-0.0,0.0,2.14,92.25 +2020-12-22 22:00:00,4.54,0.0,-0.0,0.0,2.0,92.25 +2020-12-22 23:00:00,5.25,0.0,-0.0,0.0,1.93,88.85 +2020-12-23 00:00:00,5.89,0.0,-0.0,0.0,2.34,92.3 +2020-12-23 01:00:00,6.38,0.0,-0.0,0.0,3.03,92.3 +2020-12-23 02:00:00,6.47,0.0,-0.0,0.0,3.45,92.3 +2020-12-23 03:00:00,6.4,0.0,-0.0,0.0,3.79,92.3 +2020-12-23 04:00:00,6.27,0.0,-0.0,0.0,3.93,92.3 +2020-12-23 05:00:00,6.22,0.0,-0.0,0.0,4.07,95.75 +2020-12-23 06:00:00,6.15,0.0,-0.0,0.0,4.48,95.75 +2020-12-23 07:00:00,6.04,0.0,-0.0,0.0,4.76,92.3 +2020-12-23 08:00:00,5.94,24.0,0.0,24.0,4.76,92.3 +2020-12-23 09:00:00,5.88,78.0,20.15,74.0,4.9,92.3 +2020-12-23 10:00:00,5.97,142.0,107.14,115.0,4.83,92.3 +2020-12-23 11:00:00,6.18,113.0,22.45,107.0,5.03,88.9 +2020-12-23 12:00:00,6.37,74.0,0.0,74.0,5.17,85.7 +2020-12-23 13:00:00,6.49,59.0,5.51,58.0,4.76,85.7 +2020-12-23 14:00:00,6.4,22.0,0.0,22.0,4.0,85.7 +2020-12-23 15:00:00,5.89,0.0,-0.0,0.0,3.79,85.65 +2020-12-23 16:00:00,5.64,0.0,-0.0,0.0,3.79,92.25 +2020-12-23 17:00:00,5.47,0.0,-0.0,0.0,4.0,92.25 +2020-12-23 18:00:00,5.4,0.0,-0.0,0.0,4.21,92.25 +2020-12-23 19:00:00,5.14,0.0,-0.0,0.0,4.0,92.25 +2020-12-23 20:00:00,4.95,0.0,-0.0,0.0,4.07,92.25 +2020-12-23 21:00:00,4.9,0.0,-0.0,0.0,4.07,92.25 +2020-12-23 22:00:00,5.03,0.0,-0.0,0.0,4.0,92.25 +2020-12-23 23:00:00,5.09,0.0,-0.0,0.0,4.07,92.25 +2020-12-24 00:00:00,5.18,0.0,-0.0,0.0,4.28,92.25 +2020-12-24 01:00:00,5.36,0.0,-0.0,0.0,4.34,88.85 +2020-12-24 02:00:00,5.38,0.0,-0.0,0.0,4.41,88.85 +2020-12-24 03:00:00,5.32,0.0,-0.0,0.0,4.48,88.85 +2020-12-24 04:00:00,5.23,0.0,-0.0,0.0,4.14,88.85 +2020-12-24 05:00:00,5.06,0.0,-0.0,0.0,3.93,92.25 +2020-12-24 06:00:00,4.96,0.0,-0.0,0.0,3.79,88.85 +2020-12-24 07:00:00,4.96,0.0,-0.0,0.0,3.38,92.25 +2020-12-24 08:00:00,5.02,47.0,54.63,41.0,3.38,92.25 +2020-12-24 09:00:00,5.34,96.0,60.55,84.0,3.38,88.85 +2020-12-24 10:00:00,5.79,197.0,388.94,99.0,2.83,85.65 +2020-12-24 11:00:00,6.45,201.0,332.7,112.0,3.31,79.5 +2020-12-24 12:00:00,6.75,192.0,410.24,92.0,3.31,68.4 +2020-12-24 13:00:00,6.83,120.0,241.32,76.0,2.41,71.05 +2020-12-24 14:00:00,6.68,34.0,45.76,30.0,2.14,73.7 +2020-12-24 15:00:00,6.12,0.0,-0.0,0.0,2.41,76.5 +2020-12-24 16:00:00,5.41,0.0,-0.0,0.0,2.97,79.35 +2020-12-24 17:00:00,4.32,0.0,-0.0,0.0,3.38,88.85 +2020-12-24 18:00:00,3.81,0.0,-0.0,0.0,3.59,92.2 +2020-12-24 19:00:00,4.46,0.0,-0.0,0.0,3.17,92.25 +2020-12-24 20:00:00,5.39,0.0,-0.0,0.0,3.93,92.25 +2020-12-24 21:00:00,6.4,0.0,-0.0,0.0,5.03,88.95 +2020-12-24 22:00:00,6.57,0.0,-0.0,0.0,4.9,88.95 +2020-12-24 23:00:00,6.45,0.0,-0.0,0.0,4.69,88.95 +2020-12-25 00:00:00,6.33,0.0,-0.0,0.0,4.62,92.3 +2020-12-25 01:00:00,6.16,0.0,-0.0,0.0,4.41,95.75 +2020-12-25 02:00:00,6.15,0.0,-0.0,0.0,4.28,92.3 +2020-12-25 03:00:00,6.13,0.0,-0.0,0.0,4.69,92.3 +2020-12-25 04:00:00,5.91,0.0,-0.0,0.0,4.76,92.3 +2020-12-25 05:00:00,5.97,0.0,-0.0,0.0,4.55,92.3 +2020-12-25 06:00:00,5.84,0.0,-0.0,0.0,4.0,92.3 +2020-12-25 07:00:00,5.85,0.0,-0.0,0.0,4.0,95.75 +2020-12-25 08:00:00,5.86,24.0,0.0,24.0,4.0,95.75 +2020-12-25 09:00:00,5.7,91.0,50.51,81.0,3.52,99.4 +2020-12-25 10:00:00,5.42,80.0,3.97,79.0,2.97,95.75 +2020-12-25 11:00:00,5.13,42.0,0.0,42.0,3.17,99.4 +2020-12-25 12:00:00,4.84,41.0,0.0,41.0,2.97,99.4 +2020-12-25 13:00:00,4.63,14.0,0.0,14.0,3.1,95.75 +2020-12-25 14:00:00,4.47,16.0,0.0,16.0,3.1,95.75 +2020-12-25 15:00:00,4.26,0.0,-0.0,0.0,2.76,95.75 +2020-12-25 16:00:00,4.12,0.0,-0.0,0.0,3.24,95.75 +2020-12-25 17:00:00,3.99,0.0,-0.0,0.0,3.31,95.75 +2020-12-25 18:00:00,3.91,0.0,-0.0,0.0,2.83,95.75 +2020-12-25 19:00:00,3.91,0.0,-0.0,0.0,3.45,95.75 +2020-12-25 20:00:00,3.87,0.0,-0.0,0.0,3.52,95.75 +2020-12-25 21:00:00,3.93,0.0,-0.0,0.0,2.55,95.75 +2020-12-25 22:00:00,4.07,0.0,-0.0,0.0,3.1,95.75 +2020-12-25 23:00:00,3.95,0.0,-0.0,0.0,3.66,95.75 +2020-12-26 00:00:00,3.77,0.0,-0.0,0.0,3.93,95.75 +2020-12-26 01:00:00,3.78,0.0,-0.0,0.0,3.52,95.75 +2020-12-26 02:00:00,3.68,0.0,-0.0,0.0,2.62,99.4 +2020-12-26 03:00:00,3.49,0.0,-0.0,0.0,2.48,99.4 +2020-12-26 04:00:00,3.21,0.0,-0.0,0.0,2.21,99.4 +2020-12-26 05:00:00,3.1,0.0,-0.0,0.0,2.07,99.4 +2020-12-26 06:00:00,3.1,0.0,-0.0,0.0,2.21,99.4 +2020-12-26 07:00:00,3.48,0.0,-0.0,0.0,2.41,99.4 +2020-12-26 08:00:00,3.26,53.0,91.74,43.0,2.34,99.4 +2020-12-26 09:00:00,3.53,101.0,75.8,86.0,2.83,99.4 +2020-12-26 10:00:00,4.0,137.0,87.21,115.0,3.86,88.8 +2020-12-26 11:00:00,4.43,119.0,29.8,111.0,3.59,88.85 +2020-12-26 12:00:00,4.93,103.0,24.46,97.0,3.86,85.55 +2020-12-26 13:00:00,4.83,85.0,48.81,76.0,3.31,85.55 +2020-12-26 14:00:00,4.72,38.0,66.74,32.0,2.83,85.55 +2020-12-26 15:00:00,4.01,0.0,-0.0,0.0,2.48,85.5 +2020-12-26 16:00:00,3.24,0.0,-0.0,0.0,2.28,88.75 +2020-12-26 17:00:00,2.53,0.0,-0.0,0.0,2.14,88.65 +2020-12-26 18:00:00,2.44,0.0,-0.0,0.0,2.0,88.65 +2020-12-26 19:00:00,2.67,0.0,-0.0,0.0,2.14,88.65 +2020-12-26 20:00:00,2.63,0.0,-0.0,0.0,2.07,88.65 +2020-12-26 21:00:00,2.52,0.0,-0.0,0.0,2.0,88.65 +2020-12-26 22:00:00,2.59,0.0,-0.0,0.0,1.79,85.35 +2020-12-26 23:00:00,1.99,0.0,-0.0,0.0,1.59,88.65 +2020-12-27 00:00:00,0.95,0.0,-0.0,0.0,1.45,92.05 +2020-12-27 01:00:00,0.02,0.0,-0.0,0.0,1.45,92.0 +2020-12-27 02:00:00,-0.35,0.0,-0.0,0.0,1.45,91.95 +2020-12-27 03:00:00,-0.67,0.0,-0.0,0.0,1.45,88.45 +2020-12-27 04:00:00,-0.29,0.0,-0.0,0.0,1.24,88.45 +2020-12-27 05:00:00,0.29,0.0,-0.0,0.0,1.1,85.15 +2020-12-27 06:00:00,0.38,0.0,-0.0,0.0,1.17,85.15 +2020-12-27 07:00:00,0.03,0.0,-0.0,0.0,1.31,88.45 +2020-12-27 08:00:00,0.44,49.0,64.35,42.0,1.17,88.5 +2020-12-27 09:00:00,1.37,100.0,70.73,86.0,1.31,92.05 +2020-12-27 10:00:00,2.51,102.0,15.83,98.0,2.28,92.15 +2020-12-27 11:00:00,2.99,118.0,26.01,111.0,3.1,85.4 +2020-12-27 12:00:00,2.81,62.0,0.0,62.0,3.38,85.4 +2020-12-27 13:00:00,2.76,51.0,0.0,51.0,3.38,82.2 +2020-12-27 14:00:00,2.69,35.0,43.8,31.0,3.17,85.35 +2020-12-27 15:00:00,2.13,0.0,-0.0,0.0,2.28,92.1 +2020-12-27 16:00:00,1.7,0.0,-0.0,0.0,2.07,92.1 +2020-12-27 17:00:00,1.26,0.0,-0.0,0.0,2.0,95.65 +2020-12-27 18:00:00,1.19,0.0,-0.0,0.0,2.07,95.65 +2020-12-27 19:00:00,1.63,0.0,-0.0,0.0,3.24,95.65 +2020-12-27 20:00:00,1.48,0.0,-0.0,0.0,2.9,99.4 +2020-12-27 21:00:00,1.25,0.0,-0.0,0.0,2.41,99.4 +2020-12-27 22:00:00,1.14,0.0,-0.0,0.0,2.21,99.35 +2020-12-27 23:00:00,1.1,0.0,-0.0,0.0,2.62,99.35 +2020-12-28 00:00:00,0.84,0.0,-0.0,0.0,2.76,95.65 +2020-12-28 01:00:00,0.39,0.0,-0.0,0.0,2.55,99.4 +2020-12-28 02:00:00,0.12,0.0,-0.0,0.0,2.41,99.4 +2020-12-28 03:00:00,-0.1,0.0,-0.0,0.0,2.48,99.4 +2020-12-28 04:00:00,-0.12,0.0,-0.0,0.0,2.76,99.4 +2020-12-28 05:00:00,-0.27,0.0,-0.0,0.0,2.9,95.6 +2020-12-28 06:00:00,-0.37,0.0,-0.0,0.0,2.83,99.4 +2020-12-28 07:00:00,-0.37,0.0,-0.0,0.0,2.62,95.6 +2020-12-28 08:00:00,-0.38,49.0,64.42,42.0,2.55,95.6 +2020-12-28 09:00:00,-0.12,105.0,85.81,88.0,2.62,92.0 +2020-12-28 10:00:00,0.06,124.0,47.42,112.0,2.83,88.45 +2020-12-28 11:00:00,0.65,126.0,37.06,116.0,3.1,78.75 +2020-12-28 12:00:00,0.76,86.0,4.04,85.0,2.83,75.75 +2020-12-28 13:00:00,0.92,53.0,0.0,53.0,2.9,75.75 +2020-12-28 14:00:00,0.74,40.0,64.6,34.0,3.1,75.75 +2020-12-28 15:00:00,0.22,0.0,-0.0,0.0,2.55,81.9 +2020-12-28 16:00:00,-0.37,0.0,-0.0,0.0,2.28,88.45 +2020-12-28 17:00:00,-0.63,0.0,-0.0,0.0,2.07,88.45 +2020-12-28 18:00:00,-1.02,0.0,-0.0,0.0,1.59,88.4 +2020-12-28 19:00:00,-1.03,0.0,-0.0,0.0,1.72,81.7 +2020-12-28 20:00:00,-1.13,0.0,-0.0,0.0,1.45,81.7 +2020-12-28 21:00:00,-1.27,0.0,-0.0,0.0,1.38,81.7 +2020-12-28 22:00:00,-1.65,0.0,-0.0,0.0,1.17,84.95 +2020-12-28 23:00:00,-1.97,0.0,-0.0,0.0,1.03,88.3 +2020-12-29 00:00:00,-1.76,0.0,-0.0,0.0,0.9,84.95 +2020-12-29 01:00:00,-2.16,0.0,-0.0,0.0,0.62,84.85 +2020-12-29 02:00:00,-2.37,0.0,-0.0,0.0,0.9,88.25 +2020-12-29 03:00:00,-1.69,0.0,-0.0,0.0,0.41,78.45 +2020-12-29 04:00:00,-1.76,0.0,-0.0,0.0,0.34,81.65 +2020-12-29 05:00:00,-1.91,0.0,-0.0,0.0,0.28,84.85 +2020-12-29 06:00:00,-1.99,0.0,-0.0,0.0,0.55,84.85 +2020-12-29 07:00:00,-3.95,0.0,-0.0,0.0,1.59,91.75 +2020-12-29 08:00:00,-3.32,77.0,386.44,35.0,1.72,88.25 +2020-12-29 09:00:00,-1.59,168.0,554.44,58.0,1.45,84.95 +2020-12-29 10:00:00,-0.26,240.0,725.31,56.0,1.66,72.65 +2020-12-29 11:00:00,0.59,258.0,734.93,59.0,1.24,69.95 +2020-12-29 12:00:00,1.13,227.0,663.98,62.0,1.31,64.65 +2020-12-29 13:00:00,1.35,161.0,594.51,49.0,1.79,62.25 +2020-12-29 14:00:00,0.78,60.0,296.2,32.0,1.59,67.3 +2020-12-29 15:00:00,-0.02,0.0,-0.0,0.0,1.79,69.85 +2020-12-29 16:00:00,-0.68,0.0,-0.0,0.0,2.07,72.6 +2020-12-29 17:00:00,-0.86,0.0,-0.0,0.0,2.0,72.5 +2020-12-29 18:00:00,-1.05,0.0,-0.0,0.0,2.0,72.5 +2020-12-29 19:00:00,-1.07,0.0,-0.0,0.0,2.21,66.9 +2020-12-29 20:00:00,-1.15,0.0,-0.0,0.0,2.07,69.65 +2020-12-29 21:00:00,-1.1,0.0,-0.0,0.0,2.0,69.65 +2020-12-29 22:00:00,-0.9,0.0,-0.0,0.0,2.07,69.65 +2020-12-29 23:00:00,-0.67,0.0,-0.0,0.0,2.28,67.0 +2020-12-30 00:00:00,-0.5,0.0,-0.0,0.0,2.41,64.35 +2020-12-30 01:00:00,-0.54,0.0,-0.0,0.0,2.55,61.8 +2020-12-30 02:00:00,-0.37,0.0,-0.0,0.0,2.55,61.8 +2020-12-30 03:00:00,-0.15,0.0,-0.0,0.0,2.41,59.45 +2020-12-30 04:00:00,-0.29,0.0,-0.0,0.0,2.41,59.45 +2020-12-30 05:00:00,-0.58,0.0,-0.0,0.0,2.55,59.35 +2020-12-30 06:00:00,-0.24,0.0,-0.0,0.0,2.62,54.8 +2020-12-30 07:00:00,1.03,0.0,-0.0,0.0,2.55,52.85 +2020-12-30 08:00:00,1.35,49.0,73.51,41.0,2.62,53.0 +2020-12-30 09:00:00,2.61,93.0,55.33,82.0,2.55,53.25 +2020-12-30 10:00:00,4.22,166.0,196.53,116.0,2.48,51.7 +2020-12-30 11:00:00,5.75,99.0,7.36,97.0,2.48,49.95 +2020-12-30 12:00:00,6.65,53.0,0.0,53.0,2.76,50.05 +2020-12-30 13:00:00,7.08,108.0,136.87,82.0,2.9,48.25 +2020-12-30 14:00:00,6.49,62.0,332.29,30.0,2.9,46.25 +2020-12-30 15:00:00,5.48,0.0,-0.0,0.0,2.97,46.0 +2020-12-30 16:00:00,4.93,0.0,-0.0,0.0,3.03,45.85 +2020-12-30 17:00:00,5.23,0.0,-0.0,0.0,3.1,44.15 +2020-12-30 18:00:00,5.75,0.0,-0.0,0.0,3.17,40.9 +2020-12-30 19:00:00,5.42,0.0,-0.0,0.0,3.31,42.45 +2020-12-30 20:00:00,5.89,0.0,-0.0,0.0,3.38,40.9 +2020-12-30 21:00:00,6.27,0.0,-0.0,0.0,3.59,39.4 +2020-12-30 22:00:00,6.29,0.0,-0.0,0.0,4.0,39.4 +2020-12-30 23:00:00,6.09,0.0,-0.0,0.0,4.14,39.25 +2020-12-31 00:00:00,6.01,0.0,-0.0,0.0,4.14,39.25 +2020-12-31 01:00:00,5.77,0.0,-0.0,0.0,4.28,37.7 +2020-12-31 02:00:00,5.72,0.0,-0.0,0.0,4.55,36.05 +2020-12-31 03:00:00,5.65,0.0,-0.0,0.0,4.83,34.6 +2020-12-31 04:00:00,5.31,0.0,-0.0,0.0,5.03,33.2 +2020-12-31 05:00:00,4.78,0.0,-0.0,0.0,5.17,35.9 +2020-12-31 06:00:00,4.15,0.0,-0.0,0.0,5.17,42.15 +2020-12-31 07:00:00,4.0,0.0,-0.0,0.0,5.1,51.55 +2020-12-31 08:00:00,3.89,19.0,0.0,19.0,4.76,62.8 +2020-12-31 09:00:00,3.93,21.0,0.0,21.0,4.21,73.4 +2020-12-31 10:00:00,4.33,47.0,0.0,47.0,4.41,79.3 +2020-12-31 11:00:00,4.68,58.0,0.0,58.0,5.45,82.35 +2020-12-31 12:00:00,4.72,42.0,0.0,42.0,4.55,85.55 +2020-12-31 13:00:00,4.87,124.0,219.13,82.0,5.1,79.3 +2020-12-31 14:00:00,4.62,22.0,0.0,22.0,4.0,82.35 +2020-12-31 15:00:00,4.38,0.0,-0.0,0.0,3.79,82.35 +2020-12-31 16:00:00,5.13,0.0,-0.0,0.0,3.61,85.9 +2020-12-31 17:00:00,4.26,0.0,-0.0,0.0,3.52,86.18 +2020-12-31 18:00:00,3.4,0.0,-0.0,0.0,3.42,86.46 +2020-12-31 19:00:00,2.54,0.0,-0.0,0.0,3.33,86.74 +2020-12-31 20:00:00,1.68,0.0,-0.0,0.0,3.24,87.02 +2020-12-31 21:00:00,0.82,0.0,-0.0,0.0,3.15,87.31 +2020-12-31 22:00:00,-0.04,0.0,-0.0,0.0,3.05,87.59 +2020-12-31 23:00:00,-0.91,0.0,-0.0,0.0,2.96,87.87 diff --git a/docs/notebooks/data/tutorial_data.py b/docs/notebooks/data/tutorial_data.py new file mode 100644 index 000000000..3b4997e0a --- /dev/null +++ b/docs/notebooks/data/tutorial_data.py @@ -0,0 +1,246 @@ +"""Generate tutorial data for notebooks 01-07. + +These functions return data (timesteps, profiles, prices) rather than full FlowSystems, +so notebooks can demonstrate building systems step by step. + +Usage: + from data.tutorial_data import get_quickstart_data, get_heat_system_data, ... +""" + +import numpy as np +import pandas as pd +import xarray as xr + + +def get_quickstart_data() -> dict: + """Data for 01-quickstart: minimal 4-hour example. + + Returns: + dict with: timesteps, heat_demand (xr.DataArray) + """ + timesteps = pd.date_range('2024-01-15 08:00', periods=4, freq='h') + heat_demand = xr.DataArray( + [30, 50, 45, 25], + dims=['time'], + coords={'time': timesteps}, + name='Heat Demand [kW]', + ) + return { + 'timesteps': timesteps, + 'heat_demand': heat_demand, + } + + +def get_heat_system_data() -> dict: + """Data for 02-heat-system: one week with storage. + + Returns: + dict with: timesteps, heat_demand, gas_price (arrays) + """ + timesteps = pd.date_range('2024-01-15', periods=168, freq='h') + hours = np.arange(168) + hour_of_day = hours % 24 + day_of_week = (hours // 24) % 7 + + # Office heat demand pattern + base_demand = np.where((hour_of_day >= 7) & (hour_of_day <= 18), 80, 30) + weekend_factor = np.where(day_of_week >= 5, 0.5, 1.0) + np.random.seed(42) + heat_demand = base_demand * weekend_factor + np.random.normal(0, 5, len(timesteps)) + heat_demand = np.clip(heat_demand, 20, 100) + + # Time-of-use gas prices + gas_price = np.where((hour_of_day >= 6) & (hour_of_day <= 22), 0.08, 0.05) + + return { + 'timesteps': timesteps, + 'heat_demand': heat_demand, + 'gas_price': gas_price, + } + + +def get_investment_data() -> dict: + """Data for 03-investment-optimization: solar pool heating. + + Returns: + dict with: timesteps, solar_profile, pool_demand, costs + """ + timesteps = pd.date_range('2024-07-15', periods=168, freq='h') + hours = np.arange(168) + hour_of_day = hours % 24 + + # Solar profile + solar_profile = np.maximum(0, np.sin((hour_of_day - 6) * np.pi / 12)) * 0.8 + solar_profile = np.where((hour_of_day >= 6) & (hour_of_day <= 20), solar_profile, 0) + np.random.seed(42) + solar_profile = solar_profile * np.random.uniform(0.6, 1.0, len(timesteps)) + + # Pool demand + pool_demand = np.where((hour_of_day >= 8) & (hour_of_day <= 22), 150, 50) + + return { + 'timesteps': timesteps, + 'solar_profile': solar_profile, + 'pool_demand': pool_demand, + 'gas_price': 0.12, + 'solar_cost_per_kw_week': 20 / 52, + 'tank_cost_per_kwh_week': 1.5 / 52, + } + + +def get_constraints_data() -> dict: + """Data for 04-operational-constraints: factory steam demand. + + Returns: + dict with: timesteps, steam_demand + """ + timesteps = pd.date_range('2024-03-11', periods=72, freq='h') + hours = np.arange(72) + hour_of_day = hours % 24 + + # Shift-based demand + steam_demand = np.select( + [ + (hour_of_day >= 6) & (hour_of_day < 14), + (hour_of_day >= 14) & (hour_of_day < 22), + ], + [400, 350], + default=80, + ).astype(float) + + np.random.seed(123) + steam_demand = steam_demand + np.random.normal(0, 20, len(steam_demand)) + steam_demand = np.clip(steam_demand, 50, 450) + + return { + 'timesteps': timesteps, + 'steam_demand': steam_demand, + } + + +def get_multicarrier_data() -> dict: + """Data for 05-multi-carrier-system: hospital CHP. + + Returns: + dict with: timesteps, electricity_demand, heat_demand, prices + """ + timesteps = pd.date_range('2024-02-05', periods=168, freq='h') + hours = np.arange(168) + hour_of_day = hours % 24 + + # Electricity demand + elec_base = 150 + elec_daily = 100 * np.sin((hour_of_day - 6) * np.pi / 12) + elec_daily = np.maximum(0, elec_daily) + electricity_demand = elec_base + elec_daily + + # Heat demand + heat_pattern = np.select( + [ + (hour_of_day >= 5) & (hour_of_day < 9), + (hour_of_day >= 9) & (hour_of_day < 17), + (hour_of_day >= 17) & (hour_of_day < 22), + ], + [350, 250, 300], + default=200, + ).astype(float) + + np.random.seed(456) + electricity_demand += np.random.normal(0, 15, len(timesteps)) + heat_demand = heat_pattern + np.random.normal(0, 20, len(timesteps)) + electricity_demand = np.clip(electricity_demand, 100, 300) + heat_demand = np.clip(heat_demand, 150, 400) + + # Prices + elec_buy_price = np.where((hour_of_day >= 7) & (hour_of_day <= 21), 0.35, 0.20) + + return { + 'timesteps': timesteps, + 'electricity_demand': electricity_demand, + 'heat_demand': heat_demand, + 'elec_buy_price': elec_buy_price, + 'elec_sell_price': 0.12, + 'gas_price': 0.05, + } + + +def get_time_varying_data() -> dict: + """Data for 06a-time-varying-parameters: heat pump with variable COP. + + Returns: + dict with: timesteps, outdoor_temp, heat_demand, cop + """ + timesteps = pd.date_range('2024-01-22', periods=168, freq='h') + hours = np.arange(168) + hour_of_day = hours % 24 + + # Outdoor temperature + temp_base = 2 + temp_amplitude = 5 + outdoor_temp = temp_base + temp_amplitude * np.sin((hour_of_day - 6) * np.pi / 12) + np.random.seed(789) + outdoor_temp = outdoor_temp + np.repeat(np.random.uniform(-3, 3, 7), 24) + + # Heat demand (inversely related to temperature) + heat_demand = 200 - 8 * outdoor_temp + heat_demand = np.clip(heat_demand, 100, 300) + + # COP calculation + t_supply = 45 + 273.15 + t_source = outdoor_temp + 273.15 + carnot_cop = t_supply / (t_supply - t_source) + cop = np.clip(0.45 * carnot_cop, 2.0, 5.0) + + return { + 'timesteps': timesteps, + 'outdoor_temp': outdoor_temp, + 'heat_demand': heat_demand, + 'cop': cop, + } + + +def get_scenarios_data() -> dict: + """Data for 07-scenarios-and-periods: multi-year planning. + + Returns: + dict with: timesteps, periods, scenarios, weights, heat_demand (DataFrame), prices + """ + timesteps = pd.date_range('2024-01-15', periods=168, freq='h') + periods = pd.Index([2024, 2025, 2026], name='period') + scenarios = pd.Index(['Mild Winter', 'Harsh Winter'], name='scenario') + scenario_weights = np.array([0.6, 0.4]) + + hours = np.arange(168) + hour_of_day = hours % 24 + + # Base pattern + daily_pattern = np.select( + [ + (hour_of_day >= 6) & (hour_of_day < 9), + (hour_of_day >= 9) & (hour_of_day < 17), + (hour_of_day >= 17) & (hour_of_day < 22), + ], + [180, 120, 160], + default=100, + ).astype(float) + + np.random.seed(42) + noise = np.random.normal(0, 10, len(timesteps)) + + mild_demand = np.clip(daily_pattern * 0.8 + noise, 60, 200) + harsh_demand = np.clip(daily_pattern * 1.3 + noise * 1.5, 100, 280) + + heat_demand = pd.DataFrame( + {'Mild Winter': mild_demand, 'Harsh Winter': harsh_demand}, + index=timesteps, + ) + + return { + 'timesteps': timesteps, + 'periods': periods, + 'scenarios': scenarios, + 'scenario_weights': scenario_weights, + 'heat_demand': heat_demand, + 'gas_prices': np.array([0.06, 0.08, 0.10]), + 'elec_prices': np.array([0.28, 0.34, 0.43]), + } diff --git a/docs/notebooks/fxplot_accessor_demo.ipynb b/docs/notebooks/fxplot_accessor_demo.ipynb new file mode 100644 index 000000000..db8684d82 --- /dev/null +++ b/docs/notebooks/fxplot_accessor_demo.ipynb @@ -0,0 +1,565 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Dataset Plot Accessor Demo (`.fxplot`)\n", + "\n", + "This notebook demonstrates the new `.fxplot` accessor for `xr.Dataset` objects.\n", + "It provides convenient Plotly Express plotting methods with smart auto-faceting and coloring." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import xarray as xr\n", + "\n", + "import flixopt as fx\n", + "\n", + "fx.__version__" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.io as pio\n", + "\n", + "pio.renderers.default = 'notebook_connected'" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create Sample Data\n", + "\n", + "Let's create a multi-dimensional dataset to demonstrate the plotting capabilities." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Simple time-series dataset\n", + "np.random.seed(42)\n", + "time = pd.date_range('2024-01-01', periods=24, freq='h')\n", + "\n", + "ds_simple = xr.Dataset(\n", + " {\n", + " 'Solar': (['time'], np.maximum(0, np.sin(np.linspace(0, 2 * np.pi, 24)) * 50 + np.random.randn(24) * 5)),\n", + " 'Wind': (['time'], np.abs(np.random.randn(24) * 20 + 30)),\n", + " 'Demand': (['time'], np.abs(np.sin(np.linspace(0, 2 * np.pi, 24) + 1) * 40 + 50 + np.random.randn(24) * 5)),\n", + " },\n", + " coords={'time': time},\n", + ")\n", + "\n", + "ds_simple.to_dataframe().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Line Plot" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_simple.fxplot.line(title='Energy Generation & Demand')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stacked Bar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_simple[['Solar', 'Wind']].fxplot.stacked_bar(title='Renewable Generation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Area Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_simple[['Solar', 'Wind']].fxplot.area(title='Stacked Area - Generation')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Grouped Bar Chart" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_simple.fxplot.bar(title='Grouped Bar Chart')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Heatmap" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create 2D data for heatmap\n", + "ds_heatmap = xr.Dataset(\n", + " {\n", + " 'temperature': (['day', 'hour'], np.random.randn(7, 24) * 5 + 20),\n", + " },\n", + " coords={\n", + " 'day': pd.date_range('2024-01-01', periods=7, freq='D'),\n", + " 'hour': range(24),\n", + " },\n", + ")\n", + "\n", + "ds_heatmap.fxplot.heatmap('temperature', title='Temperature Heatmap')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Automatic Faceting & Animation\n", + "\n", + "Extra dimensions are **automatically** assigned to `facet_col`, `facet_row`, and `animation_frame` based on CONFIG priority. Just call the plot method - no configuration needed!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Dataset with scenario AND period dimensions\n", + "ds_multi = xr.Dataset(\n", + " {\n", + " 'Solar': (['time', 'scenario', 'period'], np.random.rand(24, 2, 3) * 50),\n", + " 'Wind': (['time', 'scenario', 'period'], np.random.rand(24, 2, 3) * 40 + 20),\n", + " },\n", + " coords={\n", + " 'time': time,\n", + " 'scenario': ['base', 'high'],\n", + " 'period': ['winter', 'spring', 'summer'],\n", + " },\n", + ")\n", + "\n", + "ds_multi.to_dataframe().head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Just call .line() - dimensions are auto-assigned to facet_col, facet_row, animation_frame\n", + "ds_multi.fxplot.line(title='Auto-Faceted: Just Works!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Same for stacked bar - auto-assigns period to facet_col, scenario to animation\n", + "ds_multi.fxplot.stacked_bar(title='Stacked Bar: Also Just Works!')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Same for stacked bar - auto-assigns period to facet_col, scenario to animation\n", + "ds_multi.sum('time').fxplot.stacked_bar(title='Stacked Bar: Also Just Works!', x='variable', colors=None)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Customizing Facets & Animation\n", + "\n", + "Override auto-assignment when needed. Use `None` to disable a slot entirely." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Swap: put scenario in facet_col, period in animation\n", + "ds_multi.fxplot.line(facet_col='scenario', animation_frame='period', title='Swapped: Scenario in Columns')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Use both row and column facets - no animation\n", + "ds_multi.sum('time').fxplot.area(\n", + " facet_col='scenario', facet_row='period', animation_frame=None, title='Grid: Period Γ— Scenario'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Or reduce dimensions with .sel() for a simpler plot\n", + "ds_multi.sel(scenario='base', period='summer').fxplot.line(title='Single Slice: No Faceting Needed')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Custom Colors" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Using a colorscale name\n", + "ds_simple.fxplot.line(colors='viridis', title='With Viridis Colorscale')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Using explicit color mapping\n", + "ds_simple.fxplot.stacked_bar(\n", + " colors={'Solar': 'gold', 'Wind': 'skyblue', 'Demand': 'salmon'}, title='With Custom Colors'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Chaining with Plotly Methods\n", + "\n", + "Since all methods return `go.Figure`, you can chain Plotly's update methods." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " ds_simple.fxplot.line(title='Customized Plot')\n", + " .update_layout(xaxis_title='Time of Day', yaxis_title='Power (MW)', legend_title='Source', template='plotly_white')\n", + " .update_traces(line_width=2)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pre-filtering with xarray\n", + "\n", + "Filter data using xarray methods before plotting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Select specific time range\n", + "ds_simple.sel(time=slice('2024-01-01 06:00', '2024-01-01 18:00')).fxplot.line(title='Daytime Only')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Select specific variables\n", + "ds_simple[['Solar', 'Wind']].fxplot.area(title='Renewables Only')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## DataArray Accessor\n", + "\n", + "The `.fxplot` accessor also works on `xr.DataArray` objects directly." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a DataArray\n", + "da = xr.DataArray(\n", + " np.random.randn(24, 7) * 5 + 20,\n", + " dims=['time', 'day'],\n", + " coords={\n", + " 'time': pd.date_range('2024-01-01', periods=24, freq='h'),\n", + " 'day': pd.date_range('2024-01-01', periods=7, freq='D'),\n", + " },\n", + " name='temperature',\n", + ")\n", + "\n", + "# Heatmap directly from DataArray\n", + "da.fxplot.heatmap(title='DataArray Heatmap')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Line plot from DataArray (converts to Dataset internally)\n", + "da_1d = xr.DataArray(\n", + " np.sin(np.linspace(0, 4 * np.pi, 100)) * 50,\n", + " dims=['time'],\n", + " coords={'time': pd.date_range('2024-01-01', periods=100, freq='h')},\n", + " name='signal',\n", + ")\n", + "da_1d.fxplot.line(title='DataArray Line Plot')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Axis Labels\n", + "\n", + "Use `xlabel` and `ylabel` parameters to customize axis labels." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "ds_simple.fxplot.line(title='Generation with Custom Axis Labels', xlabel='Time of Day', ylabel='Power [MW]')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Scatter Plot\n", + "\n", + "Plot two variables against each other." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Basic scatter plot\n", + "ds_simple.fxplot.scatter(\n", + " x='Solar', y='Demand', title='Solar vs Demand Correlation', xlabel='Solar Generation [MW]', ylabel='Demand [MW]'\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Scatter with faceting by period, for one scenario\n", + "ds_multi.sel(scenario='high').fxplot.scatter(\n", + " x='Solar', y='Wind', facet_col='period', title='Solar vs Wind by Period (High Scenario)'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pie Chart\n", + "\n", + "Aggregate data to at most 1D per variable. Scalar data creates a single pie; 1D data creates faceted pies." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Single pie from scalar values (sum over time)\n", + "ds_simple[['Solar', 'Wind']].sum('time').fxplot.pie(\n", + " title='Total Generation by Source', colors={'Solar': 'gold', 'Wind': 'skyblue'}\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Faceted pie - auto-assigns scenario and period to facets\n", + "ds_multi.sum('time').fxplot.pie(\n", + " title='Generation by Source (Scenario Γ— Period)',\n", + " colors={'Solar': 'gold', 'Wind': 'skyblue'},\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Duration Curve\n", + "\n", + "Use `.fxstats.to_duration_curve()` to transform data, then `.fxplot.line()` to plot. Clean separation of transformation and plotting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Duration curve with normalized x-axis (percentage)\n", + "ds_simple.fxstats.to_duration_curve().fxplot.line(title='Duration Curves', xlabel='Duration [%]')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Duration curve with absolute timesteps\n", + "ds_simple.fxstats.to_duration_curve(normalize=False).fxplot.line(title='Duration Curves', xlabel='Timesteps')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Duration curve with auto-faceting - works seamlessly!\n", + "ds_multi.fxstats.to_duration_curve().fxplot.line(title='Duration Curves (Auto-Faceted)', xlabel='Duration [%]')" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Line Shape Configuration\n", + "\n", + "The default line shape is controlled by `CONFIG.Plotting.default_line_shape` (default: `'hv'` for step plots).\n", + "Override per-plot with the `line_shape` parameter. Options: `'linear'`, `'hv'`, `'vh'`, `'hvh'`, `'vhv'`, `'spline'`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Default step plot (hv)\n", + "ds_simple[['Solar']].fxplot.line(title='Default Step Plot (hv)')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Override to linear interpolation\n", + "ds_simple[['Solar']].fxplot.line(line_shape='linear', title='Linear Interpolation')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/user-guide/optimization/clustering.md b/docs/user-guide/optimization/clustering.md new file mode 100644 index 000000000..793fbf8fe --- /dev/null +++ b/docs/user-guide/optimization/clustering.md @@ -0,0 +1,264 @@ +# Time-Series Clustering + +Time-series clustering reduces large optimization problems by aggregating timesteps into representative **typical periods**. This enables fast investment optimization while preserving key system dynamics. + +## When to Use Clustering + +Use clustering when: + +- Optimizing over a **full year** or longer +- **Investment sizing** is the primary goal (not detailed dispatch) +- You need **faster solve times** and can accept approximation +- The system has **repeating patterns** (daily, weekly, seasonal) + +**Skip clustering** for: + +- Short optimization horizons (days to weeks) +- Dispatch-only problems without investments +- Systems requiring exact temporal sequences + +## Two-Stage Workflow + +The recommended approach: cluster for fast sizing, then validate at full resolution. + +```python +import flixopt as fx + +# Load or create your FlowSystem +flow_system = fx.FlowSystem(timesteps) +flow_system.add_elements(...) + +# Stage 1: Cluster and optimize (fast) +fs_clustered = flow_system.transform.cluster( + n_clusters=12, + cluster_duration='1D', + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], +) +fs_clustered.optimize(fx.solvers.HighsSolver()) + +# Stage 2: Expand back to full resolution +fs_expanded = fs_clustered.transform.expand_solution() + +# Access full-resolution results +charge_state = fs_expanded.solution['Storage|charge_state'] +flow_rates = fs_expanded.solution['Boiler(Q_th)|flow_rate'] +``` + +## Clustering Parameters + +| Parameter | Description | Example | +|-----------|-------------|---------| +| `n_clusters` | Number of typical periods | `12` (typical days for a year) | +| `cluster_duration` | Duration of each cluster | `'1D'`, `'24h'`, or `24` (hours) | +| `time_series_for_high_peaks` | Time series where peak clusters must be captured | `['HeatDemand(Q)|fixed_relative_profile']` | +| `time_series_for_low_peaks` | Time series where minimum clusters must be captured | `['SolarGen(P)|fixed_relative_profile']` | +| `cluster_method` | Clustering algorithm | `'k_means'`, `'hierarchical'`, `'k_medoids'` | +| `representation_method` | How clusters are represented | `'meanRepresentation'`, `'medoidRepresentation'` | +| `random_state` | Random seed for reproducibility | `42` | +| `rescale_cluster_periods` | Rescale clusters to match original means | `True` (default) | + +### Peak Selection + +Use `time_series_for_high_peaks` to ensure extreme conditions are represented: + +```python +# Ensure the peak demand day is included +fs_clustered = flow_system.transform.cluster( + n_clusters=8, + cluster_duration='1D', + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], +) +``` + +Without peak selection, the clustering algorithm might average out extreme days, leading to undersized equipment. + +### Advanced Clustering Options + +Fine-tune the clustering algorithm with advanced parameters: + +```python +fs_clustered = flow_system.transform.cluster( + n_clusters=8, + cluster_duration='1D', + cluster_method='hierarchical', # Alternative to k_means + representation_method='medoidRepresentation', # Use actual periods, not averages + rescale_cluster_periods=True, # Match original time series means + random_state=42, # Reproducible results +) +``` + +**Available clustering algorithms** (`cluster_method`): + +| Method | Description | +|--------|-------------| +| `'k_means'` | Fast, good for most cases (default) | +| `'hierarchical'` | Produces consistent hierarchical groupings | +| `'k_medoids'` | Uses actual periods as representatives | +| `'k_maxoids'` | Maximizes representativeness | +| `'averaging'` | Simple averaging of similar periods | + +For advanced tsam parameters not exposed directly, use `**kwargs`: + +```python +# Pass any tsam.TimeSeriesAggregation parameter +fs_clustered = flow_system.transform.cluster( + n_clusters=8, + cluster_duration='1D', + sameMean=True, # Normalize all time series to same mean + sortValues=True, # Cluster by duration curves instead of shape +) +``` + +### Clustering Quality Metrics + +After clustering, access quality metrics to evaluate the aggregation accuracy: + +```python +fs_clustered = flow_system.transform.cluster(n_clusters=8, cluster_duration='1D') + +# Access clustering metrics (xr.Dataset) +metrics = fs_clustered.clustering.metrics +print(metrics) # Shows RMSE, MAE, etc. per time series + +# Access specific metric +rmse = metrics['RMSE'] # xr.DataArray with dims [time_series, period?, scenario?] +``` + +## Storage Modes + +Storage behavior during clustering is controlled via the `cluster_mode` parameter: + +```python +storage = fx.Storage( + 'SeasonalPit', + capacity_in_flow_hours=5000, + cluster_mode='intercluster_cyclic', # Default + ... +) +``` + +### Available Modes + +| Mode | Behavior | Best For | +|------|----------|----------| +| `'intercluster_cyclic'` | Links storage across clusters + yearly cycling | Seasonal storage (pit, underground) | +| `'intercluster'` | Links storage across clusters, free start/end | Multi-year optimization | +| `'cyclic'` | Each cluster independent, but start = end | Daily storage (battery, hot water tank) | +| `'independent'` | Each cluster fully independent | Quick estimates, debugging | + +### How Inter-Cluster Linking Works + +For `'intercluster'` and `'intercluster_cyclic'` modes, the optimizer tracks: + +1. **`SOC_boundary`**: Absolute state-of-charge at the start of each original period +2. **`charge_state`**: Relative change (Ξ”E) within each typical period + +During expansion, these combine with self-discharge decay: + +``` +actual_SOC(t) = SOC_boundary[period] Γ— (1 - loss)^t + Ξ”E(t) +``` + +This enables accurate modeling of seasonal storage that charges in summer and discharges in winter. + +### Choosing the Right Mode + +```python +# Seasonal pit storage - needs yearly linking +pit_storage = fx.Storage( + 'SeasonalPit', + cluster_mode='intercluster_cyclic', + capacity_in_flow_hours=10000, + relative_loss_per_hour=0.0001, + ... +) + +# Daily hot water tank - only needs daily cycling +tank = fx.Storage( + 'HotWaterTank', + cluster_mode='cyclic', + capacity_in_flow_hours=50, + ... +) + +# Battery with quick estimate +battery = fx.Storage( + 'Battery', + cluster_mode='independent', # Fastest, ignores long-term effects + ... +) +``` + +## Multi-Dimensional Support + +Clustering works with periods and scenarios: + +```python +# FlowSystem with multiple periods and scenarios +flow_system = fx.FlowSystem( + timesteps, + periods=pd.Index([2025, 2030, 2035], name='period'), + scenarios=pd.Index(['low', 'base', 'high'], name='scenario'), +) + +# Cluster - dimensions are preserved +fs_clustered = flow_system.transform.cluster( + n_clusters=8, + cluster_duration='1D', +) + +# Solution has all dimensions +# Dims: (time, cluster, period, scenario) +flow_rate = fs_clustered.solution['Boiler(Q_th)|flow_rate'] +``` + +## Expanding Solutions + +After optimization, expand results back to full resolution: + +```python +fs_expanded = fs_clustered.transform.expand_solution() + +# Full timesteps are restored +print(f"Original: {len(flow_system.timesteps)} timesteps") +print(f"Clustered: {len(fs_clustered.timesteps)} timesteps") +print(f"Expanded: {len(fs_expanded.timesteps)} timesteps") + +# Storage charge state correctly reconstructed +charge_state = fs_expanded.solution['Storage|charge_state'] +``` + +The expansion: + +1. Maps each original timestep to its assigned cluster +2. For storage with inter-cluster linking, combines `SOC_boundary` with within-cluster `charge_state` +3. Applies self-discharge decay factors + +## Performance Tips + +### Cluster Count Selection + +| Time Horizon | Cluster Duration | Suggested n_clusters | +|----------------|------------------|---------------------| +| 1 year | 1 day | 8-16 | +| 1 year | 1 week | 4-8 | +| Multiple years | 1 day | 12-24 | + +### Speed vs Accuracy Trade-off + +```python +# Fast (less accurate) - for quick estimates +fs_fast = flow_system.transform.cluster(n_clusters=4, cluster_duration='1D') + +# Balanced - typical production use +fs_balanced = flow_system.transform.cluster(n_clusters=12, cluster_duration='1D') + +# Accurate (slower) - for final results +fs_accurate = flow_system.transform.cluster(n_clusters=24, cluster_duration='1D') +``` + +## See Also + +- [Storage Component](../mathematical-notation/elements/Storage.md) - Storage mathematical formulation +- [Notebooks: Clustering](../../notebooks/08c-clustering.ipynb) - Interactive examples +- [Notebooks: Storage Modes](../../notebooks/08c2-clustering-storage-modes.ipynb) - Storage mode comparison diff --git a/docs/user-guide/optimization/index.md b/docs/user-guide/optimization/index.md index 1d36eb9ba..103ff12ea 100644 --- a/docs/user-guide/optimization/index.md +++ b/docs/user-guide/optimization/index.md @@ -56,22 +56,18 @@ flow_system.solve(fx.solvers.HighsSolver()) For large problems, use time series clustering to reduce computational complexity: ```python -# Define clustering parameters -params = fx.ClusteringParameters( - hours_per_period=24, # Hours per typical period - nr_of_periods=8, # Number of typical periods - fix_storage_flows=True, - aggregate_data_and_fix_non_binary_vars=True, +# Cluster to 12 typical days +fs_clustered = flow_system.transform.cluster( + n_clusters=12, + cluster_duration='1D', + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], ) -# Create clustered FlowSystem -clustered_fs = flow_system.transform.cluster(params) - # Optimize the clustered system -clustered_fs.optimize(fx.solvers.HighsSolver()) +fs_clustered.optimize(fx.solvers.HighsSolver()) -# Access results - same structure as original -print(clustered_fs.solution) +# Expand back to full resolution +fs_expanded = fs_clustered.transform.expand_solution() ``` **Best for:** @@ -86,6 +82,8 @@ print(clustered_fs.solution) - Approximates the full problem - Best when patterns repeat (e.g., typical days) +See the **[Clustering Guide](clustering.md)** for details on storage modes, peak selection, and multi-dimensional support. + ## Choosing an Optimization Mode | Mode | Problem Size | Solve Time | Solution Quality | @@ -133,7 +131,7 @@ fs_4h.optimize(fx.solvers.HighsSolver()) ### Clustering -See [Clustered Optimization](#clustered-optimization) above. +See the **[Clustering Guide](clustering.md)** for comprehensive documentation. ### Use Cases diff --git a/docs/user-guide/recipes/plotting-custom-data.md b/docs/user-guide/recipes/plotting-custom-data.md index 3c539e6ce..8c19931f3 100644 --- a/docs/user-guide/recipes/plotting-custom-data.md +++ b/docs/user-guide/recipes/plotting-custom-data.md @@ -1,125 +1,30 @@ # Plotting Custom Data -The plot accessor (`flow_system.statistics.plot`) is designed for visualizing optimization results using element labels. If you want to create faceted plots with your own custom data (not from a FlowSystem), you can use Plotly Express directly with xarray data. +While the plot accessor (`flow_system.statistics.plot`) is designed for optimization results, you often need to plot custom xarray data. The `.fxplot` accessor provides the same convenience for any `xr.Dataset` or `xr.DataArray`. -## Faceted Plots with Custom xarray Data - -The key is converting your xarray Dataset to a long-form DataFrame that Plotly Express expects: +## Quick Example ```python +import flixopt as fx import xarray as xr -import pandas as pd -import plotly.express as px -# Your custom xarray Dataset -my_data = xr.Dataset({ - 'Solar': (['time', 'scenario'], solar_values), - 'Wind': (['time', 'scenario'], wind_values), - 'Demand': (['time', 'scenario'], demand_values), -}, coords={ - 'time': timestamps, - 'scenario': ['Base', 'High RE', 'Low Demand'] +ds = xr.Dataset({ + 'Solar': (['time'], solar_values), + 'Wind': (['time'], wind_values), }) -# Convert to long-form DataFrame for Plotly Express -df = ( - my_data - .to_dataframe() - .reset_index() - .melt( - id_vars=['time', 'scenario'], # Keep as columns - var_name='variable', - value_name='value' - ) -) - -# Faceted stacked bar chart -fig = px.bar( - df, - x='time', - y='value', - color='variable', - facet_col='scenario', - barmode='relative', - title='Energy Balance by Scenario' -) -fig.show() - -# Faceted line plot -fig = px.line( - df, - x='time', - y='value', - color='variable', - facet_col='scenario' -) -fig.show() - -# Faceted area chart -fig = px.area( - df, - x='time', - y='value', - color='variable', - facet_col='scenario' -) -fig.show() -``` - -## Common Plotly Express Faceting Options - -| Parameter | Description | -|-----------|-------------| -| `facet_col` | Dimension for column subplots | -| `facet_row` | Dimension for row subplots | -| `animation_frame` | Dimension for animation slider | -| `facet_col_wrap` | Number of columns before wrapping | - -```python -# Row and column facets -fig = px.line(df, x='time', y='value', color='variable', - facet_col='scenario', facet_row='region') - -# Animation over time periods -fig = px.bar(df, x='variable', y='value', color='variable', - animation_frame='period', barmode='group') - -# Wrap columns -fig = px.line(df, x='time', y='value', color='variable', - facet_col='scenario', facet_col_wrap=2) +# Plot directly - no conversion needed! +ds.fxplot.line(title='Energy Generation') +ds.fxplot.stacked_bar(title='Stacked Generation') ``` -## Heatmaps with Custom Data - -For heatmaps, you can pass 2D arrays directly to `px.imshow`: - -```python -import plotly.express as px - -# 2D data (e.g., days Γ— hours) -heatmap_data = my_data['Solar'].sel(scenario='Base').values.reshape(365, 24) +## Full Documentation -fig = px.imshow( - heatmap_data, - labels={'x': 'Hour', 'y': 'Day', 'color': 'Power [kW]'}, - aspect='auto', - color_continuous_scale='portland' -) -fig.show() - -# Faceted heatmaps using subplots -from plotly.subplots import make_subplots -import plotly.graph_objects as go - -scenarios = ['Base', 'High RE'] -fig = make_subplots(rows=1, cols=len(scenarios), subplot_titles=scenarios) - -for i, scenario in enumerate(scenarios, 1): - data = my_data['Solar'].sel(scenario=scenario).values.reshape(365, 24) - fig.add_trace(go.Heatmap(z=data, colorscale='portland'), row=1, col=i) - -fig.update_layout(title='Solar Output by Scenario') -fig.show() -``` +For comprehensive documentation with interactive examples, see the [Custom Data Plotting](../../notebooks/fxplot_accessor_demo.ipynb) notebook which covers: -This approach gives you full control over your visualizations while leveraging Plotly's powerful faceting capabilities. +- All available plot methods (line, bar, stacked_bar, area, scatter, heatmap, pie) +- Automatic x-axis selection and faceting +- Custom colors and axis labels +- Duration curves with `.fxstats.to_duration_curve()` +- Configuration options +- Combining with xarray operations diff --git a/docs/user-guide/results-plotting.md b/docs/user-guide/results-plotting.md index 1ecd26aa1..28e3d2b2b 100644 --- a/docs/user-guide/results-plotting.md +++ b/docs/user-guide/results-plotting.md @@ -2,6 +2,9 @@ After solving an optimization, flixOpt provides a powerful plotting API to visualize and analyze your results. The API is designed to be intuitive and chainable, giving you quick access to common plots while still allowing deep customization. +!!! tip "Plotting Custom Data" + For plotting arbitrary xarray data (not just flixopt results), see the [Custom Data Plotting](recipes/plotting-custom-data.md) guide which covers the `.fxplot` accessor. + ## The Plot Accessor All plotting is accessed through the `statistics.plot` accessor on your FlowSystem: diff --git a/docs/user-guide/results/index.md b/docs/user-guide/results/index.md index a9b40f7f9..500a64cd9 100644 --- a/docs/user-guide/results/index.md +++ b/docs/user-guide/results/index.md @@ -277,6 +277,156 @@ flow_system.statistics.plot.heatmap('Boiler(Q_th)|flow_rate') flow_system.to_netcdf('results/optimized_system.nc') ``` +## Comparing Multiple Systems + +Use the [`Comparison`][flixopt.comparison.Comparison] class to analyze and visualize multiple FlowSystems side-by-side. This is useful for: + +- Comparing different design alternatives (with/without CHP, different storage sizes) +- Analyzing optimization method trade-offs (full vs. two-stage, different aggregation levels) +- Sensitivity analysis (different scenarios, parameter variations) + +### Basic Usage + +```python +import flixopt as fx + +# Optimize two system variants +fs_baseline = create_system() +fs_baseline.name = 'Baseline' +fs_baseline.optimize(solver) + +fs_with_storage = create_system_with_storage() +fs_with_storage.name = 'With Storage' +fs_with_storage.optimize(solver) + +# Create comparison +comp = fx.Comparison([fs_baseline, fs_with_storage]) + +# Side-by-side balance plots (auto-faceted by 'case' dimension) +comp.statistics.plot.balance('Heat') + +# Access combined data with 'case' dimension +comp.statistics.flow_rates # xr.Dataset with dims: (time, case) +comp.solution # Combined solution dataset +``` + +### Requirements + +All FlowSystems must have **matching core dimensions** (`time`, `period`, `scenario`). Auxiliary dimensions like `cluster_boundary` are ignored. If core dimensions differ, use `.transform.sel()` to align them first: + +```python +# Systems with different scenarios +fs_both = flow_system # Has 'Mild Winter' and 'Harsh Winter' scenarios +fs_mild = flow_system.transform.sel(scenario='Mild Winter') # Single scenario + +# Cannot compare directly - scenario dimension mismatch! +# fx.Comparison([fs_both, fs_mild]) # Raises ValueError + +# Instead, select matching dimensions +fs_both_mild = fs_both.transform.sel(scenario='Mild Winter') +comp = fx.Comparison([fs_both_mild, fs_mild]) # Works! + +# Auxiliary dimensions are OK (e.g., expanded clustered solutions) +fs_expanded = fs_clustered.transform.expand_solution() # Has cluster_boundary dim +comp = fx.Comparison([fs_full, fs_expanded]) # Works! cluster_boundary is ignored +``` + +### Available Properties + +The `Comparison.statistics` accessor mirrors all `StatisticsAccessor` properties, returning combined datasets with an added `'case'` dimension: + +| Property | Description | +|----------|-------------| +| `flow_rates` | All flow rate variables | +| `flow_hours` | Flow hours (energy) | +| `sizes` | Component sizes | +| `storage_sizes` | Storage capacities | +| `charge_states` | Storage charge states | +| `temporal_effects` | Effects per timestep | +| `periodic_effects` | Investment effects | +| `total_effects` | Combined effects | + +### Available Plot Methods + +All standard plot methods work on the comparison, with the `'case'` dimension automatically used for faceting: + +```python +comp = fx.Comparison([fs_baseline, fs_modified]) + +# Balance plots - faceted by case +comp.statistics.plot.balance('Heat') +comp.statistics.plot.balance('Electricity', mode='area') + +# Flow plots +comp.statistics.plot.flows(component='CHP') + +# Effect breakdowns +comp.statistics.plot.effects() + +# Heatmaps +comp.statistics.plot.heatmap('Boiler(Q_th)') + +# Duration curves +comp.statistics.plot.duration_curve('CHP(Q_th)') + +# Storage plots +comp.statistics.plot.storage('Battery') +``` + +### Computing Differences + +Use the `diff()` method to compute differences relative to a reference case: + +```python +# Differences relative to first case (default) +differences = comp.diff() + +# Differences relative to specific case +differences = comp.diff(reference='Baseline') +differences = comp.diff(reference=0) # By index + +# Analyze differences +print(differences['costs']) # Cost difference per case +``` + +### Naming Systems + +System names come from `FlowSystem.name` by default. Override with the `names` parameter: + +```python +# Using FlowSystem.name (default) +fs1.name = 'Scenario A' +fs2.name = 'Scenario B' +comp = fx.Comparison([fs1, fs2]) + +# Or override explicitly +comp = fx.Comparison([fs1, fs2], names=['Base Case', 'Alternative']) +``` + +### Example: Comparing Optimization Methods + +```python +# Full optimization +fs_full = flow_system.copy() +fs_full.name = 'Full Optimization' +fs_full.optimize(solver) + +# Two-stage optimization +fs_sizing = flow_system.transform.resample('4h') +fs_sizing.optimize(solver) +fs_dispatch = flow_system.transform.fix_sizes(fs_sizing.statistics.sizes) +fs_dispatch.name = 'Two-Stage' +fs_dispatch.optimize(solver) + +# Compare results +comp = fx.Comparison([fs_full, fs_dispatch]) +comp.statistics.plot.balance('Heat') + +# Check cost difference +diff = comp.diff() +print(f"Cost difference: {diff['costs'].sel(case='Two-Stage').item():.0f} €") +``` + ## Next Steps - [Plotting Results](../results-plotting.md) - Detailed plotting documentation diff --git a/flixopt/__init__.py b/flixopt/__init__.py index 1e3fee5bd..b84b82a4f 100644 --- a/flixopt/__init__.py +++ b/flixopt/__init__.py @@ -3,7 +3,6 @@ """ import logging -import warnings from importlib.metadata import PackageNotFoundError, version try: @@ -13,9 +12,12 @@ __version__ = '0.0.0.dev0' # Import commonly used classes and functions -from . import linear_converters, plotting, results, solvers +from . import clustering, linear_converters, plotting, results, solvers + +# Register xr.Dataset.fxplot accessor (import triggers registration via decorator) +from . import dataset_plot_accessor as _ # noqa: F401 from .carrier import Carrier, CarrierContainer -from .clustering import ClusteringParameters +from .comparison import Comparison from .components import ( LinearConverter, Sink, @@ -30,7 +32,7 @@ from .elements import Bus, Flow from .flow_system import FlowSystem from .interface import InvestParameters, Piece, Piecewise, PiecewiseConversion, PiecewiseEffects, StatusParameters -from .optimization import ClusteredOptimization, Optimization, SegmentedOptimization +from .optimization import Optimization, SegmentedOptimization from .plot_result import PlotResult __all__ = [ @@ -38,6 +40,7 @@ 'CONFIG', 'Carrier', 'CarrierContainer', + 'Comparison', 'Flow', 'Bus', 'Effect', @@ -50,7 +53,6 @@ 'Transmission', 'FlowSystem', 'Optimization', - 'ClusteredOptimization', 'SegmentedOptimization', 'InvestParameters', 'StatusParameters', @@ -58,47 +60,15 @@ 'Piecewise', 'PiecewiseConversion', 'PiecewiseEffects', - 'ClusteringParameters', 'PlotResult', + 'clustering', 'plotting', 'results', 'linear_converters', 'solvers', ] -# Initialize logger with default configuration (silent: WARNING level, NullHandler) +# Initialize logger with default configuration (silent: WARNING level, NullHandler). logger = logging.getLogger('flixopt') logger.setLevel(logging.WARNING) logger.addHandler(logging.NullHandler()) - -# === Runtime warning suppression for third-party libraries === -# These warnings are from dependencies and cannot be fixed by end users. -# They are suppressed at runtime to provide a cleaner user experience. -# These filters match the test configuration in pyproject.toml for consistency. - -# tsam: Time series aggregation library -# - UserWarning: Informational message about minimal value constraints during clustering. -warnings.filterwarnings( - 'ignore', - category=UserWarning, - message='.*minimal value.*exceeds.*', - module='tsam.timeseriesaggregation', # More specific if possible -) -# TODO: Might be able to fix it in flixopt? - -# linopy: Linear optimization library -# - UserWarning: Coordinate mismatch warnings that don't affect functionality and are expected. -warnings.filterwarnings( - 'ignore', category=UserWarning, message='Coordinates across variables not equal', module='linopy' -) -# - FutureWarning: join parameter default will change in future versions -warnings.filterwarnings( - 'ignore', - category=FutureWarning, - message="In a future version of xarray the default value for join will change from join='outer' to join='exact'", - module='linopy', -) - -# numpy: Core numerical library -# - RuntimeWarning: Binary incompatibility warnings from compiled extensions (safe to ignore). numpy 1->2 -warnings.filterwarnings('ignore', category=RuntimeWarning, message='numpy\\.ndarray size changed') diff --git a/flixopt/clustering.py b/flixopt/clustering.py deleted file mode 100644 index d392167a1..000000000 --- a/flixopt/clustering.py +++ /dev/null @@ -1,431 +0,0 @@ -""" -This module contains the Clustering functionality for the flixopt framework. -Through this, clustering TimeSeriesData is possible. -""" - -from __future__ import annotations - -import copy -import logging -import timeit -from typing import TYPE_CHECKING - -import numpy as np - -try: - import tsam.timeseriesaggregation as tsam - - TSAM_AVAILABLE = True -except ImportError: - TSAM_AVAILABLE = False - -from .color_processing import process_colors -from .components import Storage -from .config import CONFIG -from .plot_result import PlotResult -from .structure import ( - FlowSystemModel, - Submodel, -) - -if TYPE_CHECKING: - import linopy - import pandas as pd - - from .core import Scalar, TimeSeriesData - from .elements import Component - from .flow_system import FlowSystem - -logger = logging.getLogger('flixopt') - - -class Clustering: - """ - Clustering organizing class - """ - - def __init__( - self, - original_data: pd.DataFrame, - hours_per_time_step: Scalar, - hours_per_period: Scalar, - nr_of_periods: int = 8, - weights: dict[str, float] | None = None, - time_series_for_high_peaks: list[str] | None = None, - time_series_for_low_peaks: list[str] | None = None, - ): - """ - Args: - original_data: The original data to aggregate - hours_per_time_step: The duration of each timestep in hours. - hours_per_period: The duration of each period in hours. - nr_of_periods: The number of typical periods to use in the aggregation. - weights: The weights for aggregation. If None, all time series are equally weighted. - time_series_for_high_peaks: List of time series to use for explicitly selecting periods with high values. - time_series_for_low_peaks: List of time series to use for explicitly selecting periods with low values. - """ - if not TSAM_AVAILABLE: - raise ImportError( - "The 'tsam' package is required for clustering functionality. Install it with 'pip install tsam'." - ) - self.original_data = copy.deepcopy(original_data) - self.hours_per_time_step = hours_per_time_step - self.hours_per_period = hours_per_period - self.nr_of_periods = nr_of_periods - self.nr_of_time_steps = len(self.original_data.index) - self.weights = weights or {} - self.time_series_for_high_peaks = time_series_for_high_peaks or [] - self.time_series_for_low_peaks = time_series_for_low_peaks or [] - - self.aggregated_data: pd.DataFrame | None = None - self.clustering_duration_seconds = None - self.tsam: tsam.TimeSeriesAggregation | None = None - - def cluster(self) -> None: - """ - DurchfΓΌhrung der Zeitreihenaggregation - """ - start_time = timeit.default_timer() - # Erstellen des aggregation objects - self.tsam = tsam.TimeSeriesAggregation( - self.original_data, - noTypicalPeriods=self.nr_of_periods, - hoursPerPeriod=self.hours_per_period, - resolution=self.hours_per_time_step, - clusterMethod='k_means', - extremePeriodMethod='new_cluster_center' - if self.use_extreme_periods - else 'None', # Wenn Extremperioden eingebunden werden sollen, nutze die Methode 'new_cluster_center' aus tsam - weightDict={name: weight for name, weight in self.weights.items() if name in self.original_data.columns}, - addPeakMax=self.time_series_for_high_peaks, - addPeakMin=self.time_series_for_low_peaks, - ) - - self.tsam.createTypicalPeriods() # AusfΓΌhren der Aggregation/Clustering - self.aggregated_data = self.tsam.predictOriginalData() - - self.clustering_duration_seconds = timeit.default_timer() - start_time # Zeit messen: - if logger.isEnabledFor(logging.INFO): - logger.info(self.describe_clusters()) - - def describe_clusters(self) -> str: - description = {} - for cluster in self.get_cluster_indices().keys(): - description[cluster] = [ - str(indexVector[0]) + '...' + str(indexVector[-1]) - for indexVector in self.get_cluster_indices()[cluster] - ] - - if self.use_extreme_periods: - # Zeitreihe rauslΓΆschen: - extreme_periods = self.tsam.extremePeriods.copy() - for key in extreme_periods: - del extreme_periods[key]['profile'] - else: - extreme_periods = {} - - return ( - f'{"":#^80}\n' - f'{" Clustering ":#^80}\n' - f'periods_order:\n' - f'{self.tsam.clusterOrder}\n' - f'clusterPeriodNoOccur:\n' - f'{self.tsam.clusterPeriodNoOccur}\n' - f'index_vectors_of_clusters:\n' - f'{description}\n' - f'{"":#^80}\n' - f'extreme_periods:\n' - f'{extreme_periods}\n' - f'{"":#^80}' - ) - - @property - def use_extreme_periods(self): - return self.time_series_for_high_peaks or self.time_series_for_low_peaks - - def plot(self, colormap: str | None = None, show: bool | None = None) -> PlotResult: - """Plot original vs aggregated data comparison. - - Visualizes the original time series (dashed lines) overlaid with - the aggregated/clustered time series (solid lines) for comparison. - - Args: - colormap: Colorscale name for the time series colors. - Defaults to CONFIG.Plotting.default_qualitative_colorscale. - show: Whether to display the figure. - Defaults to CONFIG.Plotting.default_show. - - Returns: - PlotResult containing the comparison figure and underlying data. - - Examples: - >>> clustering.cluster() - >>> clustering.plot() - >>> clustering.plot(colormap='Set2', show=False).to_html('clustering.html') - """ - import plotly.express as px - import xarray as xr - - df_org = self.original_data.copy().rename( - columns={col: f'Original - {col}' for col in self.original_data.columns} - ) - df_agg = self.aggregated_data.copy().rename( - columns={col: f'Aggregated - {col}' for col in self.aggregated_data.columns} - ) - colors = list( - process_colors(colormap or CONFIG.Plotting.default_qualitative_colorscale, list(df_org.columns)).values() - ) - - # Create line plot for original data (dashed) - index_name = df_org.index.name or 'index' - df_org_long = df_org.reset_index().melt(id_vars=index_name, var_name='variable', value_name='value') - fig = px.line(df_org_long, x=index_name, y='value', color='variable', color_discrete_sequence=colors) - for trace in fig.data: - trace.update(line=dict(dash='dash')) - - # Add aggregated data (solid lines) - df_agg_long = df_agg.reset_index().melt(id_vars=index_name, var_name='variable', value_name='value') - fig2 = px.line(df_agg_long, x=index_name, y='value', color='variable', color_discrete_sequence=colors) - for trace in fig2.data: - fig.add_trace(trace) - - fig.update_layout( - title='Original vs Aggregated Data (original = ---)', - xaxis_title='Time in h', - yaxis_title='Value', - ) - - # Build xarray Dataset with both original and aggregated data - data = xr.Dataset( - { - 'original': self.original_data.to_xarray().to_array(dim='variable'), - 'aggregated': self.aggregated_data.to_xarray().to_array(dim='variable'), - } - ) - result = PlotResult(data=data, figure=fig) - - if show is None: - show = CONFIG.Plotting.default_show - if show: - result.show() - - return result - - def get_cluster_indices(self) -> dict[str, list[np.ndarray]]: - """ - Generates a dictionary that maps each cluster to a list of index vectors representing the time steps - assigned to that cluster for each period. - - Returns: - dict: {cluster_0: [index_vector_3, index_vector_7, ...], - cluster_1: [index_vector_1], - ...} - """ - clusters = self.tsam.clusterPeriodNoOccur.keys() - index_vectors = {cluster: [] for cluster in clusters} - - period_length = len(self.tsam.stepIdx) - total_steps = len(self.tsam.timeSeries) - - for period, cluster_id in enumerate(self.tsam.clusterOrder): - start_idx = period * period_length - end_idx = np.min([start_idx + period_length, total_steps]) - index_vectors[cluster_id].append(np.arange(start_idx, end_idx)) - - return index_vectors - - def get_equation_indices(self, skip_first_index_of_period: bool = True) -> tuple[np.ndarray, np.ndarray]: - """ - Generates pairs of indices for the equations by comparing index vectors of the same cluster. - If `skip_first_index_of_period` is True, the first index of each period is skipped. - - Args: - skip_first_index_of_period (bool): Whether to include or skip the first index of each period. - - Returns: - tuple[np.ndarray, np.ndarray]: Two arrays of indices. - """ - idx_var1 = [] - idx_var2 = [] - - # Iterate through cluster index vectors - for index_vectors in self.get_cluster_indices().values(): - if len(index_vectors) <= 1: # Only proceed if cluster has more than one period - continue - - # Process the first vector, optionally skip first index - first_vector = index_vectors[0][1:] if skip_first_index_of_period else index_vectors[0] - - # Compare first vector to others in the cluster - for other_vector in index_vectors[1:]: - if skip_first_index_of_period: - other_vector = other_vector[1:] - - # Compare elements up to the minimum length of both vectors - min_len = min(len(first_vector), len(other_vector)) - idx_var1.extend(first_vector[:min_len]) - idx_var2.extend(other_vector[:min_len]) - - # Convert lists to numpy arrays - return np.array(idx_var1), np.array(idx_var2) - - -class ClusteringParameters: - def __init__( - self, - hours_per_period: float, - nr_of_periods: int, - fix_storage_flows: bool, - aggregate_data_and_fix_non_binary_vars: bool, - percentage_of_period_freedom: float = 0, - penalty_of_period_freedom: float = 0, - time_series_for_high_peaks: list[TimeSeriesData] | None = None, - time_series_for_low_peaks: list[TimeSeriesData] | None = None, - ): - """ - Initializes clustering parameters for time series data - - Args: - hours_per_period: Duration of each period in hours. - nr_of_periods: Number of typical periods to use in the aggregation. - fix_storage_flows: Whether to aggregate storage flows (load/unload); if other flows - are fixed, fixing storage flows is usually not required. - aggregate_data_and_fix_non_binary_vars: Whether to aggregate all time series data, which allows to fix all time series variables (like flow_rate), - or only fix binary variables. If False non time_series data is changed!! If True, the mathematical Problem - is simplified even further. - percentage_of_period_freedom: Specifies the maximum percentage (0–100) of binary values within each period - that can deviate as "free variables", chosen by the solver (default is 0). - This allows binary variables to be 'partly equated' between aggregated periods. - penalty_of_period_freedom: The penalty associated with each "free variable"; defaults to 0. Added to Penalty - time_series_for_high_peaks: List of TimeSeriesData to use for explicitly selecting periods with high values. - time_series_for_low_peaks: List of TimeSeriesData to use for explicitly selecting periods with low values. - """ - self.hours_per_period = hours_per_period - self.nr_of_periods = nr_of_periods - self.fix_storage_flows = fix_storage_flows - self.aggregate_data_and_fix_non_binary_vars = aggregate_data_and_fix_non_binary_vars - self.percentage_of_period_freedom = percentage_of_period_freedom - self.penalty_of_period_freedom = penalty_of_period_freedom - self.time_series_for_high_peaks: list[TimeSeriesData] = time_series_for_high_peaks or [] - self.time_series_for_low_peaks: list[TimeSeriesData] = time_series_for_low_peaks or [] - - @property - def use_extreme_periods(self): - return self.time_series_for_high_peaks or self.time_series_for_low_peaks - - @property - def labels_for_high_peaks(self) -> list[str]: - return [ts.name for ts in self.time_series_for_high_peaks] - - @property - def labels_for_low_peaks(self) -> list[str]: - return [ts.name for ts in self.time_series_for_low_peaks] - - @property - def use_low_peaks(self) -> bool: - return bool(self.time_series_for_low_peaks) - - -class ClusteringModel(Submodel): - """The ClusteringModel holds equations and variables related to the Clustering of a FlowSystem. - It creates Equations that equates indices of variables, and introduces penalties related to binary variables, that - escape the equation to their related binaries in other periods""" - - def __init__( - self, - model: FlowSystemModel, - clustering_parameters: ClusteringParameters, - flow_system: FlowSystem, - clustering_data: Clustering, - components_to_clusterize: list[Component] | None, - ): - """ - Modeling-Element for "index-equating"-equations - """ - super().__init__(model, label_of_element='Clustering', label_of_model='Clustering') - self.flow_system = flow_system - self.clustering_parameters = clustering_parameters - self.clustering_data = clustering_data - self.components_to_clusterize = components_to_clusterize - - def do_modeling(self): - if not self.components_to_clusterize: - components = self.flow_system.components.values() - else: - components = [component for component in self.components_to_clusterize] - - indices = self.clustering_data.get_equation_indices(skip_first_index_of_period=True) - - time_variables: set[str] = { - name for name in self._model.variables if 'time' in self._model.variables[name].dims - } - binary_variables: set[str] = set(self._model.variables.binaries) - binary_time_variables: set[str] = time_variables & binary_variables - - for component in components: - if isinstance(component, Storage) and not self.clustering_parameters.fix_storage_flows: - continue # Fix Nothing in The Storage - - all_variables_of_component = set(component.submodel.variables) - - if self.clustering_parameters.aggregate_data_and_fix_non_binary_vars: - relevant_variables = component.submodel.variables[all_variables_of_component & time_variables] - else: - relevant_variables = component.submodel.variables[all_variables_of_component & binary_time_variables] - for variable in relevant_variables: - self._equate_indices(component.submodel.variables[variable], indices) - - penalty = self.clustering_parameters.penalty_of_period_freedom - if (self.clustering_parameters.percentage_of_period_freedom > 0) and penalty != 0: - from .effects import PENALTY_EFFECT_LABEL - - for variable_name in self.variables_direct: - variable = self.variables_direct[variable_name] - # Sum correction variables over all dimensions to get periodic penalty contribution - self._model.effects.add_share_to_effects( - name='Aggregation', - expressions={PENALTY_EFFECT_LABEL: (variable * penalty).sum('time')}, - target='periodic', - ) - - def _equate_indices(self, variable: linopy.Variable, indices: tuple[np.ndarray, np.ndarray]) -> None: - assert len(indices[0]) == len(indices[1]), 'The length of the indices must match!!' - length = len(indices[0]) - - # Gleichung: - # eq1: x(p1,t) - x(p3,t) = 0 # wobei p1 und p3 im gleichen Cluster sind und t = 0..N_p - con = self.add_constraints( - variable.isel(time=indices[0]) - variable.isel(time=indices[1]) == 0, - short_name=f'equate_indices|{variable.name}', - ) - - # Korrektur: (bisher nur fΓΌr BinΓ€rvariablen:) - if ( - variable.name in self._model.variables.binaries - and self.clustering_parameters.percentage_of_period_freedom > 0 - ): - sel = variable.isel(time=indices[0]) - coords = {d: sel.indexes[d] for d in sel.dims} - var_k1 = self.add_variables(binary=True, coords=coords, short_name=f'correction1|{variable.name}') - - var_k0 = self.add_variables(binary=True, coords=coords, short_name=f'correction0|{variable.name}') - - # equation extends ... - # --> On(p3) can be 0/1 independent of On(p1,t)! - # eq1: On(p1,t) - On(p3,t) + K1(p3,t) - K0(p3,t) = 0 - # --> correction On(p3) can be: - # On(p1,t) = 1 -> On(p3) can be 0 -> K0=1 (,K1=0) - # On(p1,t) = 0 -> On(p3) can be 1 -> K1=1 (,K0=1) - con.lhs += 1 * var_k1 - 1 * var_k0 - - # interlock var_k1 and var_K2: - # eq: var_k0(t)+var_k1(t) <= 1 - self.add_constraints(var_k0 + var_k1 <= 1, short_name=f'lock_k0_and_k1|{variable.name}') - - # Begrenzung der Korrektur-Anzahl: - # eq: sum(K) <= n_Corr_max - limit = int(np.floor(self.clustering_parameters.percentage_of_period_freedom / 100 * length)) - self.add_constraints( - var_k0.sum(dim='time') + var_k1.sum(dim='time') <= limit, - short_name=f'limit_corrections|{variable.name}', - ) diff --git a/flixopt/clustering/__init__.py b/flixopt/clustering/__init__.py new file mode 100644 index 000000000..a5446a524 --- /dev/null +++ b/flixopt/clustering/__init__.py @@ -0,0 +1,42 @@ +""" +Time Series Aggregation Module for flixopt. + +This module provides data structures for time series clustering/aggregation. + +Key classes: +- ClusterResult: Universal result container for clustering +- ClusterStructure: Hierarchical structure info for storage inter-cluster linking +- Clustering: Stored on FlowSystem after clustering + +Example usage: + + # Cluster a FlowSystem to reduce timesteps + fs_clustered = flow_system.transform.cluster( + n_clusters=8, + cluster_duration='1D', + time_series_for_high_peaks=['Demand|fixed_relative_profile'], + ) + + # Access clustering metadata + info = fs_clustered.clustering + print(f'Number of clusters: {info.result.cluster_structure.n_clusters}') + + # Expand solution back to full resolution + fs_expanded = fs_clustered.transform.expand_solution() +""" + +from .base import ( + Clustering, + ClusterResult, + ClusterStructure, + create_cluster_structure_from_mapping, +) + +__all__ = [ + # Core classes + 'ClusterResult', + 'Clustering', + 'ClusterStructure', + # Utilities + 'create_cluster_structure_from_mapping', +] diff --git a/flixopt/clustering/base.py b/flixopt/clustering/base.py new file mode 100644 index 000000000..0f154484b --- /dev/null +++ b/flixopt/clustering/base.py @@ -0,0 +1,1179 @@ +""" +Base classes and data structures for time series aggregation (clustering). + +This module provides an abstraction layer for time series aggregation that +supports multiple backends (TSAM, manual/external, etc.). + +Terminology: +- "cluster" = a group of similar time chunks (e.g., similar days grouped together) +- "typical period" = a representative time chunk for a cluster (TSAM terminology) +- "cluster duration" = the length of each time chunk (e.g., 24h for daily clustering) + +Note: This is separate from the model's "period" dimension (years/months) and +"scenario" dimension. The aggregation operates on the 'time' dimension. + +All data structures use xarray for consistent handling of coordinates. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any + +import numpy as np +import pandas as pd +import xarray as xr + +if TYPE_CHECKING: + from ..color_processing import ColorType + from ..plot_result import PlotResult + from ..statistics_accessor import SelectType + + +@dataclass +class ClusterStructure: + """Structure information for inter-cluster storage linking. + + This class captures the hierarchical structure of time series clustering, + which is needed for proper storage state-of-charge tracking across + typical periods when using cluster(). + + Note: The "original_cluster" dimension indexes the original cluster-sized + time segments (e.g., 0..364 for 365 days), NOT the model's "period" dimension + (years). Each original segment gets assigned to a representative cluster. + + Attributes: + cluster_order: Maps original cluster index β†’ representative cluster ID. + dims: [original_cluster] for simple case, or + [original_cluster, period, scenario] for multi-period/scenario systems. + Values are cluster IDs (0 to n_clusters-1). + cluster_occurrences: Count of how many original time chunks each cluster represents. + dims: [cluster] for simple case, or [cluster, period, scenario] for multi-dim. + n_clusters: Number of distinct clusters (typical periods). + timesteps_per_cluster: Number of timesteps in each cluster (e.g., 24 for daily). + + Example: + For 365 days clustered into 8 typical days: + - cluster_order: shape (365,), values 0-7 indicating which cluster each day belongs to + - cluster_occurrences: shape (8,), e.g., [45, 46, 46, 46, 46, 45, 45, 46] + - n_clusters: 8 + - timesteps_per_cluster: 24 (for hourly data) + + For multi-scenario (e.g., 2 scenarios): + - cluster_order: shape (365, 2) with dims [original_cluster, scenario] + - cluster_occurrences: shape (8, 2) with dims [cluster, scenario] + """ + + cluster_order: xr.DataArray + cluster_occurrences: xr.DataArray + n_clusters: int | xr.DataArray + timesteps_per_cluster: int + + def __post_init__(self): + """Validate and ensure proper DataArray formatting.""" + # Ensure cluster_order is a DataArray with proper dims + if not isinstance(self.cluster_order, xr.DataArray): + self.cluster_order = xr.DataArray(self.cluster_order, dims=['original_cluster'], name='cluster_order') + elif self.cluster_order.name is None: + self.cluster_order = self.cluster_order.rename('cluster_order') + + # Ensure cluster_occurrences is a DataArray with proper dims + if not isinstance(self.cluster_occurrences, xr.DataArray): + self.cluster_occurrences = xr.DataArray( + self.cluster_occurrences, dims=['cluster'], name='cluster_occurrences' + ) + elif self.cluster_occurrences.name is None: + self.cluster_occurrences = self.cluster_occurrences.rename('cluster_occurrences') + + def __repr__(self) -> str: + n_clusters = ( + int(self.n_clusters) if isinstance(self.n_clusters, (int, np.integer)) else int(self.n_clusters.values) + ) + occ = [int(self.cluster_occurrences.sel(cluster=c).values) for c in range(n_clusters)] + return ( + f'ClusterStructure(\n' + f' {self.n_original_clusters} original periods β†’ {n_clusters} clusters\n' + f' timesteps_per_cluster={self.timesteps_per_cluster}\n' + f' occurrences={occ}\n' + f')' + ) + + def _create_reference_structure(self) -> tuple[dict, dict[str, xr.DataArray]]: + """Create reference structure for serialization.""" + ref = {'__class__': self.__class__.__name__} + arrays = {} + + # Store DataArrays with references + arrays[str(self.cluster_order.name)] = self.cluster_order + ref['cluster_order'] = f':::{self.cluster_order.name}' + + arrays[str(self.cluster_occurrences.name)] = self.cluster_occurrences + ref['cluster_occurrences'] = f':::{self.cluster_occurrences.name}' + + # Store scalar values + if isinstance(self.n_clusters, xr.DataArray): + n_clusters_name = self.n_clusters.name or 'n_clusters' + self.n_clusters = self.n_clusters.rename(n_clusters_name) + arrays[n_clusters_name] = self.n_clusters + ref['n_clusters'] = f':::{n_clusters_name}' + else: + ref['n_clusters'] = int(self.n_clusters) + + ref['timesteps_per_cluster'] = self.timesteps_per_cluster + + return ref, arrays + + @property + def n_original_clusters(self) -> int: + """Number of original periods (before clustering).""" + return len(self.cluster_order.coords['original_cluster']) + + @property + def has_multi_dims(self) -> bool: + """Check if cluster_order has period/scenario dimensions.""" + return 'period' in self.cluster_order.dims or 'scenario' in self.cluster_order.dims + + def get_cluster_order_for_slice(self, period: str | None = None, scenario: str | None = None) -> np.ndarray: + """Get cluster_order for a specific (period, scenario) combination. + + Args: + period: Period label (None if no period dimension). + scenario: Scenario label (None if no scenario dimension). + + Returns: + 1D numpy array of cluster indices for the specified slice. + """ + order = self.cluster_order + if 'period' in order.dims and period is not None: + order = order.sel(period=period) + if 'scenario' in order.dims and scenario is not None: + order = order.sel(scenario=scenario) + return order.values.astype(int) + + def get_cluster_occurrences_for_slice( + self, period: str | None = None, scenario: str | None = None + ) -> dict[int, int]: + """Get cluster occurrence counts for a specific (period, scenario) combination. + + Args: + period: Period label (None if no period dimension). + scenario: Scenario label (None if no scenario dimension). + + Returns: + Dict mapping cluster ID to occurrence count. + """ + occurrences = self.cluster_occurrences + if 'period' in occurrences.dims and period is not None: + occurrences = occurrences.sel(period=period) + if 'scenario' in occurrences.dims and scenario is not None: + occurrences = occurrences.sel(scenario=scenario) + return {int(c): int(occurrences.sel(cluster=c).values) for c in occurrences.coords['cluster'].values} + + def get_cluster_weight_per_timestep(self) -> xr.DataArray: + """Get weight for each representative timestep. + + Returns an array where each timestep's weight equals the number of + original periods its cluster represents. + + Returns: + DataArray with dims [time] or [time, period, scenario]. + """ + # Expand cluster_occurrences to timesteps + n_clusters = ( + int(self.n_clusters) if isinstance(self.n_clusters, (int, np.integer)) else int(self.n_clusters.values) + ) + + # Get occurrence for each cluster, then repeat for timesteps + weights_list = [] + for c in range(n_clusters): + occ = self.cluster_occurrences.sel(cluster=c) + weights_list.append(np.repeat(float(occ.values), self.timesteps_per_cluster)) + + weights = np.concatenate(weights_list) + return xr.DataArray( + weights, + dims=['time'], + coords={'time': np.arange(len(weights))}, + name='cluster_weight', + ) + + def plot(self, colors: str | list[str] | None = None, show: bool | None = None) -> PlotResult: + """Plot cluster assignment visualization. + + Shows which cluster each original period belongs to, and the + number of occurrences per cluster. + + Args: + colors: Colorscale name (str) or list of colors. + Defaults to CONFIG.Plotting.default_sequential_colorscale. + show: Whether to display the figure. Defaults to CONFIG.Plotting.default_show. + + Returns: + PlotResult containing the figure and underlying data. + """ + from ..config import CONFIG + from ..plot_result import PlotResult + + n_clusters = ( + int(self.n_clusters) if isinstance(self.n_clusters, (int, np.integer)) else int(self.n_clusters.values) + ) + + cluster_order = self.get_cluster_order_for_slice() + + # Build DataArray for fxplot heatmap + cluster_da = xr.DataArray( + cluster_order.reshape(1, -1), + dims=['y', 'original_cluster'], + coords={'y': ['Cluster'], 'original_cluster': range(1, len(cluster_order) + 1)}, + name='cluster_assignment', + ) + + # Use fxplot.heatmap for smart defaults + colorscale = colors or CONFIG.Plotting.default_sequential_colorscale + fig = cluster_da.fxplot.heatmap( + colors=colorscale, + title=f'Cluster Assignment ({self.n_original_clusters} periods β†’ {n_clusters} clusters)', + ) + fig.update_yaxes(showticklabels=False) + fig.update_coloraxes(colorbar_title='Cluster') + + # Build data for PlotResult + data = xr.Dataset( + { + 'cluster_order': self.cluster_order, + 'cluster_occurrences': self.cluster_occurrences, + } + ) + plot_result = PlotResult(data=data, figure=fig) + + if show is None: + show = CONFIG.Plotting.default_show + if show: + plot_result.show() + + return plot_result + + +@dataclass +class ClusterResult: + """Universal result from any time series aggregation method. + + This dataclass captures all information needed to: + 1. Transform a FlowSystem to use aggregated (clustered) timesteps + 2. Expand a solution back to original resolution + 3. Properly weight results for statistics + + Attributes: + timestep_mapping: Maps each original timestep to its representative index. + dims: [original_time] for simple case, or + [original_time, period, scenario] for multi-period/scenario systems. + Values are indices into the representative timesteps (0 to n_representatives-1). + n_representatives: Number of representative timesteps after aggregation. + representative_weights: Weight for each representative timestep. + dims: [time] or [time, period, scenario] + Typically equals the number of original timesteps each representative covers. + Used as cluster_weight in the FlowSystem. + aggregated_data: Time series data aggregated to representative timesteps. + Optional - some backends may not aggregate data. + cluster_structure: Hierarchical clustering structure for storage linking. + Optional - only needed when using cluster() mode. + original_data: Reference to original data before aggregation. + Optional - useful for expand_solution(). + + Example: + For 8760 hourly timesteps clustered into 192 representative timesteps (8 clusters x 24h): + - timestep_mapping: shape (8760,), values 0-191 + - n_representatives: 192 + - representative_weights: shape (192,), summing to 8760 + """ + + timestep_mapping: xr.DataArray + n_representatives: int | xr.DataArray + representative_weights: xr.DataArray + aggregated_data: xr.Dataset | None = None + cluster_structure: ClusterStructure | None = None + original_data: xr.Dataset | None = None + + def __post_init__(self): + """Validate and ensure proper DataArray formatting.""" + # Ensure timestep_mapping is a DataArray + if not isinstance(self.timestep_mapping, xr.DataArray): + self.timestep_mapping = xr.DataArray(self.timestep_mapping, dims=['original_time'], name='timestep_mapping') + elif self.timestep_mapping.name is None: + self.timestep_mapping = self.timestep_mapping.rename('timestep_mapping') + + # Ensure representative_weights is a DataArray + # Can be (cluster, time) for 2D structure or (time,) for flat structure + if not isinstance(self.representative_weights, xr.DataArray): + self.representative_weights = xr.DataArray(self.representative_weights, name='representative_weights') + elif self.representative_weights.name is None: + self.representative_weights = self.representative_weights.rename('representative_weights') + + def __repr__(self) -> str: + n_rep = ( + int(self.n_representatives) + if isinstance(self.n_representatives, (int, np.integer)) + else int(self.n_representatives.values) + ) + has_structure = self.cluster_structure is not None + has_data = self.original_data is not None and self.aggregated_data is not None + return ( + f'ClusterResult(\n' + f' {self.n_original_timesteps} original β†’ {n_rep} representative timesteps\n' + f' weights sum={float(self.representative_weights.sum().values):.0f}\n' + f' cluster_structure={has_structure}, data={has_data}\n' + f')' + ) + + def _create_reference_structure(self) -> tuple[dict, dict[str, xr.DataArray]]: + """Create reference structure for serialization.""" + ref = {'__class__': self.__class__.__name__} + arrays = {} + + # Store DataArrays with references + arrays[str(self.timestep_mapping.name)] = self.timestep_mapping + ref['timestep_mapping'] = f':::{self.timestep_mapping.name}' + + arrays[str(self.representative_weights.name)] = self.representative_weights + ref['representative_weights'] = f':::{self.representative_weights.name}' + + # Store scalar values + if isinstance(self.n_representatives, xr.DataArray): + n_rep_name = self.n_representatives.name or 'n_representatives' + self.n_representatives = self.n_representatives.rename(n_rep_name) + arrays[n_rep_name] = self.n_representatives + ref['n_representatives'] = f':::{n_rep_name}' + else: + ref['n_representatives'] = int(self.n_representatives) + + # Store nested ClusterStructure if present + if self.cluster_structure is not None: + cs_ref, cs_arrays = self.cluster_structure._create_reference_structure() + ref['cluster_structure'] = cs_ref + arrays.update(cs_arrays) + + # Skip aggregated_data and original_data - not needed for serialization + + return ref, arrays + + @property + def n_original_timesteps(self) -> int: + """Number of original timesteps (before aggregation).""" + return len(self.timestep_mapping.coords['original_time']) + + def get_expansion_mapping(self) -> xr.DataArray: + """Get mapping from original timesteps to representative indices. + + This is the same as timestep_mapping but ensures proper naming + for use in expand_solution(). + + Returns: + DataArray mapping original timesteps to representative indices. + """ + return self.timestep_mapping.rename('expansion_mapping') + + def get_timestep_mapping_for_slice(self, period: str | None = None, scenario: str | None = None) -> np.ndarray: + """Get timestep_mapping for a specific (period, scenario) combination. + + Args: + period: Period label (None if no period dimension). + scenario: Scenario label (None if no scenario dimension). + + Returns: + 1D numpy array of representative timestep indices for the specified slice. + """ + mapping = self.timestep_mapping + if 'period' in mapping.dims and period is not None: + mapping = mapping.sel(period=period) + if 'scenario' in mapping.dims and scenario is not None: + mapping = mapping.sel(scenario=scenario) + return mapping.values.astype(int) + + def expand_data(self, aggregated: xr.DataArray, original_time: xr.DataArray | None = None) -> xr.DataArray: + """Expand aggregated data back to original timesteps. + + Uses the stored timestep_mapping to map each original timestep to its + representative value from the aggregated data. Handles multi-dimensional + data with period/scenario dimensions. + + Args: + aggregated: DataArray with aggregated (reduced) time dimension. + original_time: Original time coordinates. If None, uses coords from + original_data if available. + + Returns: + DataArray expanded to original timesteps. + + Example: + >>> result = fs_clustered.clustering.result + >>> aggregated_values = result.aggregated_data['Demand|profile'] + >>> expanded = result.expand_data(aggregated_values) + >>> len(expanded.time) == len(original_timesteps) # True + """ + import pandas as pd + + if original_time is None: + if self.original_data is None: + raise ValueError('original_time required when original_data is not available') + original_time = self.original_data.coords['time'] + + timestep_mapping = self.timestep_mapping + has_periods = 'period' in timestep_mapping.dims + has_scenarios = 'scenario' in timestep_mapping.dims + has_cluster_dim = 'cluster' in aggregated.dims + + # Simple case: no period/scenario dimensions + if not has_periods and not has_scenarios: + mapping = timestep_mapping.values + if has_cluster_dim: + # 2D cluster structure: convert flat indices to (cluster, time_within) + # Use cluster_structure's timesteps_per_cluster, not aggregated.sizes['time'] + # because the solution may include extra timesteps (timesteps_extra) + timesteps_per_cluster = self.cluster_structure.timesteps_per_cluster + cluster_ids = mapping // timesteps_per_cluster + time_within = mapping % timesteps_per_cluster + expanded_values = aggregated.values[cluster_ids, time_within] + else: + expanded_values = aggregated.values[mapping] + return xr.DataArray( + expanded_values, + coords={'time': original_time}, + dims=['time'], + attrs=aggregated.attrs, + ) + + # Multi-dimensional: expand each (period, scenario) slice and recombine + periods = list(timestep_mapping.coords['period'].values) if has_periods else [None] + scenarios = list(timestep_mapping.coords['scenario'].values) if has_scenarios else [None] + + expanded_slices: dict[tuple, xr.DataArray] = {} + for p in periods: + for s in scenarios: + # Get mapping for this slice + mapping_slice = timestep_mapping + if p is not None: + mapping_slice = mapping_slice.sel(period=p) + if s is not None: + mapping_slice = mapping_slice.sel(scenario=s) + mapping = mapping_slice.values + + # Select the data slice + selector = {} + if p is not None and 'period' in aggregated.dims: + selector['period'] = p + if s is not None and 'scenario' in aggregated.dims: + selector['scenario'] = s + + slice_da = aggregated.sel(**selector, drop=True) if selector else aggregated + + if has_cluster_dim: + # 2D cluster structure: convert flat indices to (cluster, time_within) + # Use cluster_structure's timesteps_per_cluster, not slice_da.sizes['time'] + # because the solution may include extra timesteps (timesteps_extra) + timesteps_per_cluster = self.cluster_structure.timesteps_per_cluster + cluster_ids = mapping // timesteps_per_cluster + time_within = mapping % timesteps_per_cluster + expanded_values = slice_da.values[cluster_ids, time_within] + expanded = xr.DataArray(expanded_values, dims=['time']) + else: + expanded = slice_da.isel(time=xr.DataArray(mapping, dims=['time'])) + expanded_slices[(p, s)] = expanded.assign_coords(time=original_time) + + # Recombine slices using xr.concat + if has_periods and has_scenarios: + period_arrays = [] + for p in periods: + scenario_arrays = [expanded_slices[(p, s)] for s in scenarios] + period_arrays.append(xr.concat(scenario_arrays, dim=pd.Index(scenarios, name='scenario'))) + result = xr.concat(period_arrays, dim=pd.Index(periods, name='period')) + elif has_periods: + result = xr.concat([expanded_slices[(p, None)] for p in periods], dim=pd.Index(periods, name='period')) + else: + result = xr.concat( + [expanded_slices[(None, s)] for s in scenarios], dim=pd.Index(scenarios, name='scenario') + ) + + return result.transpose('time', ...).assign_attrs(aggregated.attrs) + + def validate(self) -> None: + """Validate that all fields are consistent. + + Raises: + ValueError: If validation fails. + """ + n_rep = ( + int(self.n_representatives) + if isinstance(self.n_representatives, (int, np.integer)) + else int(self.n_representatives.max().values) + ) + + # Check mapping values are within range + max_idx = int(self.timestep_mapping.max().values) + if max_idx >= n_rep: + raise ValueError(f'timestep_mapping contains index {max_idx} but n_representatives is {n_rep}') + + # Check weights dimensions + # representative_weights should have (cluster,) dimension with n_clusters elements + # (plus optional period/scenario dimensions) + if self.cluster_structure is not None: + n_clusters = self.cluster_structure.n_clusters + if 'cluster' in self.representative_weights.dims: + weights_n_clusters = self.representative_weights.sizes['cluster'] + if weights_n_clusters != n_clusters: + raise ValueError( + f'representative_weights has {weights_n_clusters} clusters ' + f'but cluster_structure has {n_clusters}' + ) + + # Check weights sum roughly equals number of original periods + # (each weight is how many original periods that cluster represents) + # Sum should be checked per period/scenario slice, not across all dimensions + if self.cluster_structure is not None: + n_original_clusters = self.cluster_structure.n_original_clusters + # Sum over cluster dimension only (keep period/scenario if present) + weight_sum_per_slice = self.representative_weights.sum(dim='cluster') + # Check each slice + if weight_sum_per_slice.size == 1: + # Simple case: no period/scenario + weight_sum = float(weight_sum_per_slice.values) + if abs(weight_sum - n_original_clusters) > 1e-6: + import warnings + + warnings.warn( + f'representative_weights sum ({weight_sum}) does not match ' + f'n_original_clusters ({n_original_clusters})', + stacklevel=2, + ) + else: + # Multi-dimensional: check each slice + for val in weight_sum_per_slice.values.flat: + if abs(float(val) - n_original_clusters) > 1e-6: + import warnings + + warnings.warn( + f'representative_weights sum per slice ({float(val)}) does not match ' + f'n_original_clusters ({n_original_clusters})', + stacklevel=2, + ) + break # Only warn once + + +class ClusteringPlotAccessor: + """Plot accessor for Clustering objects. + + Provides visualization methods for comparing original vs aggregated data + and understanding the clustering structure. + + Example: + >>> fs_clustered = flow_system.transform.cluster(n_clusters=8, cluster_duration='1D') + >>> fs_clustered.clustering.plot.compare() # timeseries comparison + >>> fs_clustered.clustering.plot.compare(kind='duration_curve') # duration curve + >>> fs_clustered.clustering.plot.heatmap() # structure visualization + >>> fs_clustered.clustering.plot.clusters() # cluster profiles + """ + + def __init__(self, clustering: Clustering): + self._clustering = clustering + + def compare( + self, + kind: str = 'timeseries', + variables: str | list[str] | None = None, + *, + select: SelectType | None = None, + colors: ColorType | None = None, + color: str | None = 'auto', + line_dash: str | None = 'representation', + facet_col: str | None = 'auto', + facet_row: str | None = 'auto', + show: bool | None = None, + **plotly_kwargs: Any, + ) -> PlotResult: + """Compare original vs aggregated data. + + Args: + kind: Type of comparison plot. + - 'timeseries': Time series comparison (default) + - 'duration_curve': Sorted duration curve comparison + variables: Variable(s) to plot. Can be a string, list of strings, + or None to plot all time-varying variables. + select: xarray-style selection dict, e.g. {'scenario': 'Base Case'}. + colors: Color specification (colorscale name, color list, or label-to-color dict). + color: Dimension for line colors. 'auto' uses CONFIG priority (typically 'variable'). + Use 'representation' to color by Original/Clustered instead of line_dash. + line_dash: Dimension for line dash styles. Defaults to 'representation'. + Set to None to disable line dash differentiation. + facet_col: Dimension for subplot columns. 'auto' uses CONFIG priority. + Use 'variable' to create separate columns per variable. + facet_row: Dimension for subplot rows. 'auto' uses CONFIG priority. + Use 'variable' to create separate rows per variable. + show: Whether to display the figure. + Defaults to CONFIG.Plotting.default_show. + **plotly_kwargs: Additional arguments passed to plotly. + + Returns: + PlotResult containing the comparison figure and underlying data. + """ + import pandas as pd + + from ..config import CONFIG + from ..plot_result import PlotResult + from ..statistics_accessor import _apply_selection + + if kind not in ('timeseries', 'duration_curve'): + raise ValueError(f"Unknown kind '{kind}'. Use 'timeseries' or 'duration_curve'.") + + result = self._clustering.result + if result.original_data is None or result.aggregated_data is None: + raise ValueError('No original/aggregated data available for comparison') + + resolved_variables = self._resolve_variables(variables) + + # Build Dataset with variables as data_vars + data_vars = {} + for var in resolved_variables: + original = result.original_data[var] + clustered = result.expand_data(result.aggregated_data[var]) + combined = xr.concat([original, clustered], dim=pd.Index(['Original', 'Clustered'], name='representation')) + data_vars[var] = combined + ds = xr.Dataset(data_vars) + + # Apply selection + ds = _apply_selection(ds, select) + + # For duration curve: flatten and sort values + if kind == 'duration_curve': + sorted_vars = {} + for var in ds.data_vars: + for rep in ds.coords['representation'].values: + values = np.sort(ds[var].sel(representation=rep).values.flatten())[::-1] + sorted_vars[(var, rep)] = values + n = len(values) + ds = xr.Dataset( + { + var: xr.DataArray( + [sorted_vars[(var, r)] for r in ['Original', 'Clustered']], + dims=['representation', 'duration'], + coords={'representation': ['Original', 'Clustered'], 'duration': range(n)}, + ) + for var in resolved_variables + } + ) + + # Set title based on kind + if kind == 'timeseries': + title = ( + 'Original vs Clustered' + if len(resolved_variables) > 1 + else f'Original vs Clustered: {resolved_variables[0]}' + ) + else: + title = 'Duration Curve' if len(resolved_variables) > 1 else f'Duration Curve: {resolved_variables[0]}' + + # Use fxplot for smart defaults + line_kwargs = {} + if line_dash is not None: + line_kwargs['line_dash'] = line_dash + if line_dash == 'representation': + line_kwargs['line_dash_map'] = {'Original': 'dot', 'Clustered': 'solid'} + + fig = ds.fxplot.line( + colors=colors, + color=color, + title=title, + facet_col=facet_col, + facet_row=facet_row, + **line_kwargs, + **plotly_kwargs, + ) + fig.update_yaxes(matches=None) + fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1])) + + plot_result = PlotResult(data=ds, figure=fig) + + if show is None: + show = CONFIG.Plotting.default_show + if show: + plot_result.show() + + return plot_result + + def _get_time_varying_variables(self) -> list[str]: + """Get list of time-varying variables from original data.""" + result = self._clustering.result + if result.original_data is None: + return [] + return [ + name + for name in result.original_data.data_vars + if 'time' in result.original_data[name].dims + and not np.isclose(result.original_data[name].min(), result.original_data[name].max()) + ] + + def _resolve_variables(self, variables: str | list[str] | None) -> list[str]: + """Resolve variables parameter to a list of valid variable names.""" + time_vars = self._get_time_varying_variables() + if not time_vars: + raise ValueError('No time-varying variables found') + + if variables is None: + return time_vars + elif isinstance(variables, str): + if variables not in time_vars: + raise ValueError(f"Variable '{variables}' not found. Available: {time_vars}") + return [variables] + else: + invalid = [v for v in variables if v not in time_vars] + if invalid: + raise ValueError(f'Variables {invalid} not found. Available: {time_vars}') + return list(variables) + + def heatmap( + self, + *, + select: SelectType | None = None, + colors: str | list[str] | None = None, + facet_col: str | None = 'auto', + animation_frame: str | None = 'auto', + show: bool | None = None, + **plotly_kwargs: Any, + ) -> PlotResult: + """Plot cluster assignments over time as a heatmap timeline. + + Shows which cluster each timestep belongs to as a horizontal color bar. + The x-axis is time, color indicates cluster assignment. This visualization + aligns with time series data, making it easy to correlate cluster + assignments with other plots. + + For multi-period/scenario data, uses faceting and/or animation. + + Args: + select: xarray-style selection dict, e.g. {'scenario': 'Base Case'}. + colors: Colorscale name (str) or list of colors for heatmap coloring. + Dicts are not supported for heatmaps. + Defaults to CONFIG.Plotting.default_sequential_colorscale. + facet_col: Dimension to facet on columns. 'auto' uses CONFIG priority. + animation_frame: Dimension for animation slider. 'auto' uses CONFIG priority. + show: Whether to display the figure. + Defaults to CONFIG.Plotting.default_show. + **plotly_kwargs: Additional arguments passed to plotly. + + Returns: + PlotResult containing the heatmap figure and cluster assignment data. + The data has 'cluster' variable with time dimension, matching original timesteps. + """ + import pandas as pd + + from ..config import CONFIG + from ..plot_result import PlotResult + from ..statistics_accessor import _apply_selection + + result = self._clustering.result + cs = result.cluster_structure + if cs is None: + raise ValueError('No cluster structure available') + + cluster_order_da = cs.cluster_order + timesteps_per_period = cs.timesteps_per_cluster + original_time = result.original_data.coords['time'] if result.original_data is not None else None + + # Apply selection if provided + if select: + cluster_order_da = _apply_selection(cluster_order_da.to_dataset(name='cluster'), select)['cluster'] + + # Check for multi-dimensional data + has_periods = 'period' in cluster_order_da.dims + has_scenarios = 'scenario' in cluster_order_da.dims + + # Get dimension values + periods = list(cluster_order_da.coords['period'].values) if has_periods else [None] + scenarios = list(cluster_order_da.coords['scenario'].values) if has_scenarios else [None] + + # Build cluster assignment per timestep for each (period, scenario) slice + cluster_slices: dict[tuple, xr.DataArray] = {} + for p in periods: + for s in scenarios: + cluster_order = cs.get_cluster_order_for_slice(period=p, scenario=s) + # Expand: each cluster repeated timesteps_per_period times + cluster_per_timestep = np.repeat(cluster_order, timesteps_per_period) + cluster_slices[(p, s)] = xr.DataArray( + cluster_per_timestep, + dims=['time'], + coords={'time': original_time} if original_time is not None else None, + ) + + # Combine slices into multi-dimensional DataArray + if has_periods and has_scenarios: + period_arrays = [] + for p in periods: + scenario_arrays = [cluster_slices[(p, s)] for s in scenarios] + period_arrays.append(xr.concat(scenario_arrays, dim=pd.Index(scenarios, name='scenario'))) + cluster_da = xr.concat(period_arrays, dim=pd.Index(periods, name='period')) + elif has_periods: + cluster_da = xr.concat( + [cluster_slices[(p, None)] for p in periods], + dim=pd.Index(periods, name='period'), + ) + elif has_scenarios: + cluster_da = xr.concat( + [cluster_slices[(None, s)] for s in scenarios], + dim=pd.Index(scenarios, name='scenario'), + ) + else: + cluster_da = cluster_slices[(None, None)] + + # Add dummy y dimension for heatmap visualization (single row) + heatmap_da = cluster_da.expand_dims('y', axis=-1) + heatmap_da = heatmap_da.assign_coords(y=['Cluster']) + heatmap_da.name = 'cluster_assignment' + + # Reorder dims so 'time' and 'y' are first (heatmap x/y axes) + # Other dims (period, scenario) will be used for faceting/animation + target_order = ['time', 'y'] + [d for d in heatmap_da.dims if d not in ('time', 'y')] + heatmap_da = heatmap_da.transpose(*target_order) + + # Use fxplot.heatmap for smart defaults + fig = heatmap_da.fxplot.heatmap( + colors=colors, + title='Cluster Assignments', + facet_col=facet_col, + animation_frame=animation_frame, + aspect='auto', + **plotly_kwargs, + ) + + # Clean up: hide y-axis since it's just a single row + fig.update_yaxes(showticklabels=False) + fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1])) + + # Data is exactly what we plotted (without dummy y dimension) + cluster_da.name = 'cluster' + data = xr.Dataset({'cluster': cluster_da}) + plot_result = PlotResult(data=data, figure=fig) + + if show is None: + show = CONFIG.Plotting.default_show + if show: + plot_result.show() + + return plot_result + + def clusters( + self, + variables: str | list[str] | None = None, + *, + select: SelectType | None = None, + colors: ColorType | None = None, + color: str | None = 'auto', + facet_col: str | None = 'cluster', + facet_cols: int | None = None, + show: bool | None = None, + **plotly_kwargs: Any, + ) -> PlotResult: + """Plot each cluster's typical period profile. + + Shows each cluster as a separate faceted subplot with all variables + colored differently. Useful for understanding what each cluster represents. + + Args: + variables: Variable(s) to plot. Can be a string, list of strings, + or None to plot all time-varying variables. + select: xarray-style selection dict, e.g. {'scenario': 'Base Case'}. + colors: Color specification (colorscale name, color list, or label-to-color dict). + color: Dimension for line colors. 'auto' uses CONFIG priority (typically 'variable'). + Use 'cluster' to color by cluster instead of faceting. + facet_col: Dimension for subplot columns. Defaults to 'cluster'. + Use 'variable' to facet by variable instead. + facet_cols: Max columns before wrapping facets. + Defaults to CONFIG.Plotting.default_facet_cols. + show: Whether to display the figure. + Defaults to CONFIG.Plotting.default_show. + **plotly_kwargs: Additional arguments passed to plotly. + + Returns: + PlotResult containing the figure and underlying data. + """ + from ..config import CONFIG + from ..plot_result import PlotResult + from ..statistics_accessor import _apply_selection + + result = self._clustering.result + cs = result.cluster_structure + if result.aggregated_data is None or cs is None: + raise ValueError('No aggregated data or cluster structure available') + + # Apply selection to aggregated data + aggregated_data = _apply_selection(result.aggregated_data, select) + + time_vars = self._get_time_varying_variables() + if not time_vars: + raise ValueError('No time-varying variables found') + + # Resolve variables + resolved_variables = self._resolve_variables(variables) + + n_clusters = int(cs.n_clusters) if isinstance(cs.n_clusters, (int, np.integer)) else int(cs.n_clusters.values) + timesteps_per_cluster = cs.timesteps_per_cluster + + # Build Dataset with cluster dimension, using labels with occurrence counts + cluster_labels = [ + f'Cluster {c} (Γ—{int(cs.cluster_occurrences.sel(cluster=c).values)})' for c in range(n_clusters) + ] + + data_vars = {} + for var in resolved_variables: + data = aggregated_data[var].values + data_by_cluster = data.reshape(n_clusters, timesteps_per_cluster) + data_vars[var] = xr.DataArray( + data_by_cluster, + dims=['cluster', 'time'], + coords={'cluster': cluster_labels, 'time': range(timesteps_per_cluster)}, + ) + + ds = xr.Dataset(data_vars) + title = 'Clusters' if len(resolved_variables) > 1 else f'Clusters: {resolved_variables[0]}' + + # Use fxplot for smart defaults + fig = ds.fxplot.line( + colors=colors, + color=color, + title=title, + facet_col=facet_col, + facet_cols=facet_cols, + **plotly_kwargs, + ) + fig.update_yaxes(matches=None) + fig.for_each_annotation(lambda a: a.update(text=a.text.split('=')[-1])) + + # Include occurrences in result data + data_vars['occurrences'] = cs.cluster_occurrences + result_data = xr.Dataset(data_vars) + plot_result = PlotResult(data=result_data, figure=fig) + + if show is None: + show = CONFIG.Plotting.default_show + if show: + plot_result.show() + + return plot_result + + +@dataclass +class Clustering: + """Information about an aggregation stored on a FlowSystem. + + This is stored on the FlowSystem after aggregation to enable: + - expand_solution() to map back to original timesteps + - Statistics to properly weight results + - Inter-cluster storage linking + - Serialization/deserialization of aggregated models + + Attributes: + result: The ClusterResult from the aggregation backend. + backend_name: Name of the aggregation backend used (e.g., 'tsam', 'manual'). + metrics: Clustering quality metrics (RMSE, MAE, etc.) as xr.Dataset. + Each metric (e.g., 'RMSE', 'MAE') is a DataArray with dims + ``[time_series, period?, scenario?]``. + + Example: + >>> fs_clustered = flow_system.transform.cluster(n_clusters=8, cluster_duration='1D') + >>> fs_clustered.clustering.n_clusters + 8 + >>> fs_clustered.clustering.plot.compare() + >>> fs_clustered.clustering.plot.heatmap() + """ + + result: ClusterResult + backend_name: str = 'unknown' + metrics: xr.Dataset | None = None + + def _create_reference_structure(self) -> tuple[dict, dict[str, xr.DataArray]]: + """Create reference structure for serialization.""" + ref = {'__class__': self.__class__.__name__} + arrays = {} + + # Store nested ClusterResult + result_ref, result_arrays = self.result._create_reference_structure() + ref['result'] = result_ref + arrays.update(result_arrays) + + # Store scalar values + ref['backend_name'] = self.backend_name + + return ref, arrays + + def __repr__(self) -> str: + cs = self.result.cluster_structure + if cs is not None: + n_clusters = ( + int(cs.n_clusters) if isinstance(cs.n_clusters, (int, np.integer)) else int(cs.n_clusters.values) + ) + structure_info = f'{cs.n_original_clusters} periods β†’ {n_clusters} clusters' + else: + structure_info = 'no structure' + return f'Clustering(\n backend={self.backend_name!r}\n {structure_info}\n)' + + @property + def plot(self) -> ClusteringPlotAccessor: + """Access plotting methods for clustering visualization. + + Returns: + ClusteringPlotAccessor with compare(), heatmap(), and clusters() methods. + + Example: + >>> fs.clustering.plot.compare() # timeseries comparison + >>> fs.clustering.plot.compare(kind='duration_curve') # duration curve + >>> fs.clustering.plot.heatmap() # structure visualization + >>> fs.clustering.plot.clusters() # cluster profiles + """ + return ClusteringPlotAccessor(self) + + # Convenience properties delegating to nested objects + + @property + def cluster_order(self) -> xr.DataArray: + """Which cluster each original period belongs to.""" + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + return self.result.cluster_structure.cluster_order + + @property + def occurrences(self) -> xr.DataArray: + """How many original periods each cluster represents.""" + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + return self.result.cluster_structure.cluster_occurrences + + @property + def n_clusters(self) -> int: + """Number of clusters.""" + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + n = self.result.cluster_structure.n_clusters + return int(n) if isinstance(n, (int, np.integer)) else int(n.values) + + @property + def n_original_clusters(self) -> int: + """Number of original periods (before clustering).""" + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + return self.result.cluster_structure.n_original_clusters + + @property + def timesteps_per_period(self) -> int: + """Number of timesteps in each period/cluster. + + Alias for :attr:`timesteps_per_cluster`. + """ + return self.timesteps_per_cluster + + @property + def timesteps_per_cluster(self) -> int: + """Number of timesteps in each cluster.""" + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + return self.result.cluster_structure.timesteps_per_cluster + + @property + def timestep_mapping(self) -> xr.DataArray: + """Mapping from original timesteps to representative timestep indices.""" + return self.result.timestep_mapping + + @property + def cluster_start_positions(self) -> np.ndarray: + """Integer positions where clusters start. + + Returns the indices of the first timestep of each cluster. + Use these positions to build masks for specific use cases. + + Returns: + 1D numpy array of positions: [0, T, 2T, ...] where T = timesteps_per_period. + + Example: + For 2 clusters with 24 timesteps each: + >>> clustering.cluster_start_positions + array([0, 24]) + """ + if self.result.cluster_structure is None: + raise ValueError('No cluster_structure available') + + n_timesteps = self.n_clusters * self.timesteps_per_period + return np.arange(0, n_timesteps, self.timesteps_per_period) + + @property + def original_timesteps(self) -> pd.DatetimeIndex: + """Original timesteps before clustering. + + Derived from the 'original_time' coordinate of timestep_mapping. + + Raises: + KeyError: If 'original_time' coordinate is missing from timestep_mapping. + """ + if 'original_time' not in self.result.timestep_mapping.coords: + raise KeyError( + "timestep_mapping is missing 'original_time' coordinate. " + 'This may indicate corrupted or incompatible clustering results.' + ) + return pd.DatetimeIndex(self.result.timestep_mapping.coords['original_time'].values) + + +def create_cluster_structure_from_mapping( + timestep_mapping: xr.DataArray, + timesteps_per_cluster: int, +) -> ClusterStructure: + """Create ClusterStructure from a timestep mapping. + + This is a convenience function for creating ClusterStructure when you + have the timestep mapping but not the full clustering metadata. + + Args: + timestep_mapping: Mapping from original timesteps to representative indices. + timesteps_per_cluster: Number of timesteps per cluster period. + + Returns: + ClusterStructure derived from the mapping. + """ + n_original = len(timestep_mapping) + n_original_clusters = n_original // timesteps_per_cluster + + # Determine cluster order from the mapping + # Each original period maps to the cluster of its first timestep + cluster_order = [] + for p in range(n_original_clusters): + start_idx = p * timesteps_per_cluster + cluster_idx = int(timestep_mapping.isel(original_time=start_idx).values) // timesteps_per_cluster + cluster_order.append(cluster_idx) + + cluster_order_da = xr.DataArray(cluster_order, dims=['original_cluster'], name='cluster_order') + + # Count occurrences of each cluster + unique_clusters = np.unique(cluster_order) + occurrences = {} + for c in unique_clusters: + occurrences[int(c)] = sum(1 for x in cluster_order if x == c) + + n_clusters = len(unique_clusters) + cluster_occurrences_da = xr.DataArray( + [occurrences.get(c, 0) for c in range(n_clusters)], + dims=['cluster'], + name='cluster_occurrences', + ) + + return ClusterStructure( + cluster_order=cluster_order_da, + cluster_occurrences=cluster_occurrences_da, + n_clusters=n_clusters, + timesteps_per_cluster=timesteps_per_cluster, + ) + + +def _register_clustering_classes(): + """Register clustering classes for IO. + + Called from flow_system.py after all imports are complete to avoid circular imports. + """ + from ..structure import CLASS_REGISTRY + + CLASS_REGISTRY['ClusterStructure'] = ClusterStructure + CLASS_REGISTRY['ClusterResult'] = ClusterResult + CLASS_REGISTRY['Clustering'] = Clustering diff --git a/flixopt/clustering/intercluster_helpers.py b/flixopt/clustering/intercluster_helpers.py new file mode 100644 index 000000000..a89a80862 --- /dev/null +++ b/flixopt/clustering/intercluster_helpers.py @@ -0,0 +1,178 @@ +"""Helper utilities for inter-cluster storage linking. + +This module provides utilities for building inter-cluster storage linking +constraints following the S-N model from Blanke et al. (2022). + +Background +---------- +When time series are clustered (aggregated into representative periods), storage +behavior needs special handling. The S-N linking model introduces: + +- **SOC_boundary**: Absolute state-of-charge at the boundary between original periods. + With N original periods, there are N+1 boundary points. + +- **Linking**: SOC_boundary[d+1] = SOC_boundary[d] + delta_SOC[cluster_order[d]] + Each boundary is connected to the next via the net charge change of the + representative cluster for that period. + +These utilities help construct the coordinates and bounds for SOC_boundary variables. + +References +---------- +- Blanke, T., et al. (2022). "Inter-Cluster Storage Linking for Time Series + Aggregation in Energy System Optimization Models." +- Kotzur, L., et al. (2018). "Time series aggregation for energy system design: + Modeling seasonal storage." + +See Also +-------- +:class:`flixopt.components.InterclusterStorageModel` + The storage model that uses these utilities. +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +import numpy as np +import xarray as xr + +if TYPE_CHECKING: + from ..flow_system import FlowSystem + from ..interface import InvestParameters + + +@dataclass +class CapacityBounds: + """Bounds for SOC_boundary variable creation. + + This dataclass holds the lower and upper bounds for the SOC_boundary variable, + along with a flag indicating whether investment sizing is used. + + Attributes: + lower: Lower bound DataArray (typically zeros). + upper: Upper bound DataArray (capacity or maximum investment size). + has_investment: True if the storage uses InvestParameters for sizing. + """ + + lower: xr.DataArray + upper: xr.DataArray + has_investment: bool + + +def extract_capacity_bounds( + capacity_param: InvestParameters | int | float | None, + boundary_coords: dict, + boundary_dims: list[str], +) -> CapacityBounds: + """Extract capacity bounds from storage parameters for SOC_boundary variable. + + This function determines the appropriate bounds for the SOC_boundary variable + based on the storage's capacity parameter: + + - **Fixed capacity** (numeric): Upper bound is the fixed value. + - **InvestParameters**: Upper bound is maximum_size (or fixed_size if set). + The actual bound is enforced via separate constraints linked to investment.size. + - **None/Unbounded**: Upper bound is set to a large value (1e9). + + The lower bound is always zero (SOC cannot be negative). + + Args: + capacity_param: Storage capacity specification. Can be: + - Numeric (int/float): Fixed capacity + - InvestParameters: Investment-based sizing with min/max + - None: Unbounded storage + boundary_coords: Coordinate dictionary for SOC_boundary variable. + Must contain 'cluster_boundary' key. + boundary_dims: Dimension names for SOC_boundary variable. + First dimension must be 'cluster_boundary'. + + Returns: + CapacityBounds with lower/upper bounds and investment flag. + + Example: + >>> coords, dims = build_boundary_coords(14, flow_system) + >>> bounds = extract_capacity_bounds(InvestParameters(maximum_size=10000), coords, dims) + >>> bounds.has_investment + True + >>> bounds.upper.max() + 10000.0 + """ + n_boundaries = len(boundary_coords['cluster_boundary']) + lb_shape = [n_boundaries] + [len(boundary_coords[d]) for d in boundary_dims[1:]] + + lb = xr.DataArray(np.zeros(lb_shape), coords=boundary_coords, dims=boundary_dims) + + # Determine has_investment and cap_value + has_investment = hasattr(capacity_param, 'maximum_size') + + if hasattr(capacity_param, 'fixed_size') and capacity_param.fixed_size is not None: + cap_value = capacity_param.fixed_size + elif hasattr(capacity_param, 'maximum_size') and capacity_param.maximum_size is not None: + cap_value = capacity_param.maximum_size + elif isinstance(capacity_param, (int, float)): + cap_value = capacity_param + else: + cap_value = 1e9 # Large default for unbounded case + + # Build upper bound + if isinstance(cap_value, xr.DataArray) and cap_value.dims: + ub = cap_value.expand_dims({'cluster_boundary': n_boundaries}, axis=0) + ub = ub.assign_coords(cluster_boundary=np.arange(n_boundaries)) + ub = ub.transpose('cluster_boundary', ...) + else: + if hasattr(cap_value, 'item'): + cap_value = float(cap_value.item()) + else: + cap_value = float(cap_value) + ub = xr.DataArray(np.full(lb_shape, cap_value), coords=boundary_coords, dims=boundary_dims) + + return CapacityBounds(lower=lb, upper=ub, has_investment=has_investment) + + +def build_boundary_coords( + n_original_clusters: int, + flow_system: FlowSystem, +) -> tuple[dict, list[str]]: + """Build coordinates and dimensions for SOC_boundary variable. + + Creates the coordinate dictionary and dimension list needed to create the + SOC_boundary variable. The primary dimension is 'cluster_boundary' with + N+1 values (one for each boundary between N original periods). + + Additional dimensions (period, scenario) are included if present in the + FlowSystem, ensuring the SOC_boundary variable has the correct shape for + multi-period or stochastic optimizations. + + Args: + n_original_clusters: Number of original (non-aggregated) time periods. + For example, if a year is clustered into 8 typical days but originally + had 365 days, this would be 365. + flow_system: The FlowSystem containing optional period/scenario dimensions. + + Returns: + Tuple of (coords, dims) where: + - coords: Dictionary mapping dimension names to coordinate arrays + - dims: List of dimension names in order + + Example: + >>> coords, dims = build_boundary_coords(14, flow_system) + >>> dims + ['cluster_boundary'] # or ['cluster_boundary', 'period'] if periods exist + >>> coords['cluster_boundary'] + array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]) + """ + n_boundaries = n_original_clusters + 1 + coords = {'cluster_boundary': np.arange(n_boundaries)} + dims = ['cluster_boundary'] + + if flow_system.periods is not None: + dims.append('period') + coords['period'] = np.array(list(flow_system.periods)) + + if flow_system.scenarios is not None: + dims.append('scenario') + coords['scenario'] = np.array(list(flow_system.scenarios)) + + return coords, dims diff --git a/flixopt/comparison.py b/flixopt/comparison.py new file mode 100644 index 000000000..63a00a0f1 --- /dev/null +++ b/flixopt/comparison.py @@ -0,0 +1,609 @@ +"""Compare multiple FlowSystems side-by-side.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +import xarray as xr + +from .config import CONFIG +from .plot_result import PlotResult + +if TYPE_CHECKING: + from .flow_system import FlowSystem + +__all__ = ['Comparison'] + +# Type aliases (matching statistics_accessor.py) +SelectType = dict[str, Any] +FilterType = str | list[str] +ColorType = str | list[str] | dict[str, str] | None + + +class Comparison: + """Compare multiple FlowSystems side-by-side. + + Combines solutions and statistics from multiple FlowSystems into unified + xarray Datasets with a 'case' dimension. The existing plotting infrastructure + automatically handles faceting by the 'case' dimension. + + All FlowSystems must have matching dimensions (time, period, scenario, etc.). + Use `flow_system.transform.sel()` to align dimensions before comparing. + + Args: + flow_systems: List of FlowSystems to compare. All must be optimized + and have matching dimensions. + names: Optional names for each case. If None, uses FlowSystem.name. + + Raises: + ValueError: If FlowSystems have mismatched dimensions. + RuntimeError: If any FlowSystem has no solution. + + Examples: + ```python + # Compare two systems (uses FlowSystem.name by default) + comp = fx.Comparison([fs_base, fs_modified]) + + # Or with custom names + comp = fx.Comparison([fs_base, fs_modified], names=['baseline', 'modified']) + + # Side-by-side plots (auto-facets by 'case') + comp.statistics.plot.balance('Heat') + comp.statistics.flow_rates.fxplot.line() + + # Access combined data + comp.solution # xr.Dataset with 'case' dimension + comp.statistics.flow_rates # xr.Dataset with 'case' dimension + + # Compute differences relative to first case + comp.diff() # Returns xr.Dataset of differences + comp.diff('baseline') # Or specify reference by name + + # For systems with different dimensions, align first: + fs_both = ... # Has scenario dimension + fs_mild = fs_both.transform.sel(scenario='Mild') # Select one scenario + fs_other = ... # Also select to match + comp = fx.Comparison([fs_mild, fs_other]) # Now dimensions match + ``` + """ + + def __init__(self, flow_systems: list[FlowSystem], names: list[str] | None = None) -> None: + if len(flow_systems) < 2: + raise ValueError('Comparison requires at least 2 FlowSystems') + + self._systems = flow_systems + self._names = names or [fs.name or f'System {i}' for i, fs in enumerate(flow_systems)] + + if len(self._names) != len(self._systems): + raise ValueError( + f'Number of names ({len(self._names)}) must match number of FlowSystems ({len(self._systems)})' + ) + + if len(set(self._names)) != len(self._names): + raise ValueError(f'Case names must be unique, got: {self._names}') + + # Validate all FlowSystems have solutions + for fs in flow_systems: + if fs.solution is None: + raise RuntimeError(f"FlowSystem '{fs.name}' has no solution. Run optimize() first.") + + # Validate matching dimensions across all FlowSystems + self._validate_matching_dimensions() + + # Caches + self._solution: xr.Dataset | None = None + self._statistics: ComparisonStatistics | None = None + + # Core dimensions that must match across FlowSystems + # Note: 'cluster' and 'cluster_boundary' are auxiliary dimensions from clustering + _CORE_DIMS = {'time', 'period', 'scenario'} + + def _validate_matching_dimensions(self) -> None: + """Validate that all FlowSystems have matching core dimensions. + + Only validates core dimensions (time, period, scenario). Auxiliary + dimensions like 'cluster_boundary' are ignored as they don't affect + the comparison logic. + """ + reference = self._systems[0] + ref_core_dims = set(reference.solution.dims) & self._CORE_DIMS + ref_name = self._names[0] + + for fs, name in zip(self._systems[1:], self._names[1:], strict=True): + fs_core_dims = set(fs.solution.dims) & self._CORE_DIMS + if fs_core_dims != ref_core_dims: + missing = ref_core_dims - fs_core_dims + extra = fs_core_dims - ref_core_dims + msg_parts = [f"Core dimension mismatch between '{ref_name}' and '{name}'."] + if missing: + msg_parts.append(f"Missing in '{name}': {missing}.") + if extra: + msg_parts.append(f"Extra in '{name}': {extra}.") + msg_parts.append('Use .transform.sel() to align dimensions before comparing.') + raise ValueError(' '.join(msg_parts)) + + @property + def names(self) -> list[str]: + """Case names for each FlowSystem.""" + return self._names + + @property + def solution(self) -> xr.Dataset: + """Combined solution Dataset with 'case' dimension.""" + if self._solution is None: + datasets = [] + for fs, name in zip(self._systems, self._names, strict=True): + ds = fs.solution.expand_dims(case=[name]) + datasets.append(ds) + self._solution = xr.concat(datasets, dim='case', join='outer', fill_value=float('nan')) + return self._solution + + @property + def statistics(self) -> ComparisonStatistics: + """Combined statistics accessor with 'case' dimension.""" + if self._statistics is None: + self._statistics = ComparisonStatistics(self) + return self._statistics + + def diff(self, reference: str | int = 0) -> xr.Dataset: + """Compute differences relative to a reference case. + + Args: + reference: Reference case name or index (default: 0, first case). + + Returns: + Dataset with differences (each case minus reference). + """ + if isinstance(reference, str): + if reference not in self._names: + raise ValueError(f"Reference '{reference}' not found. Available: {self._names}") + ref_idx = self._names.index(reference) + else: + ref_idx = reference + + ref_data = self.solution.isel(case=ref_idx) + return self.solution - ref_data + + +class ComparisonStatistics: + """Combined statistics accessor for comparing FlowSystems. + + Mirrors StatisticsAccessor properties, concatenating data with a 'case' dimension. + Access via ``Comparison.statistics``. + """ + + def __init__(self, comparison: Comparison) -> None: + self._comp = comparison + # Caches for dataset properties + self._flow_rates: xr.Dataset | None = None + self._flow_hours: xr.Dataset | None = None + self._flow_sizes: xr.Dataset | None = None + self._storage_sizes: xr.Dataset | None = None + self._sizes: xr.Dataset | None = None + self._charge_states: xr.Dataset | None = None + self._temporal_effects: xr.Dataset | None = None + self._periodic_effects: xr.Dataset | None = None + self._total_effects: xr.Dataset | None = None + # Caches for dict properties + self._carrier_colors: dict[str, str] | None = None + self._component_colors: dict[str, str] | None = None + self._bus_colors: dict[str, str] | None = None + self._carrier_units: dict[str, str] | None = None + self._effect_units: dict[str, str] | None = None + # Plot accessor + self._plot: ComparisonStatisticsPlot | None = None + + def _concat_property(self, prop_name: str) -> xr.Dataset: + """Concatenate a statistics property across all cases.""" + datasets = [] + for fs, name in zip(self._comp._systems, self._comp._names, strict=True): + ds = getattr(fs.statistics, prop_name) + datasets.append(ds.expand_dims(case=[name])) + return xr.concat(datasets, dim='case', join='outer', fill_value=float('nan')) + + def _merge_dict_property(self, prop_name: str) -> dict[str, str]: + """Merge a dict property from all cases (later cases override).""" + result: dict[str, str] = {} + for fs in self._comp._systems: + result.update(getattr(fs.statistics, prop_name)) + return result + + @property + def flow_rates(self) -> xr.Dataset: + """Combined flow rates with 'case' dimension.""" + if self._flow_rates is None: + self._flow_rates = self._concat_property('flow_rates') + return self._flow_rates + + @property + def flow_hours(self) -> xr.Dataset: + """Combined flow hours (energy) with 'case' dimension.""" + if self._flow_hours is None: + self._flow_hours = self._concat_property('flow_hours') + return self._flow_hours + + @property + def flow_sizes(self) -> xr.Dataset: + """Combined flow investment sizes with 'case' dimension.""" + if self._flow_sizes is None: + self._flow_sizes = self._concat_property('flow_sizes') + return self._flow_sizes + + @property + def storage_sizes(self) -> xr.Dataset: + """Combined storage capacity sizes with 'case' dimension.""" + if self._storage_sizes is None: + self._storage_sizes = self._concat_property('storage_sizes') + return self._storage_sizes + + @property + def sizes(self) -> xr.Dataset: + """Combined sizes (flow + storage) with 'case' dimension.""" + if self._sizes is None: + self._sizes = self._concat_property('sizes') + return self._sizes + + @property + def charge_states(self) -> xr.Dataset: + """Combined storage charge states with 'case' dimension.""" + if self._charge_states is None: + self._charge_states = self._concat_property('charge_states') + return self._charge_states + + @property + def temporal_effects(self) -> xr.Dataset: + """Combined temporal effects with 'case' dimension.""" + if self._temporal_effects is None: + self._temporal_effects = self._concat_property('temporal_effects') + return self._temporal_effects + + @property + def periodic_effects(self) -> xr.Dataset: + """Combined periodic effects with 'case' dimension.""" + if self._periodic_effects is None: + self._periodic_effects = self._concat_property('periodic_effects') + return self._periodic_effects + + @property + def total_effects(self) -> xr.Dataset: + """Combined total effects with 'case' dimension.""" + if self._total_effects is None: + self._total_effects = self._concat_property('total_effects') + return self._total_effects + + @property + def carrier_colors(self) -> dict[str, str]: + """Merged carrier colors from all cases.""" + if self._carrier_colors is None: + self._carrier_colors = self._merge_dict_property('carrier_colors') + return self._carrier_colors + + @property + def component_colors(self) -> dict[str, str]: + """Merged component colors from all cases.""" + if self._component_colors is None: + self._component_colors = self._merge_dict_property('component_colors') + return self._component_colors + + @property + def bus_colors(self) -> dict[str, str]: + """Merged bus colors from all cases.""" + if self._bus_colors is None: + self._bus_colors = self._merge_dict_property('bus_colors') + return self._bus_colors + + @property + def carrier_units(self) -> dict[str, str]: + """Merged carrier units from all cases.""" + if self._carrier_units is None: + self._carrier_units = self._merge_dict_property('carrier_units') + return self._carrier_units + + @property + def effect_units(self) -> dict[str, str]: + """Merged effect units from all cases.""" + if self._effect_units is None: + self._effect_units = self._merge_dict_property('effect_units') + return self._effect_units + + @property + def plot(self) -> ComparisonStatisticsPlot: + """Access plot methods for comparison statistics.""" + if self._plot is None: + self._plot = ComparisonStatisticsPlot(self) + return self._plot + + +class ComparisonStatisticsPlot: + """Plot accessor for comparison statistics. + + Wraps StatisticsPlotAccessor methods, combining data from all FlowSystems + with a 'case' dimension for faceting. + """ + + # Data-related kwargs for each method (everything else is plotly kwargs) + _DATA_KWARGS: dict[str, set[str]] = { + 'balance': {'select', 'include', 'exclude', 'unit'}, + 'carrier_balance': {'select', 'include', 'exclude', 'unit'}, + 'flows': {'start', 'end', 'component', 'select', 'unit'}, + 'storage': {'select', 'unit', 'charge_state_color'}, + 'charge_states': {'select'}, + 'duration_curve': {'select', 'normalize'}, + 'sizes': {'max_size', 'select'}, + 'effects': {'effect', 'by', 'select'}, + 'heatmap': {'select', 'reshape'}, + } + + def __init__(self, statistics: ComparisonStatistics) -> None: + self._stats = statistics + self._comp = statistics._comp + + def _split_kwargs(self, method_name: str, kwargs: dict) -> tuple[dict, dict]: + """Split kwargs into data kwargs and plotly kwargs.""" + data_keys = self._DATA_KWARGS.get(method_name, set()) + data_kwargs = {k: v for k, v in kwargs.items() if k in data_keys} + plotly_kwargs = {k: v for k, v in kwargs.items() if k not in data_keys} + return data_kwargs, plotly_kwargs + + def _combine_data(self, method_name: str, *args, **kwargs) -> tuple[xr.Dataset, str]: + """Call plot method on each system and combine data. Returns (combined_data, title).""" + datasets = [] + title = '' + kwargs = {**kwargs, 'show': False} # Don't mutate original + + for fs, case_name in zip(self._comp._systems, self._comp._names, strict=True): + try: + result = getattr(fs.statistics.plot, method_name)(*args, **kwargs) + datasets.append(result.data.expand_dims(case=[case_name])) + title = result.figure.layout.title.text or title + except (KeyError, ValueError): + continue + + if not datasets: + return xr.Dataset(), '' + + return xr.concat(datasets, dim='case', join='outer', fill_value=float('nan')), title + + def _finalize(self, ds: xr.Dataset, fig, show: bool | None) -> PlotResult: + """Handle show and return PlotResult.""" + import plotly.graph_objects as go + + if show is None: + show = CONFIG.Plotting.default_show + if show and fig: + fig.show() + return PlotResult(data=ds, figure=fig or go.Figure()) + + def balance( + self, + node: str, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot node balance comparison. See StatisticsPlotAccessor.balance.""" + data_kw, plotly_kw = self._split_kwargs('balance', kwargs) + ds, title = self._combine_data('balance', node, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.stacked_bar( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + return self._finalize(ds, fig, show) + + def carrier_balance( + self, + carrier: str, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot carrier balance comparison. See StatisticsPlotAccessor.carrier_balance.""" + data_kw, plotly_kw = self._split_kwargs('carrier_balance', kwargs) + ds, title = self._combine_data('carrier_balance', carrier, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.stacked_bar( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + return self._finalize(ds, fig, show) + + def flows( + self, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot flows comparison. See StatisticsPlotAccessor.flows.""" + data_kw, plotly_kw = self._split_kwargs('flows', kwargs) + ds, title = self._combine_data('flows', **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.line( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + return self._finalize(ds, fig, show) + + def storage( + self, + storage: str, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot storage operation comparison. See StatisticsPlotAccessor.storage.""" + data_kw, plotly_kw = self._split_kwargs('storage', kwargs) + ds, title = self._combine_data('storage', storage, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.stacked_bar( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + return self._finalize(ds, fig, show) + + def charge_states( + self, + storages=None, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot charge states comparison. See StatisticsPlotAccessor.charge_states.""" + data_kw, plotly_kw = self._split_kwargs('charge_states', kwargs) + ds, title = self._combine_data('charge_states', storages, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.line( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + fig.update_yaxes(title_text='Charge State') + return self._finalize(ds, fig, show) + + def duration_curve( + self, + variables, + *, + normalize: bool = False, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot duration curves comparison. See StatisticsPlotAccessor.duration_curve.""" + data_kw, plotly_kw = self._split_kwargs('duration_curve', kwargs) + ds, title = self._combine_data('duration_curve', variables, normalize=normalize, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.line( + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + fig.update_xaxes(title_text='Duration [%]' if normalize else 'Timesteps') + return self._finalize(ds, fig, show) + + def sizes( + self, + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot investment sizes comparison. See StatisticsPlotAccessor.sizes.""" + data_kw, plotly_kw = self._split_kwargs('sizes', kwargs) + ds, title = self._combine_data('sizes', **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + fig = ds.fxplot.bar( + x='variable', + color='variable', + colors=colors, + title=title, + ylabel='Size', + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + return self._finalize(ds, fig, show) + + def effects( + self, + aspect='total', + *, + colors=None, + facet_col='auto', + facet_row='auto', + animation_frame='auto', + show: bool | None = None, + **kwargs, + ) -> PlotResult: + """Plot effects comparison. See StatisticsPlotAccessor.effects.""" + data_kw, plotly_kw = self._split_kwargs('effects', kwargs) + ds, title = self._combine_data('effects', aspect, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + + by = data_kw.get('by') + # After to_dataset(dim='effect'), effects become variables -> 'variable' column + x_col = by if by else 'variable' + color_col = 'variable' if len(ds.data_vars) > 1 else x_col + + fig = ds.fxplot.bar( + x=x_col, + color=color_col, + colors=colors, + title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + **plotly_kw, + ) + fig.update_layout(bargap=0, bargroupgap=0) + fig.update_traces(marker_line_width=0) + return self._finalize(ds, fig, show) + + def heatmap( + self, variables, *, colors=None, facet_col='auto', animation_frame='auto', show: bool | None = None, **kwargs + ) -> PlotResult: + """Plot heatmap comparison. See StatisticsPlotAccessor.heatmap.""" + data_kw, plotly_kw = self._split_kwargs('heatmap', kwargs) + ds, _ = self._combine_data('heatmap', variables, **data_kw) + if not ds.data_vars: + return self._finalize(ds, None, show) + da = ds[next(iter(ds.data_vars))] + fig = da.fxplot.heatmap(colors=colors, facet_col=facet_col, animation_frame=animation_frame, **plotly_kw) + return self._finalize(ds, fig, show) diff --git a/flixopt/components.py b/flixopt/components.py index 267c144af..e962791d8 100644 --- a/flixopt/components.py +++ b/flixopt/components.py @@ -5,6 +5,7 @@ from __future__ import annotations import logging +import warnings from typing import TYPE_CHECKING, Literal import numpy as np @@ -270,7 +271,7 @@ class Storage(Component): maximum_size (or fixed_size) must be explicitly set for proper model scaling. relative_minimum_charge_state: Minimum charge state (0-1). Default: 0. relative_maximum_charge_state: Maximum charge state (0-1). Default: 1. - initial_charge_state: Charge at start. Numeric or 'equals_final'. Default: 0. + initial_charge_state: Charge at start. Numeric, 'equals_final', or None (free). Default: 0. minimal_final_charge_state: Minimum absolute charge required at end (optional). maximal_final_charge_state: Maximum absolute charge allowed at end (optional). relative_minimum_final_charge_state: Minimum relative charge at end. @@ -282,6 +283,21 @@ class Storage(Component): relative_loss_per_hour: Self-discharge per hour (0-0.1). Default: 0. prevent_simultaneous_charge_and_discharge: Prevent charging and discharging simultaneously. Adds binary variables. Default: True. + cluster_mode: How this storage is treated during clustering optimization. + Only relevant when using ``transform.cluster()``. Options: + + - ``'independent'``: Clusters are fully decoupled. No constraints between + clusters, each cluster has free start/end SOC. Fast but ignores + seasonal storage value. + - ``'cyclic'``: Each cluster is self-contained. The SOC at the start of + each cluster equals its end (cluster returns to initial state). + Good for "average day" modeling. + - ``'intercluster'``: Link storage state across the original timeline using + SOC boundary variables (Kotzur et al. approach). Properly values + seasonal storage patterns. Overall SOC can drift. + - ``'intercluster_cyclic'`` (default): Like 'intercluster' but also enforces + that overall SOC returns to initial state (yearly cyclic). + meta_data: Additional information stored in results. Python native types only. Examples: @@ -388,7 +404,7 @@ def __init__( capacity_in_flow_hours: Numeric_PS | InvestParameters | None = None, relative_minimum_charge_state: Numeric_TPS = 0, relative_maximum_charge_state: Numeric_TPS = 1, - initial_charge_state: Numeric_PS | Literal['equals_final'] = 0, + initial_charge_state: Numeric_PS | Literal['equals_final'] | None = 0, minimal_final_charge_state: Numeric_PS | None = None, maximal_final_charge_state: Numeric_PS | None = None, relative_minimum_final_charge_state: Numeric_PS | None = None, @@ -398,6 +414,7 @@ def __init__( relative_loss_per_hour: Numeric_TPS = 0, prevent_simultaneous_charge_and_discharge: bool = True, balanced: bool = False, + cluster_mode: Literal['independent', 'cyclic', 'intercluster', 'intercluster_cyclic'] = 'intercluster_cyclic', meta_data: dict | None = None, ): # TODO: fixed_relative_chargeState implementieren @@ -427,10 +444,35 @@ def __init__( self.relative_loss_per_hour: Numeric_TPS = relative_loss_per_hour self.prevent_simultaneous_charge_and_discharge = prevent_simultaneous_charge_and_discharge self.balanced = balanced + self.cluster_mode = cluster_mode def create_model(self, model: FlowSystemModel) -> StorageModel: + """Create the appropriate storage model based on cluster_mode and flow system state. + + For intercluster modes ('intercluster', 'intercluster_cyclic'), uses + :class:`InterclusterStorageModel` which implements S-N linking. + For other modes, uses the base :class:`StorageModel`. + + Args: + model: The FlowSystemModel to add constraints to. + + Returns: + StorageModel or InterclusterStorageModel instance. + """ self._plausibility_checks() - self.submodel = StorageModel(model, self) + + # Use InterclusterStorageModel for intercluster modes when clustering is active + clustering = model.flow_system.clustering + is_intercluster = clustering is not None and self.cluster_mode in ( + 'intercluster', + 'intercluster_cyclic', + ) + + if is_intercluster: + self.submodel = InterclusterStorageModel(model, self) + else: + self.submodel = StorageModel(model, self) + return self.submodel def link_to_flow_system(self, flow_system, prefix: str = '') -> None: @@ -452,7 +494,7 @@ def transform_data(self) -> None: self.relative_loss_per_hour = self._fit_coords( f'{self.prefix}|relative_loss_per_hour', self.relative_loss_per_hour ) - if not isinstance(self.initial_charge_state, str): + if self.initial_charge_state is not None and not isinstance(self.initial_charge_state, str): self.initial_charge_state = self._fit_coords( f'{self.prefix}|initial_charge_state', self.initial_charge_state, dims=['period', 'scenario'] ) @@ -531,8 +573,8 @@ def _plausibility_checks(self) -> None: min_initial_at_max_capacity = maximum_capacity * self.relative_minimum_charge_state.isel(time=0) max_initial_at_min_capacity = minimum_capacity * self.relative_maximum_charge_state.isel(time=0) - # Only perform numeric comparisons if not using 'equals_final' - if not initial_equals_final: + # Only perform numeric comparisons if using a numeric initial_charge_state + if not initial_equals_final and self.initial_charge_state is not None: if (self.initial_charge_state > max_initial_at_min_capacity).any(): raise PlausibilityError( f'{self.label_full}: {self.initial_charge_state=} ' @@ -868,6 +910,10 @@ class StorageModel(ComponentModel): Mathematical Formulation: See + + Note: + This class uses a template method pattern. Subclasses (e.g., InterclusterStorageModel) + can override individual methods to customize behavior without duplicating code. """ element: Storage @@ -876,10 +922,18 @@ def __init__(self, model: FlowSystemModel, element: Storage): super().__init__(model, element) def _do_modeling(self): - """Create charge state variables, energy balance equations, and optional investment submodels""" + """Create charge state variables, energy balance equations, and optional investment submodels.""" super()._do_modeling() - - # Create variables + self._create_storage_variables() + self._add_netto_discharge_constraint() + self._add_energy_balance_constraint() + self._add_cluster_cyclic_constraint() + self._add_investment_model() + self._add_initial_final_constraints() + self._add_balanced_sizes_constraint() + + def _create_storage_variables(self): + """Create charge_state and netto_discharge variables.""" lb, ub = self._absolute_charge_state_bounds self.add_variables( lower=lb, @@ -887,35 +941,30 @@ def _do_modeling(self): coords=self._model.get_coords(extra_timestep=True), short_name='charge_state', ) - self.add_variables(coords=self._model.get_coords(), short_name='netto_discharge') - # Create constraints (can now access flow.submodel.flow_rate) - # netto_discharge: - # eq: nettoFlow(t) - discharging(t) + charging(t) = 0 + def _add_netto_discharge_constraint(self): + """Add constraint: netto_discharge = discharging - charging.""" self.add_constraints( self.netto_discharge == self.element.discharging.submodel.flow_rate - self.element.charging.submodel.flow_rate, short_name='netto_discharge', ) - charge_state = self.charge_state - rel_loss = self.element.relative_loss_per_hour - hours_per_step = self._model.hours_per_step - charge_rate = self.element.charging.submodel.flow_rate - discharge_rate = self.element.discharging.submodel.flow_rate - eff_charge = self.element.eta_charge - eff_discharge = self.element.eta_discharge + def _add_energy_balance_constraint(self): + """Add energy balance constraint linking charge states across timesteps.""" + self.add_constraints(self._build_energy_balance_lhs() == 0, short_name='charge_state') - self.add_constraints( - charge_state.isel(time=slice(1, None)) - == charge_state.isel(time=slice(None, -1)) * ((1 - rel_loss) ** hours_per_step) - + charge_rate * eff_charge * hours_per_step - - discharge_rate * hours_per_step / eff_discharge, - short_name='charge_state', - ) + def _add_cluster_cyclic_constraint(self): + """For 'cyclic' cluster mode: each cluster's start equals its end.""" + if self._model.flow_system.clusters is not None and self.element.cluster_mode == 'cyclic': + self.add_constraints( + self.charge_state.isel(time=0) == self.charge_state.isel(time=-2), + short_name='cluster_cyclic', + ) - # Create InvestmentModel and bounding constraints for investment + def _add_investment_model(self): + """Create InvestmentModel and add capacity-scaled bounds if using investment sizing.""" if isinstance(self.element.capacity_in_flow_hours, InvestParameters): self.add_submodels( InvestmentModel( @@ -926,7 +975,6 @@ def _do_modeling(self): ), short_name='investment', ) - BoundingPatterns.scaled_bounds( self, variable=self.charge_state, @@ -934,22 +982,28 @@ def _do_modeling(self): relative_bounds=self._relative_charge_state_bounds, ) - # Initial charge state - self._initial_and_final_charge_state() + def _add_initial_final_constraints(self): + """Add initial and final charge state constraints. - # Balanced sizes - if self.element.balanced: - self.add_constraints( - self.element.charging.submodel._investment.size * 1 - == self.element.discharging.submodel._investment.size * 1, - short_name='balanced_sizes', - ) + For clustered systems with 'independent' or 'cyclic' mode, these constraints + are skipped because: + - 'independent': Each cluster has free start/end SOC + - 'cyclic': Start == end is handled by _add_cluster_cyclic_constraint, + but no specific initial value is enforced + """ + # Skip initial/final constraints for clustered systems with independent/cyclic mode + # These modes should have free or cyclic SOC, not a fixed initial value per cluster + if self._model.flow_system.clusters is not None and self.element.cluster_mode in ( + 'independent', + 'cyclic', + ): + return - def _initial_and_final_charge_state(self): if self.element.initial_charge_state is not None: if isinstance(self.element.initial_charge_state, str): self.add_constraints( - self.charge_state.isel(time=0) == self.charge_state.isel(time=-1), short_name='initial_charge_state' + self.charge_state.isel(time=0) == self.charge_state.isel(time=-1), + short_name='initial_charge_state', ) else: self.add_constraints( @@ -969,21 +1023,73 @@ def _initial_and_final_charge_state(self): short_name='final_charge_min', ) + def _add_balanced_sizes_constraint(self): + """Add constraint ensuring charging and discharging capacities are equal.""" + if self.element.balanced: + self.add_constraints( + self.element.charging.submodel._investment.size * 1 + == self.element.discharging.submodel._investment.size * 1, + short_name='balanced_sizes', + ) + + def _build_energy_balance_lhs(self): + """Build the left-hand side of the energy balance constraint. + + The energy balance equation is: + charge_state[t+1] = charge_state[t] * (1 - loss)^dt + + charge_rate * eta_charge * dt + - discharge_rate / eta_discharge * dt + + Rearranged as LHS = 0: + charge_state[t+1] - charge_state[t] * (1 - loss)^dt + - charge_rate * eta_charge * dt + + discharge_rate / eta_discharge * dt = 0 + + Returns: + The LHS expression (should equal 0). + """ + charge_state = self.charge_state + rel_loss = self.element.relative_loss_per_hour + timestep_duration = self._model.timestep_duration + charge_rate = self.element.charging.submodel.flow_rate + discharge_rate = self.element.discharging.submodel.flow_rate + eff_charge = self.element.eta_charge + eff_discharge = self.element.eta_discharge + + return ( + charge_state.isel(time=slice(1, None)) + - charge_state.isel(time=slice(None, -1)) * ((1 - rel_loss) ** timestep_duration) + - charge_rate * eff_charge * timestep_duration + + discharge_rate * timestep_duration / eff_discharge + ) + @property def _absolute_charge_state_bounds(self) -> tuple[xr.DataArray, xr.DataArray]: + """Get absolute bounds for charge_state variable. + + For base StorageModel, charge_state represents absolute SOC with bounds + derived from relative bounds scaled by capacity. + + Note: + InterclusterStorageModel overrides this to provide symmetric bounds + since charge_state represents Ξ”E (relative change from cluster start). + """ relative_lower_bound, relative_upper_bound = self._relative_charge_state_bounds + if self.element.capacity_in_flow_hours is None: - # Unbounded storage: lower bound is 0, upper bound is infinite - return (0, np.inf) + return 0, np.inf elif isinstance(self.element.capacity_in_flow_hours, InvestParameters): + cap_min = self.element.capacity_in_flow_hours.minimum_or_fixed_size + cap_max = self.element.capacity_in_flow_hours.maximum_or_fixed_size return ( - relative_lower_bound * self.element.capacity_in_flow_hours.minimum_or_fixed_size, - relative_upper_bound * self.element.capacity_in_flow_hours.maximum_or_fixed_size, + relative_lower_bound * cap_min, + relative_upper_bound * cap_max, ) else: + cap = self.element.capacity_in_flow_hours return ( - relative_lower_bound * self.element.capacity_in_flow_hours, - relative_upper_bound * self.element.capacity_in_flow_hours, + relative_lower_bound * cap, + relative_upper_bound * cap, ) @property @@ -1038,6 +1144,411 @@ def netto_discharge(self) -> linopy.Variable: return self['netto_discharge'] +class InterclusterStorageModel(StorageModel): + """Storage model with inter-cluster linking for clustered optimization. + + This class extends :class:`StorageModel` to support inter-cluster storage linking + when using time series aggregation (clustering). It implements the S-N linking model + from Blanke et al. (2022) to properly value seasonal storage in clustered optimizations. + + The Problem with Naive Clustering + --------------------------------- + When time series are clustered (e.g., 365 days β†’ 8 typical days), storage behavior + is fundamentally misrepresented if each cluster operates independently: + + - **Seasonal patterns are lost**: A battery might charge in summer and discharge in + winter, but with independent clusters, each "typical summer day" cannot transfer + energy to the "typical winter day". + - **Storage value is underestimated**: Without inter-cluster linking, storage can only + provide intra-day flexibility, not seasonal arbitrage. + + The S-N Linking Model + --------------------- + This model introduces two key concepts: + + 1. **SOC_boundary**: Absolute state-of-charge at the boundary between original periods. + With N original periods, there are N+1 boundary points (including start and end). + + 2. **charge_state (Ξ”E)**: Relative change in SOC within each representative cluster, + measured from the cluster start (where Ξ”E = 0). + + The actual SOC at any timestep t within original period d is:: + + SOC(t) = SOC_boundary[d] + Ξ”E(t) + + Key Constraints + --------------- + 1. **Cluster start constraint**: ``Ξ”E(cluster_start) = 0`` + Each representative cluster starts with zero relative charge. + + 2. **Linking constraint**: ``SOC_boundary[d+1] = SOC_boundary[d] + delta_SOC[cluster_order[d]]`` + The boundary SOC after period d equals the boundary before plus the net + charge/discharge of the representative cluster for that period. + + 3. **Combined bounds**: ``0 ≀ SOC_boundary[d] + Ξ”E(t) ≀ capacity`` + The actual SOC must stay within physical bounds. + + 4. **Cyclic constraint** (for ``intercluster_cyclic`` mode): + ``SOC_boundary[0] = SOC_boundary[N]`` + The storage returns to its initial state over the full time horizon. + + Variables Created + ----------------- + - ``SOC_boundary``: Absolute SOC at each original period boundary. + Shape: (n_original_clusters + 1,) plus any period/scenario dimensions. + + Constraints Created + ------------------- + - ``cluster_start``: Forces Ξ”E = 0 at start of each representative cluster. + - ``link``: Links consecutive SOC_boundary values via delta_SOC. + - ``cyclic`` or ``initial_SOC_boundary``: Initial/final boundary condition. + - ``soc_lb_start/mid/end``: Lower bound on combined SOC at sample points. + - ``soc_ub_start/mid/end``: Upper bound on combined SOC (if investment). + - ``SOC_boundary_ub``: Links SOC_boundary to investment size (if investment). + - ``charge_state|lb/ub``: Symmetric bounds on Ξ”E for intercluster modes. + + References + ---------- + - Blanke, T., et al. (2022). "Inter-Cluster Storage Linking for Time Series + Aggregation in Energy System Optimization Models." + - Kotzur, L., et al. (2018). "Time series aggregation for energy system design: + Modeling seasonal storage." + + See Also + -------- + :class:`StorageModel` : Base storage model without inter-cluster linking. + :class:`Storage` : The element class that creates this model. + + Example + ------- + The model is automatically used when a Storage has ``cluster_mode='intercluster'`` + or ``cluster_mode='intercluster_cyclic'`` and the FlowSystem has been clustered:: + + storage = Storage( + label='seasonal_storage', + charging=charge_flow, + discharging=discharge_flow, + capacity_in_flow_hours=InvestParameters(maximum_size=10000), + cluster_mode='intercluster_cyclic', # Enable inter-cluster linking + ) + + # Cluster the flow system + fs_clustered = flow_system.transform.cluster(n_clusters=8) + fs_clustered.optimize(solver) + + # Access the SOC_boundary in results + soc_boundary = fs_clustered.solution['seasonal_storage|SOC_boundary'] + """ + + @property + def _absolute_charge_state_bounds(self) -> tuple[xr.DataArray, xr.DataArray]: + """Get symmetric bounds for charge_state (Ξ”E) variable. + + For InterclusterStorageModel, charge_state represents Ξ”E (relative change + from cluster start), which can be negative. Therefore, we need symmetric + bounds: -capacity <= Ξ”E <= capacity. + + Note that for investment-based sizing, additional constraints are added + in _add_investment_model to link bounds to the actual investment size. + """ + _, relative_upper_bound = self._relative_charge_state_bounds + + if self.element.capacity_in_flow_hours is None: + return -np.inf, np.inf + elif isinstance(self.element.capacity_in_flow_hours, InvestParameters): + cap_max = self.element.capacity_in_flow_hours.maximum_or_fixed_size * relative_upper_bound + # Adding 0.0 converts -0.0 to 0.0 (linopy LP writer bug workaround) + return -cap_max + 0.0, cap_max + 0.0 + else: + cap = self.element.capacity_in_flow_hours * relative_upper_bound + # Adding 0.0 converts -0.0 to 0.0 (linopy LP writer bug workaround) + return -cap + 0.0, cap + 0.0 + + def _do_modeling(self): + """Create storage model with inter-cluster linking constraints. + + Uses template method pattern: calls parent's _do_modeling, then adds + inter-cluster linking. Overrides specific methods to customize behavior. + """ + super()._do_modeling() + self._add_intercluster_linking() + + def _add_cluster_cyclic_constraint(self): + """Skip cluster cyclic constraint - handled by inter-cluster linking.""" + pass + + def _add_investment_model(self): + """Create InvestmentModel with symmetric bounds for Ξ”E.""" + if isinstance(self.element.capacity_in_flow_hours, InvestParameters): + self.add_submodels( + InvestmentModel( + model=self._model, + label_of_element=self.label_of_element, + label_of_model=self.label_of_element, + parameters=self.element.capacity_in_flow_hours, + ), + short_name='investment', + ) + # Symmetric bounds: -size <= charge_state <= size + self.add_constraints( + self.charge_state >= -self.investment.size, + short_name='charge_state|lb', + ) + self.add_constraints( + self.charge_state <= self.investment.size, + short_name='charge_state|ub', + ) + + def _add_initial_final_constraints(self): + """Skip initial/final constraints - handled by SOC_boundary in inter-cluster linking.""" + pass + + def _add_intercluster_linking(self) -> None: + """Add inter-cluster storage linking following the S-K model from Blanke et al. (2022). + + This method implements the core inter-cluster linking logic: + + 1. Constrains charge_state (Ξ”E) at each cluster start to 0 + 2. Creates SOC_boundary variables to track absolute SOC at period boundaries + 3. Links boundaries via Eq. 5: SOC_boundary[d+1] = SOC_boundary[d] * (1-loss)^N + delta_SOC + 4. Adds combined bounds per Eq. 9: 0 ≀ SOC_boundary * (1-loss)^t + Ξ”E ≀ capacity + 5. Enforces initial/cyclic constraint on SOC_boundary + """ + from .clustering.intercluster_helpers import ( + build_boundary_coords, + extract_capacity_bounds, + ) + + clustering = self._model.flow_system.clustering + if clustering is None or clustering.result.cluster_structure is None: + return + + cluster_structure = clustering.result.cluster_structure + n_clusters = ( + int(cluster_structure.n_clusters) + if isinstance(cluster_structure.n_clusters, (int, np.integer)) + else int(cluster_structure.n_clusters.values) + ) + timesteps_per_cluster = cluster_structure.timesteps_per_cluster + n_original_clusters = cluster_structure.n_original_clusters + cluster_order = cluster_structure.cluster_order + + # 1. Constrain Ξ”E = 0 at cluster starts + self._add_cluster_start_constraints(n_clusters, timesteps_per_cluster) + + # 2. Create SOC_boundary variable + flow_system = self._model.flow_system + boundary_coords, boundary_dims = build_boundary_coords(n_original_clusters, flow_system) + capacity_bounds = extract_capacity_bounds(self.element.capacity_in_flow_hours, boundary_coords, boundary_dims) + + soc_boundary = self.add_variables( + lower=capacity_bounds.lower, + upper=capacity_bounds.upper, + coords=boundary_coords, + dims=boundary_dims, + short_name='SOC_boundary', + ) + + # 3. Link SOC_boundary to investment size + if capacity_bounds.has_investment and self.investment is not None: + self.add_constraints( + soc_boundary <= self.investment.size, + short_name='SOC_boundary_ub', + ) + + # 4. Compute delta_SOC for each cluster + delta_soc = self._compute_delta_soc(n_clusters, timesteps_per_cluster) + + # 5. Add linking constraints + self._add_linking_constraints( + soc_boundary, delta_soc, cluster_order, n_original_clusters, timesteps_per_cluster + ) + + # 6. Add cyclic or initial constraint + if self.element.cluster_mode == 'intercluster_cyclic': + self.add_constraints( + soc_boundary.isel(cluster_boundary=0) == soc_boundary.isel(cluster_boundary=n_original_clusters), + short_name='cyclic', + ) + else: + # Apply initial_charge_state to SOC_boundary[0] + initial = self.element.initial_charge_state + if initial is not None: + if isinstance(initial, str): + # 'equals_final' means cyclic + self.add_constraints( + soc_boundary.isel(cluster_boundary=0) + == soc_boundary.isel(cluster_boundary=n_original_clusters), + short_name='initial_SOC_boundary', + ) + else: + self.add_constraints( + soc_boundary.isel(cluster_boundary=0) == initial, + short_name='initial_SOC_boundary', + ) + + # 7. Add combined bound constraints + self._add_combined_bound_constraints( + soc_boundary, + cluster_order, + capacity_bounds.has_investment, + n_original_clusters, + timesteps_per_cluster, + ) + + def _add_cluster_start_constraints(self, n_clusters: int, timesteps_per_cluster: int) -> None: + """Constrain Ξ”E = 0 at the start of each representative cluster. + + This ensures that the relative charge state is measured from a known + reference point (the cluster start). + + With 2D (cluster, time) structure, time=0 is the start of every cluster, + so we simply select isel(time=0) which broadcasts across the cluster dimension. + + Args: + n_clusters: Number of representative clusters (unused with 2D structure). + timesteps_per_cluster: Timesteps in each cluster (unused with 2D structure). + """ + # With 2D structure: time=0 is start of every cluster + self.add_constraints( + self.charge_state.isel(time=0) == 0, + short_name='cluster_start', + ) + + def _compute_delta_soc(self, n_clusters: int, timesteps_per_cluster: int) -> xr.DataArray: + """Compute net SOC change (delta_SOC) for each representative cluster. + + The delta_SOC is the difference between the charge_state at the end + and start of each cluster: delta_SOC[c] = Ξ”E(end_c) - Ξ”E(start_c). + + Since Ξ”E(start) = 0 by constraint, this simplifies to delta_SOC[c] = Ξ”E(end_c). + + With 2D (cluster, time) structure, we can simply select isel(time=-1) and isel(time=0), + which already have the 'cluster' dimension. + + Args: + n_clusters: Number of representative clusters (unused with 2D structure). + timesteps_per_cluster: Timesteps in each cluster (unused with 2D structure). + + Returns: + DataArray with 'cluster' dimension containing delta_SOC for each cluster. + """ + # With 2D structure: result already has cluster dimension + return self.charge_state.isel(time=-1) - self.charge_state.isel(time=0) + + def _add_linking_constraints( + self, + soc_boundary: xr.DataArray, + delta_soc: xr.DataArray, + cluster_order: xr.DataArray, + n_original_clusters: int, + timesteps_per_cluster: int, + ) -> None: + """Add constraints linking consecutive SOC_boundary values. + + Per Blanke et al. (2022) Eq. 5, implements: + SOC_boundary[d+1] = SOC_boundary[d] * (1-loss)^N + delta_SOC[cluster_order[d]] + + where N is timesteps_per_cluster and loss is self-discharge rate per timestep. + + This connects the SOC at the end of original period d to the SOC at the + start of period d+1, accounting for self-discharge decay over the period. + + Args: + soc_boundary: SOC_boundary variable. + delta_soc: Net SOC change per cluster. + cluster_order: Mapping from original periods to representative clusters. + n_original_clusters: Number of original (non-clustered) periods. + timesteps_per_cluster: Number of timesteps in each cluster period. + """ + soc_after = soc_boundary.isel(cluster_boundary=slice(1, None)) + soc_before = soc_boundary.isel(cluster_boundary=slice(None, -1)) + + # Rename for alignment + soc_after = soc_after.rename({'cluster_boundary': 'original_cluster'}) + soc_after = soc_after.assign_coords(original_cluster=np.arange(n_original_clusters)) + soc_before = soc_before.rename({'cluster_boundary': 'original_cluster'}) + soc_before = soc_before.assign_coords(original_cluster=np.arange(n_original_clusters)) + + # Get delta_soc for each original period using cluster_order + delta_soc_ordered = delta_soc.isel(cluster=cluster_order) + + # Apply self-discharge decay factor (1-loss)^N to soc_before per Eq. 5 + # Use mean over time (linking operates at period level, not timestep) + # Keep as DataArray to respect per-period/scenario values + rel_loss = self.element.relative_loss_per_hour.mean('time') + decay_n = (1 - rel_loss) ** timesteps_per_cluster + + lhs = soc_after - soc_before * decay_n - delta_soc_ordered + self.add_constraints(lhs == 0, short_name='link') + + def _add_combined_bound_constraints( + self, + soc_boundary: xr.DataArray, + cluster_order: xr.DataArray, + has_investment: bool, + n_original_clusters: int, + timesteps_per_cluster: int, + ) -> None: + """Add constraints ensuring actual SOC stays within bounds. + + Per Blanke et al. (2022) Eq. 9, the actual SOC at time t in period d is: + SOC(t) = SOC_boundary[d] * (1-loss)^t + Ξ”E(t) + + This must satisfy: 0 ≀ SOC(t) ≀ capacity + + Since checking every timestep is expensive, we sample at the start, + middle, and end of each cluster. + + With 2D (cluster, time) structure, we simply select charge_state at a + given time offset, then reorder by cluster_order to get original_cluster order. + + Args: + soc_boundary: SOC_boundary variable. + cluster_order: Mapping from original periods to clusters. + has_investment: Whether the storage has investment sizing. + n_original_clusters: Number of original periods. + timesteps_per_cluster: Timesteps in each cluster. + """ + charge_state = self.charge_state + + # soc_d: SOC at start of each original period + soc_d = soc_boundary.isel(cluster_boundary=slice(None, -1)) + soc_d = soc_d.rename({'cluster_boundary': 'original_cluster'}) + soc_d = soc_d.assign_coords(original_cluster=np.arange(n_original_clusters)) + + # Get self-discharge rate for decay calculation + # Keep as DataArray to respect per-period/scenario values + rel_loss = self.element.relative_loss_per_hour.mean('time') + + sample_offsets = [0, timesteps_per_cluster // 2, timesteps_per_cluster - 1] + + for sample_name, offset in zip(['start', 'mid', 'end'], sample_offsets, strict=False): + # With 2D structure: select time offset, then reorder by cluster_order + cs_at_offset = charge_state.isel(time=offset) # Shape: (cluster, ...) + # Reorder to original_cluster order using cluster_order indexer + cs_t = cs_at_offset.isel(cluster=cluster_order) + # Suppress xarray warning about index loss - we immediately assign new coords anyway + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', message='.*does not create an index anymore.*') + cs_t = cs_t.rename({'cluster': 'original_cluster'}) + cs_t = cs_t.assign_coords(original_cluster=np.arange(n_original_clusters)) + + # Apply decay factor (1-loss)^t to SOC_boundary per Eq. 9 + decay_t = (1 - rel_loss) ** offset + combined = soc_d * decay_t + cs_t + + self.add_constraints(combined >= 0, short_name=f'soc_lb_{sample_name}') + + if has_investment and self.investment is not None: + self.add_constraints(combined <= self.investment.size, short_name=f'soc_ub_{sample_name}') + elif not has_investment and isinstance(self.element.capacity_in_flow_hours, (int, float)): + # Fixed-capacity storage: upper bound is the fixed capacity + self.add_constraints( + combined <= self.element.capacity_in_flow_hours, short_name=f'soc_ub_{sample_name}' + ) + + @register_class_for_io class SourceAndSink(Component): """ diff --git a/flixopt/config.py b/flixopt/config.py index a29027d65..3bc3d5ebf 100644 --- a/flixopt/config.py +++ b/flixopt/config.py @@ -30,7 +30,7 @@ logging.addLevelName(SUCCESS_LEVEL, 'SUCCESS') # Deprecation removal version - update this when planning the next major version -DEPRECATION_REMOVAL_VERSION = '6.0.0' +DEPRECATION_REMOVAL_VERSION = '7.0.0' class MultilineFormatter(logging.Formatter): @@ -163,6 +163,9 @@ def format(self, record): 'default_facet_cols': 3, 'default_sequential_colorscale': 'turbo', 'default_qualitative_colorscale': 'plotly', + 'default_line_shape': 'hv', + 'dim_priority': ('time', 'duration', 'duration_pct', 'variable', 'cluster', 'period', 'scenario'), + 'slot_priority': ('x', 'color', 'facet_col', 'facet_row', 'animation_frame'), } ), 'solving': MappingProxyType( @@ -558,6 +561,10 @@ class Plotting: default_facet_cols: Default number of columns for faceted plots. default_sequential_colorscale: Default colorscale for heatmaps and continuous data. default_qualitative_colorscale: Default colormap for categorical plots (bar/line/area charts). + dim_priority: Priority order for assigning dimensions to plot slots. + Dimensions are assigned to slots based on this order. + slot_priority: Order in which slots are filled during auto-assignment. + Default: x β†’ color β†’ facet_col β†’ facet_row β†’ animation_frame. Examples: ```python @@ -565,6 +572,12 @@ class Plotting: CONFIG.Plotting.default_dpi = 600 CONFIG.Plotting.default_sequential_colorscale = 'plasma' CONFIG.Plotting.default_qualitative_colorscale = 'Dark24' + + # Customize dimension priority for auto-assignment + CONFIG.Plotting.dim_priority = ('time', 'scenario', 'variable', 'period', 'cluster') + + # Change slot fill order (e.g., prioritize facets over color) + CONFIG.Plotting.slot_priority = ('x', 'facet_col', 'facet_row', 'color', 'animation_frame') ``` """ @@ -574,6 +587,9 @@ class Plotting: default_facet_cols: int = _DEFAULTS['plotting']['default_facet_cols'] default_sequential_colorscale: str = _DEFAULTS['plotting']['default_sequential_colorscale'] default_qualitative_colorscale: str = _DEFAULTS['plotting']['default_qualitative_colorscale'] + default_line_shape: str = _DEFAULTS['plotting']['default_line_shape'] + dim_priority: tuple[str, ...] = _DEFAULTS['plotting']['dim_priority'] + slot_priority: tuple[str, ...] = _DEFAULTS['plotting']['slot_priority'] class Carriers: """Default carrier definitions for common energy types. @@ -674,6 +690,9 @@ def to_dict(cls) -> dict: 'default_facet_cols': cls.Plotting.default_facet_cols, 'default_sequential_colorscale': cls.Plotting.default_sequential_colorscale, 'default_qualitative_colorscale': cls.Plotting.default_qualitative_colorscale, + 'default_line_shape': cls.Plotting.default_line_shape, + 'dim_priority': cls.Plotting.dim_priority, + 'slot_priority': cls.Plotting.slot_priority, }, } diff --git a/flixopt/core.py b/flixopt/core.py index a14aa6654..3d456fff1 100644 --- a/flixopt/core.py +++ b/flixopt/core.py @@ -15,7 +15,7 @@ logger = logging.getLogger('flixopt') -FlowSystemDimensions = Literal['time', 'period', 'scenario'] +FlowSystemDimensions = Literal['time', 'cluster', 'period', 'scenario'] """Possible dimensions of a FlowSystem.""" @@ -522,7 +522,9 @@ def _validate_and_prepare_target_coordinates( coord_index = coord_index.rename(dim_name) # Special validation for time dimensions (common pattern) - if dim_name == 'time' and not isinstance(coord_index, pd.DatetimeIndex): + # Allow integer indices when 'cluster' dimension is present (clustered mode) + has_cluster_dim = 'cluster' in coords + if dim_name == 'time' and not isinstance(coord_index, pd.DatetimeIndex) and not has_cluster_dim: raise ConversionError( f'Dimension named "time" should use DatetimeIndex for proper ' f'time-series functionality, got {type(coord_index).__name__}' diff --git a/flixopt/dataset_plot_accessor.py b/flixopt/dataset_plot_accessor.py new file mode 100644 index 000000000..6c833e652 --- /dev/null +++ b/flixopt/dataset_plot_accessor.py @@ -0,0 +1,997 @@ +"""Xarray accessors for plotting (``.fxplot``) and statistics (``.fxstats``).""" + +from __future__ import annotations + +import warnings +from typing import Any, Literal + +import pandas as pd +import plotly.express as px +import plotly.graph_objects as go +import xarray as xr + +from .color_processing import ColorType, process_colors +from .config import CONFIG + + +def assign_slots( + ds: xr.Dataset, + *, + x: str | Literal['auto'] | None = 'auto', + color: str | Literal['auto'] | None = 'auto', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + exclude_dims: set[str] | None = None, +) -> dict[str, str | None]: + """Assign dimensions to plot slots using CONFIG.Plotting.dim_priority. + + Dimensions are assigned in priority order to slots based on CONFIG.Plotting.slot_priority. + + Slot values: + - 'auto': auto-assign from available dims using priority + - None: skip this slot (not available for this plot type) + - str: use this specific dimension + + 'variable' is treated as a dimension when len(data_vars) > 1. It represents + the data_var names column in the melted DataFrame. + + Args: + ds: Dataset to analyze for available dimensions. + x: X-axis dimension. 'auto' assigns first available from priority. + color: Color grouping dimension. + facet_col: Column faceting dimension. + facet_row: Row faceting dimension. + animation_frame: Animation slider dimension. + exclude_dims: Dimensions to exclude from auto-assignment (e.g., already used for x elsewhere). + + Returns: + Dict with keys 'x', 'color', 'facet_col', 'facet_row', 'animation_frame' + and values being assigned dimension names (or None if slot skipped/unfilled). + """ + # Get available dimensions with size > 1, excluding specified dims + exclude = exclude_dims or set() + available = {d for d in ds.dims if ds.sizes[d] > 1 and d not in exclude} + # 'variable' is available when there are multiple data_vars (and not excluded) + if len(ds.data_vars) > 1 and 'variable' not in exclude: + available.add('variable') + + # Get priority-ordered list of available dims + priority_dims = [d for d in CONFIG.Plotting.dim_priority if d in available] + # Add any available dims not in priority list (fallback) + priority_dims.extend(d for d in available if d not in priority_dims) + + # Slot specification + slots = { + 'x': x, + 'color': color, + 'facet_col': facet_col, + 'facet_row': facet_row, + 'animation_frame': animation_frame, + } + # Slot fill order from config + slot_order = CONFIG.Plotting.slot_priority + + results: dict[str, str | None] = {k: None for k in slot_order} + used: set[str] = set() + + # First pass: resolve explicit dimensions (not 'auto' or None) to mark them as used + for slot_name, value in slots.items(): + if value is not None and value != 'auto': + used.add(value) + results[slot_name] = value + + # Second pass: resolve 'auto' slots in config-defined fill order + dim_iter = iter(d for d in priority_dims if d not in used) + for slot_name in slot_order: + if slots[slot_name] == 'auto': + next_dim = next(dim_iter, None) + if next_dim: + used.add(next_dim) + results[slot_name] = next_dim + + # Warn if any dimensions were not assigned to any slot + unassigned = available - used + if unassigned: + available_slots = [k for k, v in slots.items() if v is not None] + unavailable_slots = [k for k, v in slots.items() if v is None] + if unavailable_slots: + warnings.warn( + f'Dimensions {unassigned} not assigned to any plot dimension. ' + f'Not available for this plot type: {unavailable_slots}. ' + f'Reduce dimensions before plotting (e.g., .sel(), .isel(), .mean()).', + stacklevel=3, + ) + else: + warnings.warn( + f'Dimensions {unassigned} not assigned to any plot dimension ({available_slots}). ' + f'Reduce dimensions before plotting (e.g., .sel(), .isel(), .mean()).', + stacklevel=3, + ) + + return results + + +def _build_fig_kwargs( + slots: dict[str, str | None], + ds_sizes: dict[str, int], + px_kwargs: dict[str, Any], + facet_cols: int | None = None, +) -> dict[str, Any]: + """Build plotly express kwargs from slot assignments. + + Adds facet/animation args only if slots are assigned and not overridden in px_kwargs. + Handles facet_col_wrap based on dimension size. + """ + facet_col_wrap = facet_cols or CONFIG.Plotting.default_facet_cols + result: dict[str, Any] = {} + + # Add facet/animation kwargs from slots (skip if None or already in px_kwargs) + for slot in ('color', 'facet_col', 'facet_row', 'animation_frame'): + if slots.get(slot) and slot not in px_kwargs: + result[slot] = slots[slot] + + # Add facet_col_wrap if facet_col is set and dimension is large enough + if result.get('facet_col'): + dim_size = ds_sizes.get(result['facet_col'], facet_col_wrap + 1) + if facet_col_wrap < dim_size: + result['facet_col_wrap'] = facet_col_wrap + + return result + + +def _dataset_to_long_df(ds: xr.Dataset, value_name: str = 'value', var_name: str = 'variable') -> pd.DataFrame: + """Convert Dataset to long-form DataFrame for Plotly Express.""" + if not ds.data_vars: + return pd.DataFrame() + if all(ds[var].ndim == 0 for var in ds.data_vars): + rows = [{var_name: var, value_name: float(ds[var].values)} for var in ds.data_vars] + return pd.DataFrame(rows) + df = ds.to_dataframe().reset_index() + # Use dims (not just coords) as id_vars - dims without coords become integer indices + id_cols = [c for c in ds.dims if c in df.columns] + return df.melt(id_vars=id_cols, var_name=var_name, value_name=value_name) + + +@xr.register_dataset_accessor('fxplot') +class DatasetPlotAccessor: + """Plot accessor for any xr.Dataset. Access via ``dataset.fxplot``. + + Provides convenient plotting methods that automatically handle multi-dimensional + data through faceting and animation. All methods return a Plotly Figure. + + This accessor is globally registered when flixopt is imported and works on + any xr.Dataset. + + Examples: + Basic usage:: + + import flixopt + import xarray as xr + + ds = xr.Dataset({'A': (['time'], [1, 2, 3]), 'B': (['time'], [3, 2, 1])}) + ds.fxplot.stacked_bar() + ds.fxplot.line() + ds.fxplot.area() + + With faceting:: + + ds.fxplot.stacked_bar(facet_col='scenario') + ds.fxplot.line(facet_col='period', animation_frame='scenario') + + Heatmap:: + + ds.fxplot.heatmap('temperature') + """ + + def __init__(self, xarray_obj: xr.Dataset) -> None: + """Initialize the accessor with an xr.Dataset object.""" + self._ds = xarray_obj + + def bar( + self, + *, + x: str | Literal['auto'] | None = 'auto', + color: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + exclude_dims: set[str] | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a grouped bar chart from the dataset. + + Args: + x: Dimension for x-axis. 'auto' uses CONFIG.Plotting.dim_priority. + color: Dimension for color grouping. 'auto' uses 'variable' (data_var names) + if available, otherwise uses CONFIG priority. + colors: Color specification (colorscale name, color list, or dict mapping). + title: Plot title. + xlabel: X-axis label. + ylabel: Y-axis label. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. 'auto' uses CONFIG priority. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + exclude_dims: Dimensions to exclude from auto-assignment. + **px_kwargs: Additional arguments passed to plotly.express.bar. + + Returns: + Plotly Figure. + """ + slots = assign_slots( + self._ds, + x=x, + color=color, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + exclude_dims=exclude_dims, + ) + df = _dataset_to_long_df(self._ds) + if df.empty: + return go.Figure() + + color_labels = df[slots['color']].unique().tolist() if slots['color'] and slots['color'] in df.columns else [] + color_map = process_colors(colors, color_labels, CONFIG.Plotting.default_qualitative_colorscale) + + labels = {**(({slots['x']: xlabel}) if xlabel and slots['x'] else {}), **({'value': ylabel} if ylabel else {})} + fig_kwargs = { + 'data_frame': df, + 'x': slots['x'], + 'y': 'value', + 'title': title, + 'barmode': 'group', + 'color_discrete_map': color_map, + **({'labels': labels} if labels else {}), + **_build_fig_kwargs(slots, dict(self._ds.sizes), px_kwargs, facet_cols), + } + return px.bar(**{**fig_kwargs, **px_kwargs}) + + def stacked_bar( + self, + *, + x: str | Literal['auto'] | None = 'auto', + color: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + exclude_dims: set[str] | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a stacked bar chart from the dataset. + + Variables in the dataset become stacked segments. Positive and negative + values are stacked separately. + + Args: + x: Dimension for x-axis. 'auto' uses CONFIG.Plotting.dim_priority. + color: Dimension for color grouping. 'auto' uses 'variable' (data_var names) + if available, otherwise uses CONFIG priority. + colors: Color specification (colorscale name, color list, or dict mapping). + title: Plot title. + xlabel: X-axis label. + ylabel: Y-axis label. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + **px_kwargs: Additional arguments passed to plotly.express.bar. + + Returns: + Plotly Figure. + """ + slots = assign_slots( + self._ds, + x=x, + color=color, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + exclude_dims=exclude_dims, + ) + df = _dataset_to_long_df(self._ds) + if df.empty: + return go.Figure() + + color_labels = df[slots['color']].unique().tolist() if slots['color'] and slots['color'] in df.columns else [] + color_map = process_colors(colors, color_labels, CONFIG.Plotting.default_qualitative_colorscale) + + labels = {**(({slots['x']: xlabel}) if xlabel and slots['x'] else {}), **({'value': ylabel} if ylabel else {})} + fig_kwargs = { + 'data_frame': df, + 'x': slots['x'], + 'y': 'value', + 'title': title, + 'color_discrete_map': color_map, + **({'labels': labels} if labels else {}), + **_build_fig_kwargs(slots, dict(self._ds.sizes), px_kwargs, facet_cols), + } + fig = px.bar(**{**fig_kwargs, **px_kwargs}) + fig.update_layout(barmode='relative', bargap=0, bargroupgap=0) + fig.update_traces(marker_line_width=0) + return fig + + def line( + self, + *, + x: str | Literal['auto'] | None = 'auto', + color: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + line_shape: str | None = None, + exclude_dims: set[str] | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a line chart from the dataset. + + Each variable in the dataset becomes a separate line. + + Args: + x: Dimension for x-axis. 'auto' uses CONFIG.Plotting.dim_priority. + color: Dimension for color grouping. 'auto' uses 'variable' (data_var names) + if available, otherwise uses CONFIG priority. + colors: Color specification (colorscale name, color list, or dict mapping). + title: Plot title. + xlabel: X-axis label. + ylabel: Y-axis label. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + line_shape: Line interpolation ('linear', 'hv', 'vh', 'hvh', 'vhv', 'spline'). + Default from CONFIG.Plotting.default_line_shape. + **px_kwargs: Additional arguments passed to plotly.express.line. + + Returns: + Plotly Figure. + """ + slots = assign_slots( + self._ds, + x=x, + color=color, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + exclude_dims=exclude_dims, + ) + df = _dataset_to_long_df(self._ds) + if df.empty: + return go.Figure() + + color_labels = df[slots['color']].unique().tolist() if slots['color'] and slots['color'] in df.columns else [] + color_map = process_colors(colors, color_labels, CONFIG.Plotting.default_qualitative_colorscale) + + labels = {**(({slots['x']: xlabel}) if xlabel and slots['x'] else {}), **({'value': ylabel} if ylabel else {})} + fig_kwargs = { + 'data_frame': df, + 'x': slots['x'], + 'y': 'value', + 'title': title, + 'line_shape': line_shape or CONFIG.Plotting.default_line_shape, + 'color_discrete_map': color_map, + **({'labels': labels} if labels else {}), + **_build_fig_kwargs(slots, dict(self._ds.sizes), px_kwargs, facet_cols), + } + return px.line(**{**fig_kwargs, **px_kwargs}) + + def area( + self, + *, + x: str | Literal['auto'] | None = 'auto', + color: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + line_shape: str | None = None, + exclude_dims: set[str] | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a stacked area chart from the dataset. + + Args: + x: Dimension for x-axis. 'auto' uses CONFIG.Plotting.dim_priority. + color: Dimension for color grouping. 'auto' uses 'variable' (data_var names) + if available, otherwise uses CONFIG priority. + colors: Color specification (colorscale name, color list, or dict mapping). + title: Plot title. + xlabel: X-axis label. + ylabel: Y-axis label. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + line_shape: Line interpolation. Default from CONFIG.Plotting.default_line_shape. + **px_kwargs: Additional arguments passed to plotly.express.area. + + Returns: + Plotly Figure. + """ + slots = assign_slots( + self._ds, + x=x, + color=color, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + exclude_dims=exclude_dims, + ) + df = _dataset_to_long_df(self._ds) + if df.empty: + return go.Figure() + + color_labels = df[slots['color']].unique().tolist() if slots['color'] and slots['color'] in df.columns else [] + color_map = process_colors(colors, color_labels, CONFIG.Plotting.default_qualitative_colorscale) + + labels = {**(({slots['x']: xlabel}) if xlabel and slots['x'] else {}), **({'value': ylabel} if ylabel else {})} + fig_kwargs = { + 'data_frame': df, + 'x': slots['x'], + 'y': 'value', + 'title': title, + 'line_shape': line_shape or CONFIG.Plotting.default_line_shape, + 'color_discrete_map': color_map, + **({'labels': labels} if labels else {}), + **_build_fig_kwargs(slots, dict(self._ds.sizes), px_kwargs, facet_cols), + } + return px.area(**{**fig_kwargs, **px_kwargs}) + + def heatmap( + self, + variable: str | None = None, + *, + colors: str | list[str] | None = None, + title: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + **imshow_kwargs: Any, + ) -> go.Figure: + """Create a heatmap visualization. + + If the dataset has multiple variables, select one with the `variable` parameter. + If only one variable exists, it is used automatically. + + Args: + variable: Variable name to plot. Required if dataset has multiple variables. + If None and dataset has one variable, that variable is used. + colors: Colorscale name or list of colors. + title: Plot title. + facet_col: Dimension for column facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + **imshow_kwargs: Additional arguments passed to plotly.express.imshow. + + Returns: + Plotly Figure. + """ + # Select single variable + if variable is None: + if len(self._ds.data_vars) == 1: + variable = list(self._ds.data_vars)[0] + else: + raise ValueError( + f'Dataset has {len(self._ds.data_vars)} variables. ' + f"Please specify which variable to plot with variable='name'." + ) + + da = self._ds[variable] + + if da.size == 0: + return go.Figure() + + colors = colors or CONFIG.Plotting.default_sequential_colorscale + facet_col_wrap = facet_cols or CONFIG.Plotting.default_facet_cols + + # Heatmap uses imshow - first 2 dims are the x/y axes of the heatmap + # Only call assign_slots if we need to resolve 'auto' values + if facet_col == 'auto' or animation_frame == 'auto': + heatmap_axes = set(list(da.dims)[:2]) if len(da.dims) >= 2 else set() + slots = assign_slots( + self._ds, + x=None, + color=None, + facet_col=facet_col, + facet_row=None, + animation_frame=animation_frame, + exclude_dims=heatmap_axes, + ) + resolved_facet = slots['facet_col'] + resolved_animation = slots['animation_frame'] + else: + # Values already resolved (or None), use directly without re-resolving + resolved_facet = facet_col + resolved_animation = animation_frame + + imshow_args: dict[str, Any] = { + 'color_continuous_scale': colors, + 'title': title or variable, + } + + if resolved_facet and resolved_facet in da.dims: + imshow_args['facet_col'] = resolved_facet + if facet_col_wrap < da.sizes[resolved_facet]: + imshow_args['facet_col_wrap'] = facet_col_wrap + + if resolved_animation and resolved_animation in da.dims: + imshow_args['animation_frame'] = resolved_animation + + # Squeeze singleton dimensions not used for faceting/animation + # px.imshow can't handle extra singleton dims in multi-dimensional data + dims_to_preserve = set(list(da.dims)[:2]) # First 2 dims are heatmap x/y axes + if resolved_facet and resolved_facet in da.dims: + dims_to_preserve.add(resolved_facet) + if resolved_animation and resolved_animation in da.dims: + dims_to_preserve.add(resolved_animation) + for dim in list(da.dims): + if dim not in dims_to_preserve and da.sizes[dim] == 1: + da = da.squeeze(dim) + imshow_args['img'] = da + + # Use binary_string=False to handle non-numeric coords (e.g., string labels) + if 'binary_string' not in imshow_kwargs: + imshow_args['binary_string'] = False + + return px.imshow(**{**imshow_args, **imshow_kwargs}) + + def scatter( + self, + x: str, + y: str, + *, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a scatter plot from two variables in the dataset. + + Args: + x: Variable name for x-axis. + y: Variable name for y-axis. + title: Plot title. + xlabel: X-axis label. + ylabel: Y-axis label. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + **px_kwargs: Additional arguments passed to plotly.express.scatter. + + Returns: + Plotly Figure. + """ + if x not in self._ds.data_vars: + raise ValueError(f"Variable '{x}' not found in dataset. Available: {list(self._ds.data_vars)}") + if y not in self._ds.data_vars: + raise ValueError(f"Variable '{y}' not found in dataset. Available: {list(self._ds.data_vars)}") + + df = self._ds[[x, y]].to_dataframe().reset_index() + if df.empty: + return go.Figure() + + # Scatter uses explicit x/y variable names, not dimensions + slots = assign_slots( + self._ds, x=None, color=None, facet_col=facet_col, facet_row=facet_row, animation_frame=animation_frame + ) + + facet_col_wrap = facet_cols or CONFIG.Plotting.default_facet_cols + fig_kwargs: dict[str, Any] = { + 'data_frame': df, + 'x': x, + 'y': y, + 'title': title, + **px_kwargs, + } + if xlabel: + fig_kwargs['labels'] = {**fig_kwargs.get('labels', {}), x: xlabel} + if ylabel: + fig_kwargs['labels'] = {**fig_kwargs.get('labels', {}), y: ylabel} + + # Only use facets if the column actually exists in the dataframe + # (scatter uses wide format, so 'variable' column doesn't exist) + if slots['facet_col'] and slots['facet_col'] in df.columns: + fig_kwargs['facet_col'] = slots['facet_col'] + if facet_col_wrap < self._ds.sizes.get(slots['facet_col'], facet_col_wrap + 1): + fig_kwargs['facet_col_wrap'] = facet_col_wrap + if slots['facet_row'] and slots['facet_row'] in df.columns: + fig_kwargs['facet_row'] = slots['facet_row'] + if slots['animation_frame'] and slots['animation_frame'] in df.columns: + fig_kwargs['animation_frame'] = slots['animation_frame'] + + return px.scatter(**fig_kwargs) + + def pie( + self, + *, + colors: ColorType | None = None, + title: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a pie chart from aggregated dataset values. + + Extra dimensions are auto-assigned to facet_col and facet_row. + For scalar values, a single pie is shown. + + Note: + ``px.pie()`` does not support animation_frame, so only facets are available. + + Args: + colors: Color specification (colorscale name, color list, or dict mapping). + title: Plot title. + facet_col: Dimension for column facets. 'auto' uses CONFIG priority. + facet_row: Dimension for row facets. 'auto' uses CONFIG priority. + facet_cols: Number of columns in facet grid wrap. + **px_kwargs: Additional arguments passed to plotly.express.pie. + + Returns: + Plotly Figure. + + Example: + >>> ds.sum('time').fxplot.pie() # Sum over time, then pie chart + >>> ds.sum('time').fxplot.pie(facet_col='scenario') # Pie per scenario + """ + max_ndim = max((self._ds[v].ndim for v in self._ds.data_vars), default=0) + + names = list(self._ds.data_vars) + color_map = process_colors(colors, names, default_colorscale=CONFIG.Plotting.default_qualitative_colorscale) + + # Scalar case - single pie + if max_ndim == 0: + values = [float(self._ds[v].values) for v in names] + df = pd.DataFrame({'variable': names, 'value': values}) + return px.pie( + df, + names='variable', + values='value', + title=title, + color='variable', + color_discrete_map=color_map, + **px_kwargs, + ) + + # Multi-dimensional case - faceted pies (px.pie doesn't support animation_frame) + df = _dataset_to_long_df(self._ds) + if df.empty: + return go.Figure() + + # Pie uses 'variable' for names and 'value' for values, no x/color/animation_frame + slots = assign_slots( + self._ds, x=None, color=None, facet_col=facet_col, facet_row=facet_row, animation_frame=None + ) + + facet_col_wrap = facet_cols or CONFIG.Plotting.default_facet_cols + fig_kwargs: dict[str, Any] = { + 'data_frame': df, + 'names': 'variable', + 'values': 'value', + 'title': title, + 'color': 'variable', + 'color_discrete_map': color_map, + **px_kwargs, + } + + if slots['facet_col']: + fig_kwargs['facet_col'] = slots['facet_col'] + if facet_col_wrap < self._ds.sizes.get(slots['facet_col'], facet_col_wrap + 1): + fig_kwargs['facet_col_wrap'] = facet_col_wrap + if slots['facet_row']: + fig_kwargs['facet_row'] = slots['facet_row'] + + return px.pie(**fig_kwargs) + + +@xr.register_dataset_accessor('fxstats') +class DatasetStatsAccessor: + """Statistics/transformation accessor for any xr.Dataset. Access via ``dataset.fxstats``. + + Provides data transformation methods that return new datasets. + Chain with ``.fxplot`` for visualization. + + Examples: + Duration curve:: + + ds.fxstats.to_duration_curve().fxplot.line() + """ + + def __init__(self, xarray_obj: xr.Dataset) -> None: + self._ds = xarray_obj + + def to_duration_curve(self, *, normalize: bool = True) -> xr.Dataset: + """Transform dataset to duration curve format (sorted values). + + Values are sorted in descending order along the 'time' dimension. + The time coordinate is replaced with duration (percentage or index). + + Args: + normalize: If True, x-axis shows percentage (0-100). If False, shows timestep index. + + Returns: + Transformed xr.Dataset with duration coordinate instead of time. + + Example: + >>> ds.fxstats.to_duration_curve().fxplot.line(title='Duration Curve') + """ + import numpy as np + + if 'time' not in self._ds.dims: + raise ValueError("Duration curve requires a 'time' dimension.") + + # Sort each variable along time dimension (descending) + sorted_ds = self._ds.copy() + for var in sorted_ds.data_vars: + da = sorted_ds[var] + time_axis = da.dims.index('time') + # Sort along time axis (descending) - use flip for correct axis + sorted_values = np.flip(np.sort(da.values, axis=time_axis), axis=time_axis) + sorted_ds[var] = (da.dims, sorted_values) + + # Replace time coordinate with duration + n_timesteps = sorted_ds.sizes['time'] + if normalize: + duration_coord = np.linspace(0, 100, n_timesteps) + sorted_ds = sorted_ds.assign_coords({'time': duration_coord}) + sorted_ds = sorted_ds.rename({'time': 'duration_pct'}) + else: + duration_coord = np.arange(n_timesteps) + sorted_ds = sorted_ds.assign_coords({'time': duration_coord}) + sorted_ds = sorted_ds.rename({'time': 'duration'}) + + return sorted_ds + + +@xr.register_dataarray_accessor('fxplot') +class DataArrayPlotAccessor: + """Plot accessor for any xr.DataArray. Access via ``dataarray.fxplot``. + + Provides convenient plotting methods. For bar/stacked_bar/line/area, + the DataArray is converted to a Dataset first. For heatmap, it works + directly with the DataArray. + + Examples: + Basic usage:: + + import flixopt + import xarray as xr + + da = xr.DataArray([1, 2, 3], dims=['time'], name='temperature') + da.fxplot.line() + da.fxplot.heatmap() + """ + + def __init__(self, xarray_obj: xr.DataArray) -> None: + """Initialize the accessor with an xr.DataArray object.""" + self._da = xarray_obj + + def _to_dataset(self) -> xr.Dataset: + """Convert DataArray to Dataset for plotting.""" + name = self._da.name or 'value' + return self._da.to_dataset(name=name) + + def bar( + self, + *, + x: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a grouped bar chart. See DatasetPlotAccessor.bar for details.""" + return self._to_dataset().fxplot.bar( + x=x, + colors=colors, + title=title, + xlabel=xlabel, + ylabel=ylabel, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + facet_cols=facet_cols, + **px_kwargs, + ) + + def stacked_bar( + self, + *, + x: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + exclude_dims: set[str] | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a stacked bar chart. See DatasetPlotAccessor.stacked_bar for details.""" + return self._to_dataset().fxplot.stacked_bar( + x=x, + colors=colors, + title=title, + xlabel=xlabel, + ylabel=ylabel, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + facet_cols=facet_cols, + **px_kwargs, + ) + + def line( + self, + *, + x: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + line_shape: str | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a line chart. See DatasetPlotAccessor.line for details.""" + return self._to_dataset().fxplot.line( + x=x, + colors=colors, + title=title, + xlabel=xlabel, + ylabel=ylabel, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + facet_cols=facet_cols, + line_shape=line_shape, + **px_kwargs, + ) + + def area( + self, + *, + x: str | Literal['auto'] | None = 'auto', + colors: ColorType | None = None, + title: str = '', + xlabel: str = '', + ylabel: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + line_shape: str | None = None, + **px_kwargs: Any, + ) -> go.Figure: + """Create a stacked area chart. See DatasetPlotAccessor.area for details.""" + return self._to_dataset().fxplot.area( + x=x, + colors=colors, + title=title, + xlabel=xlabel, + ylabel=ylabel, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, + facet_cols=facet_cols, + line_shape=line_shape, + **px_kwargs, + ) + + def heatmap( + self, + *, + colors: str | list[str] | None = None, + title: str = '', + facet_col: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', + facet_cols: int | None = None, + **imshow_kwargs: Any, + ) -> go.Figure: + """Create a heatmap visualization directly from the DataArray. + + Args: + colors: Colorscale name or list of colors. + title: Plot title. + facet_col: Dimension for column facets. + animation_frame: Dimension for animation slider. + facet_cols: Number of columns in facet grid wrap. + **imshow_kwargs: Additional arguments passed to plotly.express.imshow. + + Returns: + Plotly Figure. + """ + da = self._da + + if da.size == 0: + return go.Figure() + + colors = colors or CONFIG.Plotting.default_sequential_colorscale + facet_col_wrap = facet_cols or CONFIG.Plotting.default_facet_cols + + # Heatmap uses imshow - first 2 dims are the x/y axes of the heatmap + # Only call assign_slots if we need to resolve 'auto' values + if facet_col == 'auto' or animation_frame == 'auto': + heatmap_axes = set(list(da.dims)[:2]) if len(da.dims) >= 2 else set() + ds_for_resolution = da.to_dataset(name='_temp') + slots = assign_slots( + ds_for_resolution, + x=None, + color=None, + facet_col=facet_col, + facet_row=None, + animation_frame=animation_frame, + exclude_dims=heatmap_axes, + ) + resolved_facet = slots['facet_col'] + resolved_animation = slots['animation_frame'] + else: + # Values already resolved (or None), use directly without re-resolving + resolved_facet = facet_col + resolved_animation = animation_frame + + imshow_args: dict[str, Any] = { + 'color_continuous_scale': colors, + 'title': title or (da.name if da.name else ''), + } + + if resolved_facet and resolved_facet in da.dims: + imshow_args['facet_col'] = resolved_facet + if facet_col_wrap < da.sizes[resolved_facet]: + imshow_args['facet_col_wrap'] = facet_col_wrap + + if resolved_animation and resolved_animation in da.dims: + imshow_args['animation_frame'] = resolved_animation + + # Squeeze singleton dimensions not used for faceting/animation + # px.imshow can't handle extra singleton dims in multi-dimensional data + dims_to_preserve = set(list(da.dims)[:2]) # First 2 dims are heatmap x/y axes + if resolved_facet and resolved_facet in da.dims: + dims_to_preserve.add(resolved_facet) + if resolved_animation and resolved_animation in da.dims: + dims_to_preserve.add(resolved_animation) + for dim in list(da.dims): + if dim not in dims_to_preserve and da.sizes[dim] == 1: + da = da.squeeze(dim) + imshow_args['img'] = da + + # Use binary_string=False to handle non-numeric coords (e.g., string labels) + if 'binary_string' not in imshow_kwargs: + imshow_args['binary_string'] = False + + return px.imshow(**{**imshow_args, **imshow_kwargs}) diff --git a/flixopt/effects.py b/flixopt/effects.py index cdac7ca7d..3a2322988 100644 --- a/flixopt/effects.py +++ b/flixopt/effects.py @@ -252,7 +252,6 @@ def transform_data(self) -> None: prefix=None, effect_values=self.share_from_temporal, suffix=f'(temporal)->{self.prefix}(temporal)', - dims=['time', 'period', 'scenario'], ) self.share_from_periodic = self._fit_effect_coords( prefix=None, diff --git a/flixopt/elements.py b/flixopt/elements.py index 2933eb95a..0cee53738 100644 --- a/flixopt/elements.py +++ b/flixopt/elements.py @@ -680,7 +680,7 @@ def _do_modeling(self): ModelingPrimitives.expression_tracking_variable( model=self, name=f'{self.label_full}|total_flow_hours', - tracked_expression=(self.flow_rate * self._model.hours_per_step).sum('time'), + tracked_expression=self._model.sum_temporal(self.flow_rate), bounds=( self.element.flow_hours_min if self.element.flow_hours_min is not None else 0, self.element.flow_hours_max if self.element.flow_hours_max is not None else None, @@ -821,12 +821,12 @@ def results_structure(self): } def _create_shares(self): - # Effects per flow hour + # Effects per flow hour (use timestep_duration only, cluster_weight is applied when summing to total) if self.element.effects_per_flow_hour: self._model.effects.add_share_to_effects( name=self.label_full, expressions={ - effect: self.flow_rate * self._model.hours_per_step * factor + effect: self.flow_rate * self._model.timestep_duration * factor for effect, factor in self.element.effects_per_flow_hour.items() }, target='temporal', @@ -837,9 +837,12 @@ def _create_bounds_for_load_factor(self): # Get the size (either from element or investment) size = self.investment.size if self.with_investment else self.element.size + # Total hours in the period (sum of temporal weights) + total_hours = self._model.temporal_weight.sum(self._model.temporal_dims) + # Maximum load factor constraint if self.element.load_factor_max is not None: - flow_hours_per_size_max = self._model.hours_per_step.sum('time') * self.element.load_factor_max + flow_hours_per_size_max = total_hours * self.element.load_factor_max self.add_constraints( self.total_flow_hours <= size * flow_hours_per_size_max, short_name='load_factor_max', @@ -847,7 +850,7 @@ def _create_bounds_for_load_factor(self): # Minimum load factor constraint if self.element.load_factor_min is not None: - flow_hours_per_size_min = self._model.hours_per_step.sum('time') * self.element.load_factor_min + flow_hours_per_size_min = total_hours * self.element.load_factor_min self.add_constraints( self.total_flow_hours >= size * flow_hours_per_size_min, short_name='load_factor_min', @@ -951,7 +954,7 @@ def _do_modeling(self): # Add virtual supply/demand to balance and penalty if needed if self.element.allows_imbalance: - imbalance_penalty = np.multiply(self._model.hours_per_step, self.element.imbalance_penalty_per_flow_hour) + imbalance_penalty = self.element.imbalance_penalty_per_flow_hour * self._model.timestep_duration self.virtual_supply = self.add_variables( lower=0, coords=self._model.get_coords(), short_name='virtual_supply' diff --git a/flixopt/features.py b/flixopt/features.py index 4dfe48964..289640ddd 100644 --- a/flixopt/features.py +++ b/flixopt/features.py @@ -196,15 +196,14 @@ def _do_modeling(self): inactive = self.add_variables(binary=True, short_name='inactive', coords=self._model.get_coords()) self.add_constraints(self.status + inactive == 1, short_name='complementary') - # 3. Total duration tracking using existing pattern + # 3. Total duration tracking + total_hours = self._model.temporal_weight.sum(self._model.temporal_dims) ModelingPrimitives.expression_tracking_variable( self, - tracked_expression=(self.status * self._model.hours_per_step).sum('time'), + tracked_expression=self._model.sum_temporal(self.status), bounds=( self.parameters.active_hours_min if self.parameters.active_hours_min is not None else 0, - self.parameters.active_hours_max - if self.parameters.active_hours_max is not None - else self._model.hours_per_step.sum('time').max().item(), + self.parameters.active_hours_max if self.parameters.active_hours_max is not None else total_hours, ), short_name='active_hours', coords=['period', 'scenario'], @@ -232,7 +231,9 @@ def _do_modeling(self): coords=self._model.get_coords(('period', 'scenario')), short_name='startup_count', ) - self.add_constraints(count == self.startup.sum('time'), short_name='startup_count') + # Sum over all temporal dimensions (time, and cluster if present) + startup_temporal_dims = [d for d in self.startup.dims if d not in ('period', 'scenario')] + self.add_constraints(count == self.startup.sum(startup_temporal_dims), short_name='startup_count') # 5. Consecutive active duration (uptime) using existing pattern if self.parameters.use_uptime_tracking: @@ -242,7 +243,7 @@ def _do_modeling(self): short_name='uptime', minimum_duration=self.parameters.min_uptime, maximum_duration=self.parameters.max_uptime, - duration_per_step=self.hours_per_step, + duration_per_step=self.timestep_duration, duration_dim='time', previous_duration=self._get_previous_uptime(), ) @@ -255,7 +256,7 @@ def _do_modeling(self): short_name='downtime', minimum_duration=self.parameters.min_downtime, maximum_duration=self.parameters.max_downtime, - duration_per_step=self.hours_per_step, + duration_per_step=self.timestep_duration, duration_dim='time', previous_duration=self._get_previous_downtime(), ) @@ -263,12 +264,12 @@ def _do_modeling(self): self._add_effects() def _add_effects(self): - """Add operational effects""" + """Add operational effects (use timestep_duration only, cluster_weight is applied when summing to total)""" if self.parameters.effects_per_active_hour: self._model.effects.add_share_to_effects( name=self.label_of_element, expressions={ - effect: self.status * factor * self._model.hours_per_step + effect: self.status * factor * self._model.timestep_duration for effect, factor in self.parameters.effects_per_active_hour.items() }, target='temporal', @@ -330,7 +331,7 @@ def _get_previous_uptime(self): Returns 0 if no previous status is provided (assumes previously inactive). """ - hours_per_step = self._model.hours_per_step.isel(time=0).min().item() + hours_per_step = self._model.timestep_duration.isel(time=0).min().item() if self._previous_status is None: return 0 else: @@ -341,7 +342,7 @@ def _get_previous_downtime(self): Returns one timestep duration if no previous status is provided (assumes previously inactive). """ - hours_per_step = self._model.hours_per_step.isel(time=0).min().item() + hours_per_step = self._model.timestep_duration.isel(time=0).min().item() if self._previous_status is None: return hours_per_step else: @@ -612,16 +613,18 @@ def _do_modeling(self): if 'time' in self._dims: self.total_per_timestep = self.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.timestep_duration, + upper=np.inf if (self._max_per_hour is None) else self._max_per_hour * self._model.timestep_duration, coords=self._model.get_coords(self._dims), short_name='per_timestep', ) self._eq_total_per_timestep = self.add_constraints(self.total_per_timestep == 0, short_name='per_timestep') - # Add it to the total - self._eq_total.lhs -= self.total_per_timestep.sum(dim='time') + # Add it to the total (cluster_weight handles cluster representation, defaults to 1.0) + # Sum over all temporal dimensions (time, and cluster if present) + weighted_per_timestep = self.total_per_timestep * self._model.weights.get('cluster', 1.0) + self._eq_total.lhs -= weighted_per_timestep.sum(dim=self._model.temporal_dims) def add_share( self, diff --git a/flixopt/flow_system.py b/flixopt/flow_system.py index 13821b35b..7e12029ed 100644 --- a/flixopt/flow_system.py +++ b/flixopt/flow_system.py @@ -38,11 +38,17 @@ import pyvis + from .clustering import Clustering from .solvers import _Solver from .types import Effect_TPS, Numeric_S, Numeric_TPS, NumericOrBool from .carrier import Carrier, CarrierContainer +# Register clustering classes for IO (deferred to avoid circular imports) +from .clustering.base import _register_clustering_classes + +_register_clustering_classes() + logger = logging.getLogger('flixopt') @@ -65,8 +71,12 @@ class FlowSystem(Interface, CompositeContainerMixin[Element]): weight_of_last_period: Weight/duration of the last period. If None, computed from the last period interval. Used for calculating sums over periods in multi-period models. scenario_weights: The weights of each scenario. If None, all scenarios have the same weight (normalized to 1). - Period weights are always computed internally from the period index (like hours_per_timestep for time). + Period weights are always computed internally from the period index (like timestep_duration for time). The final `weights` array (accessible via `flow_system.model.objective_weights`) is computed as period_weights Γ— normalized_scenario_weights, with normalization applied to the scenario weights by default. + cluster_weight: Weight for each cluster. + If None (default), all clusters have weight 1.0. Used by cluster() to specify + how many original timesteps each cluster represents. Multiply with timestep_duration + for proper time aggregation in clustered models. scenario_independent_sizes: Controls whether investment sizes are equalized across scenarios. - True: All sizes are shared/equalized across scenarios - False: All sizes are optimized separately per scenario @@ -166,10 +176,12 @@ def __init__( timesteps: pd.DatetimeIndex, periods: pd.Index | None = None, scenarios: pd.Index | None = None, + clusters: pd.Index | None = None, hours_of_last_timestep: int | float | None = None, hours_of_previous_timesteps: int | float | np.ndarray | None = None, weight_of_last_period: int | float | None = None, scenario_weights: Numeric_S | None = None, + cluster_weight: Numeric_TPS | None = None, scenario_independent_sizes: bool | list[str] = True, scenario_independent_flow_rates: bool | list[str] = False, name: str | None = None, @@ -181,13 +193,26 @@ def __init__( self.timesteps_extra, self.hours_of_last_timestep, self.hours_of_previous_timesteps, - hours_per_timestep, + timestep_duration, ) = self._compute_time_metadata(self.timesteps, hours_of_last_timestep, hours_of_previous_timesteps) self.periods = None if periods is None else self._validate_periods(periods) self.scenarios = None if scenarios is None else self._validate_scenarios(scenarios) + self.clusters = clusters # Cluster dimension for clustered FlowSystems - self.hours_per_timestep = self.fit_to_model_coords('hours_per_timestep', hours_per_timestep) + self.timestep_duration = self.fit_to_model_coords('timestep_duration', timestep_duration) + + # Cluster weight for cluster() optimization (default 1.0) + # Represents how many original timesteps each cluster represents + # May have period/scenario dimensions if cluster() was used with those + self.cluster_weight: xr.DataArray | None = ( + self.fit_to_model_coords( + 'cluster_weight', + cluster_weight, + ) + if cluster_weight is not None + else None + ) self.scenario_weights = scenario_weights # Use setter @@ -216,8 +241,8 @@ def __init__( # Solution dataset - populated after optimization or loaded from file self._solution: xr.Dataset | None = None - # Clustering info - populated by transform.cluster() - self._clustering_info: dict | None = None + # Aggregation info - populated by transform.cluster() + self.clustering: Clustering | None = None # Statistics accessor cache - lazily initialized, invalidated on new solution self._statistics: StatisticsAccessor | None = None @@ -302,11 +327,11 @@ 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: - """Calculate duration of each timestep as a 1D DataArray.""" + def calculate_timestep_duration(timesteps_extra: pd.DatetimeIndex) -> xr.DataArray: + """Calculate duration of each timestep in hours 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' + hours_per_step, coords={'time': timesteps_extra[:-1]}, dims='time', name='timestep_duration' ) @staticmethod @@ -377,22 +402,22 @@ def _compute_time_metadata( Can be a scalar or array. Returns: - Tuple of (timesteps_extra, hours_of_last_timestep, hours_of_previous_timesteps, hours_per_timestep) + Tuple of (timesteps_extra, hours_of_last_timestep, hours_of_previous_timesteps, timestep_duration) """ # Create timesteps with extra step at the end timesteps_extra = cls._create_timesteps_with_extra(timesteps, hours_of_last_timestep) - # Calculate hours per timestep - hours_per_timestep = cls.calculate_hours_per_timestep(timesteps_extra) + # Calculate timestep duration + timestep_duration = cls.calculate_timestep_duration(timesteps_extra) # Extract hours_of_last_timestep if not provided if hours_of_last_timestep is None: - hours_of_last_timestep = hours_per_timestep.isel(time=-1).item() + hours_of_last_timestep = timestep_duration.isel(time=-1).item() # Compute hours_of_previous_timesteps (handles both None and provided cases) hours_of_previous_timesteps = cls._calculate_hours_of_previous_timesteps(timesteps, hours_of_previous_timesteps) - return timesteps_extra, hours_of_last_timestep, hours_of_previous_timesteps, hours_per_timestep + return timesteps_extra, hours_of_last_timestep, hours_of_previous_timesteps, timestep_duration @classmethod def _compute_period_metadata( @@ -437,7 +462,7 @@ def _update_time_metadata( """ Update time-related attributes and data variables in dataset based on its time index. - Recomputes hours_of_last_timestep, hours_of_previous_timesteps, and hours_per_timestep + Recomputes hours_of_last_timestep, hours_of_previous_timesteps, and timestep_duration from the dataset's time index when these parameters are None. This ensures time metadata stays synchronized with the actual timesteps after operations like resampling or selection. @@ -453,14 +478,14 @@ def _update_time_metadata( new_time_index = dataset.indexes.get('time') if new_time_index is not None and len(new_time_index) >= 2: # Use shared helper to compute all time metadata - _, hours_of_last_timestep, hours_of_previous_timesteps, hours_per_timestep = cls._compute_time_metadata( + _, hours_of_last_timestep, hours_of_previous_timesteps, timestep_duration = cls._compute_time_metadata( new_time_index, hours_of_last_timestep, hours_of_previous_timesteps ) - # Update hours_per_timestep DataArray if it exists in the dataset + # Update timestep_duration DataArray if it exists in the dataset # This prevents stale data after resampling operations - if 'hours_per_timestep' in dataset.data_vars: - dataset['hours_per_timestep'] = hours_per_timestep + if 'timestep_duration' in dataset.data_vars: + dataset['timestep_duration'] = timestep_duration # Update time-related attributes only when new values are provided/computed # This preserves existing metadata instead of overwriting with None @@ -484,6 +509,9 @@ def _update_period_metadata( period index. This ensures period metadata stays synchronized with the actual periods after operations like selection. + When the period dimension is dropped (single value selected), this method + removes the scalar coordinate, period_weights DataArray, and cleans up attributes. + This is analogous to _update_time_metadata() for time-related metadata. Args: @@ -495,7 +523,16 @@ def _update_period_metadata( The same dataset with updated period-related attributes and data variables """ new_period_index = dataset.indexes.get('period') - if new_period_index is not None and len(new_period_index) >= 1: + + if new_period_index is None: + # Period dimension was dropped (single value selected) + if 'period' in dataset.coords: + dataset = dataset.drop_vars('period') + dataset = dataset.drop_vars(['period_weights'], errors='ignore') + dataset.attrs.pop('weight_of_last_period', None) + return dataset + + if len(new_period_index) >= 1: # Reuse stored weight_of_last_period when not explicitly overridden. # This is essential for single-period subsets where it cannot be inferred from intervals. if weight_of_last_period is None: @@ -524,6 +561,9 @@ def _update_scenario_metadata(cls, dataset: xr.Dataset) -> xr.Dataset: Recomputes or removes scenario weights. This ensures scenario metadata stays synchronized with the actual scenarios after operations like selection. + When the scenario dimension is dropped (single value selected), this method + removes the scalar coordinate, scenario_weights DataArray, and cleans up attributes. + This is analogous to _update_period_metadata() for time-related metadata. Args: @@ -533,7 +573,16 @@ def _update_scenario_metadata(cls, dataset: xr.Dataset) -> xr.Dataset: The same dataset with updated scenario-related attributes and data variables """ new_scenario_index = dataset.indexes.get('scenario') - if new_scenario_index is None or len(new_scenario_index) <= 1: + + if new_scenario_index is None: + # Scenario dimension was dropped (single value selected) + if 'scenario' in dataset.coords: + dataset = dataset.drop_vars('scenario') + dataset = dataset.drop_vars(['scenario_weights'], errors='ignore') + dataset.attrs.pop('scenario_weights', None) + return dataset + + if len(new_scenario_index) <= 1: dataset.attrs.pop('scenario_weights', None) return dataset @@ -627,6 +676,21 @@ def to_dataset(self, include_solution: bool = True) -> xr.Dataset: carriers_structure[name] = carrier_ref ds.attrs['carriers'] = json.dumps(carriers_structure) + # Include cluster info for clustered FlowSystems (old structure) + if self.clusters is not None: + ds.attrs['is_clustered'] = True + ds.attrs['n_clusters'] = len(self.clusters) + ds.attrs['timesteps_per_cluster'] = len(self.timesteps) + ds.attrs['timestep_duration'] = float(self.timestep_duration.mean()) + + # Serialize Clustering object if present (new structure) + if self.clustering is not None: + clustering_ref, clustering_arrays = self.clustering._create_reference_structure() + # Add clustering arrays with prefix + for name, arr in clustering_arrays.items(): + ds[f'clustering|{name}'] = arr + ds.attrs['clustering'] = json.dumps(clustering_ref) + # Add version info ds.attrs['flixopt_version'] = __version__ @@ -642,6 +706,10 @@ def from_dataset(cls, ds: xr.Dataset) -> FlowSystem: the solution will be restored to the FlowSystem. Solution time coordinates are renamed back from 'solution_time' to 'time'. + Supports clustered datasets with (cluster, time) dimensions. When detected, + creates a synthetic DatetimeIndex for compatibility and stores the clustered + data structure for later use. + Args: ds: Dataset containing the FlowSystem data @@ -666,17 +734,35 @@ def from_dataset(cls, ds: xr.Dataset) -> FlowSystem: # Create arrays dictionary from config variables only arrays_dict = config_vars + # Extract cluster index if present (clustered FlowSystem) + clusters = ds.indexes.get('cluster') + + # For clustered datasets, cluster_weight is (cluster,) shaped - set separately + if clusters is not None: + cluster_weight_for_constructor = None + else: + cluster_weight_for_constructor = ( + cls._resolve_dataarray_reference(reference_structure['cluster_weight'], arrays_dict) + if 'cluster_weight' in reference_structure + else None + ) + + # Resolve scenario_weights only if scenario dimension exists + scenario_weights = None + if ds.indexes.get('scenario') is not None and 'scenario_weights' in reference_structure: + scenario_weights = cls._resolve_dataarray_reference(reference_structure['scenario_weights'], arrays_dict) + # Create FlowSystem instance with constructor parameters flow_system = cls( timesteps=ds.indexes['time'], periods=ds.indexes.get('period'), scenarios=ds.indexes.get('scenario'), + clusters=clusters, hours_of_last_timestep=reference_structure.get('hours_of_last_timestep'), hours_of_previous_timesteps=reference_structure.get('hours_of_previous_timesteps'), weight_of_last_period=reference_structure.get('weight_of_last_period'), - scenario_weights=cls._resolve_dataarray_reference(reference_structure['scenario_weights'], arrays_dict) - if 'scenario_weights' in reference_structure - else None, + scenario_weights=scenario_weights, + cluster_weight=cluster_weight_for_constructor, scenario_independent_sizes=reference_structure.get('scenario_independent_sizes', True), scenario_independent_flow_rates=reference_structure.get('scenario_independent_flow_rates', False), name=reference_structure.get('name'), @@ -721,6 +807,19 @@ def from_dataset(cls, ds: xr.Dataset) -> FlowSystem: carrier = cls._resolve_reference_structure(carrier_data, {}) flow_system._carriers.add(carrier) + # Restore Clustering object if present + if 'clustering' in reference_structure: + clustering_structure = json.loads(reference_structure['clustering']) + # Collect clustering arrays (prefixed with 'clustering|') + clustering_arrays = {} + for name, arr in ds.data_vars.items(): + if name.startswith('clustering|'): + # Remove 'clustering|' prefix (11 chars) + arr_name = name[11:] + clustering_arrays[arr_name] = arr + clustering = cls._resolve_reference_structure(clustering_structure, clustering_arrays) + flow_system.clustering = clustering + # Reconnect network to populate bus inputs/outputs (not stored in NetCDF). flow_system.connect_and_transform() @@ -1017,6 +1116,7 @@ def connect_and_transform(self): self._connect_network() self._register_missing_carriers() self._assign_element_colors() + for element in chain(self.components.values(), self.effects.values(), self.buses.values()): element.transform_data() @@ -1230,22 +1330,29 @@ def flow_carriers(self) -> dict[str, str]: return self._flow_carriers - def create_model(self, normalize_weights: bool = True) -> FlowSystemModel: + def create_model(self, normalize_weights: bool | None = None) -> FlowSystemModel: """ Create a linopy model from the FlowSystem. Args: - normalize_weights: Whether to automatically normalize the weights (periods and scenarios) to sum up to 1 when solving. + normalize_weights: Deprecated. Scenario weights are now always normalized in FlowSystem. """ + if normalize_weights is not None: + warnings.warn( + f'\n\nnormalize_weights parameter is deprecated and will be removed in {DEPRECATION_REMOVAL_VERSION}. ' + 'Scenario weights are now always normalized when set on FlowSystem.\n', + DeprecationWarning, + stacklevel=2, + ) if not self.connected_and_transformed: raise RuntimeError( 'FlowSystem is not connected_and_transformed. Call FlowSystem.connect_and_transform() first.' ) # System integrity was already validated in connect_and_transform() - self.model = FlowSystemModel(self, normalize_weights) + self.model = FlowSystemModel(self) return self.model - def build_model(self, normalize_weights: bool = True) -> FlowSystem: + def build_model(self, normalize_weights: bool | None = None) -> FlowSystem: """ Build the optimization model for this FlowSystem. @@ -1253,12 +1360,13 @@ def build_model(self, normalize_weights: bool = True) -> FlowSystem: 1. Connecting and transforming all elements (if not already done) 2. Creating the FlowSystemModel with all variables and constraints 3. Adding clustering constraints (if this is a clustered FlowSystem) + 4. Adding typical periods modeling (if this is a reduced FlowSystem) After calling this method, `self.model` will be available for inspection before solving. Args: - normalize_weights: Whether to normalize scenario/period weights to sum to 1. + normalize_weights: Deprecated. Scenario weights are now always normalized in FlowSystem. Returns: Self, for method chaining. @@ -1268,35 +1376,20 @@ def build_model(self, normalize_weights: bool = True) -> FlowSystem: >>> print(flow_system.model.variables) # Inspect variables before solving >>> flow_system.solve(solver) """ + if normalize_weights is not None: + warnings.warn( + f'\n\nnormalize_weights parameter is deprecated and will be removed in {DEPRECATION_REMOVAL_VERSION}. ' + 'Scenario weights are now always normalized when set on FlowSystem.\n', + DeprecationWarning, + stacklevel=2, + ) self.connect_and_transform() - self.create_model(normalize_weights) - self.model.do_modeling() + self.create_model() - # Add clustering constraints if this is a clustered FlowSystem - if self._clustering_info is not None: - self._add_clustering_constraints() + self.model.do_modeling() return self - def _add_clustering_constraints(self) -> None: - """Add clustering constraints to the model.""" - from .clustering import ClusteringModel - - info = self._clustering_info or {} - required_keys = {'parameters', 'clustering', 'components_to_clusterize'} - missing_keys = required_keys - set(info) - if missing_keys: - raise KeyError(f'_clustering_info missing required keys: {sorted(missing_keys)}') - - clustering_model = ClusteringModel( - model=self.model, - clustering_parameters=info['parameters'], - flow_system=self, - clustering_data=info['clustering'], - components_to_clusterize=info['components_to_clusterize'], - ) - clustering_model.do_modeling() - def solve(self, solver: _Solver) -> FlowSystem: """ Solve the optimization model and populate the solution. @@ -1840,13 +1933,111 @@ def storages(self) -> ElementContainer[Storage]: return self._storages_cache @property - def coords(self) -> dict[FlowSystemDimensions, pd.Index]: - active_coords = {'time': self.timesteps} + def dims(self) -> list[str]: + """Active dimension names. + + Returns: + List of active dimension names in order. + + Example: + >>> fs.dims + ['time'] # simple case + >>> fs_clustered.dims + ['cluster', 'time', 'period', 'scenario'] # full case + """ + result = [] + if self.clusters is not None: + result.append('cluster') + result.append('time') if self.periods is not None: - active_coords['period'] = self.periods + result.append('period') if self.scenarios is not None: - active_coords['scenario'] = self.scenarios - return active_coords + result.append('scenario') + return result + + @property + def indexes(self) -> dict[str, pd.Index]: + """Indexes for active dimensions. + + Returns: + Dict mapping dimension names to pandas Index objects. + + Example: + >>> fs.indexes['time'] + DatetimeIndex(['2024-01-01', ...], dtype='datetime64[ns]', name='time') + """ + result: dict[str, pd.Index] = {} + if self.clusters is not None: + result['cluster'] = self.clusters + result['time'] = self.timesteps + if self.periods is not None: + result['period'] = self.periods + if self.scenarios is not None: + result['scenario'] = self.scenarios + return result + + @property + def temporal_dims(self) -> list[str]: + """Temporal dimensions for summing over time. + + Returns ['time', 'cluster'] for clustered systems, ['time'] otherwise. + """ + if self.clusters is not None: + return ['time', 'cluster'] + return ['time'] + + @property + def temporal_weight(self) -> xr.DataArray: + """Combined temporal weight (timestep_duration Γ— cluster_weight). + + Use for converting rates to totals before summing. + Note: cluster_weight is used even without a clusters dimension. + """ + # Use cluster_weight directly if set, otherwise check weights dict, fallback to 1.0 + cluster_weight = self.weights.get('cluster', self.cluster_weight if self.cluster_weight is not None else 1.0) + return self.weights['time'] * cluster_weight + + @property + def coords(self) -> dict[FlowSystemDimensions, pd.Index]: + """Active coordinates for variable creation. + + .. deprecated:: + Use :attr:`indexes` instead. + + Returns a dict of dimension names to coordinate arrays. When clustered, + includes 'cluster' dimension before 'time'. + + Returns: + Dict mapping dimension names to coordinate arrays. + """ + return self.indexes + + @property + def _use_true_cluster_dims(self) -> bool: + """Check if true (cluster, time) dimensions should be used.""" + return self.clusters is not None + + @property + def _cluster_n_clusters(self) -> int | None: + """Get number of clusters.""" + return len(self.clusters) if self.clusters is not None else None + + @property + def _cluster_timesteps_per_cluster(self) -> int | None: + """Get timesteps per cluster (same as len(timesteps) for clustered systems).""" + return len(self.timesteps) if self.clusters is not None else None + + @property + def _cluster_time_coords(self) -> pd.DatetimeIndex | None: + """Get time coordinates for clustered system (same as timesteps).""" + return self.timesteps if self.clusters is not None else None + + @property + def n_timesteps(self) -> int: + """Number of timesteps (within each cluster if clustered).""" + if self.is_clustered: + return self.clustering.timesteps_per_cluster + return len(self.timesteps) @property def used_in_calculation(self) -> bool: @@ -1865,14 +2056,15 @@ def scenario_weights(self) -> xr.DataArray | None: @scenario_weights.setter def scenario_weights(self, value: Numeric_S | None) -> None: """ - Set scenario weights. + Set scenario weights (always normalized to sum to 1). Args: - value: Scenario weights to set (will be converted to DataArray with 'scenario' dimension) - or None to clear weights. + value: Scenario weights to set (will be converted to DataArray with 'scenario' dimension + and normalized to sum to 1), or None to clear weights. Raises: ValueError: If value is not None and no scenarios are defined in the FlowSystem. + ValueError: If weights sum to zero (cannot normalize). """ if value is None: self._scenario_weights = None @@ -1884,7 +2076,82 @@ def scenario_weights(self, value: Numeric_S | None) -> None: 'Either define scenarios in FlowSystem(scenarios=...) or set scenario_weights to None.' ) - self._scenario_weights = self.fit_to_model_coords('scenario_weights', value, dims=['scenario']) + weights = self.fit_to_model_coords('scenario_weights', value, dims=['scenario']) + + # Normalize to sum to 1 + norm = weights.sum('scenario') + if np.isclose(norm, 0.0).any(): + raise ValueError('scenario_weights sum to 0; cannot normalize.') + self._scenario_weights = weights / norm + + def _unit_weight(self, dim: str) -> xr.DataArray: + """Create a unit weight DataArray (all 1.0) for a dimension.""" + index = self.indexes[dim] + return xr.DataArray( + np.ones(len(index), dtype=float), + coords={dim: index}, + dims=[dim], + name=f'{dim}_weight', + ) + + @property + def weights(self) -> dict[str, xr.DataArray]: + """Weights for active dimensions (unit weights if not explicitly set). + + Returns: + Dict mapping dimension names to weight DataArrays. + Keys match :attr:`dims` and :attr:`indexes`. + + Example: + >>> fs.weights['time'] # timestep durations + >>> fs.weights['cluster'] # cluster weights (unit if not set) + """ + result: dict[str, xr.DataArray] = {'time': self.timestep_duration} + if self.clusters is not None: + result['cluster'] = self.cluster_weight if self.cluster_weight is not None else self._unit_weight('cluster') + if self.periods is not None: + result['period'] = self.period_weights if self.period_weights is not None else self._unit_weight('period') + if self.scenarios is not None: + result['scenario'] = ( + self.scenario_weights if self.scenario_weights is not None else self._unit_weight('scenario') + ) + return result + + def sum_temporal(self, data: xr.DataArray) -> xr.DataArray: + """Sum data over temporal dimensions with full temporal weighting. + + Applies both timestep_duration and cluster_weight, then sums over temporal dimensions. + Use this to convert rates to totals (e.g., flow_rate β†’ total_energy). + + Args: + data: Data with time dimension (and optionally cluster). + Typically a rate (e.g., flow_rate in MW, status as 0/1). + + Returns: + Data summed over temporal dims with full temporal weighting applied. + + Example: + >>> total_energy = fs.sum_temporal(flow_rate) # MW β†’ MWh total + >>> active_hours = fs.sum_temporal(status) # count β†’ hours + """ + return (data * self.temporal_weight).sum(self.temporal_dims) + + @property + def is_clustered(self) -> bool: + """Check if this FlowSystem uses time series clustering. + + Returns: + True if the FlowSystem was created with transform.cluster(), + False otherwise. + + Example: + >>> fs_clustered = flow_system.transform.cluster(n_clusters=8, cluster_duration='1D') + >>> fs_clustered.is_clustered + True + >>> flow_system.is_clustered + False + """ + return getattr(self, 'clustering', None) is not None def _validate_scenario_parameter(self, value: bool | list[str], param_name: str, element_type: str) -> None: """ diff --git a/flixopt/io.py b/flixopt/io.py index 288d35db8..164a9fb2e 100644 --- a/flixopt/io.py +++ b/flixopt/io.py @@ -7,6 +7,7 @@ import pathlib import re import sys +import warnings from contextlib import contextmanager from dataclasses import dataclass from typing import TYPE_CHECKING, Any @@ -559,13 +560,16 @@ def save_dataset_to_netcdf( 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 - if compression == 0 - else {data_var: {'zlib': True, 'complevel': compression} for data_var in ds.data_vars}, - engine='netcdf4', - ) + # Suppress numpy binary compatibility warnings from netCDF4 (numpy 1->2 transition) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=RuntimeWarning, message='numpy.ndarray size changed') + ds.to_netcdf( + path, + encoding=None + if compression == 0 + else {data_var: {'zlib': True, 'complevel': compression} for data_var in ds.data_vars}, + engine='netcdf4', + ) def load_dataset_from_netcdf(path: str | pathlib.Path) -> xr.Dataset: @@ -578,7 +582,10 @@ def load_dataset_from_netcdf(path: str | pathlib.Path) -> xr.Dataset: Returns: Dataset: Loaded dataset with restored attrs. """ - ds = xr.load_dataset(str(path), engine='netcdf4') + # Suppress numpy binary compatibility warnings from netCDF4 (numpy 1->2 transition) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=RuntimeWarning, message='numpy.ndarray size changed') + ds = xr.load_dataset(str(path), engine='netcdf4') # Restore Dataset attrs if 'attrs' in ds.attrs: diff --git a/flixopt/optimization.py b/flixopt/optimization.py index b76ceaf03..0b567387f 100644 --- a/flixopt/optimization.py +++ b/flixopt/optimization.py @@ -1,11 +1,12 @@ """ This module contains the Optimization functionality for the flixopt framework. It is used to optimize a FlowSystemModel for a given FlowSystem through a solver. -There are three different Optimization types: + +There are two Optimization types: 1. Optimization: Optimizes the FlowSystemModel for the full FlowSystem - 2. ClusteredOptimization: Optimizes the FlowSystemModel for the full FlowSystem, but clusters the TimeSeriesData. - This simplifies the mathematical model and usually speeds up the solving process. - 3. SegmentedOptimization: Solves a FlowSystemModel for each individual Segment of the FlowSystem. + 2. SegmentedOptimization: Solves a FlowSystemModel for each individual Segment of the FlowSystem. + +For time series aggregation (clustering), use FlowSystem.transform.cluster() instead. """ from __future__ import annotations @@ -16,27 +17,22 @@ import sys import timeit import warnings -from collections import Counter from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable -import numpy as np from tqdm import tqdm from . import io as fx_io -from .clustering import Clustering, ClusteringModel, ClusteringParameters from .components import Storage from .config import CONFIG, DEPRECATION_REMOVAL_VERSION, SUCCESS_LEVEL -from .core import DataConverter, TimeSeriesData, drop_constant_arrays from .effects import PENALTY_EFFECT_LABEL from .features import InvestmentModel -from .flow_system import FlowSystem from .results import Results, SegmentedResults if TYPE_CHECKING: import pandas as pd import xarray as xr - from .elements import Component + from .flow_system import FlowSystem from .solvers import _Solver from .structure import FlowSystemModel @@ -86,7 +82,7 @@ def _initialize_optimization_common( name: str, flow_system: FlowSystem, folder: pathlib.Path | None = None, - normalize_weights: bool = True, + normalize_weights: bool | None = None, ) -> None: """ Shared initialization logic for all optimization types. @@ -99,7 +95,7 @@ def _initialize_optimization_common( name: Name of the optimization flow_system: FlowSystem to optimize folder: Directory for saving results - normalize_weights: Whether to normalize scenario weights + normalize_weights: Deprecated. Scenario weights are now always normalized in FlowSystem. """ obj.name = name @@ -110,7 +106,8 @@ def _initialize_optimization_common( ) flow_system = flow_system.copy() - obj.normalize_weights = normalize_weights + # normalize_weights is deprecated but kept for backwards compatibility + obj.normalize_weights = True # Always True now flow_system._used_in_optimization = True @@ -190,7 +187,7 @@ def do_modeling(self) -> Optimization: t_start = timeit.default_timer() self.flow_system.connect_and_transform() - self.model = self.flow_system.create_model(self.normalize_weights) + self.model = self.flow_system.create_model() self.model.do_modeling() self.durations['modeling'] = round(timeit.default_timer() - t_start, 2) @@ -357,162 +354,6 @@ def modeled(self) -> bool: return True if self.model is not None else False -class ClusteredOptimization(Optimization): - """ - ClusteredOptimization reduces computational complexity by clustering time series into typical periods. - - This optimization approach clusters time series data using techniques from the tsam library to identify - representative time periods, significantly reducing computation time while maintaining solution accuracy. - - Note: - The quality of the solution depends on the choice of aggregation parameters. - The optimal parameters depend on the specific problem and the characteristics of the time series data. - For more information, refer to the [tsam documentation](https://tsam.readthedocs.io/en/latest/). - - Args: - name: Name of the optimization - flow_system: FlowSystem to be optimized - clustering_parameters: Parameters for clustering. See ClusteringParameters class documentation - components_to_clusterize: list of Components to perform aggregation on. If None, all components are aggregated. - This equalizes variables in the components according to the typical periods computed in the aggregation - folder: Folder where results should be saved. If None, current working directory is used - normalize_weights: Whether to automatically normalize the weights of scenarios to sum up to 1 when solving - - Attributes: - clustering (Clustering | None): Contains the clustered time series data - clustering_model (ClusteringModel | None): Contains Variables and Constraints that equalize clusters of the time series data - """ - - def __init__( - self, - name: str, - flow_system: FlowSystem, - clustering_parameters: ClusteringParameters, - components_to_clusterize: list[Component] | None = None, - folder: pathlib.Path | None = None, - normalize_weights: bool = True, - ): - warnings.warn( - f'ClusteredOptimization is deprecated and will be removed in v{DEPRECATION_REMOVAL_VERSION}. ' - 'Use FlowSystem.transform.cluster(params) followed by FlowSystem.optimize(solver) instead. ' - 'Example: clustered_fs = flow_system.transform.cluster(params); clustered_fs.optimize(solver)', - DeprecationWarning, - stacklevel=2, - ) - if flow_system.scenarios is not None: - raise ValueError('Clustering is not supported for scenarios yet. Please use Optimization instead.') - if flow_system.periods is not None: - raise ValueError('Clustering is not supported for periods yet. Please use Optimization instead.') - # Skip parent deprecation warning by calling common init directly - _initialize_optimization_common( - self, - name=name, - flow_system=flow_system, - folder=folder, - normalize_weights=normalize_weights, - ) - self.clustering_parameters = clustering_parameters - self.components_to_clusterize = components_to_clusterize - self.clustering: Clustering | None = None - self.clustering_model: ClusteringModel | None = None - - def do_modeling(self) -> ClusteredOptimization: - t_start = timeit.default_timer() - self.flow_system.connect_and_transform() - self._perform_clustering() - - # Model the System - self.model = self.flow_system.create_model(self.normalize_weights) - self.model.do_modeling() - # Add Clustering Submodel after modeling the rest - self.clustering_model = ClusteringModel( - self.model, self.clustering_parameters, self.flow_system, self.clustering, self.components_to_clusterize - ) - self.clustering_model.do_modeling() - self.durations['modeling'] = round(timeit.default_timer() - t_start, 2) - return self - - def _perform_clustering(self): - from .clustering import Clustering - - t_start_agg = timeit.default_timer() - - # Validation - dt_min = float(self.flow_system.hours_per_timestep.min().item()) - dt_max = float(self.flow_system.hours_per_timestep.max().item()) - if not dt_min == dt_max: - raise ValueError( - f'Clustering failed due to inconsistent time step sizes:delta_t varies from {dt_min} to {dt_max} hours.' - ) - ratio = self.clustering_parameters.hours_per_period / dt_max - if not np.isclose(ratio, round(ratio), atol=1e-9): - raise ValueError( - f'The selected {self.clustering_parameters.hours_per_period=} does not match the time ' - f'step size of {dt_max} hours. It must be an integer multiple of {dt_max} hours.' - ) - - logger.info(f'{"":#^80}') - logger.info(f'{" Clustering TimeSeries Data ":#^80}') - - ds = self.flow_system.to_dataset() - - temporaly_changing_ds = drop_constant_arrays(ds, dim='time') - - # Clustering - creation of clustered timeseries: - self.clustering = Clustering( - original_data=temporaly_changing_ds.to_dataframe(), - hours_per_time_step=float(dt_min), - hours_per_period=self.clustering_parameters.hours_per_period, - nr_of_periods=self.clustering_parameters.nr_of_periods, - weights=self.calculate_clustering_weights(temporaly_changing_ds), - time_series_for_high_peaks=self.clustering_parameters.labels_for_high_peaks, - time_series_for_low_peaks=self.clustering_parameters.labels_for_low_peaks, - ) - - self.clustering.cluster() - result = self.clustering.plot(show=CONFIG.Plotting.default_show) - result.to_html(self.folder / 'clustering.html') - if self.clustering_parameters.aggregate_data_and_fix_non_binary_vars: - ds = self.flow_system.to_dataset() - for name, series in self.clustering.aggregated_data.items(): - 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) - - ds[name] = da - - self.flow_system = FlowSystem.from_dataset(ds) - self.flow_system.connect_and_transform() - self.durations['clustering'] = round(timeit.default_timer() - t_start_agg, 2) - - @classmethod - def calculate_clustering_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.get('clustering_group') for da in ds.data_vars.values() if 'clustering_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(): - clustering_group = da.attrs.get('clustering_group') - group_weight = group_weights.get(clustering_group) - if group_weight is not None: - weights[name] = group_weight - else: - weights[name] = da.attrs.get('clustering_weight', 1) - - if np.all(np.isclose(list(weights.values()), 1, atol=1e-6)): - logger.info('All Clustering weights were set to 1') - - return weights - - class SegmentedOptimization: """Solve large optimization problems by dividing time horizon into (overlapping) segments. diff --git a/flixopt/optimize_accessor.py b/flixopt/optimize_accessor.py index f88cdf982..7aee930a4 100644 --- a/flixopt/optimize_accessor.py +++ b/flixopt/optimize_accessor.py @@ -53,7 +53,7 @@ def __init__(self, flow_system: FlowSystem) -> None: """ self._fs = flow_system - def __call__(self, solver: _Solver, normalize_weights: bool = True) -> FlowSystem: + def __call__(self, solver: _Solver, normalize_weights: bool | None = None) -> FlowSystem: """ Build and solve the optimization model in one step. @@ -64,7 +64,7 @@ def __call__(self, solver: _Solver, normalize_weights: bool = True) -> FlowSyste Args: solver: The solver to use (e.g., HighsSolver, GurobiSolver). - normalize_weights: Whether to normalize scenario/period weights to sum to 1. + normalize_weights: Deprecated. Scenario weights are now always normalized in FlowSystem. Returns: The FlowSystem, for method chaining. @@ -85,7 +85,18 @@ def __call__(self, solver: _Solver, normalize_weights: bool = True) -> FlowSyste >>> solution = flow_system.optimize(solver).solution """ - self._fs.build_model(normalize_weights) + if normalize_weights is not None: + import warnings + + from .config import DEPRECATION_REMOVAL_VERSION + + warnings.warn( + f'\n\nnormalize_weights parameter is deprecated and will be removed in {DEPRECATION_REMOVAL_VERSION}. ' + 'Scenario weights are now always normalized when set on FlowSystem.\n', + DeprecationWarning, + stacklevel=2, + ) + self._fs.build_model() self._fs.solve(solver) return self._fs diff --git a/flixopt/plot_result.py b/flixopt/plot_result.py index 683fbcf3e..85e692602 100644 --- a/flixopt/plot_result.py +++ b/flixopt/plot_result.py @@ -41,7 +41,7 @@ class PlotResult: Customizing the figure: - >>> result = clustering.plot() + >>> result = clustering.plot.compare() >>> result.update(title='My Custom Title').show() """ diff --git a/flixopt/results.py b/flixopt/results.py index 16d88743a..8ec860244 100644 --- a/flixopt/results.py +++ b/flixopt/results.py @@ -99,7 +99,7 @@ class Results(CompositeContainerMixin['ComponentResults | BusResults | EffectRes buses: Dictionary mapping bus labels to BusResults objects effects: Dictionary mapping effect names to EffectResults objects timesteps_extra: Extended time index including boundary conditions - hours_per_timestep: Duration of each timestep for proper energy optimizations + timestep_duration: Duration of each timestep in hours for proper energy calculations Examples: Load and analyze saved results: @@ -285,7 +285,7 @@ def __init__( self.flows = ResultsContainer(elements=flows_dict, element_type_name='flow results', truncate_repr=10) self.timesteps_extra = self.solution.indexes['time'] - self.hours_per_timestep = FlowSystem.calculate_hours_per_timestep(self.timesteps_extra) + self.timestep_duration = FlowSystem.calculate_timestep_duration(self.timesteps_extra) self.scenarios = self.solution.indexes['scenario'] if 'scenario' in self.solution.indexes else None self.periods = self.solution.indexes['period'] if 'period' in self.solution.indexes else None @@ -623,7 +623,7 @@ def flow_hours( .. deprecated:: Use `results.plot.all_flow_hours` (Dataset) or - `results.flows['FlowLabel'].flow_rate * results.hours_per_timestep` instead. + `results.flows['FlowLabel'].flow_rate * results.timestep_duration` instead. **Note**: The new API differs from this method: @@ -675,7 +675,7 @@ def flow_hours( stacklevel=2, ) if self._flow_hours is None: - self._flow_hours = (self.flow_rates() * self.hours_per_timestep).rename('flow_hours') + self._flow_hours = (self.flow_rates() * self.timestep_duration).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) @@ -1577,14 +1577,14 @@ def plot_node_balance_pie( dpi = plot_kwargs.pop('dpi', None) # None uses CONFIG.Plotting.default_dpi inputs = sanitize_dataset( - ds=self.solution[self.inputs] * self._results.hours_per_timestep, + ds=self.solution[self.inputs] * self._results.timestep_duration, threshold=1e-5, drop_small_vars=True, zero_small_values=True, drop_suffix='|', ) outputs = sanitize_dataset( - ds=self.solution[self.outputs] * self._results.hours_per_timestep, + ds=self.solution[self.outputs] * self._results.timestep_duration, threshold=1e-5, drop_small_vars=True, zero_small_values=True, @@ -1715,7 +1715,7 @@ def node_balance( ds, _ = _apply_selection_to_data(ds, select=select, drop=True) if unit_type == 'flow_hours': - ds = ds * self._results.hours_per_timestep + ds = ds * self._results.timestep_duration ds = ds.rename_vars({var: var.replace('flow_rate', 'flow_hours') for var in ds.data_vars}) return ds @@ -2016,7 +2016,7 @@ def flow_rate(self) -> xr.DataArray: @property def flow_hours(self) -> xr.DataArray: - return (self.flow_rate * self._results.hours_per_timestep).rename(f'{self.label}|flow_hours') + return (self.flow_rate * self._results.timestep_duration).rename(f'{self.label}|flow_hours') @property def size(self) -> xr.DataArray: diff --git a/flixopt/statistics_accessor.py b/flixopt/statistics_accessor.py index 952047cc5..536c6beaf 100644 --- a/flixopt/statistics_accessor.py +++ b/flixopt/statistics_accessor.py @@ -25,7 +25,6 @@ import numpy as np import pandas as pd -import plotly.express as px import plotly.graph_objects as go import xarray as xr @@ -124,55 +123,51 @@ def _reshape_time_for_heatmap( return result.transpose('timestep', 'timeframe', *other_dims) -def _heatmap_figure( - data: xr.DataArray, - colors: str | list[str] | None = None, - title: str = '', - facet_col: str | None = None, - animation_frame: str | None = None, - facet_col_wrap: int | None = None, - **imshow_kwargs: Any, -) -> go.Figure: - """Create heatmap figure using px.imshow. +# --- Helper functions --- - Args: - data: DataArray with 2-4 dimensions. First two are heatmap axes. - colors: Colorscale name (str) or list of colors. Dicts are not supported - for heatmaps as color_continuous_scale requires a colorscale specification. - title: Plot title. - facet_col: Dimension for subplot columns. - animation_frame: Dimension for animation slider. - facet_col_wrap: Max columns before wrapping. - **imshow_kwargs: Additional args for px.imshow. - Returns: - Plotly Figure. - """ - if data.size == 0: - return go.Figure() +def _prepare_for_heatmap( + da: xr.DataArray, + reshape: tuple[str, str] | Literal['auto'] | None, + facet_col: str | Literal['auto'] | None, + animation_frame: str | Literal['auto'] | None, +) -> xr.DataArray: + """Prepare DataArray for heatmap: determine axes, reshape if needed, transpose/squeeze.""" - colors = colors or CONFIG.Plotting.default_sequential_colorscale - facet_col_wrap = facet_col_wrap or CONFIG.Plotting.default_facet_cols + def finalize(da: xr.DataArray, heatmap_dims: list[str]) -> xr.DataArray: + """Transpose, squeeze, and clear name if needed.""" + other = [d for d in da.dims if d not in heatmap_dims] + da = da.transpose(*[d for d in heatmap_dims if d in da.dims], *other) + for dim in [d for d in da.dims if d not in heatmap_dims and da.sizes[d] == 1]: + da = da.squeeze(dim, drop=True) + return da.rename('') if da.sizes.get('variable', 1) > 1 else da - imshow_args: dict[str, Any] = { - 'img': data, - 'color_continuous_scale': colors, - 'title': title, - **imshow_kwargs, - } + def fallback_dims() -> list[str]: + """Default dims: (variable, time) if multi-var, else first 2 dims with size > 1.""" + if da.sizes.get('variable', 1) > 1: + return ['variable', 'time'] + dims = [d for d in da.dims if da.sizes[d] > 1][:2] + return dims if len(dims) >= 2 else list(da.dims)[:2] - if facet_col and facet_col in data.dims: - imshow_args['facet_col'] = facet_col - if facet_col_wrap < data.sizes[facet_col]: - imshow_args['facet_col_wrap'] = facet_col_wrap + is_clustered = 'cluster' in da.dims and da.sizes['cluster'] > 1 + has_time = 'time' in da.dims - if animation_frame and animation_frame in data.dims: - imshow_args['animation_frame'] = animation_frame + # Clustered: use (time, cluster) as natural 2D + if is_clustered and reshape in ('auto', None): + return finalize(da, ['time', 'cluster']) - return px.imshow(**imshow_args) + # Explicit reshape: always apply + if reshape and reshape != 'auto' and has_time: + return finalize(_reshape_time_for_heatmap(da, reshape), ['timestep', 'timeframe']) + # Auto reshape (non-clustered): apply only if extra dims fit in available slots + if reshape == 'auto' and has_time: + extra = [d for d in da.dims if d not in ('time', 'variable') and da.sizes[d] > 1] + slots = (facet_col == 'auto') + (animation_frame == 'auto') + if len(extra) <= slots: + return finalize(_reshape_time_for_heatmap(da, ('D', 'h')), ['timestep', 'timeframe']) -# --- Helper functions --- + return finalize(da, fallback_dims()) def _filter_by_pattern( @@ -228,17 +223,6 @@ def _filter_by_carrier(ds: xr.Dataset, carrier: str | list[str] | None) -> xr.Da return ds[matching_vars] if matching_vars else xr.Dataset() -def _resolve_facets( - ds: xr.Dataset, - facet_col: str | None, - facet_row: str | None, -) -> tuple[str | None, str | None]: - """Resolve facet dimensions, returning None if not present in data.""" - actual_facet_col = facet_col if facet_col and facet_col in ds.dims else None - actual_facet_row = facet_row if facet_row and facet_row in ds.dims else None - return actual_facet_col, actual_facet_row - - def _dataset_to_long_df(ds: xr.Dataset, value_name: str = 'value', var_name: str = 'variable') -> pd.DataFrame: """Convert xarray Dataset to long-form DataFrame for plotly express.""" if not ds.data_vars: @@ -252,65 +236,6 @@ def _dataset_to_long_df(ds: xr.Dataset, value_name: str = 'value', var_name: str return df.melt(id_vars=coord_cols, var_name=var_name, value_name=value_name) -def _create_stacked_bar( - ds: xr.Dataset, - colors: ColorType, - title: str, - facet_col: str | None, - facet_row: str | None, - **plotly_kwargs: Any, -) -> go.Figure: - """Create a stacked bar chart from xarray Dataset.""" - df = _dataset_to_long_df(ds) - if df.empty: - return go.Figure() - x_col = 'time' if 'time' in df.columns else df.columns[0] - variables = df['variable'].unique().tolist() - color_map = process_colors(colors, variables, default_colorscale=CONFIG.Plotting.default_qualitative_colorscale) - fig = px.bar( - df, - x=x_col, - y='value', - color='variable', - facet_col=facet_col, - facet_row=facet_row, - color_discrete_map=color_map, - title=title, - **plotly_kwargs, - ) - fig.update_layout(barmode='relative', bargap=0, bargroupgap=0) - fig.update_traces(marker_line_width=0) - return fig - - -def _create_line( - ds: xr.Dataset, - colors: ColorType, - title: str, - facet_col: str | None, - facet_row: str | None, - **plotly_kwargs: Any, -) -> go.Figure: - """Create a line chart from xarray Dataset.""" - df = _dataset_to_long_df(ds) - if df.empty: - return go.Figure() - x_col = 'time' if 'time' in df.columns else df.columns[0] - variables = df['variable'].unique().tolist() - color_map = process_colors(colors, variables, default_colorscale=CONFIG.Plotting.default_qualitative_colorscale) - return px.line( - df, - x=x_col, - y='value', - color='variable', - facet_col=facet_col, - facet_row=facet_row, - color_discrete_map=color_map, - title=title, - **plotly_kwargs, - ) - - # --- Statistics Accessor (data only) --- @@ -471,7 +396,7 @@ def flow_hours(self) -> xr.Dataset: """ self._require_solution() if self._flow_hours is None: - hours = self._fs.hours_per_timestep + hours = self._fs.timestep_duration flow_rates = self.flow_rates # Multiply and preserve/transform attributes data_vars = {} @@ -784,9 +709,12 @@ def get_contributor_type(contributor: str) -> str: label = f'{contributor}->{source_effect}({current_mode})' if label in solution: da = solution[label] * factor - # For total mode, sum temporal over time + # For total mode, sum temporal over time (apply cluster_weight for proper weighting) + # Sum over all temporal dimensions (time, and cluster if present) if mode == 'total' and current_mode == 'temporal' and 'time' in da.dims: - da = da.sum('time') + weighted = da * self._fs.weights.get('cluster', 1.0) + temporal_dims = [d for d in weighted.dims if d not in ('period', 'scenario')] + da = weighted.sum(temporal_dims) if share_total is None: share_total = da else: @@ -1374,8 +1302,9 @@ def balance( exclude: FilterType | None = None, unit: Literal['flow_rate', 'flow_hours'] = 'flow_rate', colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1388,8 +1317,11 @@ def balance( exclude: Exclude flows containing these substrings. unit: 'flow_rate' (power) or 'flow_hours' (energy). colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. - facet_row: Dimension for row facets. + facet_col: Dimension for column facets. 'auto' uses first available of + cluster/period/scenario. None disables. + facet_row: Dimension for row facets. 'auto' uses first available after facet_col. + animation_frame: Dimension for animation slider. 'auto' uses first available + after facets. show: Whether to display the plot. Returns: @@ -1426,7 +1358,6 @@ def balance( ds[label] = -ds[label] ds = _apply_selection(ds, select) - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) # Build color map from Element.color attributes if no colors specified if colors is None: @@ -1438,12 +1369,12 @@ def balance( first_var = next(iter(ds.data_vars)) unit_label = ds[first_var].attrs.get('unit', '') - fig = _create_stacked_bar( - ds, + fig = ds.fxplot.stacked_bar( colors=colors, title=f'{node} [{unit_label}]' if unit_label else node, - facet_col=actual_facet_col, - facet_row=actual_facet_row, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) @@ -1463,8 +1394,9 @@ def carrier_balance( exclude: FilterType | None = None, unit: Literal['flow_rate', 'flow_hours'] = 'flow_rate', colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1480,8 +1412,10 @@ def carrier_balance( exclude: Exclude flows containing these substrings. unit: 'flow_rate' (power) or 'flow_hours' (energy). colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. - facet_row: Dimension for row facets. + facet_col: Dimension for column facets. 'auto' uses first available of + cluster/period/scenario. + facet_row: Dimension for row facets. 'auto' uses first available after facet_col. + animation_frame: Dimension for animation slider. show: Whether to display the plot. Returns: @@ -1532,7 +1466,6 @@ def carrier_balance( ds[label] = -ds[label] ds = _apply_selection(ds, select) - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) # Use cached component colors for flows if colors is None: @@ -1557,12 +1490,12 @@ def carrier_balance( first_var = next(iter(ds.data_vars)) unit_label = ds[first_var].attrs.get('unit', '') - fig = _create_stacked_bar( - ds, + fig = ds.fxplot.stacked_bar( colors=colors, title=f'{carrier.capitalize()} Balance [{unit_label}]' if unit_label else f'{carrier.capitalize()} Balance', - facet_col=actual_facet_col, - facet_row=actual_facet_row, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) @@ -1578,18 +1511,20 @@ def heatmap( variables: str | list[str], *, select: SelectType | None = None, - reshape: tuple[str, str] | None = ('D', 'h'), + reshape: tuple[str, str] | Literal['auto'] | None = 'auto', colors: str | list[str] | None = None, - facet_col: str | None = 'period', - animation_frame: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: """Plot heatmap of time series data. - Time is reshaped into 2D (e.g., days Γ— hours) when possible. Multiple variables - are shown as facets. If too many dimensions exist to display without data loss, - reshaping is skipped and variables are shown on the y-axis with time on x-axis. + Time is reshaped into 2D (e.g., days Γ— hours) when possible. For clustered data, + the natural (cluster, time) shape is used directly without reshaping. + + Multiple variables are shown as facets. If too many dimensions exist to display + without data loss, reshaping is skipped and variables are shown on the y-axis. Args: variables: Flow label(s) or variable name(s). Flow labels like 'Boiler(Q_th)' @@ -1597,12 +1532,13 @@ def heatmap( names like 'Storage|charge_state' are used as-is. select: xarray-style selection, e.g. {'scenario': 'Base Case'}. reshape: Time reshape frequencies as (outer, inner), e.g. ('D', 'h') for - days Γ— hours. Set to None to disable reshaping. + days Γ— hours. 'auto' uses (cluster, time) for clustered data or + ('D', 'h') otherwise. None disables reshaping. colors: Colorscale name (str) or list of colors for heatmap coloring. Dicts are not supported for heatmaps (use str or list[str]). - facet_col: Dimension for subplot columns (default: 'period'). - With multiple variables, 'variable' is used instead. - animation_frame: Dimension for animation slider (default: 'scenario'). + facet_col: Dimension for subplot columns. 'auto' uses first available of + cluster/period/scenario. With multiple variables, 'variable' is used. + animation_frame: Dimension for animation slider. 'auto' uses first available. show: Whether to display the figure. **plotly_kwargs: Additional arguments passed to px.imshow. @@ -1610,85 +1546,25 @@ def heatmap( PlotResult with processed data and figure. """ solution = self._stats._require_solution() - if isinstance(variables, str): variables = [variables] - # Resolve flow labels to variable names - resolved_variables = self._resolve_variable_names(variables, solution) - - ds = solution[resolved_variables] - ds = _apply_selection(ds, select) + # Resolve, select, and stack into single DataArray + resolved = self._resolve_variable_names(variables, solution) + ds = _apply_selection(solution[resolved], select) + da = xr.concat([ds[v] for v in ds.data_vars], dim=pd.Index(list(ds.data_vars), name='variable')) - # Stack variables into single DataArray - variable_names = list(ds.data_vars) - dataarrays = [ds[var] for var in variable_names] - da = xr.concat(dataarrays, dim=pd.Index(variable_names, name='variable')) + # Prepare for heatmap (reshape, transpose, squeeze) + da = _prepare_for_heatmap(da, reshape, facet_col, animation_frame) - # Determine facet and animation from available dims - has_multiple_vars = 'variable' in da.dims and da.sizes['variable'] > 1 - - if has_multiple_vars: - actual_facet = 'variable' - actual_animation = ( - animation_frame - if animation_frame in da.dims - else (facet_col if facet_col in da.dims and da.sizes.get(facet_col, 1) > 1 else None) - ) - else: - actual_facet = facet_col if facet_col in da.dims and da.sizes.get(facet_col, 0) > 1 else None - actual_animation = ( - animation_frame if animation_frame in da.dims and da.sizes.get(animation_frame, 0) > 1 else None - ) - - # Count non-time dims with size > 1 (these need facet/animation slots) - extra_dims = [d for d in da.dims if d != 'time' and da.sizes[d] > 1] - used_slots = len([d for d in [actual_facet, actual_animation] if d]) - would_drop = len(extra_dims) > used_slots - - # Reshape time only if we wouldn't lose data (all extra dims fit in facet + animation) - if reshape and 'time' in da.dims and not would_drop: - da = _reshape_time_for_heatmap(da, reshape) - heatmap_dims = ['timestep', 'timeframe'] - elif has_multiple_vars: - # Can't reshape but have multiple vars: use variable + time as heatmap axes - heatmap_dims = ['variable', 'time'] - # variable is now a heatmap dim, use period/scenario for facet/animation - actual_facet = facet_col if facet_col in da.dims and da.sizes.get(facet_col, 0) > 1 else None - actual_animation = ( - animation_frame if animation_frame in da.dims and da.sizes.get(animation_frame, 0) > 1 else None - ) - else: - heatmap_dims = ['time'] if 'time' in da.dims else list(da.dims)[:1] - - # Keep only dims we need - keep_dims = set(heatmap_dims) | {d for d in [actual_facet, actual_animation] if d is not None} - for dim in [d for d in da.dims if d not in keep_dims]: - da = da.isel({dim: 0}, drop=True) if da.sizes[dim] > 1 else da.squeeze(dim, drop=True) - - # Transpose to expected order - dim_order = heatmap_dims + [d for d in [actual_facet, actual_animation] if d] - da = da.transpose(*dim_order) - - # Clear name for multiple variables (colorbar would show first var's name) - if has_multiple_vars: - da = da.rename('') - - fig = _heatmap_figure( - da, - colors=colors, - facet_col=actual_facet, - animation_frame=actual_animation, - **plotly_kwargs, - ) + fig = da.fxplot.heatmap(colors=colors, facet_col=facet_col, animation_frame=animation_frame, **plotly_kwargs) if show is None: show = CONFIG.Plotting.default_show if show: fig.show() - reshaped_ds = da.to_dataset(name='value') if isinstance(da, xr.DataArray) else da - return PlotResult(data=reshaped_ds, figure=fig) + return PlotResult(data=da.to_dataset(name='value'), figure=fig) def flows( self, @@ -1699,8 +1575,9 @@ def flows( select: SelectType | None = None, unit: Literal['flow_rate', 'flow_hours'] = 'flow_rate', colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1713,8 +1590,9 @@ def flows( select: xarray-style selection. unit: 'flow_rate' or 'flow_hours'. colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. + facet_col: Dimension for column facets. 'auto' uses first available. facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -1757,7 +1635,6 @@ def flows( ds = ds[[lbl for lbl in matching_labels if lbl in ds]] ds = _apply_selection(ds, select) - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) # Get unit label from first data variable's attributes unit_label = '' @@ -1765,12 +1642,12 @@ def flows( first_var = next(iter(ds.data_vars)) unit_label = ds[first_var].attrs.get('unit', '') - fig = _create_line( - ds, + fig = ds.fxplot.line( colors=colors, title=f'Flows [{unit_label}]' if unit_label else 'Flows', - facet_col=actual_facet_col, - facet_row=actual_facet_row, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) @@ -1787,8 +1664,9 @@ def sizes( max_size: float | None = 1e6, select: SelectType | None = None, colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1798,8 +1676,9 @@ def sizes( max_size: Maximum size to include (filters defaults). select: xarray-style selection. colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. + facet_col: Dimension for column facets. 'auto' uses first available. facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -1814,24 +1693,18 @@ def sizes( valid_labels = [lbl for lbl in ds.data_vars if float(ds[lbl].max()) < max_size] ds = ds[valid_labels] - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) - - df = _dataset_to_long_df(ds) - if df.empty: + if not ds.data_vars: fig = go.Figure() else: - variables = df['variable'].unique().tolist() - color_map = process_colors(colors, variables) - fig = px.bar( - df, + fig = ds.fxplot.bar( x='variable', - y='value', color='variable', - facet_col=actual_facet_col, - facet_row=actual_facet_row, - color_discrete_map=color_map, + colors=colors, title='Investment Sizes', - labels={'variable': 'Flow', 'value': 'Size'}, + ylabel='Size', + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) @@ -1849,8 +1722,9 @@ def duration_curve( select: SelectType | None = None, normalize: bool = False, colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1864,8 +1738,9 @@ def duration_curve( select: xarray-style selection. normalize: If True, normalize x-axis to 0-100%. colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. + facet_col: Dimension for column facets. 'auto' uses first available. facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -1924,20 +1799,18 @@ def sort_descending(arr: np.ndarray) -> np.ndarray: duration_coord = np.linspace(0, 100, n_timesteps) if normalize else np.arange(n_timesteps) result_ds = result_ds.assign_coords({duration_name: duration_coord}) - actual_facet_col, actual_facet_row = _resolve_facets(result_ds, facet_col, facet_row) - # Get unit label from first data variable's attributes unit_label = '' if ds.data_vars: first_var = next(iter(ds.data_vars)) unit_label = ds[first_var].attrs.get('unit', '') - fig = _create_line( - result_ds, + fig = result_ds.fxplot.line( colors=colors, title=f'Duration Curve [{unit_label}]' if unit_label else 'Duration Curve', - facet_col=actual_facet_col, - facet_row=actual_facet_row, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) @@ -1959,8 +1832,9 @@ def effects( by: Literal['component', 'contributor', 'time'] | None = None, select: SelectType | None = None, colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -1974,8 +1848,9 @@ def effects( or None to show aggregated totals per effect. select: xarray-style selection. colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets (ignored if not in data). - facet_row: Dimension for row facets (ignored if not in data). + facet_col: Dimension for column facets. 'auto' uses first available. + facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -1991,110 +1866,67 @@ def effects( self._stats._require_solution() # Get the appropriate effects dataset based on aspect - if aspect == 'total': - effects_ds = self._stats.total_effects - elif aspect == 'temporal': - effects_ds = self._stats.temporal_effects - elif aspect == 'periodic': - effects_ds = self._stats.periodic_effects - else: + effects_ds = { + 'total': self._stats.total_effects, + 'temporal': self._stats.temporal_effects, + 'periodic': self._stats.periodic_effects, + }.get(aspect) + if effects_ds is None: raise ValueError(f"Aspect '{aspect}' not valid. Choose from 'total', 'temporal', 'periodic'.") - # Get available effects (data variables in the dataset) - available_effects = list(effects_ds.data_vars) - - # Filter to specific effect if requested + # Filter to specific effect(s) and apply selection if effect is not None: - if effect not in available_effects: - raise ValueError(f"Effect '{effect}' not found. Available: {available_effects}") - effects_to_plot = [effect] + if effect not in effects_ds: + raise ValueError(f"Effect '{effect}' not found. Available: {list(effects_ds.data_vars)}") + ds = effects_ds[[effect]] else: - effects_to_plot = available_effects - - # Build a combined DataArray with effect dimension - effect_arrays = [] - for eff in effects_to_plot: - da = effects_ds[eff] - if by == 'contributor': - # Keep individual contributors (flows) - no groupby - effect_arrays.append(da.expand_dims(effect=[eff])) - else: - # Group by component (sum over contributor within each component) - da_grouped = da.groupby('component').sum() - effect_arrays.append(da_grouped.expand_dims(effect=[eff])) + ds = effects_ds - combined = xr.concat(effect_arrays, dim='effect') + # Group by component (default) unless by='contributor' + if by != 'contributor' and 'contributor' in ds.dims: + ds = ds.groupby('component').sum() - # Apply selection - combined = _apply_selection(combined.to_dataset(name='value'), select)['value'] + ds = _apply_selection(ds, select) - # Group by the specified dimension + # Sum over dimensions based on 'by' parameter if by is None: - # Aggregate totals per effect - sum over all dimensions except effect - if 'time' in combined.dims: - combined = combined.sum(dim='time') - if 'component' in combined.dims: - combined = combined.sum(dim='component') - if 'contributor' in combined.dims: - combined = combined.sum(dim='contributor') - x_col = 'effect' - color_col = 'effect' + for dim in ['time', 'component', 'contributor']: + if dim in ds.dims: + ds = ds.sum(dim=dim) + x_col, color_col = 'variable', 'variable' elif by == 'component': - # Sum over time if present - if 'time' in combined.dims: - combined = combined.sum(dim='time') + if 'time' in ds.dims: + ds = ds.sum(dim='time') x_col = 'component' - color_col = 'effect' if len(effects_to_plot) > 1 else 'component' + color_col = 'variable' if len(ds.data_vars) > 1 else 'component' elif by == 'contributor': - # Sum over time if present - if 'time' in combined.dims: - combined = combined.sum(dim='time') + if 'time' in ds.dims: + ds = ds.sum(dim='time') x_col = 'contributor' - color_col = 'effect' if len(effects_to_plot) > 1 else 'contributor' + color_col = 'variable' if len(ds.data_vars) > 1 else 'contributor' elif by == 'time': - if 'time' not in combined.dims: + if 'time' not in ds.dims: raise ValueError(f"Cannot plot by 'time' for aspect '{aspect}' - no time dimension.") - # Sum over components or contributors - if 'component' in combined.dims: - combined = combined.sum(dim='component') - if 'contributor' in combined.dims: - combined = combined.sum(dim='contributor') + for dim in ['component', 'contributor']: + if dim in ds.dims: + ds = ds.sum(dim=dim) x_col = 'time' - color_col = 'effect' if len(effects_to_plot) > 1 else None + color_col = 'variable' if len(ds.data_vars) > 1 else None else: raise ValueError(f"'by' must be one of 'component', 'contributor', 'time', or None, got {by!r}") - # Resolve facets - actual_facet_col, actual_facet_row = _resolve_facets(combined.to_dataset(name='value'), facet_col, facet_row) + # Build title + effect_label = effect or 'Effects' + title = f'{effect_label} ({aspect})' if by is None else f'{effect_label} ({aspect}) by {by}' - # Convert to DataFrame for plotly express - df = combined.to_dataframe(name='value').reset_index() - - # Build color map - if color_col and color_col in df.columns: - color_items = df[color_col].unique().tolist() - color_map = process_colors(colors, color_items) - else: - color_map = None - - # Build title with unit if single effect - effect_label = effect if effect else 'Effects' - if effect and effect in effects_ds: - unit_label = effects_ds[effect].attrs.get('unit', '') - title = f'{effect_label} [{unit_label}]' if unit_label else effect_label - else: - title = effect_label - title = f'{title} ({aspect})' if by is None else f'{title} ({aspect}) by {by}' - - fig = px.bar( - df, + fig = ds.fxplot.bar( x=x_col, - y='value', color=color_col, - color_discrete_map=color_map, - facet_col=actual_facet_col, - facet_row=actual_facet_row, + colors=colors, title=title, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) fig.update_layout(bargap=0, bargroupgap=0) @@ -2105,7 +1937,7 @@ def effects( if show: fig.show() - return PlotResult(data=combined.to_dataset(name=aspect), figure=fig) + return PlotResult(data=ds, figure=fig) def charge_states( self, @@ -2113,8 +1945,9 @@ def charge_states( *, select: SelectType | None = None, colors: ColorType | None = None, - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -2124,8 +1957,9 @@ def charge_states( storages: Storage label(s) to plot. If None, plots all storages. select: xarray-style selection. colors: Color specification (colorscale name, color list, or label-to-color dict). - facet_col: Dimension for column facets. + facet_col: Dimension for column facets. 'auto' uses first available. facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -2140,14 +1974,13 @@ def charge_states( ds = ds[[s for s in storages if s in ds]] ds = _apply_selection(ds, select) - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) - fig = _create_line( - ds, + fig = ds.fxplot.line( colors=colors, title='Storage Charge States', - facet_col=actual_facet_col, - facet_row=actual_facet_row, + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) fig.update_yaxes(title_text='Charge State') @@ -2167,8 +2000,9 @@ def storage( unit: Literal['flow_rate', 'flow_hours'] = 'flow_rate', colors: ColorType | None = None, charge_state_color: str = 'black', - facet_col: str | None = 'period', - facet_row: str | None = 'scenario', + facet_col: str | Literal['auto'] | None = 'auto', + facet_row: str | Literal['auto'] | None = 'auto', + animation_frame: str | Literal['auto'] | None = 'auto', show: bool | None = None, **plotly_kwargs: Any, ) -> PlotResult: @@ -2184,8 +2018,9 @@ def storage( unit: 'flow_rate' (power) or 'flow_hours' (energy). colors: Color specification for flow bars. charge_state_color: Color for the charge state line overlay. - facet_col: Dimension for column facets. + facet_col: Dimension for column facets. 'auto' uses first available. facet_row: Dimension for row facets. + animation_frame: Dimension for animation slider. show: Whether to display. Returns: @@ -2229,62 +2064,68 @@ def storage( # Apply selection ds = _apply_selection(ds, select) - actual_facet_col, actual_facet_row = _resolve_facets(ds, facet_col, facet_row) - # Build color map + # Separate flow data from charge_state flow_labels = [lbl for lbl in ds.data_vars if lbl != 'charge_state'] + flow_ds = ds[flow_labels] + charge_da = ds['charge_state'] + + # Build color map for flows if colors is None: colors = self._get_color_map_for_balance(storage, flow_labels) - color_map = process_colors(colors, flow_labels) - color_map['charge_state'] = 'black' - - # Convert to long-form DataFrame - df = _dataset_to_long_df(ds) - - # Create figure with facets using px.bar for flows, then add charge_state line - flow_df = df[df['variable'] != 'charge_state'] - charge_df = df[df['variable'] == 'charge_state'] - fig = px.bar( - flow_df, + # Create stacked bar chart for flows using fxplot + fig = flow_ds.fxplot.stacked_bar( x='time', - y='value', color='variable', - facet_col=actual_facet_col, - facet_row=actual_facet_row, - color_discrete_map=color_map, + colors=colors, title=f'{storage} Operation ({unit})', + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, **plotly_kwargs, ) - fig.update_layout(bargap=0, bargroupgap=0) - fig.update_traces(marker_line_width=0) - # Add charge state as line on secondary y-axis using px.line, then merge traces - if not charge_df.empty: - line_fig = px.line( - charge_df, + # Add charge state as line on secondary y-axis + if charge_da.size > 0: + # Create line figure with same facets + line_fig = charge_da.fxplot.line( x='time', - y='value', - facet_col=actual_facet_col, - facet_row=actual_facet_row, + color=None, # Single line, no color grouping + facet_col=facet_col, + facet_row=facet_row, + animation_frame=animation_frame, ) - # Update line traces and add to main figure - for trace in line_fig.data: - trace.name = 'charge_state' - trace.line = dict(color=charge_state_color, width=2) - trace.yaxis = 'y2' - trace.showlegend = True - fig.add_trace(trace) - # Add secondary y-axis - fig.update_layout( - yaxis2=dict( - title='Charge State', - overlaying='y', + # Get the primary y-axes from the bar figure to create matching secondary axes + primary_yaxes = [key for key in fig.layout if key.startswith('yaxis')] + + # For each primary y-axis, create a secondary y-axis + for i, primary_key in enumerate(sorted(primary_yaxes, key=lambda x: int(x[5:]) if x[5:] else 0)): + primary_num = primary_key[5:] if primary_key[5:] else '1' + secondary_num = int(primary_num) + 100 + secondary_key = f'yaxis{secondary_num}' + secondary_anchor = f'x{primary_num}' if primary_num != '1' else 'x' + + fig.layout[secondary_key] = dict( + overlaying=f'y{primary_num}' if primary_num != '1' else 'y', side='right', showgrid=False, + title='Charge State' if i == len(primary_yaxes) - 1 else None, + anchor=secondary_anchor, ) - ) + + # Add line traces with correct axis assignments + for i, trace in enumerate(line_fig.data): + primary_num = i + 1 if i > 0 else 1 + secondary_yaxis = f'y{primary_num + 100}' + + trace.name = 'charge_state' + trace.line = dict(color=charge_state_color, width=2) + trace.yaxis = secondary_yaxis + trace.showlegend = i == 0 + trace.legendgroup = 'charge_state' + fig.add_trace(trace) if show is None: show = CONFIG.Plotting.default_show diff --git a/flixopt/structure.py b/flixopt/structure.py index 88fd9ce31..4b7734199 100644 --- a/flixopt/structure.py +++ b/flixopt/structure.py @@ -10,6 +10,7 @@ import logging import pathlib import re +import warnings from dataclasses import dataclass from difflib import get_close_matches from typing import ( @@ -89,13 +90,11 @@ class FlowSystemModel(linopy.Model, SubmodelsMixin): Args: flow_system: The flow_system that is used to create the model. - normalize_weights: Whether to automatically normalize the weights to sum up to 1 when solving. """ - def __init__(self, flow_system: FlowSystem, normalize_weights: bool): + def __init__(self, flow_system: FlowSystem): super().__init__(force_dim_names=True) self.flow_system = flow_system - self.normalize_weights = normalize_weights self.effects: EffectCollectionModel | None = None self.submodels: Submodels = Submodels({}) @@ -169,7 +168,15 @@ def _add_scenario_equality_constraints(self): @property def solution(self): """Build solution dataset, reindexing to timesteps_extra for consistency.""" - solution = super().solution + # Suppress the linopy warning about coordinate mismatch. + # This warning is expected when storage charge_state has one more timestep than other variables. + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', + category=UserWarning, + message='Coordinates across variables not equal', + ) + solution = super().solution solution['objective'] = self.objective.value # Store attrs as JSON strings for netCDF compatibility solution.attrs = { @@ -204,48 +211,78 @@ def solution(self): } # Ensure solution is always indexed by timesteps_extra for consistency. # Variables without extra timestep data will have NaN at the final timestep. - if 'time' in solution.coords and not solution.indexes['time'].equals(self.flow_system.timesteps_extra): - solution = solution.reindex(time=self.flow_system.timesteps_extra) + if 'time' in solution.coords: + if not solution.indexes['time'].equals(self.flow_system.timesteps_extra): + solution = solution.reindex(time=self.flow_system.timesteps_extra) return solution @property - def hours_per_step(self): - return self.flow_system.hours_per_timestep + def timestep_duration(self) -> xr.DataArray: + """Duration of each timestep in hours.""" + return self.flow_system.timestep_duration @property def hours_of_previous_timesteps(self): return self.flow_system.hours_of_previous_timesteps + @property + def dims(self) -> list[str]: + """Active dimension names.""" + return self.flow_system.dims + + @property + def indexes(self) -> dict[str, pd.Index]: + """Indexes for active dimensions.""" + return self.flow_system.indexes + + @property + def weights(self) -> dict[str, xr.DataArray]: + """Weights for active dimensions (unit weights if not set). + + Scenario weights are always normalized (handled by FlowSystem). + """ + return self.flow_system.weights + + @property + def temporal_dims(self) -> list[str]: + """Temporal dimensions for summing over time. + + Returns ['time', 'cluster'] for clustered systems, ['time'] otherwise. + """ + return self.flow_system.temporal_dims + + @property + def temporal_weight(self) -> xr.DataArray: + """Combined temporal weight (timestep_duration Γ— cluster_weight).""" + return self.flow_system.temporal_weight + + def sum_temporal(self, data: xr.DataArray) -> xr.DataArray: + """Sum data over temporal dimensions with full temporal weighting. + + Example: + >>> total_energy = model.sum_temporal(flow_rate) + """ + return self.flow_system.sum_temporal(data) + @property def scenario_weights(self) -> xr.DataArray: """ - Scenario weights of model. With optional normalization. + Scenario weights of model (always normalized, via FlowSystem). + + Returns unit weights if no scenarios defined or no explicit weights set. """ if self.flow_system.scenarios is None: return xr.DataArray(1) if self.flow_system.scenario_weights is None: - scenario_weights = xr.DataArray( - np.ones(self.flow_system.scenarios.size, dtype=float), - coords={'scenario': self.flow_system.scenarios}, - dims=['scenario'], - name='scenario_weights', - ) - else: - scenario_weights = self.flow_system.scenario_weights - - if not self.normalize_weights: - return scenario_weights + return self.flow_system._unit_weight('scenario') - norm = scenario_weights.sum('scenario') - if np.isclose(norm, 0.0).any(): - raise ValueError('FlowSystemModel.scenario_weights: weights sum to 0; cannot normalize.') - return scenario_weights / norm + return self.flow_system.scenario_weights @property def objective_weights(self) -> xr.DataArray: """ - Objective weights of model. With optional normalization of scenario weights. + Objective weights of model (period_weights Γ— scenario_weights). """ period_weights = self.flow_system.effects.objective_effect.submodel.period_weights scenario_weights = self.scenario_weights @@ -262,7 +299,8 @@ def get_coords( Args: dims: The dimensions to include in the coordinates. If None, includes all dimensions - extra_timestep: If True, uses extra timesteps instead of regular timesteps + extra_timestep: If True, uses extra timesteps instead of regular timesteps. + For clustered FlowSystems, extends time by 1 (for charge_state boundaries). Returns: The coordinates of the model, or None if no coordinates are available @@ -276,7 +314,12 @@ def get_coords( if dims is None: coords = dict(self.flow_system.coords) else: - coords = {k: v for k, v in self.flow_system.coords.items() if k in dims} + # In clustered systems, 'time' is always paired with 'cluster' + # So when 'time' is requested, also include 'cluster' if available + effective_dims = set(dims) + if 'time' in dims and 'cluster' in self.flow_system.coords: + effective_dims.add('cluster') + coords = {k: v for k, v in self.flow_system.coords.items() if k in effective_dims} if extra_timestep and coords: coords['time'] = self.flow_system.timesteps_extra @@ -1703,8 +1746,8 @@ def __repr__(self) -> str: return f'{model_string}\n{"=" * len(model_string)}\n\n{all_sections}' @property - def hours_per_step(self): - return self._model.hours_per_step + def timestep_duration(self): + return self._model.timestep_duration def _do_modeling(self): """ diff --git a/flixopt/transform_accessor.py b/flixopt/transform_accessor.py index eaec1a3b6..6a5b51caa 100644 --- a/flixopt/transform_accessor.py +++ b/flixopt/transform_accessor.py @@ -8,16 +8,15 @@ from __future__ import annotations import logging +import warnings from collections import defaultdict from typing import TYPE_CHECKING, Any, Literal +import numpy as np import pandas as pd import xarray as xr if TYPE_CHECKING: - import numpy as np - - from .clustering import ClusteringParameters from .flow_system import FlowSystem logger = logging.getLogger('flixopt') @@ -31,11 +30,11 @@ class TransformAccessor: with modified structure or data, accessible via `flow_system.transform`. Examples: - Clustered optimization: + Time series aggregation (8 typical days): - >>> clustered_fs = flow_system.transform.cluster(params) - >>> clustered_fs.optimize(solver) - >>> print(clustered_fs.solution) + >>> reduced_fs = flow_system.transform.cluster(n_clusters=8, cluster_duration='1D') + >>> reduced_fs.optimize(solver) + >>> expanded_fs = reduced_fs.transform.expand_solution() Future MGA: @@ -52,126 +51,6 @@ def __init__(self, flow_system: FlowSystem) -> None: """ self._fs = flow_system - def cluster( - self, - parameters: ClusteringParameters, - components_to_clusterize: list | None = None, - ) -> FlowSystem: - """ - Create a clustered FlowSystem for time series aggregation. - - This method creates a new FlowSystem that can be optimized with - clustered time series data. The clustering reduces computational - complexity by identifying representative time periods. - - The returned FlowSystem: - - Has the same timesteps as the original (clustering works via constraints, not reduction) - - Has aggregated time series data (if `aggregate_data_and_fix_non_binary_vars=True`) - - Will have clustering constraints added during `build_model()` - - Args: - parameters: Clustering parameters specifying period duration, - number of periods, and aggregation settings. - components_to_clusterize: List of components to apply clustering to. - If None, all components are clustered. - - Returns: - A new FlowSystem configured for clustered optimization. - - Raises: - ValueError: If timestep sizes are inconsistent. - ValueError: If hours_per_period is not a multiple of timestep size. - - Examples: - Basic clustered optimization: - - >>> from flixopt import ClusteringParameters - >>> params = ClusteringParameters( - ... hours_per_period=24, - ... nr_of_periods=8, - ... fix_storage_flows=True, - ... aggregate_data_and_fix_non_binary_vars=True, - ... ) - >>> clustered_fs = flow_system.transform.cluster(params) - >>> clustered_fs.optimize(solver) - >>> print(clustered_fs.solution) - - With model modifications: - - >>> clustered_fs = flow_system.transform.cluster(params) - >>> clustered_fs.build_model() - >>> clustered_fs.model.add_constraints(...) - >>> clustered_fs.solve(solver) - """ - import numpy as np - - from .clustering import Clustering - from .core import DataConverter, TimeSeriesData, drop_constant_arrays - - # Validation - dt_min = float(self._fs.hours_per_timestep.min().item()) - dt_max = float(self._fs.hours_per_timestep.max().item()) - if dt_min != dt_max: - raise ValueError( - f'Clustering failed due to inconsistent time step sizes: ' - f'delta_t varies from {dt_min} to {dt_max} hours.' - ) - ratio = parameters.hours_per_period / dt_max - if not np.isclose(ratio, round(ratio), atol=1e-9): - raise ValueError( - f'The selected hours_per_period={parameters.hours_per_period} does not match the time ' - f'step size of {dt_max} hours. It must be an integer multiple of {dt_max} hours.' - ) - - logger.info(f'{"":#^80}') - logger.info(f'{" Clustering TimeSeries Data ":#^80}') - - # Get dataset representation - ds = self._fs.to_dataset() - temporaly_changing_ds = drop_constant_arrays(ds, dim='time') - - # Perform clustering - clustering = Clustering( - original_data=temporaly_changing_ds.to_dataframe(), - hours_per_time_step=float(dt_min), - hours_per_period=parameters.hours_per_period, - nr_of_periods=parameters.nr_of_periods, - weights=self._calculate_clustering_weights(temporaly_changing_ds), - time_series_for_high_peaks=parameters.labels_for_high_peaks, - time_series_for_low_peaks=parameters.labels_for_low_peaks, - ) - clustering.cluster() - - # Create new FlowSystem (with aggregated data if requested) - if parameters.aggregate_data_and_fix_non_binary_vars: - # Note: A second to_dataset() call is required here because: - # 1. The first 'ds' (line 124) was processed by drop_constant_arrays() - # 2. We need the full unprocessed dataset to apply aggregated data modifications - # 3. The clustering used 'temporaly_changing_ds' for input, not the full 'ds' - ds = self._fs.to_dataset() - for name, series in clustering.aggregated_data.items(): - da = DataConverter.to_dataarray(series, self._fs.coords).rename(name).assign_attrs(ds[name].attrs) - if TimeSeriesData.is_timeseries_data(da): - da = TimeSeriesData.from_dataarray(da) - ds[name] = da - - from .flow_system import FlowSystem - - clustered_fs = FlowSystem.from_dataset(ds) - else: - # Copy without data modification - clustered_fs = self._fs.copy() - - # Store clustering info for later use - clustered_fs._clustering_info = { - 'parameters': parameters, - 'clustering': clustering, - 'components_to_clusterize': components_to_clusterize, - 'original_fs': self._fs, - } - - return clustered_fs - @staticmethod def _calculate_clustering_weights(ds) -> dict[str, float]: """Calculate weights for clustering based on dataset attributes.""" @@ -195,7 +74,7 @@ def _calculate_clustering_weights(ds) -> dict[str, float]: weights[name] = da.attrs.get('clustering_weight', 1) if np.all(np.isclose(list(weights.values()), 1, atol=1e-6)): - logger.info('All Clustering weights were set to 1') + logger.debug('All Clustering weights were set to 1') return weights @@ -696,8 +575,875 @@ def fix_sizes( return new_fs - # Future methods can be added here: - # - # def mga(self, alternatives: int = 5) -> FlowSystem: - # """Create a FlowSystem configured for Modeling to Generate Alternatives.""" - # ... + def cluster( + self, + n_clusters: int, + cluster_duration: str | float, + weights: dict[str, float] | None = None, + time_series_for_high_peaks: list[str] | None = None, + time_series_for_low_peaks: list[str] | None = None, + cluster_method: Literal['k_means', 'k_medoids', 'hierarchical', 'k_maxoids', 'averaging'] = 'hierarchical', + representation_method: Literal[ + 'meanRepresentation', 'medoidRepresentation', 'distributionAndMinMaxRepresentation' + ] = 'medoidRepresentation', + extreme_period_method: Literal['append', 'new_cluster_center', 'replace_cluster_center'] | None = None, + rescale_cluster_periods: bool = True, + predef_cluster_order: xr.DataArray | np.ndarray | list[int] | None = None, + **tsam_kwargs: Any, + ) -> FlowSystem: + """ + Create a FlowSystem with reduced timesteps using typical clusters. + + This method creates a new FlowSystem optimized for sizing studies by reducing + the number of timesteps to only the typical (representative) clusters identified + through time series aggregation using the tsam package. + + The method: + 1. Performs time series clustering using tsam (hierarchical by default) + 2. Extracts only the typical clusters (not all original timesteps) + 3. Applies timestep weighting for accurate cost representation + 4. Handles storage states between clusters based on each Storage's ``cluster_mode`` + + Use this for initial sizing optimization, then use ``fix_sizes()`` to re-optimize + at full resolution for accurate dispatch results. + + Args: + n_clusters: Number of clusters (typical periods) to extract (e.g., 8 typical days). + cluster_duration: Duration of each cluster. Can be a pandas-style string + ('1D', '24h', '6h') or a numeric value in hours. + weights: Optional clustering weights per time series. Keys are time series labels. + time_series_for_high_peaks: Time series labels for explicitly selecting high-value + clusters. **Recommended** for demand time series to capture peak demand days. + time_series_for_low_peaks: Time series labels for explicitly selecting low-value clusters. + cluster_method: Clustering algorithm to use. Options: + ``'hierarchical'`` (default), ``'k_means'``, ``'k_medoids'``, + ``'k_maxoids'``, ``'averaging'``. + representation_method: How cluster representatives are computed. Options: + ``'medoidRepresentation'`` (default), ``'meanRepresentation'``, + ``'distributionAndMinMaxRepresentation'``. + extreme_period_method: How extreme periods (peaks) are integrated. Options: + ``None`` (default, no special handling), ``'append'``, + ``'new_cluster_center'``, ``'replace_cluster_center'``. + rescale_cluster_periods: If True (default), rescale cluster periods so their + weighted mean matches the original time series mean. + predef_cluster_order: Predefined cluster assignments for manual clustering. + Array of cluster indices (0 to n_clusters-1) for each original period. + If provided, clustering is skipped and these assignments are used directly. + For multi-dimensional FlowSystems, use an xr.DataArray with dims + ``[original_cluster, period?, scenario?]`` to specify different assignments + per period/scenario combination. + **tsam_kwargs: Additional keyword arguments passed to + ``tsam.TimeSeriesAggregation``. See tsam documentation for all options. + + Returns: + A new FlowSystem with reduced timesteps (only typical clusters). + The FlowSystem has metadata stored in ``clustering`` for expansion. + + Raises: + ValueError: If timestep sizes are inconsistent. + ValueError: If cluster_duration is not a multiple of timestep size. + + Examples: + Two-stage sizing optimization: + + >>> # Stage 1: Size with reduced timesteps (fast) + >>> fs_sizing = flow_system.transform.cluster( + ... n_clusters=8, + ... cluster_duration='1D', + ... time_series_for_high_peaks=['HeatDemand(Q_th)|fixed_relative_profile'], + ... ) + >>> fs_sizing.optimize(solver) + >>> + >>> # Apply safety margin (typical clusters may smooth peaks) + >>> sizes_with_margin = { + ... name: float(size.item()) * 1.05 for name, size in fs_sizing.statistics.sizes.items() + ... } + >>> + >>> # Stage 2: Fix sizes and re-optimize at full resolution + >>> fs_dispatch = flow_system.transform.fix_sizes(sizes_with_margin) + >>> fs_dispatch.optimize(solver) + + Note: + - This is best suited for initial sizing, not final dispatch optimization + - Use ``time_series_for_high_peaks`` to ensure peak demand clusters are captured + - A 5-10% safety margin on sizes is recommended for the dispatch stage + - For seasonal storage (e.g., hydrogen, thermal storage), set + ``Storage.cluster_mode='intercluster'`` or ``'intercluster_cyclic'`` + """ + import tsam.timeseriesaggregation as tsam + + from .clustering import Clustering, ClusterResult, ClusterStructure + from .core import TimeSeriesData, drop_constant_arrays + from .flow_system import FlowSystem + + # Parse cluster_duration to hours + hours_per_cluster = ( + pd.Timedelta(cluster_duration).total_seconds() / 3600 + if isinstance(cluster_duration, str) + else float(cluster_duration) + ) + + # Validation + dt = float(self._fs.timestep_duration.min().item()) + if not np.isclose(dt, float(self._fs.timestep_duration.max().item())): + raise ValueError( + f'cluster() requires uniform timestep sizes, got min={dt}h, ' + f'max={float(self._fs.timestep_duration.max().item())}h.' + ) + if not np.isclose(hours_per_cluster / dt, round(hours_per_cluster / dt), atol=1e-9): + raise ValueError(f'cluster_duration={hours_per_cluster}h must be a multiple of timestep size ({dt}h).') + + timesteps_per_cluster = int(round(hours_per_cluster / dt)) + has_periods = self._fs.periods is not None + has_scenarios = self._fs.scenarios is not None + + # Determine iteration dimensions + periods = list(self._fs.periods) if has_periods else [None] + scenarios = list(self._fs.scenarios) if has_scenarios else [None] + + ds = self._fs.to_dataset(include_solution=False) + + # Validate tsam_kwargs doesn't override explicit parameters + reserved_tsam_keys = { + 'noTypicalPeriods', + 'hoursPerPeriod', + 'resolution', + 'clusterMethod', + 'extremePeriodMethod', + 'representationMethod', + 'rescaleClusterPeriods', + 'predefClusterOrder', + 'weightDict', + 'addPeakMax', + 'addPeakMin', + } + conflicts = reserved_tsam_keys & set(tsam_kwargs.keys()) + if conflicts: + raise ValueError( + f'Cannot override explicit parameters via tsam_kwargs: {conflicts}. ' + f'Use the corresponding cluster() parameters instead.' + ) + + # Validate predef_cluster_order dimensions if it's a DataArray + if isinstance(predef_cluster_order, xr.DataArray): + expected_dims = {'original_cluster'} + if has_periods: + expected_dims.add('period') + if has_scenarios: + expected_dims.add('scenario') + if set(predef_cluster_order.dims) != expected_dims: + raise ValueError( + f'predef_cluster_order dimensions {set(predef_cluster_order.dims)} ' + f'do not match expected {expected_dims} for this FlowSystem.' + ) + + # Cluster each (period, scenario) combination using tsam directly + tsam_results: dict[tuple, tsam.TimeSeriesAggregation] = {} + cluster_orders: dict[tuple, np.ndarray] = {} + cluster_occurrences_all: dict[tuple, dict] = {} + + # Collect metrics per (period, scenario) slice + clustering_metrics_all: dict[tuple, pd.DataFrame] = {} + + for period_label in periods: + for scenario_label in scenarios: + key = (period_label, scenario_label) + selector = {k: v for k, v in [('period', period_label), ('scenario', scenario_label)] if v is not None} + ds_slice = ds.sel(**selector, drop=True) if selector else ds + temporaly_changing_ds = drop_constant_arrays(ds_slice, dim='time') + df = temporaly_changing_ds.to_dataframe() + + if selector: + logger.info(f'Clustering {", ".join(f"{k}={v}" for k, v in selector.items())}...') + + # Handle predef_cluster_order for multi-dimensional case + predef_order_slice = None + if predef_cluster_order is not None: + if isinstance(predef_cluster_order, xr.DataArray): + # Extract slice for this (period, scenario) combination + predef_order_slice = predef_cluster_order.sel(**selector, drop=True).values + else: + # Simple array/list - use directly + predef_order_slice = predef_cluster_order + + # Use tsam directly + clustering_weights = weights or self._calculate_clustering_weights(temporaly_changing_ds) + # tsam expects 'None' as a string, not Python None + tsam_extreme_method = 'None' if extreme_period_method is None else extreme_period_method + tsam_agg = tsam.TimeSeriesAggregation( + df, + noTypicalPeriods=n_clusters, + hoursPerPeriod=hours_per_cluster, + resolution=dt, + clusterMethod=cluster_method, + extremePeriodMethod=tsam_extreme_method, + representationMethod=representation_method, + rescaleClusterPeriods=rescale_cluster_periods, + predefClusterOrder=predef_order_slice, + weightDict={name: w for name, w in clustering_weights.items() if name in df.columns}, + addPeakMax=time_series_for_high_peaks or [], + addPeakMin=time_series_for_low_peaks or [], + **tsam_kwargs, + ) + # Suppress tsam warning about minimal value constraints (informational, not actionable) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=UserWarning, message='.*minimal value.*exceeds.*') + tsam_agg.createTypicalPeriods() + + tsam_results[key] = tsam_agg + cluster_orders[key] = tsam_agg.clusterOrder + cluster_occurrences_all[key] = tsam_agg.clusterPeriodNoOccur + # Compute accuracy metrics with error handling + try: + clustering_metrics_all[key] = tsam_agg.accuracyIndicators() + except Exception as e: + logger.warning(f'Failed to compute clustering metrics for {key}: {e}') + clustering_metrics_all[key] = pd.DataFrame() + + # Use first result for structure + first_key = (periods[0], scenarios[0]) + first_tsam = tsam_results[first_key] + + # Convert metrics to xr.Dataset with period/scenario dims if multi-dimensional + # Filter out empty DataFrames (from failed accuracyIndicators calls) + non_empty_metrics = {k: v for k, v in clustering_metrics_all.items() if not v.empty} + if not non_empty_metrics: + # All metrics failed - create empty Dataset + clustering_metrics = xr.Dataset() + elif len(non_empty_metrics) == 1 or len(clustering_metrics_all) == 1: + # Simple case: convert single DataFrame to Dataset + metrics_df = non_empty_metrics.get(first_key) + if metrics_df is None: + metrics_df = next(iter(non_empty_metrics.values())) + clustering_metrics = xr.Dataset( + { + col: xr.DataArray( + metrics_df[col].values, dims=['time_series'], coords={'time_series': metrics_df.index} + ) + for col in metrics_df.columns + } + ) + else: + # Multi-dim case: combine metrics into Dataset with period/scenario dims + # First, get the metric columns from any non-empty DataFrame + sample_df = next(iter(non_empty_metrics.values())) + metric_names = list(sample_df.columns) + time_series_names = list(sample_df.index) + + # Build DataArrays for each metric + data_vars = {} + for metric in metric_names: + # Shape: (time_series, period?, scenario?) + slices = {} + for (p, s), df in clustering_metrics_all.items(): + if df.empty: + # Use NaN for failed metrics + slices[(p, s)] = xr.DataArray(np.full(len(time_series_names), np.nan), dims=['time_series']) + else: + slices[(p, s)] = xr.DataArray(df[metric].values, dims=['time_series']) + + da = self._combine_slices_to_dataarray_generic(slices, ['time_series'], periods, scenarios, metric) + da = da.assign_coords(time_series=time_series_names) + data_vars[metric] = da + + clustering_metrics = xr.Dataset(data_vars) + n_reduced_timesteps = len(first_tsam.typicalPeriods) + actual_n_clusters = len(first_tsam.clusterPeriodNoOccur) + + # ═══════════════════════════════════════════════════════════════════════ + # TRUE (cluster, time) DIMENSIONS + # ═══════════════════════════════════════════════════════════════════════ + # Create coordinates for the 2D cluster structure + cluster_coords = np.arange(actual_n_clusters) + # Use DatetimeIndex for time within cluster (e.g., 00:00-23:00 for daily clustering) + time_coords = pd.date_range( + start='2000-01-01', + periods=timesteps_per_cluster, + freq=pd.Timedelta(hours=dt), + name='time', + ) + + # Create cluster_weight: shape (cluster,) - one weight per cluster + # This is the number of original periods each cluster represents + def _build_cluster_weight_for_key(key: tuple) -> xr.DataArray: + occurrences = cluster_occurrences_all[key] + weights = np.array([occurrences.get(c, 1) for c in range(actual_n_clusters)]) + return xr.DataArray(weights, dims=['cluster'], coords={'cluster': cluster_coords}) + + # Build cluster_weight - use _combine_slices_to_dataarray_generic for multi-dim handling + weight_slices = {key: _build_cluster_weight_for_key(key) for key in cluster_occurrences_all} + cluster_weight = self._combine_slices_to_dataarray_generic( + weight_slices, ['cluster'], periods, scenarios, 'cluster_weight' + ) + + logger.info( + f'Reduced from {len(self._fs.timesteps)} to {actual_n_clusters} clusters Γ— {timesteps_per_cluster} timesteps' + ) + logger.info(f'Clusters: {actual_n_clusters} (requested: {n_clusters})') + + # Build typical periods DataArrays with (cluster, time) shape + typical_das: dict[str, dict[tuple, xr.DataArray]] = {} + for key, tsam_agg in tsam_results.items(): + typical_df = tsam_agg.typicalPeriods + for col in typical_df.columns: + # Reshape flat data to (cluster, time) + flat_data = typical_df[col].values + reshaped = flat_data.reshape(actual_n_clusters, timesteps_per_cluster) + typical_das.setdefault(col, {})[key] = xr.DataArray( + reshaped, + dims=['cluster', 'time'], + coords={'cluster': cluster_coords, 'time': time_coords}, + ) + + # Build reduced dataset with (cluster, time) dimensions + all_keys = {(p, s) for p in periods for s in scenarios} + ds_new_vars = {} + for name, original_da in ds.data_vars.items(): + if 'time' not in original_da.dims: + ds_new_vars[name] = original_da.copy() + elif name not in typical_das or set(typical_das[name].keys()) != all_keys: + # Time-dependent but constant: reshape to (cluster, time, ...) + sliced = original_da.isel(time=slice(0, n_reduced_timesteps)) + # Get the shape - time is first, other dims follow + other_dims = [d for d in sliced.dims if d != 'time'] + other_shape = [sliced.sizes[d] for d in other_dims] + # Reshape: (n_reduced_timesteps, ...) -> (n_clusters, timesteps_per_cluster, ...) + new_shape = [actual_n_clusters, timesteps_per_cluster] + other_shape + reshaped = sliced.values.reshape(new_shape) + # Build coords + new_coords = {'cluster': cluster_coords, 'time': time_coords} + for dim in other_dims: + new_coords[dim] = sliced.coords[dim].values + ds_new_vars[name] = xr.DataArray( + reshaped, + dims=['cluster', 'time'] + other_dims, + coords=new_coords, + attrs=original_da.attrs, + ) + else: + # Time-varying: combine per-(period, scenario) slices with (cluster, time) dims + da = self._combine_slices_to_dataarray_2d( + slices=typical_das[name], + original_da=original_da, + cluster_coords=cluster_coords, + time_coords=time_coords, + periods=periods, + scenarios=scenarios, + ) + if TimeSeriesData.is_timeseries_data(original_da): + da = TimeSeriesData.from_dataarray(da.assign_attrs(original_da.attrs)) + ds_new_vars[name] = da + + # Copy attrs but remove cluster_weight - the clustered FlowSystem gets its own + # cluster_weight set after from_dataset (original reference has wrong shape) + new_attrs = dict(ds.attrs) + new_attrs.pop('cluster_weight', None) + ds_new = xr.Dataset(ds_new_vars, attrs=new_attrs) + ds_new.attrs['timesteps_per_cluster'] = timesteps_per_cluster + ds_new.attrs['timestep_duration'] = dt + ds_new.attrs['n_clusters'] = actual_n_clusters + ds_new.attrs['is_clustered'] = True + + reduced_fs = FlowSystem.from_dataset(ds_new) + # Set cluster_weight - shape (cluster,) possibly with period/scenario dimensions + reduced_fs.cluster_weight = cluster_weight + + # Remove 'equals_final' from storages - doesn't make sense on reduced timesteps + # Set to None so initial SOC is free (handled by storage_mode constraints) + for storage in reduced_fs.storages.values(): + ics = storage.initial_charge_state + if isinstance(ics, str) and ics == 'equals_final': + storage.initial_charge_state = None + + # Build Clustering for inter-cluster linking and solution expansion + n_original_timesteps = len(self._fs.timesteps) + + # Build per-slice cluster_order and timestep_mapping as multi-dimensional DataArrays + # This is needed because each (period, scenario) combination may have different clustering + + def _build_timestep_mapping_for_key(key: tuple) -> np.ndarray: + """Build timestep_mapping for a single (period, scenario) slice.""" + mapping = np.zeros(n_original_timesteps, dtype=np.int32) + for period_idx, cluster_id in enumerate(cluster_orders[key]): + for pos in range(timesteps_per_cluster): + original_idx = period_idx * timesteps_per_cluster + pos + if original_idx < n_original_timesteps: + representative_idx = cluster_id * timesteps_per_cluster + pos + mapping[original_idx] = representative_idx + return mapping + + def _build_cluster_occurrences_for_key(key: tuple) -> np.ndarray: + """Build cluster_occurrences array for a single (period, scenario) slice.""" + occurrences = cluster_occurrences_all[key] + return np.array([occurrences.get(c, 0) for c in range(actual_n_clusters)]) + + # Build multi-dimensional arrays + if has_periods or has_scenarios: + # Multi-dimensional case: build arrays for each (period, scenario) combination + # cluster_order: dims [original_cluster, period?, scenario?] + cluster_order_slices = {} + timestep_mapping_slices = {} + cluster_occurrences_slices = {} + + # Use renamed timesteps as coordinates for multi-dimensional case + original_timesteps_coord = self._fs.timesteps.rename('original_time') + + for p in periods: + for s in scenarios: + key = (p, s) + cluster_order_slices[key] = xr.DataArray( + cluster_orders[key], dims=['original_cluster'], name='cluster_order' + ) + timestep_mapping_slices[key] = xr.DataArray( + _build_timestep_mapping_for_key(key), + dims=['original_time'], + coords={'original_time': original_timesteps_coord}, + name='timestep_mapping', + ) + cluster_occurrences_slices[key] = xr.DataArray( + _build_cluster_occurrences_for_key(key), dims=['cluster'], name='cluster_occurrences' + ) + + # Combine slices into multi-dimensional DataArrays + cluster_order_da = self._combine_slices_to_dataarray_generic( + cluster_order_slices, ['original_cluster'], periods, scenarios, 'cluster_order' + ) + timestep_mapping_da = self._combine_slices_to_dataarray_generic( + timestep_mapping_slices, ['original_time'], periods, scenarios, 'timestep_mapping' + ) + cluster_occurrences_da = self._combine_slices_to_dataarray_generic( + cluster_occurrences_slices, ['cluster'], periods, scenarios, 'cluster_occurrences' + ) + else: + # Simple case: single (None, None) slice + cluster_order_da = xr.DataArray(cluster_orders[first_key], dims=['original_cluster'], name='cluster_order') + # Use renamed timesteps as coordinates + original_timesteps_coord = self._fs.timesteps.rename('original_time') + timestep_mapping_da = xr.DataArray( + _build_timestep_mapping_for_key(first_key), + dims=['original_time'], + coords={'original_time': original_timesteps_coord}, + name='timestep_mapping', + ) + cluster_occurrences_da = xr.DataArray( + _build_cluster_occurrences_for_key(first_key), dims=['cluster'], name='cluster_occurrences' + ) + + cluster_structure = ClusterStructure( + cluster_order=cluster_order_da, + cluster_occurrences=cluster_occurrences_da, + n_clusters=actual_n_clusters, + timesteps_per_cluster=timesteps_per_cluster, + ) + + # Create representative_weights with (cluster,) dimension only + # Each cluster has one weight (same for all timesteps within it) + def _build_cluster_weights_for_key(key: tuple) -> xr.DataArray: + occurrences = cluster_occurrences_all[key] + # Shape: (n_clusters,) - one weight per cluster + weights = np.array([occurrences.get(c, 1) for c in range(actual_n_clusters)]) + return xr.DataArray(weights, dims=['cluster'], name='representative_weights') + + weights_slices = {key: _build_cluster_weights_for_key(key) for key in cluster_occurrences_all} + representative_weights = self._combine_slices_to_dataarray_generic( + weights_slices, ['cluster'], periods, scenarios, 'representative_weights' + ) + + aggregation_result = ClusterResult( + timestep_mapping=timestep_mapping_da, + n_representatives=n_reduced_timesteps, + representative_weights=representative_weights, + cluster_structure=cluster_structure, + original_data=ds, + aggregated_data=ds_new, + ) + + reduced_fs.clustering = Clustering( + result=aggregation_result, + backend_name='tsam', + metrics=clustering_metrics, + ) + + return reduced_fs + + @staticmethod + def _combine_slices_to_dataarray( + slices: dict[tuple, xr.DataArray], + original_da: xr.DataArray, + new_time_index: pd.DatetimeIndex, + periods: list, + scenarios: list, + ) -> xr.DataArray: + """Combine per-(period, scenario) slices into a multi-dimensional DataArray using xr.concat. + + Args: + slices: Dict mapping (period, scenario) tuples to 1D DataArrays (time only). + original_da: Original DataArray to get dimension order and attrs from. + new_time_index: New time coordinate for the output. + periods: List of period labels ([None] if no periods dimension). + scenarios: List of scenario labels ([None] if no scenarios dimension). + + Returns: + DataArray with dimensions matching original_da but reduced time. + """ + first_key = (periods[0], scenarios[0]) + has_periods = periods != [None] + has_scenarios = scenarios != [None] + + # Simple case: no period/scenario dimensions + if not has_periods and not has_scenarios: + return slices[first_key].assign_attrs(original_da.attrs) + + # Multi-dimensional: use xr.concat to stack along period/scenario dims + if has_periods and has_scenarios: + # Stack scenarios first, then periods + period_arrays = [] + for p in periods: + scenario_arrays = [slices[(p, s)] for s in scenarios] + period_arrays.append(xr.concat(scenario_arrays, dim=pd.Index(scenarios, name='scenario'))) + result = xr.concat(period_arrays, dim=pd.Index(periods, name='period')) + elif has_periods: + result = xr.concat([slices[(p, None)] for p in periods], dim=pd.Index(periods, name='period')) + else: + result = xr.concat([slices[(None, s)] for s in scenarios], dim=pd.Index(scenarios, name='scenario')) + + # Put time dimension first (standard order), preserve other dims + result = result.transpose('time', ...) + + return result.assign_attrs(original_da.attrs) + + @staticmethod + def _combine_slices_to_dataarray_generic( + slices: dict[tuple, xr.DataArray], + base_dims: list[str], + periods: list, + scenarios: list, + name: str, + ) -> xr.DataArray: + """Combine per-(period, scenario) slices into a multi-dimensional DataArray. + + Generic version that works with any base dimension (not just 'time'). + + Args: + slices: Dict mapping (period, scenario) tuples to DataArrays. + base_dims: Base dimensions of each slice (e.g., ['original_cluster'] or ['original_time']). + periods: List of period labels ([None] if no periods dimension). + scenarios: List of scenario labels ([None] if no scenarios dimension). + name: Name for the resulting DataArray. + + Returns: + DataArray with dimensions [base_dims..., period?, scenario?]. + """ + first_key = (periods[0], scenarios[0]) + has_periods = periods != [None] + has_scenarios = scenarios != [None] + + # Simple case: no period/scenario dimensions + if not has_periods and not has_scenarios: + return slices[first_key].rename(name) + + # Multi-dimensional: use xr.concat to stack along period/scenario dims + if has_periods and has_scenarios: + # Stack scenarios first, then periods + period_arrays = [] + for p in periods: + scenario_arrays = [slices[(p, s)] for s in scenarios] + period_arrays.append(xr.concat(scenario_arrays, dim=pd.Index(scenarios, name='scenario'))) + result = xr.concat(period_arrays, dim=pd.Index(periods, name='period')) + elif has_periods: + result = xr.concat([slices[(p, None)] for p in periods], dim=pd.Index(periods, name='period')) + else: + result = xr.concat([slices[(None, s)] for s in scenarios], dim=pd.Index(scenarios, name='scenario')) + + # Put base dimension first (standard order) + result = result.transpose(base_dims[0], ...) + + return result.rename(name) + + @staticmethod + def _combine_slices_to_dataarray_2d( + slices: dict[tuple, xr.DataArray], + original_da: xr.DataArray, + cluster_coords: np.ndarray, + time_coords: np.ndarray, + periods: list, + scenarios: list, + ) -> xr.DataArray: + """Combine per-(period, scenario) slices into a multi-dimensional DataArray with (cluster, time) dims. + + Args: + slices: Dict mapping (period, scenario) tuples to DataArrays with (cluster, time) dims. + original_da: Original DataArray to get attrs from. + cluster_coords: Cluster coordinate values. + time_coords: Within-cluster time coordinate values. + periods: List of period labels ([None] if no periods dimension). + scenarios: List of scenario labels ([None] if no scenarios dimension). + + Returns: + DataArray with dimensions (cluster, time, period?, scenario?). + """ + first_key = (periods[0], scenarios[0]) + has_periods = periods != [None] + has_scenarios = scenarios != [None] + + # Simple case: no period/scenario dimensions + if not has_periods and not has_scenarios: + return slices[first_key].assign_attrs(original_da.attrs) + + # Multi-dimensional: use xr.concat to stack along period/scenario dims + if has_periods and has_scenarios: + # Stack scenarios first, then periods + period_arrays = [] + for p in periods: + scenario_arrays = [slices[(p, s)] for s in scenarios] + period_arrays.append(xr.concat(scenario_arrays, dim=pd.Index(scenarios, name='scenario'))) + result = xr.concat(period_arrays, dim=pd.Index(periods, name='period')) + elif has_periods: + result = xr.concat([slices[(p, None)] for p in periods], dim=pd.Index(periods, name='period')) + else: + result = xr.concat([slices[(None, s)] for s in scenarios], dim=pd.Index(scenarios, name='scenario')) + + # Put cluster and time first (standard order for clustered data) + result = result.transpose('cluster', 'time', ...) + + return result.assign_attrs(original_da.attrs) + + def expand_solution(self) -> FlowSystem: + """Expand a reduced (clustered) FlowSystem back to full original timesteps. + + After solving a FlowSystem created with ``cluster()``, this method + disaggregates the FlowSystem by: + 1. Expanding all time series data from typical clusters to full timesteps + 2. Expanding the solution by mapping each typical cluster back to all + original clusters it represents + + For FlowSystems with periods and/or scenarios, each (period, scenario) + combination is expanded using its own cluster assignment. + + This enables using all existing solution accessors (``statistics``, ``plot``, etc.) + with full time resolution, where both the data and solution are consistently + expanded from the typical clusters. + + Returns: + FlowSystem: A new FlowSystem with full timesteps and expanded solution. + + Raises: + ValueError: If the FlowSystem was not created with ``cluster()``. + ValueError: If the FlowSystem has no solution. + + Examples: + Two-stage optimization with solution expansion: + + >>> # Stage 1: Size with reduced timesteps + >>> fs_reduced = flow_system.transform.cluster( + ... n_clusters=8, + ... cluster_duration='1D', + ... ) + >>> fs_reduced.optimize(solver) + >>> + >>> # Expand to full resolution FlowSystem + >>> fs_expanded = fs_reduced.transform.expand_solution() + >>> + >>> # Use all existing accessors with full timesteps + >>> fs_expanded.statistics.flow_rates # Full 8760 timesteps + >>> fs_expanded.statistics.plot.balance('HeatBus') # Full resolution plots + >>> fs_expanded.statistics.plot.heatmap('Boiler(Q_th)|flow_rate') + + Note: + The expanded FlowSystem repeats the typical cluster values for all + original clusters belonging to the same cluster. Both input data and solution + are consistently expanded, so they match. This is an approximation - + the actual dispatch at full resolution would differ due to + intra-cluster variations in time series data. + + For accurate dispatch results, use ``fix_sizes()`` to fix the sizes + from the reduced optimization and re-optimize at full resolution. + """ + from .flow_system import FlowSystem + + # Validate + if self._fs.clustering is None: + raise ValueError( + 'expand_solution() requires a FlowSystem created with cluster(). ' + 'This FlowSystem has no aggregation info.' + ) + if self._fs.solution is None: + raise ValueError('FlowSystem has no solution. Run optimize() or solve() first.') + + info = self._fs.clustering + cluster_structure = info.result.cluster_structure + if cluster_structure is None: + raise ValueError('No cluster structure available for expansion.') + + timesteps_per_cluster = cluster_structure.timesteps_per_cluster + n_clusters = ( + int(cluster_structure.n_clusters) + if isinstance(cluster_structure.n_clusters, (int, np.integer)) + else int(cluster_structure.n_clusters.values) + ) + + # Get original timesteps from clustering, but periods/scenarios from the FlowSystem + # (the clustered FlowSystem preserves the same periods/scenarios) + original_timesteps = info.original_timesteps + has_periods = self._fs.periods is not None + has_scenarios = self._fs.scenarios is not None + + periods = list(self._fs.periods) if has_periods else [None] + scenarios = list(self._fs.scenarios) if has_scenarios else [None] + n_original_timesteps = len(original_timesteps) + n_reduced_timesteps = n_clusters * timesteps_per_cluster + n_original_clusters = cluster_structure.n_original_clusters + + # Expand function using ClusterResult.expand_data() - handles multi-dimensional cases + # For charge_state with cluster dim, also includes the extra timestep + # Clamp to valid bounds to handle partial clusters at the end + last_original_cluster_idx = min( + (n_original_timesteps - 1) // timesteps_per_cluster, + n_original_clusters - 1, + ) + + def expand_da(da: xr.DataArray, var_name: str = '') -> xr.DataArray: + if 'time' not in da.dims: + return da.copy() + expanded = info.result.expand_data(da, original_time=original_timesteps) + + # For charge_state with cluster dim, append the extra timestep value + if var_name.endswith('|charge_state') and 'cluster' in da.dims: + # Get extra timestep from last cluster using vectorized selection + cluster_order = cluster_structure.cluster_order # (n_original_clusters,) or with period/scenario + if cluster_order.ndim == 1: + last_cluster = int(cluster_order[last_original_cluster_idx]) + extra_val = da.isel(cluster=last_cluster, time=-1) + else: + # Multi-dimensional: select last cluster for each period/scenario slice + last_clusters = cluster_order.isel(original_cluster=last_original_cluster_idx) + extra_val = da.isel(cluster=last_clusters, time=-1) + # Drop 'cluster'/'time' coords created by isel (kept as non-dim coords) + extra_val = extra_val.drop_vars(['cluster', 'time'], errors='ignore') + extra_val = extra_val.expand_dims(time=[original_timesteps_extra[-1]]) + expanded = xr.concat([expanded, extra_val], dim='time') + + return expanded + + # 1. Expand FlowSystem data (with cluster_weight set to 1.0 for all timesteps) + reduced_ds = self._fs.to_dataset(include_solution=False) + # Filter out cluster-related variables and copy attrs without clustering info + data_vars = { + name: expand_da(da, name) + for name, da in reduced_ds.data_vars.items() + if name != 'cluster_weight' and not name.startswith('clustering|') + } + attrs = { + k: v + for k, v in reduced_ds.attrs.items() + if k not in ('is_clustered', 'n_clusters', 'timesteps_per_cluster', 'clustering') + } + expanded_ds = xr.Dataset(data_vars, attrs=attrs) + # Compute timestep_duration from original timesteps + # Add extra timestep for duration calculation (assume same interval as last) + original_timesteps_extra = FlowSystem._create_timesteps_with_extra(original_timesteps, None) + timestep_duration = FlowSystem.calculate_timestep_duration(original_timesteps_extra) + expanded_ds.attrs['timestep_duration'] = timestep_duration.values.tolist() + + # Create cluster_weight with value 1.0 for all timesteps (no weighting needed for expanded) + # Use _combine_slices_to_dataarray for consistent multi-dim handling + ones_da = xr.DataArray(np.ones(n_original_timesteps), dims=['time'], coords={'time': original_timesteps}) + ones_slices = {(p, s): ones_da for p in periods for s in scenarios} + cluster_weight = self._combine_slices_to_dataarray( + ones_slices, ones_da, original_timesteps, periods, scenarios + ).rename('cluster_weight') + expanded_ds['cluster_weight'] = cluster_weight + + expanded_fs = FlowSystem.from_dataset(expanded_ds) + + # 2. Expand solution + # charge_state variables get their extra timestep via expand_da; others get NaN via reindex + reduced_solution = self._fs.solution + expanded_fs._solution = xr.Dataset( + {name: expand_da(da, name) for name, da in reduced_solution.data_vars.items()}, + attrs=reduced_solution.attrs, + ) + # Reindex to timesteps_extra for consistency with non-expanded FlowSystems + # (variables without extra timestep data will have NaN at the final timestep) + expanded_fs._solution = expanded_fs._solution.reindex(time=original_timesteps_extra) + + # 3. Combine charge_state with SOC_boundary for InterclusterStorageModel storages + # For intercluster storages, charge_state is relative (Ξ”E) and can be negative. + # Per Blanke et al. (2022) Eq. 9, actual SOC at time t in period d is: + # SOC(t) = SOC_boundary[d] * (1 - loss)^t_within_period + charge_state(t) + # where t_within_period is hours from period start (accounts for self-discharge decay). + n_original_timesteps_extra = len(original_timesteps_extra) + soc_boundary_vars = [name for name in reduced_solution.data_vars if name.endswith('|SOC_boundary')] + for soc_boundary_name in soc_boundary_vars: + storage_name = soc_boundary_name.rsplit('|', 1)[0] + charge_state_name = f'{storage_name}|charge_state' + if charge_state_name not in expanded_fs._solution: + continue + + soc_boundary = reduced_solution[soc_boundary_name] + expanded_charge_state = expanded_fs._solution[charge_state_name] + + # Map each original timestep (including extra) to its original period index + # The extra timestep belongs to the last period + original_cluster_indices = np.minimum( + np.arange(n_original_timesteps_extra) // timesteps_per_cluster, + n_original_clusters - 1, + ) + + # Select SOC_boundary for each timestep (boundary[d] for period d) + # SOC_boundary has dim 'cluster_boundary', we select indices 0..n_original_clusters-1 + soc_boundary_per_timestep = soc_boundary.isel( + cluster_boundary=xr.DataArray(original_cluster_indices, dims=['time']) + ) + soc_boundary_per_timestep = soc_boundary_per_timestep.assign_coords(time=original_timesteps_extra) + + # Apply self-discharge decay to SOC_boundary based on time within period + # Get the storage's relative_loss_per_hour from the clustered flow system + storage = self._fs.storages.get(storage_name) + if storage is not None: + # Time within period for each timestep (0, 1, 2, ..., timesteps_per_cluster-1, 0, 1, ...) + # The extra timestep is at index timesteps_per_cluster (one past the last within-cluster index) + time_within_period = np.arange(n_original_timesteps_extra) % timesteps_per_cluster + # The extra timestep gets the correct decay (timesteps_per_cluster) + time_within_period[-1] = timesteps_per_cluster + time_within_period_da = xr.DataArray( + time_within_period, dims=['time'], coords={'time': original_timesteps_extra} + ) + # Decay factor: (1 - loss)^t, using mean loss over time + loss_value = storage.relative_loss_per_hour.mean('time') + if (loss_value > 0).any(): + decay_da = (1 - loss_value) ** time_within_period_da + if 'cluster' in decay_da.dims: + # Map each timestep to its cluster's decay value + cluster_per_timestep = cluster_structure.cluster_order.values[original_cluster_indices] + decay_da = decay_da.isel(cluster=xr.DataArray(cluster_per_timestep, dims=['time'])).drop_vars( + 'cluster', errors='ignore' + ) + soc_boundary_per_timestep = soc_boundary_per_timestep * decay_da + + # Combine: actual_SOC = SOC_boundary * decay + charge_state + # Clip to non-negative since actual SOC cannot be negative + # (small negative values may occur due to constraint approximations in the model) + combined_charge_state = (expanded_charge_state + soc_boundary_per_timestep).clip(min=0) + expanded_fs._solution[charge_state_name] = combined_charge_state.assign_attrs(expanded_charge_state.attrs) + + # Remove SOC_boundary variables - they're cluster-specific and now incorporated into charge_state + for soc_boundary_name in soc_boundary_vars: + if soc_boundary_name in expanded_fs._solution: + del expanded_fs._solution[soc_boundary_name] + # Also drop the cluster_boundary coordinate (orphaned after removing SOC_boundary) + if 'cluster_boundary' in expanded_fs._solution.coords: + expanded_fs._solution = expanded_fs._solution.drop_vars('cluster_boundary') + + n_combinations = len(periods) * len(scenarios) + logger.info( + f'Expanded FlowSystem from {n_reduced_timesteps} to {n_original_timesteps} timesteps ' + f'({n_clusters} clusters' + + ( + f', {n_combinations} period/scenario combinations)' + if n_combinations > 1 + else f' β†’ {n_original_clusters} original clusters)' + ) + ) + + return expanded_fs diff --git a/mkdocs.yml b/mkdocs.yml index 551fac523..9eed96ad6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,8 +71,14 @@ nav: - Scenarios: notebooks/07-scenarios-and-periods.ipynb - Aggregation: notebooks/08a-aggregation.ipynb - Rolling Horizon: notebooks/08b-rolling-horizon.ipynb + - Clustering: + - Introduction: notebooks/08c-clustering.ipynb + - Storage Modes: notebooks/08c2-clustering-storage-modes.ipynb + - Multi-Period: notebooks/08d-clustering-multiperiod.ipynb + - Internals: notebooks/08e-clustering-internals.ipynb - Results: - Plotting: notebooks/09-plotting-and-data-access.ipynb + - Custom Data Plotting: notebooks/fxplot_accessor_demo.ipynb - API Reference: api-reference/ @@ -229,10 +235,14 @@ plugins: separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])' - mkdocs-jupyter: - execute: true # Execute notebooks during build + execute: !ENV [MKDOCS_JUPYTER_EXECUTE, true] # CI pre-executes in parallel allow_errors: false include_source: true include_requirejs: true + ignore: + - "notebooks/data/*.py" # Data generation scripts, not notebooks + execute_ignore: + - "notebooks/data/*.py" - plotly diff --git a/pyproject.toml b/pyproject.toml index e28b3048e..f80f83557 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,6 +110,10 @@ docs = [ "mkdocs-git-revision-date-localized-plugin==1.5.0", "mkdocs-minify-plugin==0.8.0", "notebook>=7.5.0", + # Realistic profile generation for examples + "demandlib >= 0.2.2, < 0.3", + "pvlib >= 0.10.0, < 0.14", + "holidays >= 0.40, < 1", ] [project.urls] @@ -205,17 +209,11 @@ filterwarnings = [ "ignore:SegmentedResults is deprecated:DeprecationWarning:flixopt", "ignore:ClusteredOptimization is deprecated:DeprecationWarning:flixopt", - # === Treat flixopt warnings as errors (strict mode for our code) === + # === Treat most flixopt warnings as errors (strict mode for our code) === # This ensures we catch deprecations, future changes, and user warnings in our own code "error::DeprecationWarning:flixopt", "error::FutureWarning:flixopt", "error::UserWarning:flixopt", - - # === Third-party warnings (mirrored from __init__.py) === - "ignore:.*minimal value.*exceeds.*:UserWarning:tsam", - "ignore:Coordinates across variables not equal:UserWarning:linopy", - "ignore:.*join will change from join='outer' to join='exact'.*:FutureWarning:linopy", - "ignore:numpy\\.ndarray size changed:RuntimeWarning", "ignore:.*network visualization is still experimental.*:UserWarning:flixopt", ] diff --git a/tests/deprecated/examples/03_Optimization_modes/example_optimization_modes.py b/tests/deprecated/examples/03_Optimization_modes/example_optimization_modes.py index e85c4329e..02e167c40 100644 --- a/tests/deprecated/examples/03_Optimization_modes/example_optimization_modes.py +++ b/tests/deprecated/examples/03_Optimization_modes/example_optimization_modes.py @@ -33,15 +33,10 @@ def get_solutions(optimizations: list, variable: str) -> xr.Dataset: # Segmented Properties segment_length, overlap_length = 96, 1 - # Aggregated Properties - clustering_parameters = fx.ClusteringParameters( - hours_per_period=6, - nr_of_periods=4, - fix_storage_flows=False, - aggregate_data_and_fix_non_binary_vars=True, - percentage_of_period_freedom=0, - penalty_of_period_freedom=0, - ) + # Clustering Properties + n_clusters = 4 + cluster_duration = '6h' + include_storage = False keep_extreme_periods = True imbalance_penalty = 1e5 # or set to None if not needed @@ -195,12 +190,31 @@ def get_solutions(optimizations: list, variable: str) -> xr.Dataset: optimizations.append(optimization) if aggregated: - if keep_extreme_periods: - clustering_parameters.time_series_for_high_peaks = [TS_heat_demand] - clustering_parameters.time_series_for_low_peaks = [TS_electricity_demand, TS_heat_demand] - optimization = fx.ClusteredOptimization('Aggregated', flow_system.copy(), clustering_parameters) - optimization.do_modeling() - optimization.solve(fx.solvers.HighsSolver(0.01 / 100, 60)) + # Use the new transform.cluster() API + # Note: time_series_for_high_peaks/low_peaks expect string labels matching dataset variables + time_series_for_high_peaks = ['WΓ€rmelast(Q_th_Last)|fixed_relative_profile'] if keep_extreme_periods else None + time_series_for_low_peaks = ( + ['Stromlast(P_el_Last)|fixed_relative_profile', 'WΓ€rmelast(Q_th_Last)|fixed_relative_profile'] + if keep_extreme_periods + else None + ) + + clustered_fs = flow_system.copy().transform.cluster( + n_clusters=n_clusters, + cluster_duration=cluster_duration, + time_series_for_high_peaks=time_series_for_high_peaks, + time_series_for_low_peaks=time_series_for_low_peaks, + ) + clustered_fs.optimize(fx.solvers.HighsSolver(0.01 / 100, 60)) + + # Wrap in a simple object for compatibility with comparison code + class ClusteredResult: + def __init__(self, name, fs): + self.name = name + self.flow_system = fs + self.durations = {'total': 0} # Placeholder + + optimization = ClusteredResult('Clustered', clustered_fs) optimizations.append(optimization) # --- Plotting for comparison --- diff --git a/tests/deprecated/test_bus.py b/tests/deprecated/test_bus.py index cc49a2073..9bb7ddbe3 100644 --- a/tests/deprecated/test_bus.py +++ b/tests/deprecated/test_bus.py @@ -74,8 +74,8 @@ def test_bus_penalty(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['TestBus->Penalty(temporal)'], model.variables['TestBus->Penalty(temporal)'] - == model.variables['TestBus|virtual_supply'] * 1e5 * model.hours_per_step - + model.variables['TestBus|virtual_demand'] * 1e5 * model.hours_per_step, + == model.variables['TestBus|virtual_supply'] * 1e5 * model.timestep_duration + + model.variables['TestBus|virtual_demand'] * 1e5 * model.timestep_duration, ) def test_bus_with_coords(self, basic_flow_system_linopy_coords, coords_config): diff --git a/tests/deprecated/test_effect.py b/tests/deprecated/test_effect.py index b3bb278f0..1cf625c1b 100644 --- a/tests/deprecated/test_effect.py +++ b/tests/deprecated/test_effect.py @@ -130,8 +130,8 @@ def test_bounds(self, basic_flow_system_linopy_coords, coords_config): assert_var_equal( model.variables['Effect1(temporal)|per_timestep'], model.add_variables( - lower=4.0 * model.hours_per_step, - upper=4.1 * model.hours_per_step, + lower=4.0 * model.timestep_duration, + upper=4.1 * model.timestep_duration, coords=model.get_coords(['time', 'period', 'scenario']), ), ) diff --git a/tests/deprecated/test_flow.py b/tests/deprecated/test_flow.py index 594bc1fbb..8e1ce1f53 100644 --- a/tests/deprecated/test_flow.py +++ b/tests/deprecated/test_flow.py @@ -23,7 +23,7 @@ def test_flow_minimal(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|total_flow_hours'], flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration).sum('time'), ) assert_var_equal(flow.submodel.flow_rate, model.add_variables(lower=0, upper=100, coords=model.get_coords())) assert_var_equal( @@ -61,7 +61,7 @@ def test_flow(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|total_flow_hours'], flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration).sum('time'), ) assert_var_equal( @@ -83,12 +83,12 @@ def test_flow(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|load_factor_min'], - flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] >= model.hours_per_step.sum('time') * 0.1 * 100, + flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] >= model.timestep_duration.sum('time') * 0.1 * 100, ) assert_conequal( model.constraints['Sink(WΓ€rme)|load_factor_max'], - flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] <= model.hours_per_step.sum('time') * 0.9 * 100, + flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] <= model.timestep_duration.sum('time') * 0.9 * 100, ) assert_sets_equal( @@ -129,13 +129,13 @@ def test_effects_per_flow_hour(self, basic_flow_system_linopy_coords, coords_con assert_conequal( model.constraints['Sink(WΓ€rme)->costs(temporal)'], model.variables['Sink(WΓ€rme)->costs(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step * costs_per_flow_hour, + == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration * costs_per_flow_hour, ) assert_conequal( model.constraints['Sink(WΓ€rme)->CO2(temporal)'], model.variables['Sink(WΓ€rme)->CO2(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step * co2_per_flow_hour, + == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration * co2_per_flow_hour, ) @@ -561,7 +561,7 @@ def test_flow_on(self, basic_flow_system_linopy_coords, coords_config): model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -580,7 +580,7 @@ def test_flow_on(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) def test_effects_per_active_hour(self, basic_flow_system_linopy_coords, coords_config): @@ -635,13 +635,13 @@ def test_effects_per_active_hour(self, basic_flow_system_linopy_coords, coords_c assert_conequal( model.constraints['Sink(WΓ€rme)->costs(temporal)'], model.variables['Sink(WΓ€rme)->costs(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step * costs_per_running_hour, + == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration * costs_per_running_hour, ) assert_conequal( model.constraints['Sink(WΓ€rme)->CO2(temporal)'], model.variables['Sink(WΓ€rme)->CO2(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step * co2_per_running_hour, + == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration * co2_per_running_hour, ) def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_config): @@ -687,7 +687,7 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.add_variables(lower=0, upper=8, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + mega = model.timestep_duration.sum('time') assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|ub'], @@ -698,7 +698,7 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.constraints['Sink(WΓ€rme)|uptime|forward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -706,14 +706,14 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.constraints['Sink(WΓ€rme)|uptime|backward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|status'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|initial'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * model.hours_per_step.isel(time=0), + == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * model.timestep_duration.isel(time=0), ) assert_conequal( @@ -768,7 +768,7 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.add_variables(lower=0, upper=8, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 3 + mega = model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 3 assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|ub'], @@ -779,7 +779,7 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.constraints['Sink(WΓ€rme)|uptime|forward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -787,14 +787,14 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.constraints['Sink(WΓ€rme)|uptime|backward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|status'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|initial'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 3)), + == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 3)), ) assert_conequal( @@ -850,7 +850,9 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.add_variables(lower=0, upper=12, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 1 # previously inactive for 1h + mega = ( + model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 1 + ) # previously inactive for 1h assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|ub'], @@ -861,7 +863,7 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.constraints['Sink(WΓ€rme)|downtime|forward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -869,14 +871,14 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.constraints['Sink(WΓ€rme)|downtime|backward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|inactive'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|initial'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 1)), + == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 1)), ) assert_conequal( @@ -933,7 +935,7 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.add_variables(lower=0, upper=12, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 2 + mega = model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 2 assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|ub'], @@ -944,7 +946,7 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.constraints['Sink(WΓ€rme)|downtime|forward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -952,14 +954,14 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.constraints['Sink(WΓ€rme)|downtime|backward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|inactive'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|initial'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 2)), + == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 2)), ) assert_conequal( @@ -1067,7 +1069,7 @@ def test_on_hours_limits(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) @@ -1131,7 +1133,7 @@ def test_flow_on_invest_optional(self, basic_flow_system_linopy_coords, coords_c model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -1157,7 +1159,7 @@ def test_flow_on_invest_optional(self, basic_flow_system_linopy_coords, coords_c assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) # Investment @@ -1233,7 +1235,7 @@ def test_flow_on_invest_non_optional(self, basic_flow_system_linopy_coords, coor model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -1251,7 +1253,7 @@ def test_flow_on_invest_non_optional(self, basic_flow_system_linopy_coords, coor assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) # Investment diff --git a/tests/deprecated/test_flow_system_resample.py b/tests/deprecated/test_flow_system_resample.py index c76946f80..549f05208 100644 --- a/tests/deprecated/test_flow_system_resample.py +++ b/tests/deprecated/test_flow_system_resample.py @@ -128,7 +128,7 @@ def test_time_metadata_updated(simple_fs): """Test time metadata correctly updated.""" fs_r = simple_fs.resample('3h', method='mean') assert len(fs_r.timesteps) == 8 - assert_allclose(fs_r.hours_per_timestep.values, 3.0) + assert_allclose(fs_r.timestep_duration.values, 3.0) assert fs_r.hours_of_last_timestep == 3.0 diff --git a/tests/deprecated/test_integration.py b/tests/deprecated/test_integration.py index 2f083b4fb..8ec23265e 100644 --- a/tests/deprecated/test_integration.py +++ b/tests/deprecated/test_integration.py @@ -258,15 +258,15 @@ def test_piecewise_conversion(self, flow_system_piecewise_conversion, highs_solv @pytest.mark.slow class TestModelingTypes: - @pytest.fixture(params=['full', 'segmented', 'aggregated']) + # Note: 'aggregated' case removed - ClusteredOptimization has been replaced by + # FlowSystem.transform.cluster(). See tests/test_clustering/ for new clustering tests. + @pytest.fixture(params=['full', 'segmented']) def modeling_calculation(self, request, flow_system_long, highs_solver): """ Fixture to run optimizations with different modeling types """ # Extract flow system and data from the fixture flow_system = flow_system_long[0] - thermal_load_ts = flow_system_long[1]['thermal_load_ts'] - electrical_load_ts = flow_system_long[1]['electrical_load_ts'] # Create calculation based on modeling type modeling_type = request.param @@ -277,23 +277,6 @@ def modeling_calculation(self, request, flow_system_long, highs_solver): elif modeling_type == 'segmented': calc = fx.SegmentedOptimization('segModel', flow_system, timesteps_per_segment=96, overlap_timesteps=1) calc.do_modeling_and_solve(highs_solver) - elif modeling_type == 'aggregated': - calc = fx.ClusteredOptimization( - 'aggModel', - flow_system, - fx.ClusteringParameters( - hours_per_period=6, - nr_of_periods=4, - fix_storage_flows=False, - aggregate_data_and_fix_non_binary_vars=True, - percentage_of_period_freedom=0, - penalty_of_period_freedom=0, - time_series_for_low_peaks=[electrical_load_ts, thermal_load_ts], - time_series_for_high_peaks=[thermal_load_ts], - ), - ) - calc.do_modeling() - calc.solve(highs_solver) return calc, modeling_type @@ -306,16 +289,15 @@ def test_modeling_types_costs(self, modeling_calculation): expected_costs = { 'full': 343613, 'segmented': 343613, # Approximate value - 'aggregated': 342967.0, } - if modeling_type in ['full', 'aggregated']: + if modeling_type == 'full': assert_almost_equal_numeric( calc.results.model['costs'].solution.item(), expected_costs[modeling_type], f'costs do not match for {modeling_type} modeling type', ) - else: + elif modeling_type == 'segmented': assert_almost_equal_numeric( calc.results.solution_without_overlap('costs(temporal)|per_timestep').sum(), expected_costs[modeling_type], diff --git a/tests/deprecated/test_linear_converter.py b/tests/deprecated/test_linear_converter.py index 57b911d64..d20d104d0 100644 --- a/tests/deprecated/test_linear_converter.py +++ b/tests/deprecated/test_linear_converter.py @@ -174,7 +174,7 @@ def test_linear_converter_with_status(self, basic_flow_system_linopy_coords, coo assert_conequal( model.constraints['Converter|active_hours'], model.variables['Converter|active_hours'] - == (model.variables['Converter|status'] * model.hours_per_step).sum('time'), + == (model.variables['Converter|status'] * model.timestep_duration).sum('time'), ) # Check conversion constraint @@ -188,7 +188,7 @@ def test_linear_converter_with_status(self, basic_flow_system_linopy_coords, coo assert_conequal( model.constraints['Converter->costs(temporal)'], model.variables['Converter->costs(temporal)'] - == model.variables['Converter|status'] * model.hours_per_step * 5, + == model.variables['Converter|status'] * model.timestep_duration * 5, ) def test_linear_converter_multidimensional(self, basic_flow_system_linopy_coords, coords_config): @@ -485,7 +485,7 @@ def test_piecewise_conversion_with_status(self, basic_flow_system_linopy_coords, assert 'Converter|active_hours' in model.constraints assert_conequal( model.constraints['Converter|active_hours'], - model['Converter|active_hours'] == (model['Converter|status'] * model.hours_per_step).sum('time'), + model['Converter|active_hours'] == (model['Converter|status'] * model.timestep_duration).sum('time'), ) # Verify that the costs effect is applied @@ -493,7 +493,7 @@ def test_piecewise_conversion_with_status(self, basic_flow_system_linopy_coords, assert_conequal( model.constraints['Converter->costs(temporal)'], model.variables['Converter->costs(temporal)'] - == model.variables['Converter|status'] * model.hours_per_step * 5, + == model.variables['Converter|status'] * model.timestep_duration * 5, ) diff --git a/tests/deprecated/test_on_hours_computation.py b/tests/deprecated/test_on_hours_computation.py index 578fd7792..c74332565 100644 --- a/tests/deprecated/test_on_hours_computation.py +++ b/tests/deprecated/test_on_hours_computation.py @@ -9,7 +9,7 @@ class TestComputeConsecutiveDuration: """Tests for the compute_consecutive_hours_in_state static method.""" @pytest.mark.parametrize( - 'binary_values, hours_per_timestep, expected', + 'binary_values, timestep_duration, expected', [ # Case 1: Single timestep DataArrays (xr.DataArray([1], dims=['time']), 5, 5), @@ -26,22 +26,22 @@ class TestComputeConsecutiveDuration: (xr.DataArray([0, 1, 1, 1, 0, 0], dims=['time']), 1, 0), # ends with 0 ], ) - def test_compute_duration(self, binary_values, hours_per_timestep, expected): + def test_compute_duration(self, binary_values, timestep_duration, expected): """Test compute_consecutive_hours_in_state with various inputs.""" - result = ModelingUtilities.compute_consecutive_hours_in_state(binary_values, hours_per_timestep) + result = ModelingUtilities.compute_consecutive_hours_in_state(binary_values, timestep_duration) assert np.isclose(result, expected) @pytest.mark.parametrize( - 'binary_values, hours_per_timestep', + 'binary_values, timestep_duration', [ - # Case: hours_per_timestep must be scalar + # Case: timestep_duration must be scalar (xr.DataArray([1, 1, 1, 1, 1], dims=['time']), np.array([1, 2])), ], ) - def test_compute_duration_raises_error(self, binary_values, hours_per_timestep): + def test_compute_duration_raises_error(self, binary_values, timestep_duration): """Test error conditions.""" with pytest.raises(TypeError): - ModelingUtilities.compute_consecutive_hours_in_state(binary_values, hours_per_timestep) + ModelingUtilities.compute_consecutive_hours_in_state(binary_values, timestep_duration) class TestComputePreviousOnStates: diff --git a/tests/deprecated/test_scenarios.py b/tests/deprecated/test_scenarios.py index 65ea62d81..2699647ad 100644 --- a/tests/deprecated/test_scenarios.py +++ b/tests/deprecated/test_scenarios.py @@ -341,12 +341,14 @@ def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): 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]) + # Scenario weights are always normalized - subset is re-normalized to sum to 1 + subset_weights = flow_system_full.scenario_weights[0:2] + expected_normalized = subset_weights / subset_weights.sum() + np.testing.assert_allclose(flow_system.scenario_weights.values, expected_normalized.values) - # Optimize using new API with normalize_weights=False + # Optimize using new API flow_system.optimize( fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60), - normalize_weights=False, ) # Penalty has same structure as other effects: 'Penalty' is the total, 'Penalty(temporal)' and 'Penalty(periodic)' are components @@ -769,7 +771,10 @@ def test_weights_selection(): # Verify weights are correctly sliced assert fs_subset.scenarios.equals(pd.Index(['base', 'high'], name='scenario')) - np.testing.assert_allclose(fs_subset.scenario_weights.values, custom_scenario_weights[[0, 2]]) + # Scenario weights are always normalized - subset is re-normalized to sum to 1 + subset_weights = np.array([0.3, 0.2]) # Original weights for selected scenarios + expected_normalized = subset_weights / subset_weights.sum() + np.testing.assert_allclose(fs_subset.scenario_weights.values, expected_normalized) # Verify weights are 1D with just scenario dimension (no period dimension) assert fs_subset.scenario_weights.dims == ('scenario',) diff --git a/tests/deprecated/test_storage.py b/tests/deprecated/test_storage.py index 15170a321..3fd47fbf8 100644 --- a/tests/deprecated/test_storage.py +++ b/tests/deprecated/test_storage.py @@ -73,8 +73,8 @@ def test_basic_storage(self, basic_flow_system_linopy_coords, coords_config): 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, + + model.variables['TestStorage(Q_th_in)|flow_rate'] * model.timestep_duration + - model.variables['TestStorage(Q_th_out)|flow_rate'] * model.timestep_duration, ) # Check initial charge state constraint assert_conequal( @@ -146,7 +146,7 @@ def test_lossy_storage(self, basic_flow_system_linopy_coords, coords_config): charge_state = model.variables['TestStorage|charge_state'] rel_loss = 0.05 - hours_per_step = model.hours_per_step + timestep_duration = model.timestep_duration charge_rate = model.variables['TestStorage(Q_th_in)|flow_rate'] discharge_rate = model.variables['TestStorage(Q_th_out)|flow_rate'] eff_charge = 0.9 @@ -155,9 +155,9 @@ def test_lossy_storage(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['TestStorage|charge_state'], charge_state.isel(time=slice(1, None)) - == charge_state.isel(time=slice(None, -1)) * (1 - rel_loss) ** hours_per_step - + charge_rate * eff_charge * hours_per_step - - discharge_rate / eff_discharge * hours_per_step, + == charge_state.isel(time=slice(None, -1)) * (1 - rel_loss) ** timestep_duration + + charge_rate * eff_charge * timestep_duration + - discharge_rate / eff_discharge * timestep_duration, ) # Check initial charge state constraint @@ -242,8 +242,8 @@ def test_charge_state_bounds(self, basic_flow_system_linopy_coords, coords_confi 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, + + model.variables['TestStorage(Q_th_in)|flow_rate'] * model.timestep_duration + - model.variables['TestStorage(Q_th_out)|flow_rate'] * model.timestep_duration, ) # Check initial charge state constraint assert_conequal( diff --git a/tests/test_bus.py b/tests/test_bus.py index cc49a2073..9bb7ddbe3 100644 --- a/tests/test_bus.py +++ b/tests/test_bus.py @@ -74,8 +74,8 @@ def test_bus_penalty(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['TestBus->Penalty(temporal)'], model.variables['TestBus->Penalty(temporal)'] - == model.variables['TestBus|virtual_supply'] * 1e5 * model.hours_per_step - + model.variables['TestBus|virtual_demand'] * 1e5 * model.hours_per_step, + == model.variables['TestBus|virtual_supply'] * 1e5 * model.timestep_duration + + model.variables['TestBus|virtual_demand'] * 1e5 * model.timestep_duration, ) def test_bus_with_coords(self, basic_flow_system_linopy_coords, coords_config): diff --git a/tests/test_cluster_reduce_expand.py b/tests/test_cluster_reduce_expand.py new file mode 100644 index 000000000..4059470ee --- /dev/null +++ b/tests/test_cluster_reduce_expand.py @@ -0,0 +1,836 @@ +"""Tests for cluster() and expand_solution() functionality.""" + +import numpy as np +import pandas as pd +import pytest +from numpy.testing import assert_allclose + +import flixopt as fx + + +def create_simple_system(timesteps: pd.DatetimeIndex) -> fx.FlowSystem: + """Create a simple FlowSystem for testing clustering.""" + # Create varying demand - different for each day to test clustering + hours = len(timesteps) + demand = np.sin(np.linspace(0, 4 * np.pi, hours)) * 10 + 15 # Oscillating demand + + flow_system = fx.FlowSystem(timesteps) + flow_system.add_elements( + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Sink('HeatDemand', inputs=[fx.Flow('Q', bus='Heat', fixed_relative_profile=demand, size=1)]), + fx.Source('GasSource', outputs=[fx.Flow('Gas', bus='Gas', effects_per_flow_hour=0.05)]), + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.9, + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + thermal_flow=fx.Flow('Q_th', bus='Heat'), + ), + ) + return flow_system + + +@pytest.fixture +def timesteps_2_days(): + """48 hour timesteps (2 days).""" + return pd.date_range('2020-01-01', periods=48, freq='h') + + +@pytest.fixture +def timesteps_8_days(): + """192 hour timesteps (8 days) - more realistic for clustering.""" + return pd.date_range('2020-01-01', periods=192, freq='h') + + +def test_cluster_creates_reduced_timesteps(timesteps_8_days): + """Test that cluster creates a FlowSystem with fewer timesteps.""" + fs = create_simple_system(timesteps_8_days) + + # Reduce to 2 typical clusters (days) + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + + # Clustered FlowSystem has 2D structure: (cluster, time) + # - timesteps: within-cluster time (24 hours) + # - clusters: cluster indices (2 clusters) + # Total effective timesteps = 2 * 24 = 48 + assert len(fs_reduced.timesteps) == 24 # Within-cluster time + assert len(fs_reduced.clusters) == 2 # Number of clusters + assert len(fs_reduced.timesteps) * len(fs_reduced.clusters) == 48 # Total + assert hasattr(fs_reduced, 'clustering') + assert fs_reduced.clustering.result.cluster_structure.n_clusters == 2 + + +def test_expand_solution_restores_full_timesteps(solver_fixture, timesteps_8_days): + """Test that expand_solution restores full timestep count.""" + fs = create_simple_system(timesteps_8_days) + + # Reduce to 2 typical clusters + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + + # Optimize + fs_reduced.optimize(solver_fixture) + assert fs_reduced.solution is not None + # Clustered: 24 within-cluster timesteps, 2 clusters + assert len(fs_reduced.timesteps) == 24 + assert len(fs_reduced.clusters) == 2 + + # Expand back to full + fs_expanded = fs_reduced.transform.expand_solution() + + # Should have original timestep count (flat, no clusters) + assert len(fs_expanded.timesteps) == 192 + assert fs_expanded.clusters is None # Expanded FlowSystem has no cluster dimension + assert fs_expanded.solution is not None + + +def test_expand_solution_preserves_solution_variables(solver_fixture, timesteps_8_days): + """Test that expand_solution keeps all solution variables.""" + fs = create_simple_system(timesteps_8_days) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + fs_reduced.optimize(solver_fixture) + + reduced_vars = set(fs_reduced.solution.data_vars) + + fs_expanded = fs_reduced.transform.expand_solution() + expanded_vars = set(fs_expanded.solution.data_vars) + + # Should have all the same variables + assert reduced_vars == expanded_vars + + +def test_expand_solution_maps_values_correctly(solver_fixture, timesteps_8_days): + """Test that expand_solution correctly maps typical cluster values to all segments.""" + fs = create_simple_system(timesteps_8_days) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + fs_reduced.optimize(solver_fixture) + + # Get cluster_order to know mapping + info = fs_reduced.clustering + cluster_order = info.result.cluster_structure.cluster_order.values + timesteps_per_cluster = info.result.cluster_structure.timesteps_per_cluster # 24 + + reduced_flow = fs_reduced.solution['Boiler(Q_th)|flow_rate'].values + + fs_expanded = fs_reduced.transform.expand_solution() + expanded_flow = fs_expanded.solution['Boiler(Q_th)|flow_rate'].values + + # Check that values are correctly mapped + # For each original segment, values should match the corresponding typical cluster + for orig_segment_idx, cluster_id in enumerate(cluster_order): + orig_start = orig_segment_idx * timesteps_per_cluster + orig_end = orig_start + timesteps_per_cluster + + # Values in the expanded solution for this original segment + # should match the reduced solution for the corresponding typical cluster + # With 2D cluster structure, use cluster_id to index the cluster dimension + # Note: solution may have extra timesteps (timesteps_extra), so slice to timesteps_per_cluster + if reduced_flow.ndim == 2: + # 2D structure: (cluster, time) - exclude extra timestep if present + expected = reduced_flow[cluster_id, :timesteps_per_cluster] + else: + # Flat structure: (time,) + typical_start = cluster_id * timesteps_per_cluster + typical_end = typical_start + timesteps_per_cluster + expected = reduced_flow[typical_start:typical_end] + actual = expanded_flow[orig_start:orig_end] + + assert_allclose(actual, expected, rtol=1e-10) + + +def test_expand_solution_enables_statistics_accessor(solver_fixture, timesteps_8_days): + """Test that statistics accessor works on expanded FlowSystem.""" + fs = create_simple_system(timesteps_8_days) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + fs_reduced.optimize(solver_fixture) + + fs_expanded = fs_reduced.transform.expand_solution() + + # These should work without errors + flow_rates = fs_expanded.statistics.flow_rates + assert 'Boiler(Q_th)' in flow_rates + assert len(flow_rates['Boiler(Q_th)'].coords['time']) == 193 # 192 + 1 extra timestep + + flow_hours = fs_expanded.statistics.flow_hours + assert 'Boiler(Q_th)' in flow_hours + + +def test_expand_solution_statistics_match_clustered(solver_fixture, timesteps_8_days): + """Test that total_effects match between clustered and expanded FlowSystem.""" + fs = create_simple_system(timesteps_8_days) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + fs_reduced.optimize(solver_fixture) + + fs_expanded = fs_reduced.transform.expand_solution() + + # Total effects should match between clustered and expanded + reduced_total = fs_reduced.statistics.total_effects['costs'].sum('contributor').item() + expanded_total = fs_expanded.statistics.total_effects['costs'].sum('contributor').item() + + assert_allclose(reduced_total, expanded_total, rtol=1e-6) + + # Flow hours should also match (need to sum over time with proper weighting) + # With 2D cluster structure, sum over both cluster and time dimensions + reduced_fh = fs_reduced.statistics.flow_hours['Boiler(Q_th)'] * fs_reduced.cluster_weight + reduced_flow_hours = reduced_fh.sum().item() # Sum over all dimensions + expanded_fh = fs_expanded.statistics.flow_hours['Boiler(Q_th)'] * fs_expanded.cluster_weight + expanded_flow_hours = expanded_fh.sum().item() + + assert_allclose(reduced_flow_hours, expanded_flow_hours, rtol=1e-6) + + +def test_expand_solution_withoutclustering_raises(solver_fixture, timesteps_2_days): + """Test that expand_solution raises error if not a reduced FlowSystem.""" + fs = create_simple_system(timesteps_2_days) + fs.optimize(solver_fixture) + + with pytest.raises(ValueError, match='cluster'): + fs.transform.expand_solution() + + +def test_expand_solution_without_solution_raises(timesteps_8_days): + """Test that expand_solution raises error if no solution.""" + fs = create_simple_system(timesteps_8_days) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + # Don't optimize - no solution + + with pytest.raises(ValueError, match='no solution'): + fs_reduced.transform.expand_solution() + + +# ==================== Multi-dimensional Tests ==================== + + +def create_system_with_scenarios(timesteps: pd.DatetimeIndex, scenarios: pd.Index) -> fx.FlowSystem: + """Create a FlowSystem with scenarios for testing.""" + hours = len(timesteps) + + # Create different demand profiles per scenario + demands = {} + for i, scenario in enumerate(scenarios): + # Different pattern per scenario + base_demand = np.sin(np.linspace(0, 4 * np.pi, hours)) * 10 + 15 + demands[scenario] = base_demand * (1 + 0.2 * i) # Scale differently per scenario + + # Create DataFrame with scenarios as columns + demand_df = pd.DataFrame(demands, index=timesteps) + + flow_system = fx.FlowSystem(timesteps, scenarios=scenarios) + flow_system.add_elements( + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Sink( + 'HeatDemand', + inputs=[fx.Flow('Q', bus='Heat', fixed_relative_profile=demand_df, size=1)], + ), + fx.Source('GasSource', outputs=[fx.Flow('Gas', bus='Gas', effects_per_flow_hour=0.05)]), + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.9, + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + thermal_flow=fx.Flow('Q_th', bus='Heat'), + ), + ) + return flow_system + + +@pytest.fixture +def scenarios_2(): + """Two scenarios for testing.""" + return pd.Index(['base', 'high'], name='scenario') + + +def test_cluster_with_scenarios(timesteps_8_days, scenarios_2): + """Test that cluster handles scenarios correctly.""" + fs = create_system_with_scenarios(timesteps_8_days, scenarios_2) + + # Verify scenarios are set up correctly + assert fs.scenarios is not None + assert len(fs.scenarios) == 2 + + # Reduce to 2 typical clusters + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + + # Clustered: 24 within-cluster timesteps, 2 clusters + # Total effective timesteps = 2 * 24 = 48 + assert len(fs_reduced.timesteps) == 24 + assert len(fs_reduced.clusters) == 2 + assert len(fs_reduced.timesteps) * len(fs_reduced.clusters) == 48 + + # Should have aggregation info with cluster structure + info = fs_reduced.clustering + assert info is not None + assert info.result.cluster_structure is not None + assert info.result.cluster_structure.n_clusters == 2 + # Clustered FlowSystem preserves scenarios + assert fs_reduced.scenarios is not None + assert len(fs_reduced.scenarios) == 2 + + +def test_cluster_and_expand_with_scenarios(solver_fixture, timesteps_8_days, scenarios_2): + """Test full cluster -> optimize -> expand_solution cycle with scenarios.""" + fs = create_system_with_scenarios(timesteps_8_days, scenarios_2) + + # Reduce + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + + # Optimize + fs_reduced.optimize(solver_fixture) + assert fs_reduced.solution is not None + + # Expand + fs_expanded = fs_reduced.transform.expand_solution() + + # Should have original timesteps + assert len(fs_expanded.timesteps) == 192 + + # Solution should have scenario dimension + flow_var = 'Boiler(Q_th)|flow_rate' + assert flow_var in fs_expanded.solution + assert 'scenario' in fs_expanded.solution[flow_var].dims + assert len(fs_expanded.solution[flow_var].coords['time']) == 193 # 192 + 1 extra timestep + + +def test_expand_solution_maps_scenarios_independently(solver_fixture, timesteps_8_days, scenarios_2): + """Test that expand_solution correctly maps scenarios in multi-scenario systems.""" + fs = create_system_with_scenarios(timesteps_8_days, scenarios_2) + + fs_reduced = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + fs_reduced.optimize(solver_fixture) + + info = fs_reduced.clustering + cluster_structure = info.result.cluster_structure + timesteps_per_cluster = cluster_structure.timesteps_per_cluster # 24 + + reduced_flow = fs_reduced.solution['Boiler(Q_th)|flow_rate'] + fs_expanded = fs_reduced.transform.expand_solution() + expanded_flow = fs_expanded.solution['Boiler(Q_th)|flow_rate'] + + # Check mapping for each scenario using its own cluster_order + for scenario in scenarios_2: + # Get the cluster_order for THIS scenario + cluster_order = cluster_structure.get_cluster_order_for_slice(scenario=scenario) + + reduced_scenario = reduced_flow.sel(scenario=scenario).values + expanded_scenario = expanded_flow.sel(scenario=scenario).values + + # Verify mapping is correct for this scenario using its own cluster_order + for orig_segment_idx, cluster_id in enumerate(cluster_order): + orig_start = orig_segment_idx * timesteps_per_cluster + orig_end = orig_start + timesteps_per_cluster + + # With 2D cluster structure, use cluster_id to index the cluster dimension + # Note: solution may have extra timesteps (timesteps_extra), so slice to timesteps_per_cluster + if reduced_scenario.ndim == 2: + # 2D structure: (cluster, time) - exclude extra timestep if present + expected = reduced_scenario[cluster_id, :timesteps_per_cluster] + else: + # Flat structure: (time,) + typical_start = cluster_id * timesteps_per_cluster + typical_end = typical_start + timesteps_per_cluster + expected = reduced_scenario[typical_start:typical_end] + actual = expanded_scenario[orig_start:orig_end] + + assert_allclose(actual, expected, rtol=1e-10, err_msg=f'Mismatch for scenario {scenario}') + + +# ==================== Storage Clustering Tests ==================== + + +def create_system_with_storage( + timesteps: pd.DatetimeIndex, + cluster_mode: str = 'intercluster_cyclic', + relative_loss_per_hour: float = 0.0, +) -> fx.FlowSystem: + """Create a FlowSystem with storage for testing clustering. + + Args: + timesteps: DatetimeIndex for the simulation. + cluster_mode: Storage cluster mode ('independent', 'cyclic', 'intercluster', 'intercluster_cyclic'). + relative_loss_per_hour: Self-discharge rate per hour (0.0 = no loss). + """ + # Create demand pattern: high during day (hours 8-18), low at night + hour_of_day = np.array([t.hour for t in timesteps]) + demand = np.where((hour_of_day >= 8) & (hour_of_day < 18), 20, 5) + + flow_system = fx.FlowSystem(timesteps) + flow_system.add_elements( + fx.Bus('Elec'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Source('Grid', outputs=[fx.Flow('P', bus='Elec', size=100, effects_per_flow_hour=0.1)]), + fx.Sink('Load', inputs=[fx.Flow('P', bus='Elec', fixed_relative_profile=demand, size=1)]), + fx.Storage( + 'Battery', + charging=fx.Flow('charge', bus='Elec', size=30), + discharging=fx.Flow('discharge', bus='Elec', size=30), + capacity_in_flow_hours=100, + relative_loss_per_hour=relative_loss_per_hour, + cluster_mode=cluster_mode, + ), + ) + return flow_system + + +class TestStorageClusterModes: + """Tests for different storage cluster_mode options.""" + + def test_storage_cluster_mode_independent(self, solver_fixture, timesteps_8_days): + """Storage with cluster_mode='independent' - each cluster starts fresh.""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='independent') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Should have charge_state in solution + assert 'Battery|charge_state' in fs_clustered.solution + + # Independent mode should NOT have SOC_boundary + assert 'Battery|SOC_boundary' not in fs_clustered.solution + + # Verify solution is valid (no errors) + assert fs_clustered.solution is not None + + def test_storage_cluster_mode_cyclic(self, solver_fixture, timesteps_8_days): + """Storage with cluster_mode='cyclic' - start equals end per cluster.""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='cyclic') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Should have charge_state in solution + assert 'Battery|charge_state' in fs_clustered.solution + + # Cyclic mode should NOT have SOC_boundary (only intercluster modes do) + assert 'Battery|SOC_boundary' not in fs_clustered.solution + + def test_storage_cluster_mode_intercluster(self, solver_fixture, timesteps_8_days): + """Storage with cluster_mode='intercluster' - SOC links across clusters.""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='intercluster') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Intercluster mode SHOULD have SOC_boundary + assert 'Battery|SOC_boundary' in fs_clustered.solution + + soc_boundary = fs_clustered.solution['Battery|SOC_boundary'] + assert 'cluster_boundary' in soc_boundary.dims + + # Number of boundaries = n_original_clusters + 1 + n_original_clusters = fs_clustered.clustering.result.cluster_structure.n_original_clusters + assert soc_boundary.sizes['cluster_boundary'] == n_original_clusters + 1 + + def test_storage_cluster_mode_intercluster_cyclic(self, solver_fixture, timesteps_8_days): + """Storage with cluster_mode='intercluster_cyclic' - linked with yearly cycling.""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='intercluster_cyclic') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Intercluster_cyclic mode SHOULD have SOC_boundary + assert 'Battery|SOC_boundary' in fs_clustered.solution + + soc_boundary = fs_clustered.solution['Battery|SOC_boundary'] + assert 'cluster_boundary' in soc_boundary.dims + + # First and last SOC_boundary values should be equal (cyclic constraint) + first_soc = soc_boundary.isel(cluster_boundary=0).item() + last_soc = soc_boundary.isel(cluster_boundary=-1).item() + assert_allclose(first_soc, last_soc, rtol=1e-6) + + +class TestInterclusterStorageLinking: + """Tests for inter-cluster storage linking and SOC_boundary behavior.""" + + def test_intercluster_storage_has_soc_boundary(self, solver_fixture, timesteps_8_days): + """Verify intercluster storage creates SOC_boundary variable.""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='intercluster_cyclic') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Verify SOC_boundary exists in solution + assert 'Battery|SOC_boundary' in fs_clustered.solution + soc_boundary = fs_clustered.solution['Battery|SOC_boundary'] + assert 'cluster_boundary' in soc_boundary.dims + + def test_expand_solution_combines_soc_boundary_with_charge_state(self, solver_fixture, timesteps_8_days): + """Expanded charge_state should be non-negative (combined with SOC_boundary).""" + fs = create_system_with_storage(timesteps_8_days, cluster_mode='intercluster_cyclic') + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Note: Before expansion, charge_state represents Ξ”E (relative to period start) + # which can be negative. After expansion, it becomes absolute SOC. + + # After expansion: charge_state should be non-negative (absolute SOC) + fs_expanded = fs_clustered.transform.expand_solution() + cs_after = fs_expanded.solution['Battery|charge_state'] + + # All values should be >= 0 (with small tolerance for numerical issues) + assert (cs_after >= -0.01).all(), f'Negative charge_state found: min={float(cs_after.min())}' + + def test_storage_self_discharge_decay_in_expansion(self, solver_fixture, timesteps_8_days): + """Verify self-discharge decay factor applied correctly during expansion.""" + # Use significant self-discharge to make decay visible + fs = create_system_with_storage( + timesteps_8_days, + cluster_mode='intercluster_cyclic', + relative_loss_per_hour=0.01, # 1% per hour + ) + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Expand solution + fs_expanded = fs_clustered.transform.expand_solution() + cs_expanded = fs_expanded.solution['Battery|charge_state'] + + # With self-discharge, SOC should decay over time within each period + # The expanded solution should still be non-negative + assert (cs_expanded >= -0.01).all() + + def test_expanded_charge_state_matches_manual_calculation(self, solver_fixture, timesteps_8_days): + """Verify expanded charge_state = SOC_boundary * decay + delta_E formula.""" + loss_rate = 0.01 # 1% per hour + fs = create_system_with_storage( + timesteps_8_days, + cluster_mode='intercluster_cyclic', + relative_loss_per_hour=loss_rate, + ) + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Get values needed for manual calculation + soc_boundary = fs_clustered.solution['Battery|SOC_boundary'] + cs_clustered = fs_clustered.solution['Battery|charge_state'] + cluster_structure = fs_clustered.clustering.result.cluster_structure + cluster_order = cluster_structure.cluster_order.values + timesteps_per_cluster = cluster_structure.timesteps_per_cluster + + fs_expanded = fs_clustered.transform.expand_solution() + cs_expanded = fs_expanded.solution['Battery|charge_state'] + + # Manual verification for first few timesteps of first period + p = 0 # First period + cluster = int(cluster_order[p]) + soc_b = soc_boundary.isel(cluster_boundary=p).item() + + for t in [0, 5, 12, 23]: + global_t = p * timesteps_per_cluster + t + delta_e = cs_clustered.isel(cluster=cluster, time=t).item() + decay = (1 - loss_rate) ** t + expected = soc_b * decay + delta_e + expected_clipped = max(0.0, expected) + actual = cs_expanded.isel(time=global_t).item() + + assert_allclose( + actual, + expected_clipped, + rtol=0.01, + err_msg=f'Mismatch at period {p}, time {t}: expected {expected_clipped}, got {actual}', + ) + + +# ==================== Multi-Period Clustering Tests ==================== + + +def create_system_with_periods(timesteps: pd.DatetimeIndex, periods: pd.Index) -> fx.FlowSystem: + """Create a FlowSystem with periods for testing multi-period clustering.""" + hours = len(timesteps) + # Create demand pattern that varies by day to ensure multiple clusters + hour_of_day = np.array([t.hour for t in timesteps]) + day_of_year = np.arange(hours) // 24 + # Add day-based variation: odd days have higher demand + base_demand = np.where((hour_of_day >= 8) & (hour_of_day < 18), 20, 8) + demand = base_demand * (1 + 0.3 * (day_of_year % 2)) # 30% higher on odd days + + flow_system = fx.FlowSystem(timesteps, periods=periods) + flow_system.add_elements( + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Sink('HeatDemand', inputs=[fx.Flow('Q', bus='Heat', fixed_relative_profile=demand, size=1)]), + fx.Source('GasSource', outputs=[fx.Flow('Gas', bus='Gas', effects_per_flow_hour=0.05)]), + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.9, + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + thermal_flow=fx.Flow('Q_th', bus='Heat'), + ), + ) + return flow_system + + +def create_system_with_periods_and_scenarios( + timesteps: pd.DatetimeIndex, periods: pd.Index, scenarios: pd.Index +) -> fx.FlowSystem: + """Create a FlowSystem with both periods and scenarios.""" + import xarray as xr + + hours = len(timesteps) + + # Create demand that varies by scenario AND by day (for clustering) + hour_of_day = np.array([t.hour for t in timesteps]) + day_of_year = np.arange(hours) // 24 + base_demand = np.where((hour_of_day >= 8) & (hour_of_day < 18), 20, 8) + # Add day variation for clustering + base_demand = base_demand * (1 + 0.3 * (day_of_year % 2)) + + # Create demand array with explicit scenario dimension using xarray + # Shape: (time, scenario) + demand_data = np.column_stack([base_demand * (1 + 0.2 * i) for i in range(len(scenarios))]) + demand_da = xr.DataArray( + demand_data, + dims=['time', 'scenario'], + coords={'time': timesteps, 'scenario': scenarios}, + ) + + flow_system = fx.FlowSystem(timesteps, periods=periods, scenarios=scenarios) + flow_system.add_elements( + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Sink( + 'HeatDemand', + inputs=[fx.Flow('Q', bus='Heat', fixed_relative_profile=demand_da, size=1)], + ), + fx.Source('GasSource', outputs=[fx.Flow('Gas', bus='Gas', effects_per_flow_hour=0.05)]), + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.9, + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + thermal_flow=fx.Flow('Q_th', bus='Heat'), + ), + ) + return flow_system + + +@pytest.fixture +def periods_2(): + """Two periods for testing.""" + return pd.Index([2020, 2021], name='period') + + +class TestMultiPeriodClustering: + """Tests for clustering with periods dimension.""" + + def test_cluster_with_periods(self, timesteps_8_days, periods_2): + """Test clustering with periods dimension.""" + fs = create_system_with_periods(timesteps_8_days, periods_2) + + # Verify periods are set up correctly + assert fs.periods is not None + assert len(fs.periods) == 2 + + # Cluster + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Should have period dimension preserved + assert fs_clustered.periods is not None + assert len(fs_clustered.periods) == 2 + + # Clustered: 24 within-cluster timesteps, 2 clusters + assert len(fs_clustered.timesteps) == 24 + assert len(fs_clustered.clusters) == 2 + + def test_cluster_with_periods_optimizes(self, solver_fixture, timesteps_8_days, periods_2): + """Test that clustering with periods can be optimized.""" + fs = create_system_with_periods(timesteps_8_days, periods_2) + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Should have solution with period dimension + assert fs_clustered.solution is not None + flow_var = 'Boiler(Q_th)|flow_rate' + assert flow_var in fs_clustered.solution + assert 'period' in fs_clustered.solution[flow_var].dims + + def test_expand_solution_with_periods(self, solver_fixture, timesteps_8_days, periods_2): + """Verify expansion handles period dimension correctly.""" + fs = create_system_with_periods(timesteps_8_days, periods_2) + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Expand + fs_expanded = fs_clustered.transform.expand_solution() + + # Should have original timesteps and periods + assert len(fs_expanded.timesteps) == 192 + assert fs_expanded.periods is not None + assert len(fs_expanded.periods) == 2 + + # Solution should have period dimension + flow_var = 'Boiler(Q_th)|flow_rate' + assert 'period' in fs_expanded.solution[flow_var].dims + assert len(fs_expanded.solution[flow_var].coords['time']) == 193 # 192 + 1 extra timestep + + def test_cluster_with_periods_and_scenarios(self, solver_fixture, timesteps_8_days, periods_2, scenarios_2): + """Clustering should work with both periods and scenarios.""" + fs = create_system_with_periods_and_scenarios(timesteps_8_days, periods_2, scenarios_2) + + # Verify setup + assert fs.periods is not None + assert fs.scenarios is not None + assert len(fs.periods) == 2 + assert len(fs.scenarios) == 2 + + # Cluster and optimize + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + fs_clustered.optimize(solver_fixture) + + # Verify dimensions + flow_var = 'Boiler(Q_th)|flow_rate' + assert 'period' in fs_clustered.solution[flow_var].dims + assert 'scenario' in fs_clustered.solution[flow_var].dims + assert 'cluster' in fs_clustered.solution[flow_var].dims + + # Expand and verify + fs_expanded = fs_clustered.transform.expand_solution() + assert 'period' in fs_expanded.solution[flow_var].dims + assert 'scenario' in fs_expanded.solution[flow_var].dims + assert len(fs_expanded.solution[flow_var].coords['time']) == 193 # 192 + 1 extra timestep + + +# ==================== Peak Selection Tests ==================== + + +def create_system_with_peak_demand(timesteps: pd.DatetimeIndex) -> fx.FlowSystem: + """Create a FlowSystem with clearly identifiable peak demand days.""" + hours = len(timesteps) + + # Create demand with distinct patterns to ensure multiple clusters + # Days 0,1: low demand (base pattern) + # Days 2,3: medium demand (higher pattern) + # Days 4,5,6: normal demand (moderate pattern) + # Day 7: extreme peak (very high) + day = np.arange(hours) // 24 + hour_of_day = np.arange(hours) % 24 + + # Base pattern varies by day group + base = np.where((hour_of_day >= 8) & (hour_of_day < 18), 15, 5) + + demand = np.where( + (day == 7) & (hour_of_day >= 10) & (hour_of_day < 14), + 50, # Extreme peak on day 7 + np.where( + day <= 1, + base * 0.7, # Low days + np.where(day <= 3, base * 1.3, base), # Medium days vs normal + ), + ) + + flow_system = fx.FlowSystem(timesteps) + flow_system.add_elements( + fx.Bus('Heat'), + fx.Bus('Gas'), + fx.Effect('costs', '€', is_standard=True, is_objective=True), + fx.Sink('HeatDemand', inputs=[fx.Flow('Q', bus='Heat', fixed_relative_profile=demand, size=1)]), + fx.Source('GasSource', outputs=[fx.Flow('Gas', bus='Gas', effects_per_flow_hour=0.05)]), + fx.linear_converters.Boiler( + 'Boiler', + thermal_efficiency=0.9, + fuel_flow=fx.Flow('Q_fu', bus='Gas'), + thermal_flow=fx.Flow('Q_th', bus='Heat'), + ), + ) + return flow_system + + +class TestPeakSelection: + """Tests for time_series_for_high_peaks and time_series_for_low_peaks parameters.""" + + def test_time_series_for_high_peaks_parameter_accepted(self, timesteps_8_days): + """Verify time_series_for_high_peaks parameter is accepted.""" + fs = create_system_with_peak_demand(timesteps_8_days) + + # Should not raise an error + fs_clustered = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], + ) + + assert fs_clustered is not None + assert len(fs_clustered.clusters) == 2 + + def test_time_series_for_low_peaks_parameter_accepted(self, timesteps_8_days): + """Verify time_series_for_low_peaks parameter is accepted.""" + fs = create_system_with_peak_demand(timesteps_8_days) + + # Should not raise an error + # Note: tsam requires n_clusters >= 3 when using low_peaks to avoid index error + fs_clustered = fs.transform.cluster( + n_clusters=3, + cluster_duration='1D', + time_series_for_low_peaks=['HeatDemand(Q)|fixed_relative_profile'], + ) + + assert fs_clustered is not None + assert len(fs_clustered.clusters) == 3 + + def test_high_peaks_captures_extreme_demand_day(self, solver_fixture, timesteps_8_days): + """Verify high peak selection captures day with maximum demand.""" + fs = create_system_with_peak_demand(timesteps_8_days) + + # Cluster WITH high peak selection + fs_with_peaks = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + time_series_for_high_peaks=['HeatDemand(Q)|fixed_relative_profile'], + ) + fs_with_peaks.optimize(solver_fixture) + + # The peak day (day 7 with demand=50) should be captured + # Check that the clustered solution can handle the peak demand + flow_rates = fs_with_peaks.solution['Boiler(Q_th)|flow_rate'] + + # At least one cluster should have flow rate >= 50 (the peak) + max_flow = float(flow_rates.max()) + assert max_flow >= 49, f'Peak demand not captured: max_flow={max_flow}' + + def test_clustering_without_peaks_may_miss_extremes(self, solver_fixture, timesteps_8_days): + """Show that without peak selection, extreme days might be averaged out.""" + fs = create_system_with_peak_demand(timesteps_8_days) + + # Cluster WITHOUT high peak selection (may or may not capture peak) + fs_no_peaks = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + # No time_series_for_high_peaks + ) + fs_no_peaks.optimize(solver_fixture) + + # This test just verifies the clustering works + # The peak may or may not be captured depending on clustering algorithm + assert fs_no_peaks.solution is not None diff --git a/tests/test_clustering/__init__.py b/tests/test_clustering/__init__.py new file mode 100644 index 000000000..3d546645c --- /dev/null +++ b/tests/test_clustering/__init__.py @@ -0,0 +1 @@ +"""Tests for the flixopt.clustering module.""" diff --git a/tests/test_clustering/test_base.py b/tests/test_clustering/test_base.py new file mode 100644 index 000000000..9cca4de81 --- /dev/null +++ b/tests/test_clustering/test_base.py @@ -0,0 +1,158 @@ +"""Tests for flixopt.clustering.base module.""" + +import numpy as np +import pytest +import xarray as xr + +from flixopt.clustering import ( + Clustering, + ClusterResult, + ClusterStructure, + create_cluster_structure_from_mapping, +) + + +class TestClusterStructure: + """Tests for ClusterStructure dataclass.""" + + def test_basic_creation(self): + """Test basic ClusterStructure creation.""" + cluster_order = xr.DataArray([0, 1, 0, 1, 2, 0], dims=['original_cluster']) + cluster_occurrences = xr.DataArray([3, 2, 1], dims=['cluster']) + + structure = ClusterStructure( + cluster_order=cluster_order, + cluster_occurrences=cluster_occurrences, + n_clusters=3, + timesteps_per_cluster=24, + ) + + assert structure.n_clusters == 3 + assert structure.timesteps_per_cluster == 24 + assert structure.n_original_clusters == 6 + + def test_creation_from_numpy(self): + """Test ClusterStructure creation from numpy arrays.""" + structure = ClusterStructure( + cluster_order=np.array([0, 0, 1, 1, 0]), + cluster_occurrences=np.array([3, 2]), + n_clusters=2, + timesteps_per_cluster=12, + ) + + assert isinstance(structure.cluster_order, xr.DataArray) + assert isinstance(structure.cluster_occurrences, xr.DataArray) + assert structure.n_original_clusters == 5 + + def test_get_cluster_weight_per_timestep(self): + """Test weight calculation per timestep.""" + structure = ClusterStructure( + cluster_order=xr.DataArray([0, 1, 0], dims=['original_cluster']), + cluster_occurrences=xr.DataArray([2, 1], dims=['cluster']), + n_clusters=2, + timesteps_per_cluster=4, + ) + + weights = structure.get_cluster_weight_per_timestep() + + # Cluster 0 has 4 timesteps, each with weight 2 + # Cluster 1 has 4 timesteps, each with weight 1 + assert len(weights) == 8 + assert float(weights.isel(time=0).values) == 2.0 + assert float(weights.isel(time=4).values) == 1.0 + + +class TestClusterResult: + """Tests for ClusterResult dataclass.""" + + def test_basic_creation(self): + """Test basic ClusterResult creation.""" + result = ClusterResult( + timestep_mapping=xr.DataArray([0, 0, 1, 1, 2, 2], dims=['original_time']), + n_representatives=3, + representative_weights=xr.DataArray([2, 2, 2], dims=['time']), + ) + + assert result.n_representatives == 3 + assert result.n_original_timesteps == 6 + + def test_creation_from_numpy(self): + """Test ClusterResult creation from numpy arrays.""" + result = ClusterResult( + timestep_mapping=np.array([0, 1, 0, 1]), + n_representatives=2, + representative_weights=np.array([2.0, 2.0]), + ) + + assert isinstance(result.timestep_mapping, xr.DataArray) + assert isinstance(result.representative_weights, xr.DataArray) + + def test_validation_success(self): + """Test validation passes for valid result.""" + result = ClusterResult( + timestep_mapping=xr.DataArray([0, 1, 0, 1], dims=['original_time']), + n_representatives=2, + representative_weights=xr.DataArray([2.0, 2.0], dims=['time']), + ) + + # Should not raise + result.validate() + + def test_validation_invalid_mapping(self): + """Test validation fails for out-of-range mapping.""" + result = ClusterResult( + timestep_mapping=xr.DataArray([0, 5, 0, 1], dims=['original_time']), # 5 is out of range + n_representatives=2, + representative_weights=xr.DataArray([2.0, 2.0], dims=['time']), + ) + + with pytest.raises(ValueError, match='timestep_mapping contains index'): + result.validate() + + def test_get_expansion_mapping(self): + """Test get_expansion_mapping returns named DataArray.""" + result = ClusterResult( + timestep_mapping=xr.DataArray([0, 1, 0], dims=['original_time']), + n_representatives=2, + representative_weights=xr.DataArray([2.0, 1.0], dims=['time']), + ) + + mapping = result.get_expansion_mapping() + assert mapping.name == 'expansion_mapping' + + +class TestCreateClusterStructureFromMapping: + """Tests for create_cluster_structure_from_mapping function.""" + + def test_basic_creation(self): + """Test creating ClusterStructure from timestep mapping.""" + # 12 original timesteps, 4 per period, 3 periods + # Mapping: period 0 -> cluster 0, period 1 -> cluster 1, period 2 -> cluster 0 + mapping = xr.DataArray( + [0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3], # First and third period map to cluster 0 + dims=['original_time'], + ) + + structure = create_cluster_structure_from_mapping(mapping, timesteps_per_cluster=4) + + assert structure.timesteps_per_cluster == 4 + assert structure.n_original_clusters == 3 + + +class TestClustering: + """Tests for Clustering dataclass.""" + + def test_creation(self): + """Test Clustering creation.""" + result = ClusterResult( + timestep_mapping=xr.DataArray([0, 1], dims=['original_time']), + n_representatives=2, + representative_weights=xr.DataArray([1.0, 1.0], dims=['time']), + ) + + info = Clustering( + result=result, + backend_name='tsam', + ) + + assert info.backend_name == 'tsam' diff --git a/tests/test_clustering/test_integration.py b/tests/test_clustering/test_integration.py new file mode 100644 index 000000000..16c638c95 --- /dev/null +++ b/tests/test_clustering/test_integration.py @@ -0,0 +1,286 @@ +"""Integration tests for flixopt.aggregation module with FlowSystem.""" + +import numpy as np +import pandas as pd +import pytest +import xarray as xr + +from flixopt import FlowSystem + + +class TestWeights: + """Tests for FlowSystem.weights dict property.""" + + def test_weights_is_dict(self): + """Test weights returns a dict.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + weights = fs.weights + + assert isinstance(weights, dict) + assert 'time' in weights + + def test_time_weight(self): + """Test weights['time'] returns timestep_duration.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + weights = fs.weights + + # For hourly data, timestep_duration is 1.0 + assert float(weights['time'].mean()) == 1.0 + + def test_cluster_not_in_weights_when_non_clustered(self): + """Test weights doesn't have 'cluster' key for non-clustered systems.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + weights = fs.weights + + # Non-clustered: 'cluster' not in weights + assert 'cluster' not in weights + + def test_temporal_dims_non_clustered(self): + """Test temporal_dims is ['time'] for non-clustered systems.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + assert fs.temporal_dims == ['time'] + + def test_temporal_weight(self): + """Test temporal_weight returns time * cluster.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + expected = fs.weights['time'] * fs.weights.get('cluster', 1.0) + xr.testing.assert_equal(fs.temporal_weight, expected) + + def test_sum_temporal(self): + """Test sum_temporal applies full temporal weighting (time * cluster) and sums.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=3, freq='h')) + + # Input is a rate (e.g., flow_rate in MW) + data = xr.DataArray([10.0, 20.0, 30.0], dims=['time'], coords={'time': fs.timesteps}) + + result = fs.sum_temporal(data) + + # For hourly non-clustered: temporal = time * cluster = 1.0 * 1.0 = 1.0 + # result = sum(data * temporal) = sum(data) = 60 + assert float(result.values) == 60.0 + + +class TestFlowSystemDimsIndexesWeights: + """Tests for FlowSystem.dims, .indexes, .weights properties.""" + + def test_dims_property(self): + """Test that FlowSystem.dims returns active dimension names.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + dims = fs.dims + assert dims == ['time'] + + def test_indexes_property(self): + """Test that FlowSystem.indexes returns active indexes.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + indexes = fs.indexes + assert isinstance(indexes, dict) + assert 'time' in indexes + assert len(indexes['time']) == 24 + + def test_weights_keys_match_dims(self): + """Test that weights.keys() is subset of dims (only 'time' for simple case).""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + # For non-clustered, weights only has 'time' + assert set(fs.weights.keys()) == {'time'} + + def test_temporal_weight_calculation(self): + """Test that temporal_weight = timestep_duration * cluster_weight.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=24, freq='h')) + + expected = fs.timestep_duration * 1.0 # cluster is 1.0 for non-clustered + + np.testing.assert_array_almost_equal(fs.temporal_weight.values, expected.values) + + def test_weights_with_cluster_weight(self): + """Test weights property includes cluster_weight when provided.""" + # Create FlowSystem with custom cluster_weight + timesteps = pd.date_range('2024-01-01', periods=24, freq='h') + cluster_weight = xr.DataArray( + np.array([2.0] * 12 + [1.0] * 12), + dims=['time'], + coords={'time': timesteps}, + ) + + fs = FlowSystem(timesteps=timesteps, cluster_weight=cluster_weight) + + weights = fs.weights + + # cluster weight should be in weights (FlowSystem has cluster_weight set) + # But note: 'cluster' only appears in weights if clusters dimension exists + # Since we didn't set clusters, 'cluster' won't be in weights + # The cluster_weight is applied via temporal_weight + assert 'cluster' not in weights # No cluster dimension + + # temporal_weight = timestep_duration * cluster_weight + # timestep_duration is 1h for all + expected = 1.0 * cluster_weight + np.testing.assert_array_almost_equal(fs.temporal_weight.values, expected.values) + + +class TestClusterMethod: + """Tests for FlowSystem.transform.cluster method.""" + + def test_cluster_method_exists(self): + """Test that transform.cluster method exists.""" + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=48, freq='h')) + + assert hasattr(fs.transform, 'cluster') + assert callable(fs.transform.cluster) + + def test_cluster_reduces_timesteps(self): + """Test that cluster reduces timesteps.""" + # This test requires tsam to be installed + pytest.importorskip('tsam') + from flixopt import Bus, Flow, Sink, Source + from flixopt.core import TimeSeriesData + + # Create FlowSystem with 7 days of data (168 hours) + n_hours = 168 # 7 days + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=n_hours, freq='h')) + + # Add some basic components with time series data + demand_data = np.sin(np.linspace(0, 14 * np.pi, n_hours)) + 2 # Varying demand over 7 days + bus = Bus('electricity') + # Bus label is passed as string to Flow + grid_flow = Flow('grid_in', bus='electricity', size=100) + demand_flow = Flow( + 'demand_out', bus='electricity', size=100, fixed_relative_profile=TimeSeriesData(demand_data / 100) + ) + source = Source('grid', outputs=[grid_flow]) + sink = Sink('demand', inputs=[demand_flow]) + fs.add_elements(source, sink, bus) + + # Reduce 7 days to 2 representative days + fs_clustered = fs.transform.cluster( + n_clusters=2, + cluster_duration='1D', + ) + + # Clustered FlowSystem has 2D structure: (cluster, time) + # - timesteps: within-cluster time (24 hours) + # - clusters: cluster indices (2 clusters) + # Total effective timesteps = 2 * 24 = 48 + assert len(fs_clustered.timesteps) == 24 # Within-cluster time + assert len(fs_clustered.clusters) == 2 # Number of clusters + assert len(fs_clustered.timesteps) * len(fs_clustered.clusters) == 48 + + +class TestClusterAdvancedOptions: + """Tests for advanced clustering options.""" + + @pytest.fixture + def basic_flow_system(self): + """Create a basic FlowSystem for testing.""" + pytest.importorskip('tsam') + from flixopt import Bus, Flow, Sink, Source + from flixopt.core import TimeSeriesData + + n_hours = 168 # 7 days + fs = FlowSystem(timesteps=pd.date_range('2024-01-01', periods=n_hours, freq='h')) + + demand_data = np.sin(np.linspace(0, 14 * np.pi, n_hours)) + 2 + bus = Bus('electricity') + grid_flow = Flow('grid_in', bus='electricity', size=100) + demand_flow = Flow( + 'demand_out', bus='electricity', size=100, fixed_relative_profile=TimeSeriesData(demand_data / 100) + ) + source = Source('grid', outputs=[grid_flow]) + sink = Sink('demand', inputs=[demand_flow]) + fs.add_elements(source, sink, bus) + return fs + + def test_cluster_method_parameter(self, basic_flow_system): + """Test that cluster_method parameter works.""" + fs_clustered = basic_flow_system.transform.cluster( + n_clusters=2, cluster_duration='1D', cluster_method='hierarchical' + ) + assert len(fs_clustered.clusters) == 2 + + def test_hierarchical_is_deterministic(self, basic_flow_system): + """Test that hierarchical clustering (default) produces deterministic results.""" + fs1 = basic_flow_system.transform.cluster(n_clusters=2, cluster_duration='1D') + fs2 = basic_flow_system.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Hierarchical clustering should produce identical cluster orders + xr.testing.assert_equal(fs1.clustering.cluster_order, fs2.clustering.cluster_order) + + def test_metrics_available(self, basic_flow_system): + """Test that clustering metrics are available after clustering.""" + fs_clustered = basic_flow_system.transform.cluster(n_clusters=2, cluster_duration='1D') + + assert fs_clustered.clustering.metrics is not None + assert isinstance(fs_clustered.clustering.metrics, xr.Dataset) + assert 'time_series' in fs_clustered.clustering.metrics.dims + assert len(fs_clustered.clustering.metrics.data_vars) > 0 + + def test_representation_method_parameter(self, basic_flow_system): + """Test that representation_method parameter works.""" + fs_clustered = basic_flow_system.transform.cluster( + n_clusters=2, cluster_duration='1D', representation_method='medoidRepresentation' + ) + assert len(fs_clustered.clusters) == 2 + + def test_rescale_cluster_periods_parameter(self, basic_flow_system): + """Test that rescale_cluster_periods parameter works.""" + fs_clustered = basic_flow_system.transform.cluster( + n_clusters=2, cluster_duration='1D', rescale_cluster_periods=False + ) + assert len(fs_clustered.clusters) == 2 + + def test_tsam_kwargs_passthrough(self, basic_flow_system): + """Test that additional kwargs are passed to tsam.""" + # sameMean is a valid tsam parameter + fs_clustered = basic_flow_system.transform.cluster(n_clusters=2, cluster_duration='1D', sameMean=True) + assert len(fs_clustered.clusters) == 2 + + def test_metrics_with_periods(self): + """Test that metrics have period dimension for multi-period FlowSystems.""" + pytest.importorskip('tsam') + from flixopt import Bus, Flow, Sink, Source + from flixopt.core import TimeSeriesData + + n_hours = 168 # 7 days + fs = FlowSystem( + timesteps=pd.date_range('2024-01-01', periods=n_hours, freq='h'), + periods=pd.Index([2025, 2030], name='period'), + ) + + demand_data = np.sin(np.linspace(0, 14 * np.pi, n_hours)) + 2 + bus = Bus('electricity') + grid_flow = Flow('grid_in', bus='electricity', size=100) + demand_flow = Flow( + 'demand_out', bus='electricity', size=100, fixed_relative_profile=TimeSeriesData(demand_data / 100) + ) + source = Source('grid', outputs=[grid_flow]) + sink = Sink('demand', inputs=[demand_flow]) + fs.add_elements(source, sink, bus) + + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Metrics should have period dimension + assert fs_clustered.clustering.metrics is not None + assert 'period' in fs_clustered.clustering.metrics.dims + assert len(fs_clustered.clustering.metrics.period) == 2 + + +class TestClusteringModuleImports: + """Tests for flixopt.clustering module imports.""" + + def test_import_from_flixopt(self): + """Test that clustering module can be imported from flixopt.""" + from flixopt import clustering + + assert hasattr(clustering, 'ClusterResult') + assert hasattr(clustering, 'ClusterStructure') + assert hasattr(clustering, 'Clustering') + + def test_create_cluster_structure_from_mapping_available(self): + """Test that create_cluster_structure_from_mapping is available.""" + from flixopt.clustering import create_cluster_structure_from_mapping + + assert callable(create_cluster_structure_from_mapping) diff --git a/tests/test_clustering_io.py b/tests/test_clustering_io.py new file mode 100644 index 000000000..483cdc447 --- /dev/null +++ b/tests/test_clustering_io.py @@ -0,0 +1,241 @@ +"""Tests for clustering serialization and deserialization.""" + +import numpy as np +import pandas as pd +import pytest + +import flixopt as fx + + +@pytest.fixture +def simple_system_24h(): + """Create a simple flow system with 24 hourly timesteps.""" + timesteps = pd.date_range('2023-01-01', periods=24, freq='h') + + fs = fx.FlowSystem(timesteps) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=np.ones(24), size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + +@pytest.fixture +def simple_system_8_days(): + """Create a simple flow system with 8 days of hourly timesteps.""" + timesteps = pd.date_range('2023-01-01', periods=8 * 24, freq='h') + + # Create varying demand profile with different patterns for different days + # 4 "weekdays" with high demand, 4 "weekend" days with low demand + hourly_pattern = np.sin(np.linspace(0, 2 * np.pi, 24)) * 0.5 + 0.5 + weekday_profile = hourly_pattern * 1.5 # Higher demand + weekend_profile = hourly_pattern * 0.5 # Lower demand + demand_profile = np.concatenate( + [ + weekday_profile, + weekday_profile, + weekday_profile, + weekday_profile, + weekend_profile, + weekend_profile, + weekend_profile, + weekend_profile, + ] + ) + + fs = fx.FlowSystem(timesteps) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=demand_profile, size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + +class TestClusteringRoundtrip: + """Test that clustering survives dataset roundtrip.""" + + def test_clustering_to_dataset_has_clustering_attrs(self, simple_system_8_days): + """Clustered FlowSystem dataset should have clustering info.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + ds = fs_clustered.to_dataset(include_solution=False) + + # Check that clustering attrs are present + assert 'clustering' in ds.attrs + + # Check that clustering arrays are present with prefix + clustering_vars = [name for name in ds.data_vars if name.startswith('clustering|')] + assert len(clustering_vars) > 0 + + def test_clustering_roundtrip_preserves_clustering_object(self, simple_system_8_days): + """Clustering object should be restored after roundtrip.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Roundtrip + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + # Clustering should be restored + assert fs_restored.clustering is not None + assert fs_restored.clustering.backend_name == 'tsam' + + def test_clustering_roundtrip_preserves_n_clusters(self, simple_system_8_days): + """Number of clusters should be preserved after roundtrip.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + assert fs_restored.clustering.n_clusters == 2 + + def test_clustering_roundtrip_preserves_timesteps_per_cluster(self, simple_system_8_days): + """Timesteps per cluster should be preserved after roundtrip.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + assert fs_restored.clustering.timesteps_per_cluster == 24 + + def test_clustering_roundtrip_preserves_original_timesteps(self, simple_system_8_days): + """Original timesteps should be preserved after roundtrip.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + original_timesteps = fs_clustered.clustering.original_timesteps + + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + pd.testing.assert_index_equal(fs_restored.clustering.original_timesteps, original_timesteps) + + def test_clustering_roundtrip_preserves_timestep_mapping(self, simple_system_8_days): + """Timestep mapping should be preserved after roundtrip.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + original_mapping = fs_clustered.clustering.timestep_mapping.values.copy() + + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + np.testing.assert_array_equal(fs_restored.clustering.timestep_mapping.values, original_mapping) + + +class TestClusteringWithSolutionRoundtrip: + """Test that clustering with solution survives roundtrip.""" + + def test_expand_solution_after_roundtrip(self, simple_system_8_days, solver_fixture): + """expand_solution should work after loading from dataset.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Solve + fs_clustered.optimize(solver_fixture) + + # Roundtrip + ds = fs_clustered.to_dataset(include_solution=True) + fs_restored = fx.FlowSystem.from_dataset(ds) + + # expand_solution should work + fs_expanded = fs_restored.transform.expand_solution() + + # Check expanded FlowSystem has correct number of timesteps + assert len(fs_expanded.timesteps) == 8 * 24 + + def test_expand_solution_after_netcdf_roundtrip(self, simple_system_8_days, tmp_path, solver_fixture): + """expand_solution should work after loading from NetCDF file.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Solve + fs_clustered.optimize(solver_fixture) + + # Save to NetCDF + nc_path = tmp_path / 'clustered.nc' + fs_clustered.to_netcdf(nc_path) + + # Load from NetCDF + fs_restored = fx.FlowSystem.from_netcdf(nc_path) + + # expand_solution should work + fs_expanded = fs_restored.transform.expand_solution() + + # Check expanded FlowSystem has correct number of timesteps + assert len(fs_expanded.timesteps) == 8 * 24 + + +class TestClusteringDerivedProperties: + """Test derived properties on Clustering object.""" + + def test_original_timesteps_property(self, simple_system_8_days): + """original_timesteps property should return correct DatetimeIndex.""" + fs = simple_system_8_days + original_timesteps = fs.timesteps + + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # Check values are equal (name attribute may differ) + pd.testing.assert_index_equal( + fs_clustered.clustering.original_timesteps, + original_timesteps, + check_names=False, + ) + + def test_simple_system_has_no_periods_or_scenarios(self, simple_system_8_days): + """Clustered simple system should preserve that it has no periods/scenarios.""" + fs = simple_system_8_days + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + # FlowSystem without periods/scenarios should remain so after clustering + assert fs_clustered.periods is None + assert fs_clustered.scenarios is None + + +class TestClusteringWithScenarios: + """Test clustering IO with scenarios.""" + + @pytest.fixture + def system_with_scenarios(self): + """Create a flow system with scenarios.""" + timesteps = pd.date_range('2023-01-01', periods=4 * 24, freq='h') + scenarios = pd.Index(['Low', 'High'], name='scenario') + + # Create varying demand profile for clustering + demand_profile = np.tile(np.sin(np.linspace(0, 2 * np.pi, 24)) * 0.5 + 0.5, 4) + + fs = fx.FlowSystem(timesteps, scenarios=scenarios) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=demand_profile, size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + def test_clustering_roundtrip_preserves_scenarios(self, system_with_scenarios): + """Scenarios should be preserved after clustering and roundtrip.""" + fs = system_with_scenarios + fs_clustered = fs.transform.cluster(n_clusters=2, cluster_duration='1D') + + ds = fs_clustered.to_dataset(include_solution=False) + fs_restored = fx.FlowSystem.from_dataset(ds) + + # Scenarios should be preserved in the FlowSystem itself + pd.testing.assert_index_equal( + fs_restored.scenarios, + pd.Index(['Low', 'High'], name='scenario'), + check_names=False, + ) diff --git a/tests/test_effect.py b/tests/test_effect.py index 015e054eb..60fbb0166 100644 --- a/tests/test_effect.py +++ b/tests/test_effect.py @@ -129,8 +129,8 @@ def test_bounds(self, basic_flow_system_linopy_coords, coords_config): assert_var_equal( model.variables['Effect1(temporal)|per_timestep'], model.add_variables( - lower=4.0 * model.hours_per_step, - upper=4.1 * model.hours_per_step, + lower=4.0 * model.timestep_duration, + upper=4.1 * model.timestep_duration, coords=model.get_coords(['time', 'period', 'scenario']), ), ) diff --git a/tests/test_flow.py b/tests/test_flow.py index 594bc1fbb..8e1ce1f53 100644 --- a/tests/test_flow.py +++ b/tests/test_flow.py @@ -23,7 +23,7 @@ def test_flow_minimal(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|total_flow_hours'], flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration).sum('time'), ) assert_var_equal(flow.submodel.flow_rate, model.add_variables(lower=0, upper=100, coords=model.get_coords())) assert_var_equal( @@ -61,7 +61,7 @@ def test_flow(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|total_flow_hours'], flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration).sum('time'), ) assert_var_equal( @@ -83,12 +83,12 @@ def test_flow(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|load_factor_min'], - flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] >= model.hours_per_step.sum('time') * 0.1 * 100, + flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] >= model.timestep_duration.sum('time') * 0.1 * 100, ) assert_conequal( model.constraints['Sink(WΓ€rme)|load_factor_max'], - flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] <= model.hours_per_step.sum('time') * 0.9 * 100, + flow.submodel.variables['Sink(WΓ€rme)|total_flow_hours'] <= model.timestep_duration.sum('time') * 0.9 * 100, ) assert_sets_equal( @@ -129,13 +129,13 @@ def test_effects_per_flow_hour(self, basic_flow_system_linopy_coords, coords_con assert_conequal( model.constraints['Sink(WΓ€rme)->costs(temporal)'], model.variables['Sink(WΓ€rme)->costs(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step * costs_per_flow_hour, + == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration * costs_per_flow_hour, ) assert_conequal( model.constraints['Sink(WΓ€rme)->CO2(temporal)'], model.variables['Sink(WΓ€rme)->CO2(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.hours_per_step * co2_per_flow_hour, + == flow.submodel.variables['Sink(WΓ€rme)|flow_rate'] * model.timestep_duration * co2_per_flow_hour, ) @@ -561,7 +561,7 @@ def test_flow_on(self, basic_flow_system_linopy_coords, coords_config): model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -580,7 +580,7 @@ def test_flow_on(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) def test_effects_per_active_hour(self, basic_flow_system_linopy_coords, coords_config): @@ -635,13 +635,13 @@ def test_effects_per_active_hour(self, basic_flow_system_linopy_coords, coords_c assert_conequal( model.constraints['Sink(WΓ€rme)->costs(temporal)'], model.variables['Sink(WΓ€rme)->costs(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step * costs_per_running_hour, + == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration * costs_per_running_hour, ) assert_conequal( model.constraints['Sink(WΓ€rme)->CO2(temporal)'], model.variables['Sink(WΓ€rme)->CO2(temporal)'] - == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step * co2_per_running_hour, + == flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration * co2_per_running_hour, ) def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_config): @@ -687,7 +687,7 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.add_variables(lower=0, upper=8, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + mega = model.timestep_duration.sum('time') assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|ub'], @@ -698,7 +698,7 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.constraints['Sink(WΓ€rme)|uptime|forward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -706,14 +706,14 @@ def test_consecutive_on_hours(self, basic_flow_system_linopy_coords, coords_conf model.constraints['Sink(WΓ€rme)|uptime|backward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|status'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|initial'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * model.hours_per_step.isel(time=0), + == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * model.timestep_duration.isel(time=0), ) assert_conequal( @@ -768,7 +768,7 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.add_variables(lower=0, upper=8, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 3 + mega = model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 3 assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|ub'], @@ -779,7 +779,7 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.constraints['Sink(WΓ€rme)|uptime|forward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -787,14 +787,14 @@ def test_consecutive_on_hours_previous(self, basic_flow_system_linopy_coords, co model.constraints['Sink(WΓ€rme)|uptime|backward'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|uptime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|status'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|uptime|initial'], model.variables['Sink(WΓ€rme)|uptime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 3)), + == model.variables['Sink(WΓ€rme)|status'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 3)), ) assert_conequal( @@ -850,7 +850,9 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.add_variables(lower=0, upper=12, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 1 # previously inactive for 1h + mega = ( + model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 1 + ) # previously inactive for 1h assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|ub'], @@ -861,7 +863,7 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.constraints['Sink(WΓ€rme)|downtime|forward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -869,14 +871,14 @@ def test_consecutive_off_hours(self, basic_flow_system_linopy_coords, coords_con model.constraints['Sink(WΓ€rme)|downtime|backward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|inactive'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|initial'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 1)), + == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 1)), ) assert_conequal( @@ -933,7 +935,7 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.add_variables(lower=0, upper=12, coords=model.get_coords()), ) - mega = model.hours_per_step.sum('time') + model.hours_per_step.isel(time=0) * 2 + mega = model.timestep_duration.sum('time') + model.timestep_duration.isel(time=0) * 2 assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|ub'], @@ -944,7 +946,7 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.constraints['Sink(WΓ€rme)|downtime|forward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) <= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)), + + model.timestep_duration.isel(time=slice(None, -1)), ) # eq: duration(t) >= duration(t - 1) + dt(t) + (On(t) - 1) * BIG @@ -952,14 +954,14 @@ def test_consecutive_off_hours_previous(self, basic_flow_system_linopy_coords, c model.constraints['Sink(WΓ€rme)|downtime|backward'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(1, None)) >= model.variables['Sink(WΓ€rme)|downtime'].isel(time=slice(None, -1)) - + model.hours_per_step.isel(time=slice(None, -1)) + + model.timestep_duration.isel(time=slice(None, -1)) + (model.variables['Sink(WΓ€rme)|inactive'].isel(time=slice(1, None)) - 1) * mega, ) assert_conequal( model.constraints['Sink(WΓ€rme)|downtime|initial'], model.variables['Sink(WΓ€rme)|downtime'].isel(time=0) - == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.hours_per_step.isel(time=0) * (1 + 2)), + == model.variables['Sink(WΓ€rme)|inactive'].isel(time=0) * (model.timestep_duration.isel(time=0) * (1 + 2)), ) assert_conequal( @@ -1067,7 +1069,7 @@ def test_on_hours_limits(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) @@ -1131,7 +1133,7 @@ def test_flow_on_invest_optional(self, basic_flow_system_linopy_coords, coords_c model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -1157,7 +1159,7 @@ def test_flow_on_invest_optional(self, basic_flow_system_linopy_coords, coords_c assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) # Investment @@ -1233,7 +1235,7 @@ def test_flow_on_invest_non_optional(self, basic_flow_system_linopy_coords, coor model.add_variables(binary=True, coords=model.get_coords()), ) # Upper bound is total hours when active_hours_max is not specified - total_hours = model.hours_per_step.sum('time') + total_hours = model.timestep_duration.sum('time') assert_var_equal( model.variables['Sink(WΓ€rme)|active_hours'], model.add_variables(lower=0, upper=total_hours, coords=model.get_coords(['period', 'scenario'])), @@ -1251,7 +1253,7 @@ def test_flow_on_invest_non_optional(self, basic_flow_system_linopy_coords, coor assert_conequal( model.constraints['Sink(WΓ€rme)|active_hours'], flow.submodel.variables['Sink(WΓ€rme)|active_hours'] - == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.hours_per_step).sum('time'), + == (flow.submodel.variables['Sink(WΓ€rme)|status'] * model.timestep_duration).sum('time'), ) # Investment diff --git a/tests/test_flow_system_resample.py b/tests/test_flow_system_resample.py index 7486b173c..dd5e19176 100644 --- a/tests/test_flow_system_resample.py +++ b/tests/test_flow_system_resample.py @@ -128,7 +128,7 @@ def test_time_metadata_updated(simple_fs): """Test time metadata correctly updated.""" fs_r = simple_fs.resample('3h', method='mean') assert len(fs_r.timesteps) == 8 - assert_allclose(fs_r.hours_per_timestep.values, 3.0) + assert_allclose(fs_r.timestep_duration.values, 3.0) assert fs_r.hours_of_last_timestep == 3.0 diff --git a/tests/test_io_conversion.py b/tests/test_io_conversion.py index 33bda8c91..7775f987a 100644 --- a/tests/test_io_conversion.py +++ b/tests/test_io_conversion.py @@ -762,6 +762,11 @@ def test_v4_reoptimized_objective_matches_original(self, result_name): new_objective = float(fs.solution['objective'].item()) new_effect_total = float(fs.solution[objective_effect_label].sum().item()) + # Skip comparison for scenarios test case - scenario weights are now always normalized, + # which changes the objective value when loading old results with non-normalized weights + if result_name == '04_scenarios': + pytest.skip('Scenario weights are now always normalized - old results have different weights') + # Verify objective matches (within tolerance) assert new_objective == pytest.approx(old_objective, rel=1e-5, abs=1), ( f'Objective mismatch for {result_name}: new={new_objective}, old={old_objective}' diff --git a/tests/test_linear_converter.py b/tests/test_linear_converter.py index 57b911d64..d20d104d0 100644 --- a/tests/test_linear_converter.py +++ b/tests/test_linear_converter.py @@ -174,7 +174,7 @@ def test_linear_converter_with_status(self, basic_flow_system_linopy_coords, coo assert_conequal( model.constraints['Converter|active_hours'], model.variables['Converter|active_hours'] - == (model.variables['Converter|status'] * model.hours_per_step).sum('time'), + == (model.variables['Converter|status'] * model.timestep_duration).sum('time'), ) # Check conversion constraint @@ -188,7 +188,7 @@ def test_linear_converter_with_status(self, basic_flow_system_linopy_coords, coo assert_conequal( model.constraints['Converter->costs(temporal)'], model.variables['Converter->costs(temporal)'] - == model.variables['Converter|status'] * model.hours_per_step * 5, + == model.variables['Converter|status'] * model.timestep_duration * 5, ) def test_linear_converter_multidimensional(self, basic_flow_system_linopy_coords, coords_config): @@ -485,7 +485,7 @@ def test_piecewise_conversion_with_status(self, basic_flow_system_linopy_coords, assert 'Converter|active_hours' in model.constraints assert_conequal( model.constraints['Converter|active_hours'], - model['Converter|active_hours'] == (model['Converter|status'] * model.hours_per_step).sum('time'), + model['Converter|active_hours'] == (model['Converter|status'] * model.timestep_duration).sum('time'), ) # Verify that the costs effect is applied @@ -493,7 +493,7 @@ def test_piecewise_conversion_with_status(self, basic_flow_system_linopy_coords, assert_conequal( model.constraints['Converter->costs(temporal)'], model.variables['Converter->costs(temporal)'] - == model.variables['Converter|status'] * model.hours_per_step * 5, + == model.variables['Converter|status'] * model.timestep_duration * 5, ) diff --git a/tests/test_on_hours_computation.py b/tests/test_on_hours_computation.py index 578fd7792..c74332565 100644 --- a/tests/test_on_hours_computation.py +++ b/tests/test_on_hours_computation.py @@ -9,7 +9,7 @@ class TestComputeConsecutiveDuration: """Tests for the compute_consecutive_hours_in_state static method.""" @pytest.mark.parametrize( - 'binary_values, hours_per_timestep, expected', + 'binary_values, timestep_duration, expected', [ # Case 1: Single timestep DataArrays (xr.DataArray([1], dims=['time']), 5, 5), @@ -26,22 +26,22 @@ class TestComputeConsecutiveDuration: (xr.DataArray([0, 1, 1, 1, 0, 0], dims=['time']), 1, 0), # ends with 0 ], ) - def test_compute_duration(self, binary_values, hours_per_timestep, expected): + def test_compute_duration(self, binary_values, timestep_duration, expected): """Test compute_consecutive_hours_in_state with various inputs.""" - result = ModelingUtilities.compute_consecutive_hours_in_state(binary_values, hours_per_timestep) + result = ModelingUtilities.compute_consecutive_hours_in_state(binary_values, timestep_duration) assert np.isclose(result, expected) @pytest.mark.parametrize( - 'binary_values, hours_per_timestep', + 'binary_values, timestep_duration', [ - # Case: hours_per_timestep must be scalar + # Case: timestep_duration must be scalar (xr.DataArray([1, 1, 1, 1, 1], dims=['time']), np.array([1, 2])), ], ) - def test_compute_duration_raises_error(self, binary_values, hours_per_timestep): + def test_compute_duration_raises_error(self, binary_values, timestep_duration): """Test error conditions.""" with pytest.raises(TypeError): - ModelingUtilities.compute_consecutive_hours_in_state(binary_values, hours_per_timestep) + ModelingUtilities.compute_consecutive_hours_in_state(binary_values, timestep_duration) class TestComputePreviousOnStates: diff --git a/tests/test_scenarios.py b/tests/test_scenarios.py index 65ea62d81..2699647ad 100644 --- a/tests/test_scenarios.py +++ b/tests/test_scenarios.py @@ -341,12 +341,14 @@ def test_scenarios_selection(flow_system_piecewise_conversion_scenarios): 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]) + # Scenario weights are always normalized - subset is re-normalized to sum to 1 + subset_weights = flow_system_full.scenario_weights[0:2] + expected_normalized = subset_weights / subset_weights.sum() + np.testing.assert_allclose(flow_system.scenario_weights.values, expected_normalized.values) - # Optimize using new API with normalize_weights=False + # Optimize using new API flow_system.optimize( fx.solvers.GurobiSolver(mip_gap=0.01, time_limit_seconds=60), - normalize_weights=False, ) # Penalty has same structure as other effects: 'Penalty' is the total, 'Penalty(temporal)' and 'Penalty(periodic)' are components @@ -769,7 +771,10 @@ def test_weights_selection(): # Verify weights are correctly sliced assert fs_subset.scenarios.equals(pd.Index(['base', 'high'], name='scenario')) - np.testing.assert_allclose(fs_subset.scenario_weights.values, custom_scenario_weights[[0, 2]]) + # Scenario weights are always normalized - subset is re-normalized to sum to 1 + subset_weights = np.array([0.3, 0.2]) # Original weights for selected scenarios + expected_normalized = subset_weights / subset_weights.sum() + np.testing.assert_allclose(fs_subset.scenario_weights.values, expected_normalized) # Verify weights are 1D with just scenario dimension (no period dimension) assert fs_subset.scenario_weights.dims == ('scenario',) diff --git a/tests/test_sel_isel_single_selection.py b/tests/test_sel_isel_single_selection.py new file mode 100644 index 000000000..4d84ced51 --- /dev/null +++ b/tests/test_sel_isel_single_selection.py @@ -0,0 +1,193 @@ +"""Tests for sel/isel with single period/scenario selection.""" + +import numpy as np +import pandas as pd +import pytest + +import flixopt as fx + + +@pytest.fixture +def fs_with_scenarios(): + """FlowSystem with scenarios for testing single selection.""" + timesteps = pd.date_range('2023-01-01', periods=24, freq='h') + scenarios = pd.Index(['A', 'B', 'C'], name='scenario') + scenario_weights = np.array([0.5, 0.3, 0.2]) + + fs = fx.FlowSystem(timesteps, scenarios=scenarios, scenario_weights=scenario_weights) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=np.ones(24), size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + +@pytest.fixture +def fs_with_periods(): + """FlowSystem with periods for testing single selection.""" + timesteps = pd.date_range('2023-01-01', periods=24, freq='h') + periods = pd.Index([2020, 2030, 2040], name='period') + + fs = fx.FlowSystem(timesteps, periods=periods, weight_of_last_period=10) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=np.ones(24), size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + +@pytest.fixture +def fs_with_periods_and_scenarios(): + """FlowSystem with both periods and scenarios.""" + timesteps = pd.date_range('2023-01-01', periods=24, freq='h') + periods = pd.Index([2020, 2030], name='period') + scenarios = pd.Index(['Low', 'High'], name='scenario') + + fs = fx.FlowSystem(timesteps, periods=periods, scenarios=scenarios, weight_of_last_period=10) + fs.add_elements( + fx.Bus('heat'), + fx.Effect('costs', unit='EUR', description='costs', is_objective=True, is_standard=True), + ) + fs.add_elements( + fx.Sink('demand', inputs=[fx.Flow('in', bus='heat', fixed_relative_profile=np.ones(24), size=10)]), + fx.Source('source', outputs=[fx.Flow('out', bus='heat', size=50, effects_per_flow_hour={'costs': 0.05})]), + ) + return fs + + +class TestIselSingleScenario: + """Test isel with single scenario selection.""" + + def test_isel_single_scenario_drops_dimension(self, fs_with_scenarios): + """Selecting a single scenario with isel should drop the scenario dimension.""" + fs_selected = fs_with_scenarios.transform.isel(scenario=0) + + assert fs_selected.scenarios is None + assert 'scenario' not in fs_selected.to_dataset().dims + + def test_isel_single_scenario_removes_scenario_weights(self, fs_with_scenarios): + """scenario_weights should be removed when scenario dimension is dropped.""" + fs_selected = fs_with_scenarios.transform.isel(scenario=0) + + ds = fs_selected.to_dataset() + assert 'scenario_weights' not in ds.data_vars + assert 'scenario_weights' not in ds.attrs + + def test_isel_single_scenario_preserves_time(self, fs_with_scenarios): + """Time dimension should be preserved.""" + fs_selected = fs_with_scenarios.transform.isel(scenario=0) + + assert len(fs_selected.timesteps) == 24 + + def test_isel_single_scenario_roundtrip(self, fs_with_scenarios): + """FlowSystem should survive to_dataset/from_dataset roundtrip after single selection.""" + fs_selected = fs_with_scenarios.transform.isel(scenario=0) + + ds = fs_selected.to_dataset() + fs_restored = fx.FlowSystem.from_dataset(ds) + + assert fs_restored.scenarios is None + assert len(fs_restored.timesteps) == 24 + + +class TestSelSingleScenario: + """Test sel with single scenario selection.""" + + def test_sel_single_scenario_drops_dimension(self, fs_with_scenarios): + """Selecting a single scenario with sel should drop the scenario dimension.""" + fs_selected = fs_with_scenarios.transform.sel(scenario='B') + + assert fs_selected.scenarios is None + + +class TestIselSinglePeriod: + """Test isel with single period selection.""" + + def test_isel_single_period_drops_dimension(self, fs_with_periods): + """Selecting a single period with isel should drop the period dimension.""" + fs_selected = fs_with_periods.transform.isel(period=0) + + assert fs_selected.periods is None + assert 'period' not in fs_selected.to_dataset().dims + + def test_isel_single_period_removes_period_weights(self, fs_with_periods): + """period_weights should be removed when period dimension is dropped.""" + fs_selected = fs_with_periods.transform.isel(period=0) + + ds = fs_selected.to_dataset() + assert 'period_weights' not in ds.data_vars + assert 'weight_of_last_period' not in ds.attrs + + def test_isel_single_period_roundtrip(self, fs_with_periods): + """FlowSystem should survive roundtrip after single period selection.""" + fs_selected = fs_with_periods.transform.isel(period=0) + + ds = fs_selected.to_dataset() + fs_restored = fx.FlowSystem.from_dataset(ds) + + assert fs_restored.periods is None + + +class TestSelSinglePeriod: + """Test sel with single period selection.""" + + def test_sel_single_period_drops_dimension(self, fs_with_periods): + """Selecting a single period with sel should drop the period dimension.""" + fs_selected = fs_with_periods.transform.sel(period=2030) + + assert fs_selected.periods is None + + +class TestMixedSelection: + """Test mixed selections (single + multiple).""" + + def test_single_period_multiple_scenarios(self, fs_with_periods_and_scenarios): + """Single period but multiple scenarios should only drop period.""" + fs_selected = fs_with_periods_and_scenarios.transform.isel(period=0) + + assert fs_selected.periods is None + assert fs_selected.scenarios is not None + assert len(fs_selected.scenarios) == 2 + + def test_multiple_periods_single_scenario(self, fs_with_periods_and_scenarios): + """Multiple periods but single scenario should only drop scenario.""" + fs_selected = fs_with_periods_and_scenarios.transform.isel(scenario=0) + + assert fs_selected.periods is not None + assert len(fs_selected.periods) == 2 + assert fs_selected.scenarios is None + + def test_single_period_single_scenario(self, fs_with_periods_and_scenarios): + """Single period and single scenario should drop both.""" + fs_selected = fs_with_periods_and_scenarios.transform.isel(period=0, scenario=0) + + assert fs_selected.periods is None + assert fs_selected.scenarios is None + + +class TestSliceSelection: + """Test that slice selection preserves dimensions.""" + + def test_slice_scenarios_preserves_dimension(self, fs_with_scenarios): + """Slice selection should preserve dimension even with 1 element.""" + # Select a slice that results in 2 elements + fs_selected = fs_with_scenarios.transform.isel(scenario=slice(0, 2)) + + assert fs_selected.scenarios is not None + assert len(fs_selected.scenarios) == 2 + + def test_list_selection_preserves_dimension(self, fs_with_scenarios): + """List selection should preserve dimension even with 1 element.""" + fs_selected = fs_with_scenarios.transform.isel(scenario=[0]) + + # List selection should preserve dimension + assert fs_selected.scenarios is not None + assert len(fs_selected.scenarios) == 1 diff --git a/tests/test_storage.py b/tests/test_storage.py index 15170a321..3fd47fbf8 100644 --- a/tests/test_storage.py +++ b/tests/test_storage.py @@ -73,8 +73,8 @@ def test_basic_storage(self, basic_flow_system_linopy_coords, coords_config): 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, + + model.variables['TestStorage(Q_th_in)|flow_rate'] * model.timestep_duration + - model.variables['TestStorage(Q_th_out)|flow_rate'] * model.timestep_duration, ) # Check initial charge state constraint assert_conequal( @@ -146,7 +146,7 @@ def test_lossy_storage(self, basic_flow_system_linopy_coords, coords_config): charge_state = model.variables['TestStorage|charge_state'] rel_loss = 0.05 - hours_per_step = model.hours_per_step + timestep_duration = model.timestep_duration charge_rate = model.variables['TestStorage(Q_th_in)|flow_rate'] discharge_rate = model.variables['TestStorage(Q_th_out)|flow_rate'] eff_charge = 0.9 @@ -155,9 +155,9 @@ def test_lossy_storage(self, basic_flow_system_linopy_coords, coords_config): assert_conequal( model.constraints['TestStorage|charge_state'], charge_state.isel(time=slice(1, None)) - == charge_state.isel(time=slice(None, -1)) * (1 - rel_loss) ** hours_per_step - + charge_rate * eff_charge * hours_per_step - - discharge_rate / eff_discharge * hours_per_step, + == charge_state.isel(time=slice(None, -1)) * (1 - rel_loss) ** timestep_duration + + charge_rate * eff_charge * timestep_duration + - discharge_rate / eff_discharge * timestep_duration, ) # Check initial charge state constraint @@ -242,8 +242,8 @@ def test_charge_state_bounds(self, basic_flow_system_linopy_coords, coords_confi 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, + + model.variables['TestStorage(Q_th_in)|flow_rate'] * model.timestep_duration + - model.variables['TestStorage(Q_th_out)|flow_rate'] * model.timestep_duration, ) # Check initial charge state constraint assert_conequal(