function pred = load_predicates(plant) % LOAD_PREDICATES Read predicates.json and resolve rhs_expr into numbers. % % Returns: % pred.constants - struct with T_c0, T_cold0, T_f0, T_standby % pred.operational_deadbands - struct of predicates (each: A_poly, b_poly, meaning) % pred.safety_limits - struct of halfspace limits (each: A_poly, b_poly, meaning) % pred.mode_invariants - struct mapping mode-invariant name to % its conjoined (A_poly, b_poly, components) % % Each halfspace is of the form { x : A_poly * x <= b_poly }. Conjunctions % (polytopes) stack rows into A_poly / b_poly. % % Usage: % plant = pke_params(); % pred = load_predicates(plant); % inv2 = pred.mode_invariants.inv2_holds; % is_in = all(inv2.A_poly * x <= inv2.b_poly); here = fileparts(mfilename('fullpath')); raw = fileread(fullfile(here, 'predicates.json')); J = jsondecode(raw); % --- Constants used in rhs_expr evaluations --- T_c0 = plant.T_c0; %#ok T_f0 = plant.T_f0; %#ok T_cold0 = plant.T_cold0; %#ok T_standby_offset_C = J.derived.T_standby_offset_C; T_standby = T_c0 + T_standby_offset_C; %#ok pred.constants = struct( ... 'T_c0', plant.T_c0, ... 'T_f0', plant.T_f0, ... 'T_cold0', plant.T_cold0, ... 'T_standby', T_standby, ... 'T_standby_offset_C', T_standby_offset_C, ... 'T_standby_offset_F', J.derived.T_standby_offset_F, ... 't_avg_in_range_halfwidth_C', J.derived.t_avg_in_range_halfwidth_C, ... 'p_above_crit_threshold_n', J.derived.p_above_crit_threshold_n, ... 'T_fuel_limit_C', J.derived.T_fuel_limit_C, ... 'T_c_high_trip_C', J.derived.T_c_high_trip_C, ... 'n_high_trip', J.derived.n_high_trip); % --- operational_deadbands --- pred.operational_deadbands = parse_group(J.operational_deadbands, ... T_c0, T_f0, T_cold0, T_standby); % --- safety_limits --- pred.safety_limits = parse_group(J.safety_limits, ... T_c0, T_f0, T_cold0, T_standby); % --- mode_invariants: conjunctions of safety_limits entries --- inv_names = fieldnames(J.mode_invariants); for k = 1:numel(inv_names) name = inv_names{k}; if startsWith(name, '_') || startsWith(name, 'x_'), continue, end entry = J.mode_invariants.(name); if ~isstruct(entry) || ~isfield(entry, 'conjunction_of'), continue, end components = entry.conjunction_of; if ischar(components), components = {components}; end A_all = []; b_all = []; for i = 1:numel(components) comp = components{i}; A_all = [A_all; pred.safety_limits.(comp).A_poly]; %#ok b_all = [b_all; pred.safety_limits.(comp).b_poly]; %#ok end pred.mode_invariants.(name).A_poly = A_all; pred.mode_invariants.(name).b_poly = b_all; pred.mode_invariants.(name).meaning = entry.meaning; pred.mode_invariants.(name).components = components; end end function group_out = parse_group(group_in, T_c0, T_f0, T_cold0, T_standby) names = fieldnames(group_in); group_out = struct(); for k = 1:numel(names) name = names{k}; % MATLAB jsondecode renames "_comment" -> "x_comment" and similar if startsWith(name, '_') || startsWith(name, 'x_'), continue, end entry = group_in.(name); if ~isstruct(entry) || ~isfield(entry, 'halfspaces'), continue, end hs_list = entry.halfspaces; if iscell(hs_list) n_hs = numel(hs_list); get_hs = @(i) hs_list{i}; else n_hs = numel(hs_list); get_hs = @(i) hs_list(i); end A_poly = zeros(n_hs, 10); b_poly = zeros(n_hs, 1); for i = 1:n_hs hs = get_hs(i); A_poly(i, hs.state_index) = hs.coeff; b_poly(i) = evalin_context(hs.rhs_expr, T_c0, T_f0, T_cold0, T_standby); end group_out.(name).A_poly = A_poly; group_out.(name).b_poly = b_poly; group_out.(name).meaning = entry.meaning; end end function val = evalin_context(expr, T_c0, T_f0, T_cold0, T_standby) %#ok val = eval(expr); end