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)