Audit every document in the repository, convert the non-markdown ones into markdown reports, and split current documentation from outdated material. docs/ — 31 markdown documents in seven numbered sections. Twenty are new reports generated from .docx / .pdf / .xlsx / .mlx / .m sources that were previously unreadable in the browser and undiffable in git. Each report carries a provenance block (source path, format, MD5) and links back to its original; all 13 recorded checksums verify against the files on disk. Machine-extraction losses (PDF table column interleaving, Word OMML equations, embedded figures) are called out explicitly rather than silently smoothed over. superseded/ — outdated material with a documented reason per entry: two byte-identical ClickUp re-exports, an older revision of the BIDMC/UCSD energy-flow doc (the retained copy adds the SoC Violation Rate KPI), a duplicate of Shift input data.docx, the May 2026 simulation plan, the root PV+Battery.md now covered by a fuller report, GitHub's stock demo-repository template, and a zero-byte placeholder. Its README also records what was deliberately NOT retired and why — the "Old Frameworks" and "Old Simulations" folders hold unique Simulink revisions, and "Big Ugly Folder" holds the only copy of framework revision 1.3. Findings worth flagging, all documented in the reports: - Simulink lineage recovered from each .slx's internal coreProperties.xml revision counter. The current model is Current Framework/Bobert0206_Initial_Simulation_Framework.slx (rev 2.7); the top-level copy is rev 1.3, five revisions behind. - Simulations/Constants.m is a truncated byte-prefix of the Current Framework copy, silently missing H2_leak, H2_cap and E_H2_vol_h. - The PEM electrolyser and fuel cell are unmodified MathWorks Simscape examples still at vendor defaults; the "10x bigger" sizing TODO recorded in Constants.m was never carried out. - controller-claude.m does not compile — undefined P_Electro_max, outputs unassigned on several paths. - The specification set uses two incompatible variable naming conventions and disagrees on action-space size (5 vs 16). - MA_hourly_load.csv (13.7 MB) is the same 35,040 rows as 89993-0.parquet (2.4 MB). - Clinical data is the MIMIC-IV *demo* (ODbL, 100 patients), not full MIMIC-IV — redistributable, but the licence and citation are unrecorded. Housekeeping: untrack 21 Simulink build artefacts (slprj/, *.slxc) and add ignore rules for them. Root README rewritten around the new layout. Recruitment notes naming individual candidates are excluded from version control via .gitignore rather than committed; the generic question template is kept in docs/07-team-and-operations/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9.5 KiB
Rule-Based Controller — MATLAB Implementation
Markdown report of a non-markdown source file.
Source Shift Matlab Drive/Shift Matlab Drive/controller-claude.mFormat MATLAB, 92 lines, 7,388 B MD5 745f573a203238a32cbcd5b1a3d6ecc7Status Draft — does not run. See Blocking defects Report generated 2026-07-25
What it is
The MATLAB implementation of the priority-cascade dispatch policy specified in
Rule-Based Controller. Written as the body of
a Simulink MATLAB Function block — the signature is function [...] = fcn(...), and fcn
is Simulink's default block function name.
function [P_load_P, P_load_PB, P_battery_cont, P_load_PBH, P_FuelCell_cont, P_Electro_cont] = ...
fcn(Current_A, P_load, P_PV, E_battery_SOC, P_battery_max, E_battery_max, ...
E_H2_SOC, P_FuelCell_max, E_H2_max)
It resolves load in a fixed merit order, exactly as the specification describes:
| Stage | Lines | Behaviour |
|---|---|---|
| 1. PV direct | 8 | P_load_P = P_load - P_PV — residual after solar |
| 2. Battery | 11–36 | Residual > 0 → discharge (capped by P_battery_max or remaining SOC); residual < 0 → charge (capped by max power or remaining headroom) |
| 3. Hydrogen | 39–64 | Same two-sided pattern — fuel cell discharges the tank, electrolyser charges it |
| 4. Grid | 67–72 | Whatever remains is bought or sold |
Warning
This file does not run. It has hard undefined-variable errors and cannot compile as a Simulink MATLAB Function block. It is an explicitly-marked work in progress — the author's own notes at lines 74–79 say as much. The defect list below is documentation of a known draft, not a code review of something anyone claimed was finished.
Blocking defects
B1 — undefined Electro_max (line 54).
P_Electro_cont = -Electro_max % The ELECTROLYZER charges at it's maximum power allowed
Electro_max exists nowhere. Intended P_Electro_max — missing the P_ prefix.
B2 — P_Electro_max is never defined either (lines 52, 53, 54).
The signature declares nine inputs; P_Electro_max is not among them and is never assigned in
the body. Even after fixing B1, the reference is still undefined. The entire electrolyser
branch (lines 51–63) is dead. The function signature needs a tenth input.
B3 — outputs not assigned on all paths.
A Simulink MATLAB Function block requires every declared output to be assigned on every path.
| Output | Unassigned when |
|---|---|
P_FuelCell_cont / P_Electro_cont |
Mutually exclusive — the if at line 39 assigns only the first, the else at 51 only the second. Both are declared outputs |
P_load_PB |
Never assigned on the path through lines 20–21 (battery too empty to discharge at max) |
P_load_PBH |
Never assigned on the path through lines 48–49 (tank too empty) |
Consequently line 39 can read an unassigned P_load_PB, and line 67 an unassigned
P_load_PBH.
Logic defects — silently wrong results
B4 — wrong right-hand side (line 43).
P_FuelCell_cont = P_load_PBH;
The battery analogue it was copied from (line 15) is P_battery_cont = P_load_P; — the
incoming residual. Here P_load_PBH is, under the guard at line 42 (P_load_PBH < 0), the
negative overshoot. It should read P_FuelCell_cont = P_load_PB;. As written the fuel cell
is commanded to a negative power — to consume — exactly when it is supposed to supply.
B5 — sign convention inverted (lines 33 and 61).
The stated convention (line 29) is "negative P_battery_cont is battery charging, positive is
discharging". Line 26 respects it (-P_battery_max). But in the same charging branch:
33: P_battery_cont = E_battery_max - E_battery_SOC; % headroom — POSITIVE
61: P_Electro_cont = E_H2_max - E_H2_SOC; % headroom — POSITIVE
Both assign positive values while on the charging path. Storage will be commanded to discharge when it should charge.
B6 — energy and power conflated throughout.
Energy variables (E_*, kWh) are compared against and assigned to power variables (P_*, kW)
with no timestep factor:
| Line | Expression | Problem |
|---|---|---|
| 12 | if E_battery_SOC > P_battery_max |
kWh compared to kW |
| 21 | P_battery_cont = E_battery_SOC; |
kWh assigned to a kW command |
| 40 | if E_H2_SOC > P_FuelCell_max |
kWh compared to kW |
| 49 | P_FuelCell_cont = E_H2_SOC; |
kWh assigned to a kW command |
| 24, 33, 52, 61 | headroom expressions | same |
The author flags this at lines 78–79:
%IMPLEMENT THE ARRAY-SCANNING FOR-LOOP AND MAKE SURE THE Power = Energy%Equations make sense????
The arithmetic is only correct if the timestep is exactly 1 h and units are consistent —
neither is enforced anywhere. This is the same dt question the
Simulator I/O Interface leaves open.
B7 — load sign clashes with Constants.m.
if P_load < 0
P_load = 0;
end
Constants.m defines E_HospitalLoad = -1000000; %kWh — hospital load is stored negative.
If that convention feeds P_load, this guard zeroes out the entire hospital demand and the
controller does nothing.
Console output
B8/B9 — two assignments missing a terminating semicolon: line 26
(P_battery_cont = -P_battery_max) and line 54 (P_Electro_cont = -Electro_max). Both echo to
the console on every hit, every timestep. An automated scan found exactly these two.
B10 — unconditional disp every timestep (lines 67–72):
if P_load_PBH > 0
disp('buy')
else
disp('sell')
end
disp(P_load_PBH)
Beyond the spam, disp is an extrinsic function in a Simulink MATLAB Function block —
unsupported for code generation. It will either force interpreted execution or fail the build.
Incomplete sections
I1 — grid exchange is never returned. Lines 67–72 determine buy vs. sell but only print it. There is no output argument for grid power. The author's note at lines 74–76:
%Doublecheck these and repeat the logic for the hydrogen, finally%selling/buying the rest from the grid. Make sure the discharging is at the%tank level and charging at hydrogen level
I2 — the time-series loop was never implemented (lines 78–79, quoted above).
I3 — dead commented-out hydrogen integrator (lines 83–89):
%for t = 1:24 %Check if this makes sense
% H2_stor(end) = H2_stor(end)*(1-H2_leak);
% H2_stor(end+1) = H2_stor(end)+H2_vol(t);
% H2_Flow_L_min(t) = (-P_H2(t)/E_H2_vol_h(t))*(1000/60);
%end
%E_H2_stor = H2_stor*E_H2_vol_h;
Uncommenting it would still fail. H2_stor, H2_vol and P_H2 are undefined anywhere in the
repository. H2_leak and E_H2_vol_h do exist — but only in the complete Constants.m,
not the stale top-level copy (see
Simulink Model Inventory).
And line 86 indexes E_H2_vol_h(t) as an array when it is the scalar 3000 — out of bounds
for any t > 1.
Other observations
| # | Observation |
|---|---|
| Q1 | Input Current_A is declared but never used in the 92-line body — a dead parameter |
| Q2 | The filename is not a legal MATLAB function file. controller-claude.m contains a hyphen; MATLAB identifiers cannot. It can never be called as controller-claude(...). It only works pasted into a Simulink MATLAB Function block — consistent with the function being named fcn |
| Q3 | It is the only file at the Shift Matlab Drive/Shift Matlab Drive/ root, outside Simulations/ entirely, and is not attached to any model in the repository |
| Q4 | The hydrogen block is a verbatim copy of the battery block with incomplete renaming. Lines 45–46 sit in the fuel cell branch but say "max battery power"; line 57 references P_battery_cont inside the electrolyser branch; line 61's comment says "Battery charges only as much as there is space left" while assigning P_Electro_cont. This is how B4 and B5 arose |
| Q5 | Comment typos: dirrectly (7), to empty for "too empty" (20, 48), ELETROLYZER (52), SOTRAGE (60) |
Fixing it
In rough dependency order:
- Add
P_Electro_maxas a tenth input; fix theElectro_maxtypo (B1, B2). - Assign every output on every path — initialise all six to
0at the top (B3). - Fix line 43 to
P_FuelCell_cont = P_load_PB;(B4). - Negate lines 33 and 61 (B5).
- Decide the timestep and make every
E_*/P_*conversion explicit (B6). - Settle the load sign convention against
Constants.m(B7). - Add a grid-power output; delete the
dispcalls (I1, B10). - Rename the file to a legal MATLAB identifier, e.g.
rule_based_controller.m, and move it next to the model it drives.
Steps 5 and 6 are the ones that need a decision rather than an edit — they are the same open questions the Simulator I/O Interface raises.
Related
- Rule-Based Controller — the specification this implements
- Simulator I/O Interface — the contract, including sign conventions and the
dtquestion - Simulink Model Inventory — the
Constants.mstaleness this depends on - RL Action Variables — what an RL policy would emit instead