classdef EDFMAppController < handle properties (Access = private) App Config struct = struct() Results struct = struct() ProjectRoot char CurrentPlotAction string = "" CurrentFracWellIndex double = 1 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('ModelGridModelDropDown', {'1', '2'}, '1'); obj.setDropDownItems('ModelFlowModelDropDown', {'1', '2', '3'}, '1'); obj.setDropDownItems('GWgas_modelDropDown', {'1'}, '1'); obj.setDropDownItems('OWoil_modelDropDown', {'1', '2'}, '1'); obj.setDropDownItems('MCgas_modelDropDown', {'1'}, '1'); obj.configureModelSelectionCallbacks(); obj.configureWellTables(); obj.configureFracWellControls(); obj.applyFixedUIState(); obj.Results = struct(); obj.loadTemplate('case01'); obj.appendLog('Controller startup completed.'); obj.setStatus('Ready'); end function newConfig(obj) obj.Config = normalize_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 progress_callback = @(message, meta) obj.onRunProgress(message, meta); [r, Times, OutputRs, Wellpara, trun] = run_case(obj.Config, progress_callback); obj.Results = struct( ... 'r', r, ... 'Times', Times, ... 'OutputRs', {OutputRs}, ... 'Wellpara', {Wellpara}, ... 'trun', trun, ... 'captured_log', ''); obj.refreshResults(); if strlength(obj.CurrentPlotAction) ~= 0 obj.refreshCasePlotControls(obj.CurrentPlotAction); end obj.appendLog('Run completed.'); 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'); obj.showAlert(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)) obj.showAlert('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 onTableSelectionChanged(obj, tableName, indices) if ~isprop(obj.App, tableName) return; end if isempty(indices) obj.App.(tableName).UserData = []; else obj.App.(tableName).UserData = indices(1, 1); if strcmp(tableName, 'Well2Table') obj.setSelectedFracWellIndex(indices(1, 1), true); end end end function addTableRow(obj, tableName) if ~isprop(obj.App, tableName) return; end switch tableName case 'Well2Table' obj.pushUIToConfig(); c = normalize_config(obj.Config); well2 = obj.getTableValue('Well2Table', c.wells.well2); newRow = obj.getDefaultRowForTable(tableName); nextIndex = size(well2, 1) + 1; if isempty(newRow{1}) newRow{1} = sprintf('wf%d', nextIndex); end if isempty(well2) well2 = newRow; else well2(end + 1, :) = newRow; end c.wells.well2 = well2; c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, size(well2, 1)); c.wells.num_fracture_wells = size(well2, 1); obj.Config = c; obj.CurrentFracWellIndex = c.wells.num_fracture_wells; obj.refreshUIFromConfig(); return; case 'FractureWellLocationTable' if obj.getFractureWellCount() == 0 obj.showAlert(... 'Please add a fracture well before editing perforation coordinates.', ... 'No Fracture Well'); return; end end data = obj.App.(tableName).Data; newRow = obj.getDefaultRowForTable(tableName); if isempty(data) data = newRow; else if isnumeric(data) data = num2cell(data); end data(end + 1, :) = newRow; end obj.App.(tableName).Data = data; obj.App.(tableName).UserData = size(data, 1); end function deleteSelectedTableRow(obj, tableName) if ~isprop(obj.App, tableName) return; end if strcmp(tableName, 'Well2Table') obj.pushUIToConfig(); c = normalize_config(obj.Config); selectedRow = []; if isprop(obj.App.(tableName), 'UserData') selectedRow = obj.App.(tableName).UserData; end if isempty(selectedRow) || ~isscalar(selectedRow) || selectedRow < 1 obj.showAlert(... sprintf('Please select a row in %s first.', tableName), ... 'Delete Row'); return; end if selectedRow > size(c.wells.well2, 1) return; end c.wells.well2(selectedRow, :) = []; if iscell(c.wells.welloc) && numel(c.wells.welloc) >= selectedRow c.wells.welloc(selectedRow, :) = []; end c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, size(c.wells.well2, 1)); c.wells.num_fracture_wells = size(c.wells.well2, 1); if c.wells.num_fracture_wells == 0 obj.CurrentFracWellIndex = 1; else obj.CurrentFracWellIndex = min(selectedRow, c.wells.num_fracture_wells); end obj.Config = c; obj.refreshUIFromConfig(); return; end data = obj.App.(tableName).Data; if isempty(data) return; end selectedRow = []; if isprop(obj.App.(tableName), 'UserData') selectedRow = obj.App.(tableName).UserData; end if isempty(selectedRow) || ~isscalar(selectedRow) || selectedRow < 1 obj.showAlert(... sprintf('Please select a row in %s first.', tableName), ... 'Delete Row'); return; end if selectedRow > size(data, 1) return; end data(selectedRow, :) = []; obj.App.(tableName).Data = data; obj.App.(tableName).UserData = []; end function runCasePlot(obj, actionId) if isempty(fieldnames(obj.Results)) obj.showAlert('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); obj.showAlert(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 obj.applyDiscretizationVisibility(gridModel); obj.applyFlowVisibility(flowModel); end end methods (Access = private) function configureModelSelectionCallbacks(obj) if isprop(obj.App, 'ModelFlowModelDropDown') obj.App.ModelFlowModelDropDown.ValueChangedFcn = @(src, event) obj.onModelSelectionChanged(); end if isprop(obj.App, 'ModelGridModelDropDown') obj.App.ModelGridModelDropDown.ValueChangedFcn = @(src, event) obj.onModelSelectionChanged(); end end function onModelSelectionChanged(obj) obj.syncModelTabs(); end 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 = normalize_config(obj.Config); obj.Config = c; 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.setTextAreaExpr('FractureInputTextArea', c.fracture.input_content); 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('SPRockDensityEditField', c.discretization.sp.matrix.rock_density); obj.setTextAreaExpr('DPFractureLayerKxTextArea', c.discretization.dp.fracture_layer.kx); obj.setTextAreaExpr('DPFractureLayerKyTextArea', c.discretization.dp.fracture_layer.ky); obj.setTextAreaExpr('DPFractureLayerKzTextArea', c.discretization.dp.fracture_layer.kz); obj.setTextAreaExpr('DPMatrixLayerKxTextArea', c.discretization.dp.matrix_layer.kx); obj.setTextAreaExpr('DPMatrixLayerKyTextArea', c.discretization.dp.matrix_layer.ky); obj.setTextAreaExpr('DPMatrixLayerKzTextArea', c.discretization.dp.matrix_layer.kz); obj.setTextAreaExpr('DPMatrixLayerPoriTextArea', c.discretization.dp.matrix_layer.pori); obj.setTextAreaExpr('DPShapeFactorTextArea', c.discretization.dp.shape_factor); commonDiscretization = obj.getCommonDiscretizationSource(c); obj.setNumericFieldValue('CommonPrporEditField', commonDiscretization.prpor); obj.setNumericFieldValue('CommonCporEditField', commonDiscretization.cpor); obj.setTextAreaExpr('CommonKfTextArea', commonDiscretization.Kf); obj.setTextAreaExpr('CommonWfTextArea', commonDiscretization.Wf); obj.setTextAreaExpr('CommonPorfTextArea', commonDiscretization.Porf); obj.setNumericFieldValue('CommonPrporfEditField', commonDiscretization.prporf); obj.setNumericFieldValue('CommonCporfEditField', commonDiscretization.cporf); obj.setNumericFieldValue('CommonStressFractureEditField', commonDiscretization.stress_fracture); obj.setNumericFieldValue('CommonStressMatrixEditField', commonDiscretization.stress_matrix); obj.setNumericFieldValue('CommonStressRefPressureEditField', commonDiscretization.stress_ref_pressure); obj.setNumericFieldValue('CommonRptEditField', commonDiscretization.Rpt); obj.setNumericFieldValue('CommonCfEditField', commonDiscretization.cf); obj.setNumericFieldValue('CommonCaEditField', commonDiscretization.ca); 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)); commonFlow = obj.getCommonFlowSource(c); obj.setNumericFieldValue('FlowCommonDensityWScEditField', commonFlow.density_w_sc); obj.setNumericFieldValue('FlowCommonPrwEditField', commonFlow.prw); obj.setNumericFieldValue('FlowCommonBwiEditField', commonFlow.Bwi); obj.setNumericFieldValue('FlowCommonCwEditField', commonFlow.cw); obj.setNumericFieldValue('FlowCommonVwiEditField', commonFlow.vwi); obj.setNumericFieldValue('FlowCommonCvwEditField', commonFlow.cvw); obj.setNumericFieldValue('FlowCommonPGradThresholdEditField', commonFlow.p_grad_threshold); obj.setNumericFieldValue('FlowCommonDensityGScEditField', commonFlow.density_g_sc); obj.setNumericFieldValue('FlowCommonGasPrgEditField', commonFlow.prg); obj.setNumericFieldValue('FlowCommonGasBgiEditField', commonFlow.Bgi); obj.setNumericFieldValue('FlowCommonGasCgEditField', commonFlow.cg); obj.setNumericFieldValue('FlowCommonGasVgiEditField', commonFlow.vgi); obj.setNumericFieldValue('FlowCommonGasCvgEditField', commonFlow.cvg); obj.setNumericFieldValue('FlowCommonGasVLEditField', commonFlow.gas_VL); obj.setNumericFieldValue('FlowCommonGasPLEditField', commonFlow.gas_PL); obj.setNumericFieldValue('FlowCommonGasKnEditField', commonFlow.gas_Kn); obj.setNumericFieldValue('FlowCommonGasBetaNonDarcyEditField', commonFlow.gas_beta_non_darcy); obj.setTextAreaExpr('GWPRFTextArea', c.flow.gas_water.fracture_relperm_table); obj.setTextAreaExpr('GWRPGWTextArea', c.flow.gas_water.matrix_relperm_table); obj.setNumericFieldValue('GWifpcglEditField', c.flow.gas_water.ifpcgl); obj.setTextAreaExpr('OWPRFTextArea', c.flow.oil_water.fracture_relperm_table); obj.setTextAreaExpr('OWPRMTextArea', c.flow.oil_water.matrix_relperm_table); obj.setDropDownValue('OWoil_modelDropDown', c.flow.oil_water.oil_model); obj.setNumericFieldValue('OWdensity_o_scEditField', c.flow.oil_water.density_o_sc); obj.setNumericFieldValue('OWproEditField', obj.firstScalar(c.flow.oil_water.pro)); obj.setNumericFieldValue('OWBoiEditField', obj.firstScalar(c.flow.oil_water.Boi)); obj.setNumericFieldValue('OWcoEditField', obj.firstScalar(c.flow.oil_water.co)); obj.setNumericFieldValue('OWvoiEditField', obj.firstScalar(c.flow.oil_water.voi)); obj.setNumericFieldValue('OWcvoEditField', obj.firstScalar(c.flow.oil_water.cvo)); obj.setNumericFieldValue('OWifpcowEditField', c.flow.oil_water.ifpcow); 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('MCifpcglEditField', c.flow.multi_component.ifpcgl); 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.setTableValue('Well1Table', c.wells.well1); obj.setTableValue('Well2Table', c.wells.well2); c = obj.normalizeScheduleConfig(c); obj.Config = c; obj.refreshFracWellControlsFromConfig(); 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.applyFixedUIState(); obj.syncModelTabs(); end function pushUIToConfig(obj) if isempty(fieldnames(obj.Config)) obj.Config = create_empty_config(); end c = normalize_config(obj.Config); 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_content = obj.getTextAreaExpr('FractureInputTextArea', c.fracture.input_content); 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.rock_density = obj.getNumericFieldValue('SPRockDensityEditField', c.discretization.sp.matrix.rock_density); c.discretization.dp.fracture_layer.kx = obj.getTextAreaExpr('DPFractureLayerKxTextArea', c.discretization.dp.fracture_layer.kx); c.discretization.dp.fracture_layer.ky = obj.getTextAreaExpr('DPFractureLayerKyTextArea', c.discretization.dp.fracture_layer.ky); c.discretization.dp.fracture_layer.kz = obj.getTextAreaExpr('DPFractureLayerKzTextArea', c.discretization.dp.fracture_layer.kz); c.discretization.dp.matrix_layer.kx = obj.getTextAreaExpr('DPMatrixLayerKxTextArea', c.discretization.dp.matrix_layer.kx); c.discretization.dp.matrix_layer.ky = obj.getTextAreaExpr('DPMatrixLayerKyTextArea', c.discretization.dp.matrix_layer.ky); c.discretization.dp.matrix_layer.kz = obj.getTextAreaExpr('DPMatrixLayerKzTextArea', c.discretization.dp.matrix_layer.kz); c.discretization.dp.matrix_layer.pori = obj.getTextAreaExpr('DPMatrixLayerPoriTextArea', c.discretization.dp.matrix_layer.pori); c.discretization.dp.shape_factor = obj.getTextAreaExpr('DPShapeFactorTextArea', c.discretization.dp.shape_factor); c.discretization.dp.NTG = c.grid.NTG; commonPrpor = obj.getNumericFieldValue('CommonPrporEditField', c.discretization.sp.matrix.prpor); commonCpor = obj.getNumericFieldValue('CommonCporEditField', c.discretization.sp.matrix.cpor); commonKf = obj.getTextAreaExpr('CommonKfTextArea', c.discretization.sp.fracture.Kf); commonWf = obj.getTextAreaExpr('CommonWfTextArea', c.discretization.sp.fracture.Wf); commonPorf = obj.getTextAreaExpr('CommonPorfTextArea', c.discretization.sp.fracture.Porf); commonPrporf = obj.getNumericFieldValue('CommonPrporfEditField', c.discretization.sp.fracture.prporf); commonCporf = obj.getNumericFieldValue('CommonCporfEditField', c.discretization.sp.fracture.cporf); commonStressFracture = obj.getNumericFieldValue('CommonStressFractureEditField', c.discretization.sp.stress.fracture_factor); commonStressMatrix = obj.getNumericFieldValue('CommonStressMatrixEditField', c.discretization.sp.stress.matrix_factor); commonStressRefPressure = obj.getNumericFieldValue('CommonStressRefPressureEditField', c.discretization.sp.stress.ref_pressure); commonRpt = obj.getNumericFieldValue('CommonRptEditField', c.discretization.sp.stress.Rpt); commonCf = obj.getNumericFieldValue('CommonCfEditField', c.discretization.sp.stress.cf); commonCa = obj.getNumericFieldValue('CommonCaEditField', c.discretization.sp.stress.ca); c.discretization.sp.matrix.prpor = commonPrpor; c.discretization.sp.matrix.cpor = commonCpor; c.discretization.sp.fracture.Kf = commonKf; c.discretization.sp.fracture.Wf = commonWf; c.discretization.sp.fracture.Porf = commonPorf; c.discretization.sp.fracture.prporf = commonPrporf; c.discretization.sp.fracture.cporf = commonCporf; c.discretization.sp.stress.fracture_factor = commonStressFracture; c.discretization.sp.stress.matrix_factor = commonStressMatrix; c.discretization.sp.stress.ref_pressure = commonStressRefPressure; c.discretization.sp.stress.Rpt = commonRpt; c.discretization.sp.stress.cf = commonCf; c.discretization.sp.stress.ca = commonCa; c.discretization.dp.prpor = commonPrpor; c.discretization.dp.cpor = commonCpor; c.discretization.dp.fracture.Kf = commonKf; c.discretization.dp.fracture.Wf = commonWf; c.discretization.dp.fracture.Porf = commonPorf; c.discretization.dp.fracture.prporf = commonPrporf; c.discretization.dp.fracture.cporf = commonCporf; c.discretization.dp.stress.fracture_factor = commonStressFracture; c.discretization.dp.stress.matrix_factor = commonStressMatrix; c.discretization.dp.stress.ref_pressure = commonStressRefPressure; c.discretization.dp.Rpt = commonRpt; c.discretization.dp.cf = commonCf; c.discretization.dp.ca = commonCa; 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); commonDensityWSc = obj.getNumericFieldValue('FlowCommonDensityWScEditField', c.flow.gas_water.density_w_sc); commonPrw = obj.getNumericFieldValue('FlowCommonPrwEditField', c.flow.gas_water.prw); commonBwi = obj.getNumericFieldValue('FlowCommonBwiEditField', c.flow.gas_water.Bwi); commonCw = obj.getNumericFieldValue('FlowCommonCwEditField', c.flow.gas_water.cw); commonVwi = obj.getNumericFieldValue('FlowCommonVwiEditField', c.flow.gas_water.vwi); commonCvw = obj.getNumericFieldValue('FlowCommonCvwEditField', c.flow.gas_water.cvw); commonPGradThreshold = obj.getNumericFieldValue('FlowCommonPGradThresholdEditField', c.flow.gas_water.p_grad_threshold); commonDensityGSc = obj.getNumericFieldValue('FlowCommonDensityGScEditField', c.flow.gas_water.density_g_sc); commonGasPrg = obj.getNumericFieldValue('FlowCommonGasPrgEditField', c.flow.gas_water.prg); commonGasBgi = obj.getNumericFieldValue('FlowCommonGasBgiEditField', c.flow.gas_water.Bgi); commonGasCg = obj.getNumericFieldValue('FlowCommonGasCgEditField', c.flow.gas_water.cg); commonGasVgi = obj.getNumericFieldValue('FlowCommonGasVgiEditField', c.flow.gas_water.vgi); commonGasCvg = obj.getNumericFieldValue('FlowCommonGasCvgEditField', c.flow.gas_water.cvg); commonGasVL = obj.getNumericFieldValue('FlowCommonGasVLEditField', c.flow.gas_water.gas_prop.VL); commonGasPL = obj.getNumericFieldValue('FlowCommonGasPLEditField', c.flow.gas_water.gas_prop.PL); commonGasKn = obj.getNumericFieldValue('FlowCommonGasKnEditField', c.flow.gas_water.gas_prop.Kn); commonGasBeta = obj.getNumericFieldValue('FlowCommonGasBetaNonDarcyEditField', c.flow.gas_water.gas_prop.beta_non_darcy_flow); c.flow.gas_water.density_w_sc = commonDensityWSc; c.flow.gas_water.prw = commonPrw; c.flow.gas_water.Bwi = commonBwi; c.flow.gas_water.cw = commonCw; c.flow.gas_water.vwi = commonVwi; c.flow.gas_water.cvw = commonCvw; c.flow.gas_water.p_grad_threshold = commonPGradThreshold; c.flow.gas_water.density_g_sc = commonDensityGSc; c.flow.gas_water.prg = commonGasPrg; c.flow.gas_water.Bgi = commonGasBgi; c.flow.gas_water.cg = commonGasCg; c.flow.gas_water.vgi = commonGasVgi; c.flow.gas_water.cvg = commonGasCvg; c.flow.gas_water.gas_model = 1; c.flow.gas_water.gas_prop.VL = commonGasVL; c.flow.gas_water.gas_prop.PL = commonGasPL; c.flow.gas_water.gas_prop.Kn = commonGasKn; c.flow.gas_water.gas_prop.beta_non_darcy_flow = commonGasBeta; 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.gas_water.ifpcgl = obj.getNumericFieldValue('GWifpcglEditField', c.flow.gas_water.ifpcgl); c.flow.oil_water.density_w_sc = commonDensityWSc; c.flow.oil_water.prw = commonPrw; c.flow.oil_water.Bwi = commonBwi; c.flow.oil_water.cw = commonCw; c.flow.oil_water.vwi = commonVwi; c.flow.oil_water.cvw = commonCvw; c.flow.oil_water.p_grad_threshold = commonPGradThreshold; c.flow.oil_water.oil_model = obj.getNumericDropDownValue('OWoil_modelDropDown', c.flow.oil_water.oil_model); c.flow.oil_water.density_o_sc = obj.getNumericFieldValue('OWdensity_o_scEditField', c.flow.oil_water.density_o_sc); c.flow.oil_water.pro = obj.getNumericFieldValue('OWproEditField', c.flow.oil_water.pro); c.flow.oil_water.Boi = obj.getNumericFieldValue('OWBoiEditField', c.flow.oil_water.Boi); c.flow.oil_water.co = obj.getNumericFieldValue('OWcoEditField', c.flow.oil_water.co); c.flow.oil_water.voi = obj.getNumericFieldValue('OWvoiEditField', c.flow.oil_water.voi); c.flow.oil_water.cvo = obj.getNumericFieldValue('OWcvoEditField', c.flow.oil_water.cvo); 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.oil_water.ifpcow = obj.getNumericFieldValue('OWifpcowEditField', c.flow.oil_water.ifpcow); c.flow.oil_water.beta_non_darcy_flow = commonGasBeta; 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.ifpcgl = obj.getNumericFieldValue('MCifpcglEditField', c.flow.multi_component.ifpcgl); c.flow.multi_component.p_grad_threshold = commonPGradThreshold; c.flow.multi_component.gas_prop.VL = commonGasVL; c.flow.multi_component.gas_prop.PL = commonGasPL; c.flow.multi_component.gas_prop.Kn = commonGasKn; 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 = commonGasBeta; c.flow.multi_component.density_g_sc = commonDensityGSc; c.flow.multi_component.gas_model = 1; c.flow.multi_component.prg = commonGasPrg; c.flow.multi_component.Bgi = commonGasBgi; c.flow.multi_component.cg = commonGasCg; c.flow.multi_component.vgi = commonGasVgi; c.flow.multi_component.cvg = commonGasCvg; c.flow.multi_component.density_w_sc = commonDensityWSc; c.flow.multi_component.prw = commonPrw; c.flow.multi_component.Bwi = commonBwi; c.flow.multi_component.cw = commonCw; c.flow.multi_component.vwi = commonVwi; c.flow.multi_component.cvw = commonCvw; c.wells.well1 = obj.getTableValue('Well1Table', c.wells.well1); c.wells.well2 = obj.getTableValue('Well2Table', c.wells.well2); c.wells.num_fracture_wells = size(c.wells.well2, 1); c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, c.wells.num_fracture_wells); c = obj.persistCurrentFractureWellLocation(c); c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, c.wells.num_fracture_wells); 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 = obj.normalizeScheduleConfig(c); 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 onSelectedFracWellChanged(obj) if isempty(fieldnames(obj.Config)) obj.Config = normalize_config(create_empty_config()); else obj.Config = obj.persistCurrentFractureWellLocation(normalize_config(obj.Config)); end obj.CurrentFracWellIndex = obj.getSelectedFracWellIndexFromUI(); obj.refreshFracWellControlsFromConfig(); end function applyFixedUIState(obj) obj.setControlVisible('ModelFlagDropDownLabel', false); obj.setControlVisible('ModelFlagDropDown', false); obj.setControlVisible('FractureInputStyleDropDownLabel', false); obj.setControlVisible('FractureInputStyleDropDown', false); obj.setControlVisible('FractureLinesTextAreaLabel', false); obj.setControlVisible('FractureLinesTextArea', false); obj.setControlVisible('FractureHeightsTextAreaLabel', false); obj.setControlVisible('FractureHeightsTextArea', false); obj.setControlVisible('FlowBarrierFlagsTextAreaLabel', false); obj.setControlVisible('FractureFlowBarrierFlagsTextArea', false); obj.setEditableState('ModelFlagDropDown', false); obj.setEditableState('FractureInputStyleDropDown', false); end function applyDiscretizationVisibility(obj, gridModel) showBoundary = false; if gridModel == 1 && ~isempty(fieldnames(obj.Config)) ... && isfield(obj.Config, 'discretization') ... && isfield(obj.Config.discretization, 'sp') ... && isfield(obj.Config.discretization.sp, 'boundary_polygon') showBoundary = ~isempty(obj.Config.discretization.sp.boundary_polygon); end obj.setControlVisible('BoundaryTextAreaLabel', showBoundary); obj.setControlVisible('SPBoundaryTextArea', showBoundary); end function applyFlowVisibility(obj, flowModel) showGasCommon = flowModel ~= 2; showOilWaterTab = flowModel == 2; showGasWaterTab = flowModel == 1; showMultiComponentTab = flowModel == 3; gasCommonProps = { ... 'GasLabel', ... 'FlowCommonDensityGScEditField', 'DensityGScLabel', ... 'FlowCommonGasPrgEditField', 'GasPrgLabel', ... 'FlowCommonGasBgiEditField', 'GasBgiLabel', ... 'FlowCommonGasCgEditField', 'GasCgLabel', ... 'FlowCommonGasVgiEditField', 'GasVgiLabel', ... 'FlowCommonGasCvgEditField', 'GasCvgLabel', ... 'FlowCommonGasVLEditField', 'GasVLLabel', ... 'FlowCommonGasPLEditField', 'GasPLLabel', ... 'FlowCommonGasKnEditField', 'GasKnLabel', ... 'FlowCommonGasBetaNonDarcyEditField', 'GasBetaNonDarcyLabel'}; waterCommonProps = { ... 'WaterLabel', ... 'FlowCommonDensityWScEditField', 'DensityWScLabel', ... 'FlowCommonPrwEditField', 'PrwLabel', ... 'FlowCommonBwiEditField', 'BwiLabel', ... 'FlowCommonCwEditField', 'CwLabel', ... 'FlowCommonVwiEditField', 'VwiLabel', ... 'FlowCommonCvwEditField', 'CvwLabel', ... 'FlowCommonPGradThresholdEditField', 'PGradThresholdLabel'}; for i = 1:numel(gasCommonProps) obj.setControlVisible(gasCommonProps{i}, showGasCommon); end for i = 1:numel(waterCommonProps) obj.setControlVisible(waterCommonProps{i}, true); end obj.setControlVisible('GasWaterTab', showGasWaterTab); obj.setControlVisible('OilWaterTab', showOilWaterTab); obj.setControlVisible('MultiComponentTab', showMultiComponentTab); obj.setFlowTabControlsVisible('GasWater', showGasWaterTab); obj.setFlowTabControlsVisible('OilWater', showOilWaterTab); obj.setFlowTabControlsVisible('MultiComponent', showMultiComponentTab); 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 end function setFlowTabControlsVisible(obj, flowTabName, isVisible) switch flowTabName case 'GasWater' controlNames = { ... 'GasWaterTab', ... 'GWPRFTextArea', 'PRFTextAreaLabel', ... 'GWRPGWTextArea', 'RPGWTextAreaLabel', ... 'GWifpcglEditField', 'ifpcglEditFieldLabel'}; case 'OilWater' controlNames = { ... 'OilWaterTab', ... 'OWoil_modelDropDown', 'oil_modelLabel', ... 'OWPRFTextArea', 'PRFTextAreaLabel_2', ... 'OWPRMTextArea', 'PRMTextAreaLabel', ... 'OWifpcowEditField', 'ifpcowEditFieldLabel', ... 'OWcvoEditField', 'cvoEditFieldLabel', ... 'OWvoiEditField', 'voiEditFieldLabel', ... 'OWcoEditField', 'coEditFieldLabel', ... 'OWBoiEditField', 'BoiEditFieldLabel', ... 'OWproEditField', 'proEditFieldLabel', ... 'OWdensity_o_scEditField', 'density_o_scEditFieldLabel'}; case 'MultiComponent' controlNames = { ... 'MultiComponentTab', ... 'MCstress_factor_ref_pressureEditField', 'stress_factor_ref_pressureEditFieldLabel_2', ... 'MCstress_factor_matrixEditField', 'stress_factor_matrixEditFieldLabel_2', ... 'MCstress_factor_fractureEditField', 'stress_factor_fractureEditFieldLabel_2', ... 'MCifpcglEditField', 'ifpcglEditFieldLabel_2', ... 'MCPC_fractureTextArea', 'PC_fractureTextAreaLabel', ... 'MCkr_surf_fractureTextArea', 'kr_surf_fractureTextAreaLabel', ... 'MCkr_nosurf_fractureTextArea', 'kr_nosurf_fractureTextAreaLabel', ... 'MCcs_Nc_fractureTextArea', 'cs_Nc_fractureTextAreaLabel', ... 'MCPCTextArea', 'PCTextAreaLabel', ... 'MCkr_surfTextArea', 'kr_surfTextAreaLabel', ... 'MCkr_nosurfTextArea', 'kr_nosurfTextAreaLabel', ... 'MCcs_NcTextArea', 'cs_NcTextAreaLabel'}; otherwise controlNames = {}; end for i = 1:numel(controlNames) obj.setControlVisible(controlNames{i}, isVisible); end end function commonData = getCommonFlowSource(obj, c) if c.model.flow_model == 3 waterSource = struct( ... 'density_w_sc', c.flow.multi_component.density_w_sc, ... 'prw', c.flow.multi_component.prw, ... 'Bwi', c.flow.multi_component.Bwi, ... 'cw', c.flow.multi_component.cw, ... 'vwi', c.flow.multi_component.vwi, ... 'cvw', c.flow.multi_component.cvw, ... 'p_grad_threshold', c.flow.multi_component.p_grad_threshold); gasSource = struct( ... 'density_g_sc', c.flow.multi_component.density_g_sc, ... 'prg', c.flow.multi_component.prg, ... 'Bgi', c.flow.multi_component.Bgi, ... 'cg', c.flow.multi_component.cg, ... 'vgi', c.flow.multi_component.vgi, ... 'cvg', c.flow.multi_component.cvg, ... 'gas_VL', c.flow.multi_component.gas_prop.VL, ... 'gas_PL', c.flow.multi_component.gas_prop.PL, ... 'gas_Kn', c.flow.multi_component.gas_prop.Kn, ... 'gas_beta_non_darcy', c.flow.multi_component.gas_prop.beta_non_darcy_flow); elseif c.model.flow_model == 2 waterSource = struct( ... 'density_w_sc', c.flow.oil_water.density_w_sc, ... 'prw', c.flow.oil_water.prw, ... 'Bwi', c.flow.oil_water.Bwi, ... 'cw', c.flow.oil_water.cw, ... 'vwi', c.flow.oil_water.vwi, ... 'cvw', c.flow.oil_water.cvw, ... 'p_grad_threshold', c.flow.oil_water.p_grad_threshold); gasSource = struct( ... 'density_g_sc', c.flow.gas_water.density_g_sc, ... 'prg', c.flow.gas_water.prg, ... 'Bgi', c.flow.gas_water.Bgi, ... 'cg', c.flow.gas_water.cg, ... 'vgi', c.flow.gas_water.vgi, ... 'cvg', c.flow.gas_water.cvg, ... 'gas_VL', c.flow.gas_water.gas_prop.VL, ... 'gas_PL', c.flow.gas_water.gas_prop.PL, ... 'gas_Kn', c.flow.gas_water.gas_prop.Kn, ... 'gas_beta_non_darcy', c.flow.gas_water.gas_prop.beta_non_darcy_flow); else waterSource = struct( ... 'density_w_sc', c.flow.gas_water.density_w_sc, ... 'prw', c.flow.gas_water.prw, ... 'Bwi', c.flow.gas_water.Bwi, ... 'cw', c.flow.gas_water.cw, ... 'vwi', c.flow.gas_water.vwi, ... 'cvw', c.flow.gas_water.cvw, ... 'p_grad_threshold', c.flow.gas_water.p_grad_threshold); gasSource = struct( ... 'density_g_sc', c.flow.gas_water.density_g_sc, ... 'prg', c.flow.gas_water.prg, ... 'Bgi', c.flow.gas_water.Bgi, ... 'cg', c.flow.gas_water.cg, ... 'vgi', c.flow.gas_water.vgi, ... 'cvg', c.flow.gas_water.cvg, ... 'gas_VL', c.flow.gas_water.gas_prop.VL, ... 'gas_PL', c.flow.gas_water.gas_prop.PL, ... 'gas_Kn', c.flow.gas_water.gas_prop.Kn, ... 'gas_beta_non_darcy', c.flow.gas_water.gas_prop.beta_non_darcy_flow); end commonData = struct( ... 'density_w_sc', waterSource.density_w_sc, ... 'prw', waterSource.prw, ... 'Bwi', waterSource.Bwi, ... 'cw', waterSource.cw, ... 'vwi', waterSource.vwi, ... 'cvw', waterSource.cvw, ... 'p_grad_threshold', waterSource.p_grad_threshold, ... 'density_g_sc', gasSource.density_g_sc, ... 'prg', gasSource.prg, ... 'Bgi', gasSource.Bgi, ... 'cg', gasSource.cg, ... 'vgi', gasSource.vgi, ... 'cvg', gasSource.cvg, ... 'gas_VL', gasSource.gas_VL, ... 'gas_PL', gasSource.gas_PL, ... 'gas_Kn', gasSource.gas_Kn, ... 'gas_beta_non_darcy', gasSource.gas_beta_non_darcy); end function commonData = getCommonDiscretizationSource(obj, c) if c.model.grid_model == 2 commonData = struct( ... 'prpor', c.discretization.dp.prpor, ... 'cpor', c.discretization.dp.cpor, ... 'Kf', c.discretization.dp.fracture.Kf, ... 'Wf', c.discretization.dp.fracture.Wf, ... 'Porf', c.discretization.dp.fracture.Porf, ... 'prporf', c.discretization.dp.fracture.prporf, ... 'cporf', c.discretization.dp.fracture.cporf, ... 'stress_fracture', c.discretization.dp.stress.fracture_factor, ... 'stress_matrix', c.discretization.dp.stress.matrix_factor, ... 'stress_ref_pressure', c.discretization.dp.stress.ref_pressure, ... 'Rpt', c.discretization.dp.Rpt, ... 'cf', c.discretization.dp.cf, ... 'ca', c.discretization.dp.ca); else commonData = struct( ... 'prpor', c.discretization.sp.matrix.prpor, ... 'cpor', c.discretization.sp.matrix.cpor, ... 'Kf', c.discretization.sp.fracture.Kf, ... 'Wf', c.discretization.sp.fracture.Wf, ... 'Porf', c.discretization.sp.fracture.Porf, ... 'prporf', c.discretization.sp.fracture.prporf, ... 'cporf', c.discretization.sp.fracture.cporf, ... 'stress_fracture', c.discretization.sp.stress.fracture_factor, ... 'stress_matrix', c.discretization.sp.stress.matrix_factor, ... 'stress_ref_pressure', c.discretization.sp.stress.ref_pressure, ... 'Rpt', c.discretization.sp.stress.Rpt, ... 'cf', c.discretization.sp.stress.cf, ... 'ca', c.discretization.sp.stress.ca); end 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 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 end if isfield(obj.Results.trun, 'Newton_step') summary{end + 1} = sprintf('Newton steps: %g', obj.Results.trun.Newton_step); %#ok 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; try scroll(obj.App.RunLogTextArea, 'bottom'); catch % scroll(...) is unavailable in older MATLAB releases. end end function onRunProgress(obj, message, meta) if nargin < 3 || ~isstruct(meta) meta = struct(); end if nargin >= 2 && strlength(strtrim(string(message))) ~= 0 obj.appendLog(message); end if isfield(meta, 'status') && ~isempty(meta.status) obj.setRunProgress(meta.status); end if isfield(meta, 'step') || isfield(meta, 'time') || isfield(meta, 'dt') stageText = obj.getMetaText(meta, 'step', '-'); timeText = obj.getMetaText(meta, 'time', '-'); dtText = obj.getMetaText(meta, 'dt', '-'); obj.setRunHeader(stageText, timeText, dtText); end end function textValue = getMetaText(obj, meta, fieldName, fallback) if isfield(meta, fieldName) && ~isempty(meta.(fieldName)) textValue = obj.valueToShortText(meta.(fieldName)); else textValue = fallback; end 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); selectedLayer = obj.pickValidItem(obj.getDropDownValue('PlotLayerDropDown', layerItems{1}), layerItems); selectedWell = obj.pickValidItem(obj.getDropDownValue('PlotWellDropDown', wellItems{1}), wellItems); selectedMetric = obj.pickValidItem(obj.getDropDownValue('PlotMetricDropDown', metricItems{1}), metricItems); selectedProperty = obj.pickValidItem(obj.getDropDownValue('PlotPropertyDropDown', propertyItems{1}), propertyItems); 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, selectedLayer); obj.setDropDownItems('PlotWellDropDown', wellItems, selectedWell); obj.setDropDownItems('PlotMetricDropDown', metricItems, selectedMetric); obj.setDropDownItems('PlotPropertyDropDown', propertyItems, selectedProperty); 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; if ~(isfinite(minValue) && isfinite(maxValue)) || maxValue <= minValue slider.Limits = [1, 2]; slider.MajorTicks = [1, 2]; slider.Value = 1; else slider.Limits = [minValue, maxValue]; slider.MajorTicks = obj.buildSparseTicks(minValue, maxValue); slider.Value = currentValue; end slider.MinorTicks = []; 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 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 obj.configureSingleTable(propName); 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 end if isfield(sampleWell, 'qo') metricList(end + 1) = "OPR"; %#ok end if isfield(sampleWell, 'qw') metricList(end + 1) = "WPR"; %#ok end if isfield(sampleWell, 'pwf') metricList(end + 1) = "dPWF"; %#ok 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 if obj.Config.model.flow_model == 2 propertyList(end + 1) = "oil_saturation"; %#ok end end if isfield(output, 'sg') propertyList(end + 1) = "gas_saturation"; %#ok elseif obj.Config.model.flow_model == 1 && isfield(output, 'sw') propertyList(end + 1) = "gas_saturation"; %#ok 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 = obj.getTimeStepControlValue(); currentValue = min(max(currentValue, minValue), maxValue); end end function value = pickValidItem(~, candidate, items) if isempty(items) value = ''; return; end candidate = char(string(candidate)); if any(strcmp(items, candidate)) value = candidate; else value = items{1}; end end function ticks = buildSparseTicks(~, minValue, maxValue) if maxValue <= minValue ticks = minValue; return; end tickCount = min(6, maxValue - minValue + 1); ticks = unique(round(linspace(minValue, maxValue, tickCount))); if ticks(1) ~= minValue ticks = [minValue, ticks]; end if ticks(end) ~= maxValue ticks = [ticks, maxValue]; end end function showAlert(obj, message, titleText, varargin) if nargin < 3 || strlength(string(titleText)) == 0 titleText = 'EDFM Simulator'; end try uialert(obj.App.UIFigure, message, titleText, varargin{:}); catch dialogTitle = obj.formatDialogMessage(titleText); errordlg(obj.formatDialogMessage(message), dialogTitle, 'modal'); end end function textValue = formatDialogMessage(~, value) if iscell(value) textValue = strjoin(string(value), newline); else textValue = string(value); end textValue = char(textValue); 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 lines{end + 1} = sprintf('File: %s', topFrame.file); %#ok 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 row = getDefaultRowForTable(~, tableName) switch tableName case 'Well1Table' row = {'', 1, '[1 1 1]', 0.178/2, 0, 1}; case 'Well2Table' row = {'', 0, '[]', 0.178/2, 0, 4}; case 'FractureWellLocationTable' row = {0, 0, 0}; case 'ScheduleTable' row = {'w1', 'open', 'inj', 'const_pwf', 40, 40, '', '', '', ''}; otherwise row = {''}; end 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 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 configureWellTables(obj) tableNames = {'Well1Table', 'FractureWellLocationTable', 'Well2Table', 'ScheduleTable'}; for i = 1:numel(tableNames) obj.configureSingleTable(tableNames{i}); end end function configureFracWellControls(obj) if isprop(obj.App, 'SelectedFracWellDropDown') obj.App.SelectedFracWellDropDown.Items = {''}; obj.App.SelectedFracWellDropDown.Value = ''; obj.App.SelectedFracWellDropDown.ValueChangedFcn = @(src, event) obj.onSelectedFracWellChanged(); end end function configureSingleTable(obj, tableName) if ~isprop(obj.App, tableName) return; end columnNames = obj.getColumnNamesForTable(tableName); if isempty(columnNames) return; end tableHandle = obj.App.(tableName); tableHandle.ColumnName = columnNames; tableHandle.RowName = {}; switch tableName case 'Well2Table' tableHandle.ColumnEditable = [true, false, false, true, true, true]; otherwise tableHandle.ColumnEditable = true(1, numel(columnNames)); end end function columnNames = getColumnNamesForTable(~, tableName) switch tableName case 'Well1Table' columnNames = {'well_name', 'nperf', 'index_xyz', 'rw', 'skin', 'well_type'}; case 'FractureWellLocationTable' columnNames = {'x', 'y', 'z'}; case 'Well2Table' columnNames = {'well_name', 'nperf', 'perfnum', 'rw', 'skin', 'well_type'}; case 'ScheduleTable' columnNames = {'well_name', 'state(open/close)', 'role(inj/pro)', ... 'control(const_q/const_pwf)', 'target_1', 'target_2', ... 'Cs_key', 'Cs_inj', 'Cb_key', 'inj_salinity'}; otherwise columnNames = {}; end 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 function c = persistCurrentFractureWellLocation(obj, c) c = normalize_config(c); fractureWellCount = size(c.wells.well2, 1); c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, fractureWellCount); if fractureWellCount == 0 c.wells.welloc = cell(0, 1); c.wells.num_fracture_wells = 0; return; end selectedIndex = min(max(obj.getSelectedFracWellIndex(), 1), fractureWellCount); fractureWellLocation = obj.getNumericTableValue('FractureWellLocationTable', []); c.wells.welloc{selectedIndex, 1} = fractureWellLocation; c.wells.num_fracture_wells = fractureWellCount; end function refreshFracWellControlsFromConfig(obj) c = normalize_config(obj.Config); fractureWellCount = size(c.wells.well2, 1); c.wells.welloc = obj.resizeWellocCell(c.wells.welloc, fractureWellCount); c.wells.num_fracture_wells = fractureWellCount; obj.Config = c; obj.refreshFracWellDropDown(fractureWellCount, c.wells.well2); if fractureWellCount == 0 obj.CurrentFracWellIndex = 1; obj.setTableValue('FractureWellLocationTable', []); obj.setControlEnabled('SelectedFracWellDropDown', false); obj.setControlEnabled('FractureWellLocationTable', false); obj.setControlEnabled('AddFracLocRowButton', false); obj.setControlEnabled('DeleteFracLocRowButton', false); return; end obj.CurrentFracWellIndex = min(max(obj.CurrentFracWellIndex, 1), fractureWellCount); obj.setSelectedFracWellIndex(obj.CurrentFracWellIndex, false); obj.setControlEnabled('SelectedFracWellDropDown', true); obj.setControlEnabled('FractureWellLocationTable', true); obj.setControlEnabled('AddFracLocRowButton', true); obj.setControlEnabled('DeleteFracLocRowButton', true); obj.setTableValue('FractureWellLocationTable', c.wells.welloc{obj.CurrentFracWellIndex, 1}); end function refreshFracWellDropDown(obj, fractureWellCount, well2) if ~isprop(obj.App, 'SelectedFracWellDropDown') return; end if fractureWellCount == 0 obj.App.SelectedFracWellDropDown.Items = {''}; obj.App.SelectedFracWellDropDown.Value = ''; return; end items = cell(fractureWellCount, 1); for i = 1:fractureWellCount wellName = sprintf('wf%d', i); if size(well2, 1) >= i && size(well2, 2) >= 1 && ~isempty(well2{i, 1}) wellName = char(string(well2{i, 1})); end items{i} = sprintf('%d: %s', i, wellName); end obj.App.SelectedFracWellDropDown.Items = items; selectedValue = items{min(max(obj.CurrentFracWellIndex, 1), fractureWellCount)}; obj.App.SelectedFracWellDropDown.Value = selectedValue; end function count = getFractureWellCount(obj) if isempty(fieldnames(obj.Config)) count = 0; return; end c = normalize_config(obj.Config); count = size(c.wells.well2, 1); end function index = getSelectedFracWellIndex(obj) fractureWellCount = obj.getFractureWellCount(); if fractureWellCount == 0 index = 1; return; end index = min(max(obj.CurrentFracWellIndex, 1), fractureWellCount); end function index = getSelectedFracWellIndexFromUI(obj) if ~isprop(obj.App, 'SelectedFracWellDropDown') index = obj.CurrentFracWellIndex; return; end value = string(obj.App.SelectedFracWellDropDown.Value); token = regexp(char(value), '^\s*(\d+)', 'tokens', 'once'); if isempty(token) index = obj.CurrentFracWellIndex; else index = str2double(token{1}); end if isnan(index) || index < 1 index = obj.CurrentFracWellIndex; end end function setSelectedFracWellIndex(obj, index, refreshTable) if nargin < 3 refreshTable = true; end fractureWellCount = obj.getFractureWellCount(); if fractureWellCount == 0 obj.CurrentFracWellIndex = 1; return; end obj.CurrentFracWellIndex = min(max(index, 1), fractureWellCount); if isprop(obj.App, 'SelectedFracWellDropDown') && ~isempty(obj.App.SelectedFracWellDropDown.Items) items = obj.App.SelectedFracWellDropDown.Items; obj.App.SelectedFracWellDropDown.Value = items{obj.CurrentFracWellIndex}; end if refreshTable obj.onSelectedFracWellChanged(); end end function welloc = resizeWellocCell(~, welloc, targetCount) if nargin < 3 || isempty(targetCount) || targetCount <= 0 welloc = cell(0, 1); return; end if isempty(welloc) || ~iscell(welloc) welloc = cell(targetCount, 1); else welloc = welloc(:); currentCount = numel(welloc); if currentCount < targetCount welloc(currentCount + 1:targetCount, 1) = {[]}; elseif currentCount > targetCount welloc = welloc(1:targetCount, 1); end end for i = 1:targetCount if isempty(welloc{i, 1}) welloc{i, 1} = []; end end end function setControlEnabled(obj, propName, isEnabled) if ~isprop(obj.App, propName) return; end component = obj.App.(propName); if isprop(component, 'Enable') if isEnabled component.Enable = 'on'; else component.Enable = 'off'; end end end function c = normalizeScheduleConfig(obj, c) c = normalize_config(c); wellNames = obj.getAllConfiguredWellNames(c); totalWells = numel(wellNames); phaseCount = max([ ... numel(c.schedule.well_schedules), ... numel(c.schedule.time), ... numel(c.schedule.dtmin), ... numel(c.schedule.dtmax), ... c.schedule.number_phases, ... 1]); if ~iscell(c.schedule.well_schedules) c.schedule.well_schedules = cell(phaseCount, 1); elseif numel(c.schedule.well_schedules) < phaseCount c.schedule.well_schedules(end + 1:phaseCount, 1) = {[]}; elseif numel(c.schedule.well_schedules) > phaseCount c.schedule.well_schedules = c.schedule.well_schedules(1:phaseCount, 1); else c.schedule.well_schedules = c.schedule.well_schedules(:); end for k = 1:phaseCount c.schedule.well_schedules{k, 1} = obj.normalizeSinglePhaseSchedule( ... c.schedule.well_schedules{k, 1}, wellNames, totalWells); end c.schedule.number_phases = phaseCount; end function phaseSchedule = normalizeSinglePhaseSchedule(obj, phaseSchedule, wellNames, totalWells) if totalWells == 0 phaseSchedule = cell(0, 10); return; end phaseSchedule = obj.ensureScheduleCellMatrix(phaseSchedule); normalized = cell(totalWells, 10); usedRows = false(size(phaseSchedule, 1), 1); for i = 1:totalWells matchedRow = []; for rowIndex = 1:size(phaseSchedule, 1) if usedRows(rowIndex) continue; end if size(phaseSchedule, 2) >= 1 && strcmp(string(phaseSchedule{rowIndex, 1}), string(wellNames{i})) matchedRow = rowIndex; break; end end if isempty(matchedRow) && i <= size(phaseSchedule, 1) && ~usedRows(i) matchedRow = i; end if isempty(matchedRow) normalized(i, :) = obj.getDefaultClosedScheduleRow(wellNames{i}); else rowData = obj.padScheduleRow(phaseSchedule(matchedRow, :)); rowData{1} = wellNames{i}; normalized(i, :) = rowData; usedRows(matchedRow) = true; end end phaseSchedule = normalized; end function phaseSchedule = ensureScheduleCellMatrix(~, phaseSchedule) if isempty(phaseSchedule) phaseSchedule = cell(0, 10); return; end if ~iscell(phaseSchedule) phaseSchedule = num2cell(phaseSchedule); end if isvector(phaseSchedule) && size(phaseSchedule, 1) == 1 if size(phaseSchedule, 2) == 10 return; end end if isvector(phaseSchedule) && size(phaseSchedule, 2) == 1 phaseSchedule = phaseSchedule'; end if size(phaseSchedule, 2) > 10 phaseSchedule = phaseSchedule(:, 1:10); elseif size(phaseSchedule, 2) < 10 phaseSchedule(:, end + 1:10) = {[]}; end end function rowData = padScheduleRow(~, rowData) if ~iscell(rowData) rowData = num2cell(rowData); end if size(rowData, 1) ~= 1 rowData = rowData(1, :); end if size(rowData, 2) > 10 rowData = rowData(:, 1:10); elseif size(rowData, 2) < 10 rowData(:, end + 1:10) = {[]}; end end function row = getDefaultClosedScheduleRow(~, wellName) row = {char(string(wellName)), 'close', 'pro', 'const_pwf', 0, 0, '', '', '', ''}; end function wellNames = getAllConfiguredWellNames(~, c) well1Names = {}; well2Names = {}; if isfield(c.wells, 'well1') && iscell(c.wells.well1) && ~isempty(c.wells.well1) well1Names = cell(size(c.wells.well1, 1), 1); for i = 1:size(c.wells.well1, 1) name = ''; if size(c.wells.well1, 2) >= 1 && ~isempty(c.wells.well1{i, 1}) name = char(string(c.wells.well1{i, 1})); end if isempty(name) name = sprintf('w%d', i); end well1Names{i} = name; end end if isfield(c.wells, 'well2') && iscell(c.wells.well2) && ~isempty(c.wells.well2) well2Names = cell(size(c.wells.well2, 1), 1); for i = 1:size(c.wells.well2, 1) name = ''; if size(c.wells.well2, 2) >= 1 && ~isempty(c.wells.well2{i, 1}) name = char(string(c.wells.well2{i, 1})); end if isempty(name) name = sprintf('wf%d', i); c.wells.well2{i, 1} = name; end well2Names{i} = name; end end wellNames = [well1Names; well2Names]; end end end