add result button making plot

This commit is contained in:
xinxiao
2026-03-20 17:01:50 +08:00
parent 36f0592bff
commit 44cfe7c1e3
8 changed files with 1059 additions and 419 deletions
+229
View File
@@ -4,6 +4,7 @@ classdef EDFMAppController < handle
Config struct = struct()
Results struct = struct()
ProjectRoot char
CurrentPlotAction string = ""
end
methods
@@ -48,6 +49,9 @@ classdef EDFMAppController < handle
obj.Config = load_case_template(caseId);
obj.refreshUIFromConfig();
obj.refreshResults();
if strlength(obj.CurrentPlotAction) ~= 0
obj.refreshCasePlotControls(obj.CurrentPlotAction);
end
obj.appendLog(sprintf('Loaded template: %s', string(caseId)));
obj.setStatus(sprintf('Template %s loaded', string(caseId)));
end
@@ -95,6 +99,9 @@ classdef EDFMAppController < handle
obj.appendLog(logText);
end
obj.refreshResults();
if strlength(obj.CurrentPlotAction) ~= 0
obj.refreshCasePlotControls(obj.CurrentPlotAction);
end
obj.setRunProgress('Completed');
obj.setStatus('Run completed');
catch ME
@@ -117,6 +124,9 @@ classdef EDFMAppController < handle
end
obj.Results = load_results_mat(fullfile(folder, fileName));
obj.refreshResults();
if strlength(obj.CurrentPlotAction) ~= 0
obj.refreshCasePlotControls(obj.CurrentPlotAction);
end
obj.appendLog(sprintf('Imported results: %s', fullfile(folder, fileName)));
obj.setStatus('Results imported');
end
@@ -135,6 +145,63 @@ classdef EDFMAppController < handle
obj.setStatus('Results exported');
end
function actions = getCasePlotActions(obj)
obj.pushUIToConfig();
actions = get_case_plot_actions(obj.Config);
end
function activateCasePlot(obj, actionId)
obj.pushUIToConfig();
obj.CurrentPlotAction = string(actionId);
obj.refreshCasePlotControls(actionId);
if ~isempty(fieldnames(obj.Results))
obj.runCasePlot(actionId);
end
end
function onPlotOptionChanged(obj)
if strlength(obj.CurrentPlotAction) == 0
return;
end
if ~isempty(fieldnames(obj.Results))
obj.runCasePlot(obj.CurrentPlotAction);
end
end
function refreshCasePlotControls(obj, actionId)
obj.pushUIToConfig();
obj.configurePlotControls(actionId);
end
function runCasePlot(obj, actionId)
if isempty(fieldnames(obj.Results))
uialert(obj.App.UIFigure, 'Run or import results before plotting.', 'Plot');
return;
end
obj.pushUIToConfig();
try
obj.CurrentPlotAction = string(actionId);
obj.configurePlotControls(actionId);
if isprop(obj.App, 'ResultAxes') && ~isempty(obj.App.ResultAxes)
options = obj.getCurrentPlotOptions(actionId);
render_case_plot_in_axes(obj.App.ResultAxes, obj.Config, obj.Results, actionId, options);
else
run_case_plot(obj.Config, obj.Results, actionId);
end
obj.appendLog(sprintf('Executed plot action: %s', string(actionId)));
obj.setStatus(sprintf('Plot: %s', string(actionId)));
catch ME
errorReport = getReport(ME, 'extended', 'hyperlinks', 'off');
errorSummary = obj.formatExceptionSummary(ME);
obj.publishExceptionToBase(ME, errorReport);
fprintf(2, '\n[EDFM GUI Plot Error]\n%s\n', errorReport);
obj.appendLog(errorSummary);
obj.appendLog(errorReport);
uialert(obj.App.UIFigure, errorSummary, 'Plot Failed', 'Interpreter', 'none');
end
end
function plotResults(obj)
if isempty(fieldnames(obj.Results))
obj.refreshResults();
@@ -596,6 +663,51 @@ classdef EDFMAppController < handle
end
end
function configurePlotControls(obj, actionId)
actionId = lower(string(actionId));
layerItems = obj.buildLayerItems();
wellItems = obj.buildWellItems();
metricItems = obj.buildMetricItems();
propertyItems = obj.buildPropertyItems(actionId);
[stepMin, stepMax, stepValue] = obj.buildTimeStepRange(actionId);
obj.setControlVisible('PlotLayerDropDown', ismember(actionId, ["plot_2d_layer", "plot_sp_dp", "plot_perm"]));
obj.setControlVisible('PlotLayerDropDownLabel', ismember(actionId, ["plot_2d_layer", "plot_sp_dp", "plot_perm"]));
obj.setControlVisible('PlotTimeStepSlider', ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]));
obj.setControlVisible('PlotTimeStepSliderLabel', ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]));
obj.setControlVisible('PlotTimeStepValueLabel', ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]));
obj.setControlVisible('PlotPropertyDropDown', ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]));
obj.setControlVisible('PlotPropertyDropDownLabel', ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]));
obj.setControlVisible('PlotWellDropDown', actionId == "plot_well_response");
obj.setControlVisible('PlotWellDropDownLabel', actionId == "plot_well_response");
obj.setControlVisible('PlotMetricDropDown', actionId == "plot_well_response");
obj.setControlVisible('PlotMetricDropDownLabel', actionId == "plot_well_response");
obj.setDropDownItems('PlotLayerDropDown', layerItems, layerItems{1});
obj.setDropDownItems('PlotWellDropDown', wellItems, wellItems{1});
obj.setDropDownItems('PlotMetricDropDown', metricItems, metricItems{1});
obj.setDropDownItems('PlotPropertyDropDown', propertyItems, propertyItems{1});
obj.configureTimeStepControl(stepMin, stepMax, stepValue);
end
function options = getCurrentPlotOptions(obj, actionId)
options = struct();
actionId = lower(string(actionId));
if ismember(actionId, ["plot_2d_layer", "plot_sp_dp", "plot_perm"])
options.layer_index = obj.getNumericDropDownValue('PlotLayerDropDown', 1);
end
if ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"])
options.time_step = obj.getTimeStepControlValue();
options.property_id = obj.getDropDownValue('PlotPropertyDropDown', 'pressure');
end
if actionId == "plot_well_response"
options.well_index = obj.getNumericDropDownValue('PlotWellDropDown', 1);
options.metric = obj.getDropDownValue('PlotMetricDropDown', 'BHP');
end
end
function setDropDownValue(obj, propName, value)
if ~isprop(obj.App, propName)
return;
@@ -629,6 +741,30 @@ classdef EDFMAppController < handle
end
end
function configureTimeStepControl(obj, minValue, maxValue, currentValue)
if isprop(obj.App, 'PlotTimeStepSlider')
slider = obj.App.PlotTimeStepSlider;
slider.Limits = [minValue, maxValue];
slider.MajorTicks = minValue:maxValue;
slider.MinorTicks = [];
slider.Value = currentValue;
end
if isprop(obj.App, 'PlotTimeStepValueLabel')
obj.App.PlotTimeStepValueLabel.Text = sprintf('Step: %d', currentValue);
end
end
function value = getTimeStepControlValue(obj)
value = 1;
if isprop(obj.App, 'PlotTimeStepSlider')
value = max(1, round(obj.App.PlotTimeStepSlider.Value));
obj.App.PlotTimeStepSlider.Value = value;
end
if isprop(obj.App, 'PlotTimeStepValueLabel')
obj.App.PlotTimeStepValueLabel.Text = sprintf('Step: %d', value);
end
end
function setNumericFieldValue(obj, propName, value)
if ~isprop(obj.App, propName)
return;
@@ -738,6 +874,99 @@ classdef EDFMAppController < handle
end
end
function setControlVisible(obj, propName, isVisible)
if ~isprop(obj.App, propName)
return;
end
component = obj.App.(propName);
if isprop(component, 'Visible')
if isVisible
component.Visible = 'on';
else
component.Visible = 'off';
end
end
end
function items = buildLayerItems(obj)
layerCount = 1;
if isfield(obj.Config, 'grid') && isfield(obj.Config.grid, 'dz') && ~isempty(obj.Config.grid.dz)
layerCount = numel(obj.Config.grid.dz);
elseif ~isempty(fieldnames(obj.Results)) && isfield(obj.Results, 'r') && isfield(obj.Results.r, 'nz')
layerCount = obj.Results.r.nz;
end
items = cellstr(string(1:layerCount));
end
function items = buildWellItems(obj)
wellCount = 1;
if ~isempty(fieldnames(obj.Results)) && isfield(obj.Results, 'Wellpara') && ~isempty(obj.Results.Wellpara)
wellCount = size(obj.Results.Wellpara{1}, 2);
else
wellCount = size(obj.Config.wells.well1, 1) + size(obj.Config.wells.well2, 1);
wellCount = max(wellCount, 1);
end
items = cellstr(string(1:wellCount));
end
function items = buildMetricItems(obj)
metricList = ["BHP", "dPWF"];
if ~isempty(fieldnames(obj.Results)) && isfield(obj.Results, 'Wellpara') && ~isempty(obj.Results.Wellpara)
sampleWell = obj.Results.Wellpara{1}{1, 1};
metricList = "BHP";
if isfield(sampleWell, 'qg')
metricList(end + 1) = "GPR"; %#ok<AGROW>
end
if isfield(sampleWell, 'qo')
metricList(end + 1) = "OPR"; %#ok<AGROW>
end
if isfield(sampleWell, 'qw')
metricList(end + 1) = "WPR"; %#ok<AGROW>
end
if isfield(sampleWell, 'pwf')
metricList(end + 1) = "dPWF"; %#ok<AGROW>
end
end
items = cellstr(unique(metricList, 'stable'));
end
function items = buildPropertyItems(obj, actionId)
if ~isfield(obj.Results, 'OutputRs') || isempty(obj.Results.OutputRs)
items = {'pressure', 'water_saturation'};
return;
end
output = obj.Results.OutputRs{end};
propertyList = "pressure";
if isfield(output, 'sw')
propertyList(end + 1) = "water_saturation"; %#ok<AGROW>
if obj.Config.model.flow_model == 2
propertyList(end + 1) = "oil_saturation"; %#ok<AGROW>
end
end
if isfield(output, 'sg')
propertyList(end + 1) = "gas_saturation"; %#ok<AGROW>
elseif obj.Config.model.flow_model == 1 && isfield(output, 'sw')
propertyList(end + 1) = "gas_saturation"; %#ok<AGROW>
end
if ~ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"])
items = {'pressure'};
else
items = cellstr(unique(propertyList, 'stable'));
end
end
function [minValue, maxValue, currentValue] = buildTimeStepRange(obj, actionId)
minValue = 1;
maxValue = 1;
currentValue = 1;
if ismember(actionId, ["plot_2d_layer", "plot_3d_distribution"]) ...
&& isfield(obj.Results, 'OutputRs') && ~isempty(obj.Results.OutputRs)
maxValue = numel(obj.Results.OutputRs);
currentValue = maxValue;
end
end
function textValue = formatExceptionSummary(~, ME)
lines = {
sprintf('Error: %s', ME.message)
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,46 @@
function actions = get_case_plot_actions(config)
%GET_CASE_PLOT_ACTIONS Return available legacy plot actions for a case.
case_id = lower(string(config.meta.case_id));
base_actions = struct( ...
'id', "plot_well_response", ...
'label', "Well Response", ...
'script', "plotWellResponse", ...
'description', "Plot the legacy well response figure.");
actions = base_actions;
switch case_id
case "case01"
actions(end + 1) = make_action("plot_2d_layer", "2D Layer", "plot_2D_layer", "Plot the default 2D layer pressure map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_3d_distribution", "3D Distribution", "plot_3D_dis", "Plot the default 3D distribution map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_perm", "Permeability", "plot_perm", "Plot the permeability layer map."); %#ok<AGROW>
case "case02"
actions(end + 1) = make_action("plot_2d_layer", "2D Layer", "plot_2D_layer", "Plot the default 2D layer pressure map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_3d_distribution", "3D Distribution", "plot_3D_dis", "Plot the default 3D distribution map."); %#ok<AGROW>
case "case03"
actions(end + 1) = make_action("plot_2d_layer", "2D Layer", "plot_2D_layer", "Plot the default 2D layer pressure map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_3d_distribution", "3D Distribution", "plot_3D_dis", "Plot the default 3D distribution map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_sp_dp", "SP/DP Map", "plot_SP_DP", "Plot the SP/DP shape-factor map."); %#ok<AGROW>
case "case04"
% well response only
case "case05"
% well response only
case "case06"
actions(end + 1) = make_action("plot_2d_layer", "2D Layer", "plot_2D_layer", "Plot the default 2D layer pressure map."); %#ok<AGROW>
actions(end + 1) = make_action("plot_3d_distribution", "3D Distribution", "plot_3D_dis", "Plot the default 3D distribution map."); %#ok<AGROW>
case "case07"
% well response only
otherwise
% no additional actions
end
end
function action = make_action(id, label, script, description)
action = struct( ...
'id', string(id), ...
'label', string(label), ...
'script', string(script), ...
'description', string(description));
end
@@ -0,0 +1,261 @@
function render_case_plot_in_axes(ax, config, results, action_id, options)
%RENDER_CASE_PLOT_IN_AXES Render a legacy plot directly into UIAxes.
arguments
ax
config (1,1) struct
results (1,1) struct
action_id
options struct = struct()
end
action_id = lower(string(action_id));
cla(ax);
switch action_id
case "plot_well_response"
render_well_response(ax, results, options);
case "plot_2d_layer"
render_2d_layer(ax, config, results, options);
case "plot_3d_distribution"
render_3d_distribution(ax, config, results, options);
case "plot_sp_dp"
render_sp_dp(ax, results, options);
case "plot_perm"
render_perm(ax, results, options);
otherwise
error('render_case_plot_in_axes:UnsupportedAction', ...
'Unsupported plot action: %s', string(action_id));
end
end
function render_well_response(ax, results, options)
times = results.Times(:);
wellpara = results.Wellpara;
well_index = get_option(options, 'well_index', 1);
metric = upper(string(get_option(options, 'metric', "BHP")));
set(ax, 'XScale', 'linear', 'YScale', 'linear');
if isempty(wellpara)
error('render_case_plot_in_axes:NoWellpara', 'Well response data is empty.');
end
n = numel(times);
data = zeros(n, 1);
switch metric
case "GPR"
field_name = 'qg';
y_label = 'Gas production rate, m^3/d';
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
case "OPR"
field_name = 'qo';
y_label = 'Oil production rate, m^3/d';
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
case "WPR"
field_name = 'qw';
y_label = 'Water production rate, m^3/d';
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
case "DPWF"
y_label = 'Pressure derivative, MPa';
for i = 1:n
if i == 1
data(i) = (wellpara{i + 1}{1, well_index}.pwf - wellpara{i}{1, well_index}.pwf) / ...
(log(times(i + 1)) - log(times(i)));
elseif i == n
data(i) = (wellpara{i}{1, well_index}.pwf - wellpara{i - 1}{1, well_index}.pwf) / ...
(log(times(i)) - log(times(i - 1)));
else
data(i) = (wellpara{i + 1}{1, well_index}.pwf - wellpara{i - 1}{1, well_index}.pwf) / ...
(log(times(i + 1)) - log(times(i - 1)));
end
end
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
set(ax, 'XScale', 'log', 'YScale', 'log');
otherwise
field_name = 'pwf';
y_label = 'BHP, MPa';
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
end
grid(ax, 'on');
xlabel(ax, 'Time, day');
ylabel(ax, y_label);
title(ax, sprintf('Well Response: well %d, %s', well_index, metric));
end
function render_2d_layer(ax, config, results, options)
r = results.r;
output = results.OutputRs{get_option(options, 'time_step', numel(results.OutputRs))};
layer_index = get_option(options, 'layer_index', 1);
property_id = string(get_option(options, 'property_id', "pressure"));
values = extract_plot_values(config, output, property_id);
coordinate = r.coordinates;
nodes = r.nodes;
nz = r.nz;
nx = r.nx;
ny = r.ny;
nel = 4;
dis = zeros(nel, nx * ny);
x = zeros(nel, nx * ny);
y = zeros(nel, nx * ny);
for i = 1:nx * ny
grid_numbering = i + (nz - layer_index) * nx * ny;
for j = 1:nel
x(j, i) = coordinate(nodes(grid_numbering, j), 1);
y(j, i) = coordinate(nodes(grid_numbering, j), 2);
if j == 3
x(j, i) = coordinate(nodes(grid_numbering, 4), 1);
y(j, i) = coordinate(nodes(grid_numbering, 4), 2);
elseif j == 4
x(j, i) = coordinate(nodes(grid_numbering, 3), 1);
y(j, i) = coordinate(nodes(grid_numbering, 3), 2);
end
dis(j, i) = values(grid_numbering);
end
end
fill(ax, x, y, dis, 'EdgeColor', 'interp');
axis(ax, 'equal');
axis(ax, 'tight');
colormap(ax, jet);
colorbar(ax);
xlabel(ax, 'x, m');
ylabel(ax, 'y, m');
title(ax, sprintf('2D Layer: layer %d, %s', layer_index, char(property_id)));
end
function render_3d_distribution(ax, config, results, options)
r = results.r;
output = results.OutputRs{get_option(options, 'time_step', numel(results.OutputRs))};
property_id = string(get_option(options, 'property_id', "pressure"));
v = extract_plot_values(config, output, property_id);
[downgrid, frontgrid, leftgrid, rightgrid, backgrid, upgrid] = preplot_dis(r);
coordinate = r.coordinates;
nodes = r.nodes;
hold(ax, 'on');
draw_surface(ax, downgrid, [1,2,4,3,1], coordinate, nodes, v);
draw_surface(ax, leftgrid, [1,5,7,3,1], coordinate, nodes, v);
draw_surface(ax, rightgrid, [2,4,8,6,2], coordinate, nodes, v);
draw_surface(ax, frontgrid, [1,2,6,5,1], coordinate, nodes, v);
draw_surface(ax, backgrid, [3,4,8,7,3], coordinate, nodes, v);
draw_surface(ax, upgrid, [5,6,8,7,5], coordinate, nodes, v);
hold(ax, 'off');
colormap(ax, jet);
colorbar(ax);
xlabel(ax, 'x, m');
ylabel(ax, 'y, m');
zlabel(ax, 'z, m');
title(ax, sprintf('3D Distribution: %s', char(property_id)));
view(ax, 3);
grid(ax, 'on');
axis(ax, 'tight');
end
function render_sp_dp(ax, results, options)
r = results.r;
layer_index = get_option(options, 'layer_index', 1);
render_static_layer_map(ax, r, r.sigma, layer_index, 'SP/DP Sigma');
end
function render_perm(ax, results, options)
r = results.r;
layer_index = get_option(options, 'layer_index', 1);
render_static_layer_map(ax, r, r.kx, layer_index, 'Permeability');
end
function render_static_layer_map(ax, r, values, layer_index, title_text)
coordinate = r.coordinates;
nodes = r.nodes;
nz = r.nz;
nx = r.nx;
ny = r.ny;
nel = 4;
dis = zeros(nel, nx * ny);
x = zeros(nel, nx * ny);
y = zeros(nel, nx * ny);
for i = 1:nx * ny
grid_numbering = i + (nz - layer_index) * nx * ny;
for j = 1:nel
x(j, i) = coordinate(nodes(grid_numbering, j), 1);
y(j, i) = coordinate(nodes(grid_numbering, j), 2);
if j == 3
x(j, i) = coordinate(nodes(grid_numbering, 4), 1);
y(j, i) = coordinate(nodes(grid_numbering, 4), 2);
elseif j == 4
x(j, i) = coordinate(nodes(grid_numbering, 3), 1);
y(j, i) = coordinate(nodes(grid_numbering, 3), 2);
end
dis(j, i) = values(grid_numbering);
end
end
fill(ax, x, y, dis, 'EdgeColor', 'interp');
axis(ax, 'equal');
axis(ax, 'tight');
colormap(ax, jet);
colorbar(ax);
xlabel(ax, 'x, m');
ylabel(ax, 'y, m');
title(ax, sprintf('%s: layer %d', title_text, layer_index));
end
function draw_surface(ax, grid_indices, order, coordinate, nodes, values)
nel = numel(order);
nmc = size(grid_indices, 2);
dis = zeros(nel, nmc);
x = zeros(nel, nmc);
y = zeros(nel, nmc);
z = zeros(nel, nmc);
z_shift = max(coordinate(:, 3)) + 2000;
for i = 1:nmc
for j = 1:nel
x(j, i) = coordinate(nodes(grid_indices(i), order(j)), 1);
y(j, i) = coordinate(nodes(grid_indices(i), order(j)), 2);
z(j, i) = coordinate(nodes(grid_indices(i), order(j)), 3) - z_shift;
dis(j, i) = values(grid_indices(i));
end
end
fill3(ax, x, y, z, dis, 'EdgeColor', 'interp');
end
function values = extract_plot_values(config, output, property_id)
property_id = lower(string(property_id));
switch property_id
case "pressure"
values = output.p;
case "water_saturation"
values = output.sw;
case "oil_saturation"
values = 1 - output.sw;
case "gas_saturation"
if isfield(output, 'sg')
values = output.sg;
else
values = 1 - output.sw;
end
otherwise
error('render_case_plot_in_axes:UnsupportedProperty', ...
'Unsupported plot property: %s', char(property_id));
end
end
function value = get_option(options, field_name, fallback)
if isfield(options, field_name) && ~isempty(options.(field_name))
value = options.(field_name);
else
value = fallback;
end
end
+104
View File
@@ -0,0 +1,104 @@
function run_case_plot(config, results, action_id)
%RUN_CASE_PLOT Run a legacy plot script for the current case/results.
arguments
config (1,1) struct
results (1,1) struct
action_id
end
if isempty(fieldnames(results))
error('run_case_plot:NoResults', 'No results are available for plotting.');
end
project_root = fileparts(fileparts(fileparts(mfilename('fullpath'))));
source_case_folder = resolve_source_case_folder(project_root, config.meta);
if isempty(source_case_folder) || ~isfolder(source_case_folder)
error('run_case_plot:MissingCaseFolder', ...
'Cannot resolve source case folder for case %s.', string(config.meta.case_id));
end
old_dir = pwd;
cleanup_obj = onCleanup(@() cd(old_dir)); %#ok<NASGU>
addpath(genpath(project_root));
cd(source_case_folder);
action_id = lower(string(action_id));
switch action_id
case "plot_well_response"
plotWellResponse(results.Times, results.Wellpara);
case "plot_2d_layer"
assert_plot_function_exists('plot_2D_layer');
plot_2D_layer(default_time_step(results), default_layer(results.r), results.r, results.OutputRs, default_plot_type(config));
case "plot_3d_distribution"
assert_plot_function_exists('plot_3D_dis');
plot_3D_dis(default_time_step(results), results.r, results.OutputRs, default_plot_type(config));
case "plot_sp_dp"
assert_plot_function_exists('plot_SP_DP');
plot_SP_DP(default_layer(results.r), results.r);
case "plot_perm"
assert_plot_function_exists('plot_perm');
plot_perm(default_layer(results.r), results.r);
otherwise
error('run_case_plot:UnsupportedAction', ...
'Unsupported plot action: %s', string(action_id));
end
end
function source_case_folder = resolve_source_case_folder(project_root, meta_config)
source_case_folder = meta_config.source_case_folder;
if isfolder(source_case_folder)
return;
end
case_id = lower(string(meta_config.case_id));
case_num = sscanf(char(case_id), 'case%d');
if isempty(case_num)
source_case_folder = '';
return;
end
folder_candidates = dir(fullfile(project_root, sprintf('*%d-*', case_num)));
folder_candidates = folder_candidates([folder_candidates.isdir]);
if isempty(folder_candidates)
source_case_folder = '';
else
source_case_folder = fullfile(project_root, folder_candidates(1).name);
end
end
function idx = default_time_step(results)
if isfield(results, 'OutputRs') && ~isempty(results.OutputRs)
idx = numel(results.OutputRs);
else
idx = 1;
end
end
function k = default_layer(r)
k = 1;
if isfield(r, 'nz') && ~isempty(r.nz)
k = min(1, r.nz);
end
end
function type = default_plot_type(config)
switch config.model.flow_model
case 1
type = 1;
case 2
type = 1;
case 3
type = 1;
otherwise
type = 1;
end
end
function assert_plot_function_exists(function_name)
if exist(function_name, 'file') ~= 2
error('run_case_plot:MissingPlotFunction', ...
'Plot function not found in current case folder: %s', function_name);
end
end