1594 lines
75 KiB
Matlab
1594 lines
75 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('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.configureWellTables();
|
|
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');
|
|
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 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);
|
|
end
|
|
end
|
|
|
|
function addTableRow(obj, tableName)
|
|
if ~isprop(obj.App, tableName)
|
|
return;
|
|
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
|
|
|
|
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
|
|
uialert(obj.App.UIFigure, ...
|
|
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))
|
|
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
|
|
|
|
obj.applyDiscretizationVisibility(gridModel);
|
|
obj.applyFlowVisibility(flowModel);
|
|
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 = 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('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.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);
|
|
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 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;
|
|
gasCommonProps = { ...
|
|
'GasLabel', ...
|
|
'FlowCommonDensityGScEditField', 'DensityGScLabel', ...
|
|
'FlowCommonGasPrgEditField', 'GasPrgLabel', ...
|
|
'FlowCommonGasBgiEditField', 'GasBgiLabel', ...
|
|
'FlowCommonGasCgEditField', 'GasCgLabel', ...
|
|
'FlowCommonGasVgiEditField', 'GasVgiLabel', ...
|
|
'FlowCommonGasCvgEditField', 'GasCvgLabel', ...
|
|
'FlowCommonGasVLEditField', 'GasVLLabel', ...
|
|
'FlowCommonGasPLEditField', 'GasPLLabel', ...
|
|
'FlowCommonGasKnEditField', 'GasKnLabel', ...
|
|
'FlowCommonGasBetaNonDarcyEditField', 'GasBetaNonDarcyLabel'};
|
|
for i = 1:numel(gasCommonProps)
|
|
obj.setControlVisible(gasCommonProps{i}, showGasCommon);
|
|
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<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;
|
|
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<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
|
|
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<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 = 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 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 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<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 configureWellTables(obj)
|
|
tableNames = {'Well1Table', 'FractureWellLocationTable', 'Well2Table', 'ScheduleTable'};
|
|
for i = 1:numel(tableNames)
|
|
obj.configureSingleTable(tableNames{i});
|
|
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 = {};
|
|
tableHandle.ColumnEditable = true(1, numel(columnNames));
|
|
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
|
|
end
|
|
end
|