# PV + Battery Simulink Model
> **Markdown report consolidating two non-markdown sources plus one duplicate markdown file.**
>
> | | |
> |---|---|
> | **Sources** | `Shift Matlab Drive/.../Simulations/PV+Battery Simulink/PV+Battery.docx`
`Shift Matlab Drive/.../Simulations/PV+Battery Simulink/PVBatteryRead.mlx`
`Shift Matlab Drive/.../Simulations/PV+Battery Simulink/Constants.mlx` |
> | **Also duplicated as** | `PV+Battery.md` at the repository root — **retired to [`superseded/`](../../superseded/)** |
> | **Owner** | Simulations cluster |
> | **Status** | Current — **model has a known unresolved bug** (see [Known issue](#known-issue--model-is-not-time-dependent)) |
> | **Report generated** | 2026-07-25 |
## What this is
The Simulations cluster's first end-to-end vertical slice: read real profile data out of a
spreadsheet, hand it to Simulink, and run coupled PV and battery models against it. It is a
methodology trial as much as a model — the closing question in the source is *"Discuss if this
is a good standard for all parts."*
## Components
### 1. Profiles SHIFT (spreadsheet)
- A new tab has been added to the existing Excel data sheet.
- Quick-search data has been filled in for fast experimentation with the Simulink models.
See [Generation Profiles Workbook](generation-profiles-workbook.md) for the sheet contents.
### 2. `PVBatteryRead.mlx`
- Code developed with large assistance from ChatGPT.
- Originally intended to read *everything* off the spreadsheet; it currently reads **dynamic
data** and prepares it for Simulink use.
- The constants reading would not function, for reasons not established — so constants are set
in `Constants.mlx` instead.
**What the script actually does**, step by step:
| Step | Behaviour |
|---|---|
| File settings | Reads `Profiles SHIFT(Generators Factors + Battery).xlsx`, sheet **`Fake Simulink Data`**, with `HeaderLines = 1` and `VariableNamingRule = 'preserve'` |
| 1 — Type conversion | Converts cell-text numbers to double, **replacing `,` with `.`** for decimal commas; converts `duration` columns to hours |
| 2 — Trim | Detects the last valid signal row (any non-NaN numeric, and a date after 2000-01-01) and truncates the table there |
| 3 — Extract | Parses variable names out of headers of the form `Name - var (unit)` via the regex `-\s*(.*?)\s*\(`, then `matlab.lang.makeValidName`. Time (`t`) is converted to **seconds** — a max ≤ 1 is treated as fraction-of-day, otherwise as hours. A column whose values are all identical becomes a **constant**; otherwise a time series |
| 4 — Simulink signals | For every numeric variable of the same length as `t`, creates `_simulink = [t, values]` in the base workspace — the two-column `[time, data]` format Simulink's *From Workspace* block expects |
> The sheet name is literally **`Fake Simulink Data`** — placeholder data for wiring up the
> pipeline, not a real profile.
### 3. `Constants.mlx`
Remember to run this **and** `PVBatteryRead.mlx` before attempting to run the Simulink models.
```matlab
A_PV = 3060; % PV Area (m2)
mu_PV = 0.2; % Panel Efficiency (%)
V_PV = 24; % PV Voltage (V)
P_ele = 1500000; % Electrolyzer Power Consumption (W)
E_t0 = 0; % Initial Battery Energy (Wh)
E_rated = 170000; % Battery Rated Energy (W)
Q_rated = 100000; % Battery Rated Charge Capacity (W)
Charge_max = 1; % Maximum Charge (%)
Disharge_max = 2; % Maximum Discharge (%)
SoC_max = 1; % SoC limit (%)
SoC_t0 = 0; % Initial SoC (%)
P_B_int = 0; % Initial Battery power (W)
I_B_int = 0; % Initial Battery current (A)
```
**Parameter cross-check against the project spec:**
| Constant | Value here | Project spec | Comment |
|---|---|---|---|
| `A_PV` | 3060 m² | **2,300 m²** ([Simulator I/O §1.4](../02-specifications/simulator-io-interface.md#14-pv)) | 33% larger than spec |
| `mu_PV` | 0.2 | — | Reasonable for commercial silicon |
| `P_ele` | 1.5 MW | — | Large relative to a 2,300 m² × 20% array (~460 kW peak) |
| `E_rated` | 170000, commented **W** | Rated energy capacity in **Wh** | Unit comment is wrong; should be Wh |
| `Q_rated` | 100000, commented **W** | Rated charge capacity in **Ah** | Unit comment is wrong; should be Ah |
| `Disharge_max` | 2 | — | Commented "%" but a discharge *limit* of 2 with `SoC_max = 1` is dimensionally inconsistent. Also a typo for `Discharge_max` |
| `SoC_t0` | 0 | — | Starting from an empty battery |
None of these break the model — they are annotation errors and a not-yet-calibrated parameter
set — but they should be reconciled with the simulator contract before results are quoted.
### 4. `SolarPanel.slx` + `Battery.slx` (and `SolarPanel_n_Battery.slx`)
- Two individual models using the equations from *paper 1* to derive the desired outputs over
time.
- **Both do this in two different ways**, using different variables, to compare consistency.
- Most SoC-related quantities are handled in **%**. The dynamic data found is likely in
another unit — that is what `Q_rated` and `E_rated` are for, to be used according to paper 1.
## Known issue — model is not time-dependent
> *"An issue was encountered where this code does not appear to be time dependant despite
> drawing the appropriate `_simulink` matrices from the workspace. This is most likely some
> kind of conversion issue between time in seconds in code and time measured in the simulinks
> themselves."*
This is unresolved and is the first item on the to-do list. The author's diagnosis is
consistent with what `PVBatteryRead.mlx` does: it converts `t` to **seconds** in step 3, so any
Simulink block configured in hours — or a solver whose stop time is set in hours — will see a
signal that appears constant across the run because the simulated interval covers only the
first few samples.
**Where to look first:** the `[t, values]` matrices are built with `t` in seconds; check the
Simulink model's configured stop time and the *From Workspace* block's sample-time
interpretation match that unit.
## To be continued
Carried forward verbatim from the source:
- Fix the aforementioned time-dependency issue.
- Test and see if these values make sense. Compare the two versions for both.
- Discuss if this is a good standard for all parts.
## File locations
All of these files were submitted on MATLAB Drive and ClickUp under
*Simulations → PV+Battery Simulink*. In this repository:
| File | Size | Note |
|---|---|---|
| `PV+Battery.docx` | 1.6 kB | Source of this report |
| `PVBatteryRead.mlx` | — | Data import script |
| `Constants.mlx` | — | Parameter definitions |
| `SolarPanel.slx` | — | PV model |
| `Battery.slx` | — | Battery model |
| `SolarPanel_n_Battery.slx` | — | Combined model |
| `SolarPanel.slxc` | — | Simulink cache — build artefact |
| `Profiles SHIFT(Generators Factors + Battery).xlsx` | — | Byte-identical to the copy in `Current Framework/` |
| `slprj/` | — | Simulink build cache — machine-generated |
## Duplicate source note
The repository root contained `PV+Battery.md`, a markdown transcription of `PV+Battery.docx`
with the same content. Since this report supersedes both, the root markdown copy is retired to
[`superseded/`](../../superseded/) and the `.docx` remains in place as the original.
## Related
- [Generation Profiles Workbook](generation-profiles-workbook.md) — the spreadsheet this reads
- [MATLAB Live Scripts](matlab-live-scripts.md) — inventory of all `.mlx` files
- [Simulink Model Inventory](simulink-model-inventory.md) — all `.slx` files and their lineage
- [Simulator I/O Interface §1.4–1.5](../02-specifications/simulator-io-interface.md#14-pv) — the parameter spec these constants should match