Files
3D_EDFM_SIM-X/gui_support/app/EDFMAppController.m
T
2026-03-20 17:01:50 +08:00

1167 lines
57 KiB
Matlab

classdef EDFMAppController < handle
properties (Access = private)
App
Config struct = struct()
Results struct = struct()
ProjectRoot char
CurrentPlotAction string = ""
end
methods
function obj = EDFMAppController(app)
obj.App = app;
obj.ProjectRoot = fileparts(fileparts(fileparts(mfilename('fullpath'))));
obj.addSupportPaths();
end
function startup(obj)
obj.setDropDownItems('TemplateDropDown', ...
{'case01', 'case02', 'case03', 'case04', 'case05', 'case06', 'case07'}, ...
'case01');
obj.setDropDownItems('ResultTypeDropDown', {'Time Steps', 'Newtons vs Time'}, 'Time Steps');
obj.setDropDownItems('ModelFlagDropDown', {'1'}, '1');
obj.setDropDownItems('ModelGridModelDropDown', {'1', '2'}, '1');
obj.setDropDownItems('ModelFlowModelDropDown', {'1', '2', '3'}, '1');
obj.setDropDownItems('FractureInputStyleDropDown', {'1', '2', '3', '4'}, '1');
obj.setDropDownItems('GWgas_modelDropDown', {'1', '2'}, '1');
obj.setDropDownItems('OWoil_modelDropDown', {'1', '2'}, '1');
obj.setDropDownItems('MCgas_modelDropDown', {'1', '2'}, '1');
obj.Results = struct();
obj.loadTemplate('case01');
obj.appendLog('Controller startup completed.');
obj.setStatus('Ready');
end
function newConfig(obj)
obj.Config = create_empty_config();
obj.Results = struct();
obj.refreshUIFromConfig();
obj.refreshResults();
obj.appendLog('Created empty config.');
obj.setStatus('Empty config');
end
function loadTemplate(obj, caseId)
if nargin < 2 || strlength(string(caseId)) == 0
caseId = 'case01';
end
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
function importConfig(obj)
[fileName, folder] = uigetfile('*.mat', 'Import Config');
if isequal(fileName, 0)
return;
end
obj.Config = load_config_mat(fullfile(folder, fileName));
obj.refreshUIFromConfig();
obj.appendLog(sprintf('Imported config: %s', fullfile(folder, fileName)));
obj.setStatus('Config imported');
end
function exportConfig(obj)
obj.pushUIToConfig();
[fileName, folder] = uiputfile('*.mat', 'Export Config', 'config.mat');
if isequal(fileName, 0)
return;
end
save_config_mat(obj.Config, fullfile(folder, fileName));
obj.appendLog(sprintf('Exported config: %s', fullfile(folder, fileName)));
obj.setStatus('Config exported');
end
function runCurrentConfig(obj)
obj.pushUIToConfig();
obj.setStatus('Running');
obj.setRunProgress('Running...');
obj.setRunHeader('-', '-', '-');
obj.appendLog('Run started.');
drawnow;
try
logText = evalc('[r, Times, OutputRs, Wellpara, trun] = run_case(obj.Config);');
obj.Results = struct( ...
'r', r, ...
'Times', Times, ...
'OutputRs', {OutputRs}, ...
'Wellpara', {Wellpara}, ...
'trun', trun, ...
'captured_log', logText);
if ~isempty(strtrim(logText))
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
errorReport = getReport(ME, 'extended', 'hyperlinks', 'off');
errorSummary = obj.formatExceptionSummary(ME);
obj.publishExceptionToBase(ME, errorReport);
fprintf(2, '\n[EDFM GUI Run Error]\n%s\n', errorReport);
obj.appendLog(errorSummary);
obj.appendLog(errorReport);
obj.setRunProgress('Failed');
obj.setStatus('Run failed');
uialert(obj.App.UIFigure, errorSummary, 'Run Failed', 'Interpreter', 'none');
end
end
function importResults(obj)
[fileName, folder] = uigetfile('*.mat', 'Import Results');
if isequal(fileName, 0)
return;
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
function exportResults(obj)
if isempty(fieldnames(obj.Results))
uialert(obj.App.UIFigure, 'No results available to export.', 'Export Results');
return;
end
[fileName, folder] = uiputfile('*.mat', 'Export Results', 'results.mat');
if isequal(fileName, 0)
return;
end
save_results_mat(obj.Results, fullfile(folder, fileName));
obj.appendLog(sprintf('Exported results: %s', fullfile(folder, fileName)));
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();
return;
end
if ~isprop(obj.App, 'ResultAxes') || isempty(obj.App.ResultAxes)
return;
end
axesHandle = obj.App.ResultAxes;
cla(axesHandle);
plotType = obj.getDropDownValue('ResultTypeDropDown', 'Time Steps');
if strcmp(plotType, 'Newtons vs Time') && isfield(obj.Results, 'trun') ...
&& isstruct(obj.Results.trun) ...
&& isfield(obj.Results.trun, 'Newtons_vs_time') ...
&& ~isempty(obj.Results.trun.Newtons_vs_time)
data = obj.Results.trun.Newtons_vs_time;
plot(axesHandle, data(:, 1), data(:, 2), '-o', 'LineWidth', 1.2);
title(axesHandle, 'Newtons vs Time');
xlabel(axesHandle, 'Time');
ylabel(axesHandle, 'Newton Count');
elseif isfield(obj.Results, 'Times') && ~isempty(obj.Results.Times)
times = obj.Results.Times(:);
plot(axesHandle, times, 1:numel(times), '-o', 'LineWidth', 1.2);
title(axesHandle, 'Time Step Index');
xlabel(axesHandle, 'Time');
ylabel(axesHandle, 'Step');
else
title(axesHandle, 'No plottable data');
end
end
function syncModelTabs(obj)
gridModel = obj.getNumericDropDownValue('ModelGridModelDropDown', 1);
flowModel = obj.getNumericDropDownValue('ModelFlowModelDropDown', 1);
if isprop(obj.App, 'TabGroup3')
if gridModel == 2 && isprop(obj.App, 'DPTab')
obj.App.TabGroup3.SelectedTab = obj.App.DPTab;
elseif isprop(obj.App, 'SPTab')
obj.App.TabGroup3.SelectedTab = obj.App.SPTab;
end
end
if isprop(obj.App, 'TabGroup2')
switch flowModel
case 1
if isprop(obj.App, 'GasWaterTab')
obj.App.TabGroup2.SelectedTab = obj.App.GasWaterTab;
end
case 2
if isprop(obj.App, 'OilWaterTab')
obj.App.TabGroup2.SelectedTab = obj.App.OilWaterTab;
end
otherwise
if isprop(obj.App, 'MultiComponentTab')
obj.App.TabGroup2.SelectedTab = obj.App.MultiComponentTab;
end
end
end
obj.setEditableState('InitialCsEditField', flowModel == 3);
obj.setEditableState('InitialCbEditField', flowModel == 3);
if isprop(obj.App, 'InitStatusLabel')
switch flowModel
case 1
obj.App.InitStatusLabel.Text = 'Init: gas-water';
case 2
obj.App.InitStatusLabel.Text = 'Init: oil-water';
otherwise
obj.App.InitStatusLabel.Text = 'Init: multi-component';
end
end
end
end
methods (Access = private)
function addSupportPaths(obj)
addpath(fullfile(obj.ProjectRoot, 'gui_support', 'app'));
addpath(fullfile(obj.ProjectRoot, 'gui_support', 'config'));
addpath(fullfile(obj.ProjectRoot, 'gui_support', 'runtime'));
addpath(fullfile(obj.ProjectRoot, 'gui_support', 'results'));
addpath(fullfile(obj.ProjectRoot, 'gui_support', 'templates'));
addpath(genpath(obj.ProjectRoot));
end
function refreshUIFromConfig(obj)
if isempty(fieldnames(obj.Config))
return;
end
c = obj.Config;
obj.setDropDownValue('ModelFlagDropDown', c.model.modelflag);
obj.setDropDownValue('ModelGridModelDropDown', c.model.grid_model);
obj.setDropDownValue('ModelFlowModelDropDown', c.model.flow_model);
obj.setTextAreaExpr('GridDxTextArea', c.grid.dx);
obj.setTextAreaExpr('GridDyTextArea', c.grid.dy);
obj.setTextAreaExpr('GridDzTextArea', c.grid.dz);
obj.setTextAreaExpr('GridNTGTextArea', c.grid.NTG);
obj.setDropDownValue('FractureInputStyleDropDown', c.fracture.input_style);
obj.setTextAreaExpr('FractureInputTextArea', c.fracture.input_content);
obj.setTextAreaExpr('FractureLinesTextArea', c.fracture.fractureLines);
obj.setTextAreaExpr('FractureHeightsTextArea', c.fracture.fractureHeights);
obj.setTextAreaExpr('FractureFlowBarrierFlagsTextArea', c.fracture.flowBarrierFlags);
obj.setTextAreaExpr('SPBoundaryTextArea', c.discretization.sp.boundary_polygon);
obj.setTextAreaExpr('SPInvalidLayerTextArea', c.discretization.sp.invalid_layer);
obj.setTextAreaExpr('SPMatrixKxTextArea', c.discretization.sp.matrix.kx);
obj.setTextAreaExpr('SPMatrixKyTextArea', c.discretization.sp.matrix.ky);
obj.setTextAreaExpr('SPMatrixKzTextArea', c.discretization.sp.matrix.kz);
obj.setTextAreaExpr('SPMatrixPoriTextArea', c.discretization.sp.matrix.pori);
obj.setNumericFieldValue('SPMatrixPrporEditField', c.discretization.sp.matrix.prpor);
obj.setNumericFieldValue('SPMatrixCporEditField', c.discretization.sp.matrix.cpor);
obj.setNumericFieldValue('SPRockDensityEditField', c.discretization.sp.matrix.rock_density);
obj.setTextAreaExpr('SPFractureKfTextArea', c.discretization.sp.fracture.Kf);
obj.setTextAreaExpr('SPFractureWfTextArea', c.discretization.sp.fracture.Wf);
obj.setTextAreaExpr('SPFracturePorfTextArea', c.discretization.sp.fracture.Porf);
obj.setNumericFieldValue('SPFracturePrporEditField', c.discretization.sp.fracture.prporf);
obj.setNumericFieldValue('SPFractureCporfEditField', c.discretization.sp.fracture.cporf);
obj.setNumericFieldValue('SPStressFractureEditField', c.discretization.sp.stress.fracture_factor);
obj.setNumericFieldValue('SPStressMatrixEditField', c.discretization.sp.stress.matrix_factor);
obj.setNumericFieldValue('SPStressRefPressureEditField', c.discretization.sp.stress.ref_pressure);
obj.setTextAreaExpr('DPkxTextArea', c.discretization.dp.fracture_layer.kx);
obj.setTextAreaExpr('DPkyTextArea', c.discretization.dp.fracture_layer.ky);
obj.setTextAreaExpr('DPkzTextArea', c.discretization.dp.fracture_layer.kz);
obj.setTextAreaExpr('DPkx_matrixLayerTextArea', c.discretization.dp.matrix_layer.kx);
obj.setTextAreaExpr('DPky_matrixLayerTextArea', c.discretization.dp.matrix_layer.ky);
obj.setTextAreaExpr('DPkz_matrixLayerTextArea', c.discretization.dp.matrix_layer.kz);
obj.setTextAreaExpr('DPpori_matrixLayerTextArea', c.discretization.dp.matrix_layer.pori);
obj.setTextAreaExpr('DPsigmaTextArea', c.discretization.dp.shape_factor);
obj.setTextAreaExpr('DPNTGTextArea', c.discretization.dp.NTG);
obj.setNumericFieldValue('DPprporEditField', c.discretization.dp.prpor);
obj.setNumericFieldValue('DPcporEditField', c.discretization.dp.cpor);
obj.setTextAreaExpr('DPKfTextArea', c.discretization.dp.fracture.Kf);
obj.setTextAreaExpr('DPWfTextArea', c.discretization.dp.fracture.Wf);
obj.setTextAreaExpr('DPPorfTextArea', c.discretization.dp.fracture.Porf);
obj.setNumericFieldValue('DPprporfEditField', c.discretization.dp.fracture.prporf);
obj.setNumericFieldValue('DPcporfEditField', c.discretization.dp.fracture.cporf);
obj.setNumericFieldValue('DPstress_factor_fractureEditField', c.discretization.dp.stress.fracture_factor);
obj.setNumericFieldValue('DPstress_factor_matrixEditField', c.discretization.dp.stress.matrix_factor);
obj.setNumericFieldValue('DPstress_factor_ref_pressureEditField', c.discretization.dp.stress.ref_pressure);
obj.setNumericFieldValue('InitialPressureEditField', obj.firstScalar(c.initial.pressure));
obj.setNumericFieldValue('InitialSwEditField', obj.firstScalar(c.initial.sw));
obj.setNumericFieldValue('InitialCsEditField', obj.firstScalar(c.initial.cs));
obj.setNumericFieldValue('InitialCbEditField', obj.firstScalar(c.initial.cb));
obj.setTextAreaExpr('GWPRFTextArea', c.flow.gas_water.fracture_relperm_table);
obj.setTextAreaExpr('GWRPGWTextArea', c.flow.gas_water.matrix_relperm_table);
obj.setTextAreaExpr('OWPRFTextArea', c.flow.oil_water.fracture_relperm_table);
obj.setTextAreaExpr('OWPRMTextArea', c.flow.oil_water.matrix_relperm_table);
obj.setTextAreaExpr('MCcs_NcTextArea', c.flow.multi_component.cs_Nc);
obj.setTextAreaExpr('MCkr_nosurfTextArea', c.flow.multi_component.kr_nosurf);
obj.setTextAreaExpr('MCkr_surfTextArea', c.flow.multi_component.kr_surf);
obj.setTextAreaExpr('MCPCTextArea', c.flow.multi_component.PC);
obj.setTextAreaExpr('MCcs_Nc_fractureTextArea', c.flow.multi_component.cs_Nc_fracture);
obj.setTextAreaExpr('MCkr_nosurf_fractureTextArea', c.flow.multi_component.kr_nosurf_fracture);
obj.setTextAreaExpr('MCkr_surf_fractureTextArea', c.flow.multi_component.kr_surf_fracture);
obj.setTextAreaExpr('MCPC_fractureTextArea', c.flow.multi_component.PC_fracture);
obj.setNumericFieldValue('MCp_grad_thresholdEditField', c.flow.multi_component.p_grad_threshold);
obj.setNumericFieldValue('MCgas_propVLEditField', c.flow.multi_component.gas_prop.VL);
obj.setNumericFieldValue('MCgas_propPLEditField', c.flow.multi_component.gas_prop.PL);
obj.setNumericFieldValue('MCgas_propKnEditField', c.flow.multi_component.gas_prop.Kn);
obj.setNumericFieldValue('MCstress_factor_fractureEditField', c.flow.multi_component.gas_prop.stress_factor_fracture);
obj.setNumericFieldValue('MCstress_factor_matrixEditField', c.flow.multi_component.gas_prop.stress_factor_matrix);
obj.setNumericFieldValue('MCstress_factor_ref_pressureEditField', c.flow.multi_component.gas_prop.stress_factor_ref_pressure);
obj.setNumericFieldValue('MCbeta_non_darcy_flowEditField', c.flow.multi_component.gas_prop.beta_non_darcy_flow);
obj.setNumericFieldValue('MCdensity_g_scEditField', c.flow.multi_component.density_g_sc);
obj.setDropDownValue('MCgas_modelDropDown', c.flow.multi_component.gas_model);
obj.setNumericFieldValue('MCprgEditField', obj.firstScalar(c.flow.multi_component.prg));
obj.setNumericFieldValue('MCBgiEditField', obj.firstScalar(c.flow.multi_component.Bgi));
obj.setNumericFieldValue('MCcgEditField', obj.firstScalar(c.flow.multi_component.cg));
obj.setNumericFieldValue('MCvgiEditField', obj.firstScalar(c.flow.multi_component.vgi));
obj.setNumericFieldValue('MCcvgEditField', obj.firstScalar(c.flow.multi_component.cvg));
obj.setTextAreaExpr('MCPprTextArea', c.flow.multi_component.Ppr);
obj.setTextAreaExpr('MCBGTextArea', c.flow.multi_component.BG);
obj.setTextAreaExpr('MCMUGTextArea', c.flow.multi_component.MUG);
obj.setNumericFieldValue('MCdensity_w_scEditField', c.flow.multi_component.density_w_sc);
obj.setNumericFieldValue('MCprwEditField', obj.firstScalar(c.flow.multi_component.prw));
obj.setNumericFieldValue('MCBwiEditField', obj.firstScalar(c.flow.multi_component.Bwi));
obj.setNumericFieldValue('MCcwEditField', obj.firstScalar(c.flow.multi_component.cw));
obj.setNumericFieldValue('MCvwiEditField', obj.firstScalar(c.flow.multi_component.vwi));
obj.setNumericFieldValue('MCcvwEditField', obj.firstScalar(c.flow.multi_component.cvw));
obj.setTableValue('Well1Table', c.wells.well1);
obj.setTableValue('FractureWellLocationTable', obj.unwrapFirstCell(c.wells.welloc));
obj.setTableValue('Well2Table', c.wells.well2);
obj.setTableValue('ScheduleTable', obj.unwrapFirstCell(c.schedule.well_schedules));
obj.setTextAreaExpr('TimeTextArea', c.schedule.time);
obj.setTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
obj.setTextAreaExpr('DtMaxTextArea', c.schedule.dtmax);
obj.setNumericFieldValue('YitaPEditField', c.solver.yitap);
obj.setNumericFieldValue('YitaSEditField', c.solver.yitas);
obj.setNumericFieldValue('OmegaEditField', c.solver.omega);
obj.setNumericFieldValue('NmaxEditField', c.solver.Nmax);
obj.setNumericFieldValue('EpsAveEditField', c.solver.epsave);
obj.setNumericFieldValue('EpsMaxEditField', c.solver.epsmax);
obj.syncModelTabs();
end
function pushUIToConfig(obj)
if isempty(fieldnames(obj.Config))
obj.Config = create_empty_config();
end
c = obj.Config;
c.model.modelflag = obj.getNumericDropDownValue('ModelFlagDropDown', c.model.modelflag);
c.model.grid_model = obj.getNumericDropDownValue('ModelGridModelDropDown', c.model.grid_model);
c.model.flow_model = obj.getNumericDropDownValue('ModelFlowModelDropDown', c.model.flow_model);
c.grid.dx = obj.getTextAreaExpr('GridDxTextArea', c.grid.dx);
c.grid.dy = obj.getTextAreaExpr('GridDyTextArea', c.grid.dy);
c.grid.dz = obj.getTextAreaExpr('GridDzTextArea', c.grid.dz);
c.grid.NTG = obj.getTextAreaExpr('GridNTGTextArea', c.grid.NTG);
c.grid.nx = numel(c.grid.dx);
c.grid.ny = numel(c.grid.dy);
c.grid.nz = numel(c.grid.dz);
c.fracture.input_style = obj.getNumericDropDownValue('FractureInputStyleDropDown', c.fracture.input_style);
c.fracture.input_content = obj.getTextAreaExpr('FractureInputTextArea', c.fracture.input_content);
c.fracture.fractureLines = obj.getTextAreaExpr('FractureLinesTextArea', c.fracture.fractureLines);
c.fracture.fractureHeights = obj.getTextAreaExpr('FractureHeightsTextArea', c.fracture.fractureHeights);
c.fracture.flowBarrierFlags = obj.getTextAreaExpr('FractureFlowBarrierFlagsTextArea', c.fracture.flowBarrierFlags);
c.discretization.sp.boundary_polygon = obj.getTextAreaExpr('SPBoundaryTextArea', c.discretization.sp.boundary_polygon);
c.discretization.sp.invalid_layer = obj.getTextAreaExpr('SPInvalidLayerTextArea', c.discretization.sp.invalid_layer);
c.discretization.sp.matrix.kx = obj.getTextAreaExpr('SPMatrixKxTextArea', c.discretization.sp.matrix.kx);
c.discretization.sp.matrix.ky = obj.getTextAreaExpr('SPMatrixKyTextArea', c.discretization.sp.matrix.ky);
c.discretization.sp.matrix.kz = obj.getTextAreaExpr('SPMatrixKzTextArea', c.discretization.sp.matrix.kz);
c.discretization.sp.matrix.pori = obj.getTextAreaExpr('SPMatrixPoriTextArea', c.discretization.sp.matrix.pori);
c.discretization.sp.matrix.prpor = obj.getNumericFieldValue('SPMatrixPrporEditField', c.discretization.sp.matrix.prpor);
c.discretization.sp.matrix.cpor = obj.getNumericFieldValue('SPMatrixCporEditField', c.discretization.sp.matrix.cpor);
c.discretization.sp.matrix.rock_density = obj.getNumericFieldValue('SPRockDensityEditField', c.discretization.sp.matrix.rock_density);
c.discretization.sp.fracture.Kf = obj.getTextAreaExpr('SPFractureKfTextArea', c.discretization.sp.fracture.Kf);
c.discretization.sp.fracture.Wf = obj.getTextAreaExpr('SPFractureWfTextArea', c.discretization.sp.fracture.Wf);
c.discretization.sp.fracture.Porf = obj.getTextAreaExpr('SPFracturePorfTextArea', c.discretization.sp.fracture.Porf);
c.discretization.sp.fracture.prporf = obj.getNumericFieldValue('SPFracturePrporEditField', c.discretization.sp.fracture.prporf);
c.discretization.sp.fracture.cporf = obj.getNumericFieldValue('SPFractureCporfEditField', c.discretization.sp.fracture.cporf);
c.discretization.sp.stress.fracture_factor = obj.getNumericFieldValue('SPStressFractureEditField', c.discretization.sp.stress.fracture_factor);
c.discretization.sp.stress.matrix_factor = obj.getNumericFieldValue('SPStressMatrixEditField', c.discretization.sp.stress.matrix_factor);
c.discretization.sp.stress.ref_pressure = obj.getNumericFieldValue('SPStressRefPressureEditField', c.discretization.sp.stress.ref_pressure);
c.discretization.dp.fracture_layer.kx = obj.getTextAreaExpr('DPkxTextArea', c.discretization.dp.fracture_layer.kx);
c.discretization.dp.fracture_layer.ky = obj.getTextAreaExpr('DPkyTextArea', c.discretization.dp.fracture_layer.ky);
c.discretization.dp.fracture_layer.kz = obj.getTextAreaExpr('DPkzTextArea', c.discretization.dp.fracture_layer.kz);
c.discretization.dp.matrix_layer.kx = obj.getTextAreaExpr('DPkx_matrixLayerTextArea', c.discretization.dp.matrix_layer.kx);
c.discretization.dp.matrix_layer.ky = obj.getTextAreaExpr('DPky_matrixLayerTextArea', c.discretization.dp.matrix_layer.ky);
c.discretization.dp.matrix_layer.kz = obj.getTextAreaExpr('DPkz_matrixLayerTextArea', c.discretization.dp.matrix_layer.kz);
c.discretization.dp.matrix_layer.pori = obj.getTextAreaExpr('DPpori_matrixLayerTextArea', c.discretization.dp.matrix_layer.pori);
c.discretization.dp.shape_factor = obj.getTextAreaExpr('DPsigmaTextArea', c.discretization.dp.shape_factor);
c.discretization.dp.NTG = obj.getTextAreaExpr('DPNTGTextArea', c.discretization.dp.NTG);
c.discretization.dp.prpor = obj.getNumericFieldValue('DPprporEditField', c.discretization.dp.prpor);
c.discretization.dp.cpor = obj.getNumericFieldValue('DPcporEditField', c.discretization.dp.cpor);
c.discretization.dp.fracture.Kf = obj.getTextAreaExpr('DPKfTextArea', c.discretization.dp.fracture.Kf);
c.discretization.dp.fracture.Wf = obj.getTextAreaExpr('DPWfTextArea', c.discretization.dp.fracture.Wf);
c.discretization.dp.fracture.Porf = obj.getTextAreaExpr('DPPorfTextArea', c.discretization.dp.fracture.Porf);
c.discretization.dp.fracture.prporf = obj.getNumericFieldValue('DPprporfEditField', c.discretization.dp.fracture.prporf);
c.discretization.dp.fracture.cporf = obj.getNumericFieldValue('DPcporfEditField', c.discretization.dp.fracture.cporf);
c.discretization.dp.stress.fracture_factor = obj.getNumericFieldValue('DPstress_factor_fractureEditField', c.discretization.dp.stress.fracture_factor);
c.discretization.dp.stress.matrix_factor = obj.getNumericFieldValue('DPstress_factor_matrixEditField', c.discretization.dp.stress.matrix_factor);
c.discretization.dp.stress.ref_pressure = obj.getNumericFieldValue('DPstress_factor_ref_pressureEditField', c.discretization.dp.stress.ref_pressure);
c.initial.pressure = obj.getNumericFieldValue('InitialPressureEditField', c.initial.pressure);
c.initial.sw = obj.getNumericFieldValue('InitialSwEditField', c.initial.sw);
c.initial.cs = obj.getNumericFieldValue('InitialCsEditField', c.initial.cs);
c.initial.cb = obj.getNumericFieldValue('InitialCbEditField', c.initial.cb);
c.flow.gas_water.matrix_relperm_table = obj.getTextAreaExpr('GWRPGWTextArea', c.flow.gas_water.matrix_relperm_table);
c.flow.gas_water.fracture_relperm_table = obj.getTextAreaExpr('GWPRFTextArea', c.flow.gas_water.fracture_relperm_table);
c.flow.oil_water.matrix_relperm_table = obj.getTextAreaExpr('OWPRMTextArea', c.flow.oil_water.matrix_relperm_table);
c.flow.oil_water.fracture_relperm_table = obj.getTextAreaExpr('OWPRFTextArea', c.flow.oil_water.fracture_relperm_table);
c.flow.multi_component.cs_Nc = obj.getTextAreaExpr('MCcs_NcTextArea', c.flow.multi_component.cs_Nc);
c.flow.multi_component.kr_nosurf = obj.getTextAreaExpr('MCkr_nosurfTextArea', c.flow.multi_component.kr_nosurf);
c.flow.multi_component.kr_surf = obj.getTextAreaExpr('MCkr_surfTextArea', c.flow.multi_component.kr_surf);
c.flow.multi_component.PC = obj.getTextAreaExpr('MCPCTextArea', c.flow.multi_component.PC);
c.flow.multi_component.cs_Nc_fracture = obj.getTextAreaExpr('MCcs_Nc_fractureTextArea', c.flow.multi_component.cs_Nc_fracture);
c.flow.multi_component.kr_nosurf_fracture = obj.getTextAreaExpr('MCkr_nosurf_fractureTextArea', c.flow.multi_component.kr_nosurf_fracture);
c.flow.multi_component.kr_surf_fracture = obj.getTextAreaExpr('MCkr_surf_fractureTextArea', c.flow.multi_component.kr_surf_fracture);
c.flow.multi_component.PC_fracture = obj.getTextAreaExpr('MCPC_fractureTextArea', c.flow.multi_component.PC_fracture);
c.flow.multi_component.p_grad_threshold = obj.getNumericFieldValue('MCp_grad_thresholdEditField', c.flow.multi_component.p_grad_threshold);
c.flow.multi_component.gas_prop.VL = obj.getNumericFieldValue('MCgas_propVLEditField', c.flow.multi_component.gas_prop.VL);
c.flow.multi_component.gas_prop.PL = obj.getNumericFieldValue('MCgas_propPLEditField', c.flow.multi_component.gas_prop.PL);
c.flow.multi_component.gas_prop.Kn = obj.getNumericFieldValue('MCgas_propKnEditField', c.flow.multi_component.gas_prop.Kn);
c.flow.multi_component.gas_prop.stress_factor_fracture = obj.getNumericFieldValue('MCstress_factor_fractureEditField', c.flow.multi_component.gas_prop.stress_factor_fracture);
c.flow.multi_component.gas_prop.stress_factor_matrix = obj.getNumericFieldValue('MCstress_factor_matrixEditField', c.flow.multi_component.gas_prop.stress_factor_matrix);
c.flow.multi_component.gas_prop.stress_factor_ref_pressure = obj.getNumericFieldValue('MCstress_factor_ref_pressureEditField', c.flow.multi_component.gas_prop.stress_factor_ref_pressure);
c.flow.multi_component.gas_prop.beta_non_darcy_flow = obj.getNumericFieldValue('MCbeta_non_darcy_flowEditField', c.flow.multi_component.gas_prop.beta_non_darcy_flow);
c.flow.multi_component.density_g_sc = obj.getNumericFieldValue('MCdensity_g_scEditField', c.flow.multi_component.density_g_sc);
c.flow.multi_component.gas_model = obj.getNumericDropDownValue('MCgas_modelDropDown', c.flow.multi_component.gas_model);
c.flow.multi_component.prg = obj.getNumericFieldValue('MCprgEditField', c.flow.multi_component.prg);
c.flow.multi_component.Bgi = obj.getNumericFieldValue('MCBgiEditField', c.flow.multi_component.Bgi);
c.flow.multi_component.cg = obj.getNumericFieldValue('MCcgEditField', c.flow.multi_component.cg);
c.flow.multi_component.vgi = obj.getNumericFieldValue('MCvgiEditField', c.flow.multi_component.vgi);
c.flow.multi_component.cvg = obj.getNumericFieldValue('MCcvgEditField', c.flow.multi_component.cvg);
c.flow.multi_component.Ppr = obj.getTextAreaExpr('MCPprTextArea', c.flow.multi_component.Ppr);
c.flow.multi_component.BG = obj.getTextAreaExpr('MCBGTextArea', c.flow.multi_component.BG);
c.flow.multi_component.MUG = obj.getTextAreaExpr('MCMUGTextArea', c.flow.multi_component.MUG);
c.flow.multi_component.density_w_sc = obj.getNumericFieldValue('MCdensity_w_scEditField', c.flow.multi_component.density_w_sc);
c.flow.multi_component.prw = obj.getNumericFieldValue('MCprwEditField', c.flow.multi_component.prw);
c.flow.multi_component.Bwi = obj.getNumericFieldValue('MCBwiEditField', c.flow.multi_component.Bwi);
c.flow.multi_component.cw = obj.getNumericFieldValue('MCcwEditField', c.flow.multi_component.cw);
c.flow.multi_component.vwi = obj.getNumericFieldValue('MCvwiEditField', c.flow.multi_component.vwi);
c.flow.multi_component.cvw = obj.getNumericFieldValue('MCcvwEditField', c.flow.multi_component.cvw);
c.wells.well1 = obj.getTableValue('Well1Table', c.wells.well1);
fractureWellLocation = obj.getNumericTableValue( ...
'FractureWellLocationTable', obj.unwrapFirstCell(c.wells.welloc));
c.wells.welloc = obj.replaceFirstCell(c.wells.welloc, fractureWellLocation);
c.wells.num_fracture_wells = numel(c.wells.welloc);
c.wells.well2 = obj.getTableValue('Well2Table', c.wells.well2);
firstPhaseSchedule = obj.getTableValue( ...
'ScheduleTable', obj.unwrapFirstCell(c.schedule.well_schedules));
c.schedule.well_schedules = obj.replaceFirstCell(c.schedule.well_schedules, firstPhaseSchedule);
c.schedule.number_phases = numel(c.schedule.well_schedules);
c.schedule.time = obj.getTextAreaExpr('TimeTextArea', c.schedule.time);
c.schedule.dtmin = obj.getTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
c.schedule.dtmax = obj.getTextAreaExpr('DtMaxTextArea', c.schedule.dtmax);
c.solver.yitap = obj.getNumericFieldValue('YitaPEditField', c.solver.yitap);
c.solver.yitas = obj.getNumericFieldValue('YitaSEditField', c.solver.yitas);
c.solver.omega = obj.getNumericFieldValue('OmegaEditField', c.solver.omega);
c.solver.Nmax = obj.getNumericFieldValue('NmaxEditField', c.solver.Nmax);
c.solver.epsave = obj.getNumericFieldValue('EpsAveEditField', c.solver.epsave);
c.solver.epsmax = obj.getNumericFieldValue('EpsMaxEditField', c.solver.epsmax);
obj.Config = c;
obj.syncModelTabs();
end
function refreshResults(obj)
summary = {};
if isempty(fieldnames(obj.Results))
summary = {'No results loaded.'};
obj.setRunHeader('-', '-', '-');
obj.setRunProgress('Idle');
obj.setResultsTable({});
if isprop(obj.App, 'ResultAxes')
cla(obj.App.ResultAxes);
title(obj.App.ResultAxes, 'Results');
end
else
if isfield(obj.Results, 'Times') && ~isempty(obj.Results.Times)
summary{end + 1} = sprintf('Time steps: %d', numel(obj.Results.Times)); %#ok<AGROW>
end
if isfield(obj.Results, 'trun') && isstruct(obj.Results.trun)
if isfield(obj.Results.trun, 'run_time')
summary{end + 1} = sprintf('Run time: %.3f s', obj.Results.trun.run_time); %#ok<AGROW>
end
if isfield(obj.Results.trun, 'Newton_step')
summary{end + 1} = sprintf('Newton steps: %g', obj.Results.trun.Newton_step); %#ok<AGROW>
end
end
if isempty(summary)
summary = {'Results loaded.'};
end
obj.populateRunHeaderFromResults();
obj.setResultsTable(obj.buildResultsTableData());
obj.plotResults();
end
if isprop(obj.App, 'ResultSummaryTextArea')
obj.App.ResultSummaryTextArea.Value = summary;
end
end
function populateRunHeaderFromResults(obj)
stageText = '-';
timeText = '-';
dtText = '-';
if isfield(obj.Results, 'Times') && ~isempty(obj.Results.Times)
stageText = sprintf('%d', numel(obj.Results.Times));
timeText = obj.valueToShortText(obj.Results.Times(end));
if numel(obj.Results.Times) >= 2
dtText = obj.valueToShortText(obj.Results.Times(end) - obj.Results.Times(end - 1));
end
end
obj.setRunHeader(stageText, timeText, dtText);
end
function tableData = buildResultsTableData(obj)
tableData = {
'has_r', isfield(obj.Results, 'r');
'num_times', obj.safeLength(obj.Results, 'Times');
'num_outputs', obj.safeLength(obj.Results, 'OutputRs');
'num_wellpara', obj.safeLength(obj.Results, 'Wellpara')
};
end
function n = safeLength(~, s, fieldName)
if isfield(s, fieldName) && ~isempty(s.(fieldName))
n = numel(s.(fieldName));
else
n = 0;
end
end
function setStatus(obj, textValue)
if isprop(obj.App, 'StatusLabel')
obj.App.StatusLabel.Text = ['Status: ', char(string(textValue))];
end
end
function appendLog(obj, textValue)
if ~isprop(obj.App, 'RunLogTextArea')
return;
end
newLines = splitlines(string(textValue));
if isempty(obj.App.RunLogTextArea.Value)
obj.App.RunLogTextArea.Value = cellstr(newLines);
else
current = string(obj.App.RunLogTextArea.Value);
obj.App.RunLogTextArea.Value = cellstr([current; newLines]);
end
drawnow limitrate;
end
function setRunProgress(obj, textValue)
if isprop(obj.App, 'RunProgressLabel')
obj.App.RunProgressLabel.Text = ['RunProgress: ', char(string(textValue))];
end
end
function setRunHeader(obj, stageText, timeText, dtText)
if isprop(obj.App, 'CurrentStageLabel')
obj.App.CurrentStageLabel.Text = ['Stage: ', char(string(stageText))];
end
if isprop(obj.App, 'CurrentTimeLabel')
obj.App.CurrentTimeLabel.Text = ['Time: ', char(string(timeText))];
end
if isprop(obj.App, 'CurrentDtLabel')
obj.App.CurrentDtLabel.Text = ['dt: ', char(string(dtText))];
end
end
function setResultsTable(obj, data)
if isprop(obj.App, 'ResultTab')
obj.App.ResultTab.Data = data;
end
end
function setDropDownItems(obj, propName, items, defaultValue)
if ~isprop(obj.App, propName)
return;
end
component = obj.App.(propName);
component.Items = items;
if nargin >= 4
component.Value = char(string(defaultValue));
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;
end
component = obj.App.(propName);
stringValue = char(string(value));
if ~any(strcmp(component.Items, stringValue))
component.Items = unique([component.Items(:); {stringValue}], 'stable');
end
component.Value = stringValue;
end
function value = getDropDownValue(obj, propName, fallback)
if nargin < 3
fallback = '';
end
if ~isprop(obj.App, propName)
value = fallback;
return;
end
value = obj.App.(propName).Value;
if isempty(value)
value = fallback;
end
end
function value = getNumericDropDownValue(obj, propName, fallback)
value = str2double(obj.getDropDownValue(propName, num2str(fallback)));
if isnan(value)
value = fallback;
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;
end
obj.App.(propName).Value = obj.firstScalar(value);
end
function value = getNumericFieldValue(obj, propName, fallback)
if ~isprop(obj.App, propName)
value = fallback;
return;
end
value = obj.App.(propName).Value;
if isempty(value) || (isnumeric(value) && isscalar(value) && ~isfinite(value))
value = fallback;
end
end
function setTextAreaExpr(obj, propName, value)
if ~isprop(obj.App, propName)
return;
end
obj.App.(propName).Value = splitlines(string(obj.valueToExpression(value)));
end
function value = getTextAreaExpr(obj, propName, fallback)
if ~isprop(obj.App, propName)
value = fallback;
return;
end
textValue = strtrim(strjoin(string(obj.App.(propName).Value), newline));
if textValue == ""
value = fallback;
return;
end
try
value = eval(char(textValue)); %#ok<EVLDIR>
catch ME
error('EDFMAppController:ParseError', ...
'Failed to parse %s. MATLAB expression expected. %s', propName, ME.message);
end
end
function setTableValue(obj, propName, value)
if ~isprop(obj.App, propName)
return;
end
if isempty(value)
obj.App.(propName).Data = {};
elseif isnumeric(value)
obj.App.(propName).Data = num2cell(value);
elseif iscell(value)
obj.App.(propName).Data = obj.prepareCellTableForUI(value);
else
obj.App.(propName).Data = value;
end
end
function value = getTableValue(obj, propName, fallback)
if ~isprop(obj.App, propName)
value = fallback;
return;
end
data = obj.App.(propName).Data;
if isempty(data)
value = fallback;
else
value = obj.parseUITableData(data);
end
end
function value = getNumericTableValue(obj, propName, fallback)
value = obj.getTableValue(propName, fallback);
if isempty(value)
return;
end
if isnumeric(value)
return;
end
if iscell(value)
try
value = cellfun(@obj.convertTableCellToDouble, value);
catch ME
error('EDFMAppController:NumericTableParseError', ...
'Failed to parse %s as a numeric table. %s', propName, ME.message);
end
return;
end
error('EDFMAppController:NumericTableTypeError', ...
'Unsupported table data type for %s: %s', propName, class(value));
end
function setEditableState(obj, propName, isEnabled)
if ~isprop(obj.App, propName)
return;
end
component = obj.App.(propName);
if isprop(component, 'Editable')
component.Editable = matlab.lang.OnOffSwitchState(isEnabled);
end
if isprop(component, 'Enable')
if isEnabled
component.Enable = 'on';
else
component.Enable = 'off';
end
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)
sprintf('Identifier: %s', ME.identifier)
};
if ~isempty(ME.stack)
topFrame = ME.stack(1);
lines{end + 1} = sprintf('Location: %s (line %d)', topFrame.name, topFrame.line); %#ok<AGROW>
lines{end + 1} = sprintf('File: %s', topFrame.file); %#ok<AGROW>
end
textValue = strjoin(lines, newline);
end
function publishExceptionToBase(obj, ME, errorReport)
assignin('base', 'edfm_last_run_error', ME);
assignin('base', 'edfm_last_run_error_report', errorReport);
assignin('base', 'edfm_last_run_config', obj.Config);
end
function value = unwrapFirstCell(~, rawValue)
if iscell(rawValue) && ~isempty(rawValue)
value = rawValue{1};
else
value = rawValue;
end
end
function values = replaceFirstCell(~, existingValues, firstValue)
if isempty(existingValues)
values = {firstValue};
return;
end
if ~iscell(existingValues)
values = {firstValue};
return;
end
values = existingValues;
values{1} = firstValue;
end
function tableData = prepareCellTableForUI(obj, rawData)
tableData = rawData;
for i = 1:size(rawData, 1)
for j = 1:size(rawData, 2)
tableData{i, j} = obj.serializeTableCell(rawData{i, j});
end
end
end
function value = parseUITableData(obj, data)
if isnumeric(data)
value = data;
return;
end
if ~iscell(data)
value = data;
return;
end
value = data;
for i = 1:size(data, 1)
for j = 1:size(data, 2)
value{i, j} = obj.parseTableCell(data{i, j});
end
end
end
function value = serializeTableCell(obj, rawValue)
if isnumeric(rawValue) || islogical(rawValue)
if isscalar(rawValue)
value = rawValue;
else
value = obj.valueToExpression(rawValue);
end
return;
end
if iscell(rawValue)
value = obj.valueToExpression(rawValue);
return;
end
if isstring(rawValue) && isscalar(rawValue)
value = char(rawValue);
return;
end
value = rawValue;
end
function value = parseTableCell(~, rawValue)
if isnumeric(rawValue) || islogical(rawValue)
value = rawValue;
return;
end
if isstring(rawValue) && isscalar(rawValue)
rawValue = char(rawValue);
end
if ~ischar(rawValue)
value = rawValue;
return;
end
textValue = strtrim(rawValue);
if isempty(textValue)
value = [];
return;
end
numericValue = str2double(textValue);
if ~isnan(numericValue) && isempty(regexp(textValue, '[A-Za-z_''\[\]\{\};,]', 'once'))
value = numericValue;
return;
end
if startsWith(textValue, '[') || startsWith(textValue, '{')
try
value = eval(textValue); %#ok<EVLDIR>
return;
catch
end
end
value = rawValue;
end
function value = convertTableCellToDouble(~, cellValue)
if isnumeric(cellValue) && isscalar(cellValue)
value = double(cellValue);
return;
end
if islogical(cellValue) && isscalar(cellValue)
value = double(cellValue);
return;
end
if isstring(cellValue) && isscalar(cellValue)
cellValue = char(cellValue);
end
if ischar(cellValue)
parsedValue = str2double(strtrim(cellValue));
if ~isnan(parsedValue)
value = parsedValue;
return;
end
end
error('Cell value "%s" is not a scalar numeric entry.', string(cellValue));
end
function value = firstScalar(~, rawValue)
if isempty(rawValue)
value = 0;
elseif isscalar(rawValue)
value = rawValue;
else
value = rawValue(1);
end
end
function textValue = valueToShortText(~, rawValue)
if isempty(rawValue)
textValue = '-';
elseif isscalar(rawValue)
textValue = num2str(rawValue);
else
textValue = sprintf('[%s]', strjoin(string(size(rawValue)), 'x'));
end
end
function textValue = valueToExpression(obj, value)
if isempty(value)
textValue = '[]';
return;
end
if isnumeric(value) || islogical(value)
textValue = mat2str(value);
return;
end
if ischar(value)
textValue = ['''', strrep(value, '''', ''''''), ''''];
return;
end
if isstring(value) && isscalar(value)
textValue = ['''', strrep(char(value), '''', ''''''), ''''];
return;
end
if iscell(value)
rows = cell(size(value, 1), 1);
for i = 1:size(value, 1)
cols = cell(1, size(value, 2));
for j = 1:size(value, 2)
cols{j} = obj.valueToExpression(value{i, j});
end
rows{i} = strjoin(cols, ', ');
end
textValue = ['{', strjoin(rows, [';', newline]), '}'];
return;
end
textValue = strtrim(evalc('disp(value)'));
end
end
end