优化点5,6
This commit is contained in:
@@ -0,0 +1,392 @@
|
|||||||
|
# Fracture Well UI Redesign Plan
|
||||||
|
|
||||||
|
## Problem Summary
|
||||||
|
|
||||||
|
Current `Wells` page structure is not suitable for the following scenario:
|
||||||
|
|
||||||
|
- reservoir may contain zero fracture wells
|
||||||
|
- reservoir may contain one fracture well
|
||||||
|
- reservoir may contain multiple fracture wells
|
||||||
|
- `num_fracture_wells` must equal the fracture-well count
|
||||||
|
- `welloc{i,1}` stores perforation coordinates for fracture well `i`
|
||||||
|
- perforation coordinates are often fracture coordinates, but they may also be independent coordinates
|
||||||
|
- fracture engineering input can stay unified in `fracture.input_content`
|
||||||
|
- perforation data cannot be merged into one shared array, otherwise multiple fracture wells cannot be distinguished
|
||||||
|
|
||||||
|
The current controller only reads and writes the first entry of `c.wells.welloc`, so the present UI effectively assumes only one fracture well.
|
||||||
|
|
||||||
|
## Current Constraint In Existing Code
|
||||||
|
|
||||||
|
Existing runtime logic already supports multiple fracture wells in principle:
|
||||||
|
|
||||||
|
- `run_case.m`
|
||||||
|
- `build_fracture_wells(...)` loops over `num_fracture_wells`
|
||||||
|
- original case files
|
||||||
|
- example case 7 uses `num_fracture_wells = 6`
|
||||||
|
- `welloc{1}` to `welloc{6}` are defined separately
|
||||||
|
- `well2` also has 6 rows and matches them one by one
|
||||||
|
|
||||||
|
The main mismatch is in the GUI/controller layer:
|
||||||
|
|
||||||
|
- `FractureWellLocationTable` is a single table
|
||||||
|
- controller uses `unwrapFirstCell(c.wells.welloc)`
|
||||||
|
- controller writes back with `replaceFirstCell(...)`
|
||||||
|
|
||||||
|
So the GUI schema is the real bottleneck.
|
||||||
|
|
||||||
|
## Recommended UI Principle
|
||||||
|
|
||||||
|
Do not organize the fracture-well section as:
|
||||||
|
|
||||||
|
- one table for all perforation coordinates only
|
||||||
|
|
||||||
|
Instead organize it as:
|
||||||
|
|
||||||
|
- one summary table for fracture well definitions
|
||||||
|
- one detail editor for the currently selected fracture well's perforation coordinates
|
||||||
|
|
||||||
|
This is the safest structure because:
|
||||||
|
|
||||||
|
- it supports `0 / 1 / N` fracture wells
|
||||||
|
- each fracture well keeps its own `welloc{i,1}`
|
||||||
|
- `well2(i,:)` and `welloc{i,1}` can stay naturally linked by row index
|
||||||
|
- fracture geometry remains independent in `fracture.input_content`
|
||||||
|
|
||||||
|
## Recommended Data Mapping
|
||||||
|
|
||||||
|
### Keep These Backend Fields
|
||||||
|
|
||||||
|
Keep the runtime/backend idea below:
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
c.wells.num_fracture_wells
|
||||||
|
c.wells.well2
|
||||||
|
c.wells.welloc
|
||||||
|
```
|
||||||
|
|
||||||
|
Recommended interpretation:
|
||||||
|
|
||||||
|
- `c.wells.num_fracture_wells = size(c.wells.well2, 1)`
|
||||||
|
- `c.wells.welloc{i,1}` is the perforation coordinate matrix of fracture well `i`
|
||||||
|
- `size(c.wells.welloc, 1)` should equal `size(c.wells.well2, 1)`
|
||||||
|
|
||||||
|
Recommended invariant:
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
num_fracture_wells == size(well2,1) == numel(welloc)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Suggested Meaning Of `well2`
|
||||||
|
|
||||||
|
Treat `well2` as the fracture-well summary table only.
|
||||||
|
|
||||||
|
Recommended editable columns:
|
||||||
|
|
||||||
|
1. `well_name`
|
||||||
|
2. `rw`
|
||||||
|
3. `skin`
|
||||||
|
4. `welltype`
|
||||||
|
|
||||||
|
Recommended non-editable or derived columns:
|
||||||
|
|
||||||
|
1. `nperf`
|
||||||
|
2. `perf_index`
|
||||||
|
|
||||||
|
Reason:
|
||||||
|
|
||||||
|
- `nperf` is derived from `welloc{i,1}`
|
||||||
|
- `perf_index` is generated by `findWelloc(...)`
|
||||||
|
- these two should not be manually edited in the GUI
|
||||||
|
|
||||||
|
If keeping the current 6-column backend shape for compatibility, then GUI can still display 6 columns, but:
|
||||||
|
|
||||||
|
- column 2 `nperf` should be read-only
|
||||||
|
- column 3 `perf_index` should be read-only or hidden
|
||||||
|
|
||||||
|
## Recommended Wells Page Layout
|
||||||
|
|
||||||
|
### Section A: Conventional Wells
|
||||||
|
|
||||||
|
Keep existing `Well1Table`.
|
||||||
|
|
||||||
|
This remains for ordinary wells already handled by `handle_well1(...)`.
|
||||||
|
|
||||||
|
### Section B: Fracture Wells Summary
|
||||||
|
|
||||||
|
Repurpose current `Well2Table` into a fracture-well summary table.
|
||||||
|
|
||||||
|
Recommended label:
|
||||||
|
|
||||||
|
- `Fracture Wells`
|
||||||
|
|
||||||
|
Recommended columns:
|
||||||
|
|
||||||
|
1. `well_name`
|
||||||
|
2. `rw`
|
||||||
|
3. `skin`
|
||||||
|
4. `welltype`
|
||||||
|
5. `nperf` optional read-only display
|
||||||
|
|
||||||
|
Buttons:
|
||||||
|
|
||||||
|
- `Add Fracture Well`
|
||||||
|
- `Delete Fracture Well`
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
|
||||||
|
- add one row to `well2`
|
||||||
|
- also create one empty `welloc{i,1} = []`
|
||||||
|
- delete row `i` from both `well2` and `welloc`
|
||||||
|
- after any add/delete:
|
||||||
|
- `num_fracture_wells = size(well2,1)`
|
||||||
|
|
||||||
|
### Section C: Perforation Detail Editor
|
||||||
|
|
||||||
|
Do not keep one generic `FractureWellLocationTable` without context.
|
||||||
|
|
||||||
|
Replace it conceptually with:
|
||||||
|
|
||||||
|
- one selector showing which fracture well is being edited
|
||||||
|
- one table that edits only that well's perforation coordinates
|
||||||
|
|
||||||
|
Recommended controls:
|
||||||
|
|
||||||
|
1. `SelectedFracWellDropDown` or `SelectedFracWellListBox`
|
||||||
|
2. `FractureWellLocationTable`
|
||||||
|
3. `Add Perf Row`
|
||||||
|
4. `Delete Perf Row`
|
||||||
|
5. optional helper text label
|
||||||
|
|
||||||
|
Recommended table columns:
|
||||||
|
|
||||||
|
1. `x`
|
||||||
|
2. `y`
|
||||||
|
3. `z`
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
|
||||||
|
- when user selects fracture well `i`
|
||||||
|
- table shows `welloc{i,1}`
|
||||||
|
- when table changes
|
||||||
|
- write back to `welloc{i,1}`
|
||||||
|
- when no fracture well exists
|
||||||
|
- disable this table and show hint text like:
|
||||||
|
- `No fracture well is defined. Please add a fracture well first.`
|
||||||
|
|
||||||
|
This is the key redesign.
|
||||||
|
|
||||||
|
## Relationship Between Fracture Input And Perforation Input
|
||||||
|
|
||||||
|
These two parts should stay separate in the UI.
|
||||||
|
|
||||||
|
### Fracture Tab
|
||||||
|
|
||||||
|
Keep:
|
||||||
|
|
||||||
|
- `fracture.input_content`
|
||||||
|
|
||||||
|
This is for fracture geometry/engineering input.
|
||||||
|
|
||||||
|
### Wells Tab
|
||||||
|
|
||||||
|
Keep separate:
|
||||||
|
|
||||||
|
- fracture-well summary
|
||||||
|
- per-well perforation coordinates
|
||||||
|
|
||||||
|
Reason:
|
||||||
|
|
||||||
|
- fractures describe the reservoir fracture system
|
||||||
|
- perforations describe where a specific fracture well connects to the model
|
||||||
|
- one fracture well may connect to one or more fracture coordinates
|
||||||
|
- perforation coordinates may match fracture coordinates, but not always
|
||||||
|
|
||||||
|
So they should not be merged into the same editable widget.
|
||||||
|
|
||||||
|
## Whether `num_fracture_wells` Should Be A Separate UI Field
|
||||||
|
|
||||||
|
Recommendation:
|
||||||
|
|
||||||
|
- do not expose `num_fracture_wells` as an independent editable numeric field
|
||||||
|
|
||||||
|
Instead:
|
||||||
|
|
||||||
|
- derive it automatically from fracture-well summary row count
|
||||||
|
|
||||||
|
Reason:
|
||||||
|
|
||||||
|
- avoids inconsistency
|
||||||
|
- user only manages actual fracture-well rows
|
||||||
|
- controller maintains `num_fracture_wells`
|
||||||
|
|
||||||
|
## Recommended App Designer Changes
|
||||||
|
|
||||||
|
These are the parts you should change manually in `.mlapp`.
|
||||||
|
|
||||||
|
### Minimum Change Version
|
||||||
|
|
||||||
|
If you want the smallest UI change:
|
||||||
|
|
||||||
|
1. keep `Well1Table`
|
||||||
|
2. keep `Well2Table`
|
||||||
|
3. keep `FractureWellLocationTable`
|
||||||
|
4. add one selector control above `FractureWellLocationTable`
|
||||||
|
- name suggestion: `SelectedFracWellDropDown`
|
||||||
|
5. change labels so that:
|
||||||
|
- `Well2Label` -> `Fracture Wells`
|
||||||
|
- `WellLocationLabel` -> `Perforation Coordinates`
|
||||||
|
6. change `FractureWellLocationTable` columns from one generic column to three columns:
|
||||||
|
- `x`, `y`, `z`
|
||||||
|
7. add a small note label:
|
||||||
|
- `Each fracture well has its own perforation coordinate list.`
|
||||||
|
|
||||||
|
This is the recommended minimum viable fix.
|
||||||
|
|
||||||
|
### Better Version
|
||||||
|
|
||||||
|
If you can slightly improve layout:
|
||||||
|
|
||||||
|
Use a 3-block layout in `WellsTab`:
|
||||||
|
|
||||||
|
1. top-left: `Well1Table`
|
||||||
|
2. top-right: `Well2Table`
|
||||||
|
3. bottom-wide: selected fracture well perforation editor
|
||||||
|
|
||||||
|
This will be clearer than the current four unrelated tables.
|
||||||
|
|
||||||
|
## Recommended Table Definitions
|
||||||
|
|
||||||
|
### `Well1Table`
|
||||||
|
|
||||||
|
No major structural change required.
|
||||||
|
|
||||||
|
### `Well2Table`
|
||||||
|
|
||||||
|
Recommended columns:
|
||||||
|
|
||||||
|
1. `well_name`
|
||||||
|
2. `rw`
|
||||||
|
3. `skin`
|
||||||
|
4. `welltype`
|
||||||
|
|
||||||
|
Optional extra read-only columns:
|
||||||
|
|
||||||
|
5. `nperf`
|
||||||
|
|
||||||
|
If you want to keep backend compatibility without changing much controller code later, you may still visually keep 6 columns:
|
||||||
|
|
||||||
|
1. `well_name`
|
||||||
|
2. `nperf` read-only
|
||||||
|
3. `perf_index` hidden or read-only expression
|
||||||
|
4. `rw`
|
||||||
|
5. `skin`
|
||||||
|
6. `welltype`
|
||||||
|
|
||||||
|
But from user experience perspective, 4 editable columns are better.
|
||||||
|
|
||||||
|
### `FractureWellLocationTable`
|
||||||
|
|
||||||
|
Recommended columns:
|
||||||
|
|
||||||
|
1. `x`
|
||||||
|
2. `y`
|
||||||
|
3. `z`
|
||||||
|
|
||||||
|
Each row is one perforation point for the currently selected fracture well.
|
||||||
|
|
||||||
|
## Controller Change Direction
|
||||||
|
|
||||||
|
When you later modify `EDFMAppController.m`, the well section should change conceptually as follows.
|
||||||
|
|
||||||
|
### Current Wrong Logic
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
obj.setTableValue('FractureWellLocationTable', obj.unwrapFirstCell(c.wells.welloc));
|
||||||
|
...
|
||||||
|
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);
|
||||||
|
```
|
||||||
|
|
||||||
|
This only handles the first fracture well.
|
||||||
|
|
||||||
|
### Target Logic
|
||||||
|
|
||||||
|
Need a selected index such as:
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
selectedFracWellIndex
|
||||||
|
```
|
||||||
|
|
||||||
|
Read UI:
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
c.wells.well2 = ...
|
||||||
|
c.wells.num_fracture_wells = size(c.wells.well2,1);
|
||||||
|
ensure c.wells.welloc has same length
|
||||||
|
c.wells.welloc{selectedFracWellIndex,1} = currentPerfTable
|
||||||
|
```
|
||||||
|
|
||||||
|
Write UI:
|
||||||
|
|
||||||
|
```matlab
|
||||||
|
set Well2Table from c.wells.well2
|
||||||
|
refresh fracture-well selector items
|
||||||
|
load FractureWellLocationTable from c.wells.welloc{selectedFracWellIndex,1}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Validation Rules To Add
|
||||||
|
|
||||||
|
When this section is later coded, these checks are recommended:
|
||||||
|
|
||||||
|
1. `size(well2,1) == numel(welloc)`
|
||||||
|
2. `num_fracture_wells == size(well2,1)`
|
||||||
|
3. each `welloc{i,1}` must be empty or an `N x 3` numeric matrix
|
||||||
|
4. if `well2(i,1)` is empty, generate default name like `wf1`, `wf2`
|
||||||
|
5. when no fracture well exists:
|
||||||
|
- `well2 = {}`
|
||||||
|
- `welloc = {}`
|
||||||
|
- `num_fracture_wells = 0`
|
||||||
|
6. if user deletes a fracture well, also delete its perforation list
|
||||||
|
|
||||||
|
## Practical Recommendation
|
||||||
|
|
||||||
|
For your current stage, the best balance is:
|
||||||
|
|
||||||
|
1. fracture geometry remains in `FractureTab`
|
||||||
|
2. ordinary wells remain in `Well1Table`
|
||||||
|
3. fracture wells use `Well2Table`
|
||||||
|
4. perforations are edited through one selected-well detail table
|
||||||
|
5. `num_fracture_wells` is auto-derived, not manually entered
|
||||||
|
|
||||||
|
This will match both:
|
||||||
|
|
||||||
|
- the old case-script logic
|
||||||
|
- the future GUI editing logic
|
||||||
|
|
||||||
|
And it also naturally supports the case where there is no fracture well.
|
||||||
|
|
||||||
|
## Manual UI Rename Suggestions
|
||||||
|
|
||||||
|
Suggested visible labels:
|
||||||
|
|
||||||
|
- `Well1Label` -> `Conventional Wells`
|
||||||
|
- `Well2Label` -> `Fracture Wells`
|
||||||
|
- `WellLocationLabel` -> `Perforation Coordinates`
|
||||||
|
- `ScheduleLabel` keep as `Schedule`
|
||||||
|
|
||||||
|
Suggested new control names if you add them:
|
||||||
|
|
||||||
|
- `SelectedFracWellDropDown`
|
||||||
|
- `SelectedFracWellDropDownLabel`
|
||||||
|
- `FracWellHintLabel`
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The main point is:
|
||||||
|
|
||||||
|
- fracture information can stay unified in `fracture.input_content`
|
||||||
|
- perforation information must be stored per fracture well
|
||||||
|
- therefore the GUI must be changed from "one perforation table" to "fracture-well summary + selected-well perforation detail"
|
||||||
|
|
||||||
|
That is the most stable and least confusing design for the current codebase.
|
||||||
@@ -5,6 +5,7 @@ classdef EDFMAppController < handle
|
|||||||
Results struct = struct()
|
Results struct = struct()
|
||||||
ProjectRoot char
|
ProjectRoot char
|
||||||
CurrentPlotAction string = ""
|
CurrentPlotAction string = ""
|
||||||
|
CurrentFracWellIndex double = 1
|
||||||
end
|
end
|
||||||
|
|
||||||
methods
|
methods
|
||||||
@@ -25,6 +26,7 @@ classdef EDFMAppController < handle
|
|||||||
obj.setDropDownItems('OWoil_modelDropDown', {'1', '2'}, '1');
|
obj.setDropDownItems('OWoil_modelDropDown', {'1', '2'}, '1');
|
||||||
obj.setDropDownItems('MCgas_modelDropDown', {'1'}, '1');
|
obj.setDropDownItems('MCgas_modelDropDown', {'1'}, '1');
|
||||||
obj.configureWellTables();
|
obj.configureWellTables();
|
||||||
|
obj.configureFracWellControls();
|
||||||
obj.applyFixedUIState();
|
obj.applyFixedUIState();
|
||||||
|
|
||||||
obj.Results = struct();
|
obj.Results = struct();
|
||||||
@@ -180,6 +182,9 @@ classdef EDFMAppController < handle
|
|||||||
obj.App.(tableName).UserData = [];
|
obj.App.(tableName).UserData = [];
|
||||||
else
|
else
|
||||||
obj.App.(tableName).UserData = indices(1, 1);
|
obj.App.(tableName).UserData = indices(1, 1);
|
||||||
|
if strcmp(tableName, 'Well2Table')
|
||||||
|
obj.setSelectedFracWellIndex(indices(1, 1), true);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -188,6 +193,37 @@ classdef EDFMAppController < handle
|
|||||||
return;
|
return;
|
||||||
end
|
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
|
||||||
|
uialert(obj.App.UIFigure, ...
|
||||||
|
'Please add a fracture well before editing perforation coordinates.', ...
|
||||||
|
'No Fracture Well');
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
data = obj.App.(tableName).Data;
|
data = obj.App.(tableName).Data;
|
||||||
newRow = obj.getDefaultRowForTable(tableName);
|
newRow = obj.getDefaultRowForTable(tableName);
|
||||||
|
|
||||||
@@ -209,6 +245,38 @@ classdef EDFMAppController < handle
|
|||||||
return;
|
return;
|
||||||
end
|
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
|
||||||
|
uialert(obj.App.UIFigure, ...
|
||||||
|
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;
|
data = obj.App.(tableName).Data;
|
||||||
if isempty(data)
|
if isempty(data)
|
||||||
return;
|
return;
|
||||||
@@ -455,8 +523,10 @@ classdef EDFMAppController < handle
|
|||||||
obj.setNumericFieldValue('MCstress_factor_ref_pressureEditField', c.flow.multi_component.gas_prop.stress_factor_ref_pressure);
|
obj.setNumericFieldValue('MCstress_factor_ref_pressureEditField', c.flow.multi_component.gas_prop.stress_factor_ref_pressure);
|
||||||
|
|
||||||
obj.setTableValue('Well1Table', c.wells.well1);
|
obj.setTableValue('Well1Table', c.wells.well1);
|
||||||
obj.setTableValue('FractureWellLocationTable', obj.unwrapFirstCell(c.wells.welloc));
|
|
||||||
obj.setTableValue('Well2Table', c.wells.well2);
|
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.setTableValue('ScheduleTable', obj.unwrapFirstCell(c.schedule.well_schedules));
|
||||||
obj.setTextAreaExpr('TimeTextArea', c.schedule.time);
|
obj.setTextAreaExpr('TimeTextArea', c.schedule.time);
|
||||||
obj.setTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
|
obj.setTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
|
||||||
@@ -648,11 +718,11 @@ classdef EDFMAppController < handle
|
|||||||
c.flow.multi_component.cvw = commonCvw;
|
c.flow.multi_component.cvw = commonCvw;
|
||||||
|
|
||||||
c.wells.well1 = obj.getTableValue('Well1Table', c.wells.well1);
|
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);
|
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( ...
|
firstPhaseSchedule = obj.getTableValue( ...
|
||||||
'ScheduleTable', obj.unwrapFirstCell(c.schedule.well_schedules));
|
'ScheduleTable', obj.unwrapFirstCell(c.schedule.well_schedules));
|
||||||
@@ -661,6 +731,7 @@ classdef EDFMAppController < handle
|
|||||||
c.schedule.time = obj.getTextAreaExpr('TimeTextArea', c.schedule.time);
|
c.schedule.time = obj.getTextAreaExpr('TimeTextArea', c.schedule.time);
|
||||||
c.schedule.dtmin = obj.getTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
|
c.schedule.dtmin = obj.getTextAreaExpr('DtMinTextArea', c.schedule.dtmin);
|
||||||
c.schedule.dtmax = obj.getTextAreaExpr('DtMaxTextArea', c.schedule.dtmax);
|
c.schedule.dtmax = obj.getTextAreaExpr('DtMaxTextArea', c.schedule.dtmax);
|
||||||
|
c = obj.normalizeScheduleConfig(c);
|
||||||
|
|
||||||
c.solver.yitap = obj.getNumericFieldValue('YitaPEditField', c.solver.yitap);
|
c.solver.yitap = obj.getNumericFieldValue('YitaPEditField', c.solver.yitap);
|
||||||
c.solver.yitas = obj.getNumericFieldValue('YitaSEditField', c.solver.yitas);
|
c.solver.yitas = obj.getNumericFieldValue('YitaSEditField', c.solver.yitas);
|
||||||
@@ -673,6 +744,16 @@ classdef EDFMAppController < handle
|
|||||||
obj.syncModelTabs();
|
obj.syncModelTabs();
|
||||||
end
|
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)
|
function applyFixedUIState(obj)
|
||||||
obj.setControlVisible('ModelFlagDropDownLabel', false);
|
obj.setControlVisible('ModelFlagDropDownLabel', false);
|
||||||
obj.setControlVisible('ModelFlagDropDown', false);
|
obj.setControlVisible('ModelFlagDropDown', false);
|
||||||
@@ -1505,6 +1586,14 @@ classdef EDFMAppController < handle
|
|||||||
end
|
end
|
||||||
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)
|
function configureSingleTable(obj, tableName)
|
||||||
if ~isprop(obj.App, tableName)
|
if ~isprop(obj.App, tableName)
|
||||||
return;
|
return;
|
||||||
@@ -1518,7 +1607,12 @@ classdef EDFMAppController < handle
|
|||||||
tableHandle = obj.App.(tableName);
|
tableHandle = obj.App.(tableName);
|
||||||
tableHandle.ColumnName = columnNames;
|
tableHandle.ColumnName = columnNames;
|
||||||
tableHandle.RowName = {};
|
tableHandle.RowName = {};
|
||||||
tableHandle.ColumnEditable = true(1, numel(columnNames));
|
switch tableName
|
||||||
|
case 'Well2Table'
|
||||||
|
tableHandle.ColumnEditable = [true, false, false, true, true, true];
|
||||||
|
otherwise
|
||||||
|
tableHandle.ColumnEditable = true(1, numel(columnNames));
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function columnNames = getColumnNamesForTable(~, tableName)
|
function columnNames = getColumnNamesForTable(~, tableName)
|
||||||
@@ -1589,5 +1683,321 @@ classdef EDFMAppController < handle
|
|||||||
end
|
end
|
||||||
textValue = strtrim(evalc('disp(value)'));
|
textValue = strtrim(evalc('disp(value)'));
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
|
|||||||
Binary file not shown.
+33
-25
@@ -183,10 +183,11 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
MCcs_NcTextArea matlab.ui.control.TextArea
|
MCcs_NcTextArea matlab.ui.control.TextArea
|
||||||
cs_NcTextAreaLabel matlab.ui.control.Label
|
cs_NcTextAreaLabel matlab.ui.control.Label
|
||||||
WellsTab matlab.ui.container.Tab
|
WellsTab matlab.ui.container.Tab
|
||||||
|
SelectedFracWellDropDown matlab.ui.control.DropDown
|
||||||
|
SelectedFractureWellLabel matlab.ui.control.Label
|
||||||
ScheduleLabel matlab.ui.control.Label
|
ScheduleLabel matlab.ui.control.Label
|
||||||
WellLocationLabel matlab.ui.control.Label
|
FractureWellsLabel matlab.ui.control.Label
|
||||||
Well2Label matlab.ui.control.Label
|
ConventionalWellsLabel matlab.ui.control.Label
|
||||||
Well1Label matlab.ui.control.Label
|
|
||||||
ScheduleTimeConfigLabel matlab.ui.control.Label
|
ScheduleTimeConfigLabel matlab.ui.control.Label
|
||||||
AddFracLocRowButton matlab.ui.control.Button
|
AddFracLocRowButton matlab.ui.control.Button
|
||||||
DeleteFracLocRowButton matlab.ui.control.Button
|
DeleteFracLocRowButton matlab.ui.control.Button
|
||||||
@@ -1390,7 +1391,7 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
app.Well1Table.RowName = {};
|
app.Well1Table.RowName = {};
|
||||||
app.Well1Table.ColumnEditable = true;
|
app.Well1Table.ColumnEditable = true;
|
||||||
app.Well1Table.CellSelectionCallback = createCallbackFcn(app, @Well1TableCellSelection, true);
|
app.Well1Table.CellSelectionCallback = createCallbackFcn(app, @Well1TableCellSelection, true);
|
||||||
app.Well1Table.Position = [35 475 372 182];
|
app.Well1Table.Position = [43 194 446 167];
|
||||||
|
|
||||||
% Create FractureWellLocationTable
|
% Create FractureWellLocationTable
|
||||||
app.FractureWellLocationTable = uitable(app.WellsTab);
|
app.FractureWellLocationTable = uitable(app.WellsTab);
|
||||||
@@ -1398,7 +1399,7 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
app.FractureWellLocationTable.RowName = {};
|
app.FractureWellLocationTable.RowName = {};
|
||||||
app.FractureWellLocationTable.ColumnEditable = true;
|
app.FractureWellLocationTable.ColumnEditable = true;
|
||||||
app.FractureWellLocationTable.CellSelectionCallback = createCallbackFcn(app, @FractureWellLocationTableCellSelection, true);
|
app.FractureWellLocationTable.CellSelectionCallback = createCallbackFcn(app, @FractureWellLocationTableCellSelection, true);
|
||||||
app.FractureWellLocationTable.Position = [44 179 366 185];
|
app.FractureWellLocationTable.Position = [737 520 445 100];
|
||||||
|
|
||||||
% Create Well2Table
|
% Create Well2Table
|
||||||
app.Well2Table = uitable(app.WellsTab);
|
app.Well2Table = uitable(app.WellsTab);
|
||||||
@@ -1406,7 +1407,7 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
app.Well2Table.RowName = {};
|
app.Well2Table.RowName = {};
|
||||||
app.Well2Table.ColumnEditable = true;
|
app.Well2Table.ColumnEditable = true;
|
||||||
app.Well2Table.CellSelectionCallback = createCallbackFcn(app, @Well2TableCellSelection, true);
|
app.Well2Table.CellSelectionCallback = createCallbackFcn(app, @Well2TableCellSelection, true);
|
||||||
app.Well2Table.Position = [552 472 594 185];
|
app.Well2Table.Position = [35 476 594 185];
|
||||||
|
|
||||||
% Create ScheduleTable
|
% Create ScheduleTable
|
||||||
app.ScheduleTable = uitable(app.WellsTab);
|
app.ScheduleTable = uitable(app.WellsTab);
|
||||||
@@ -1449,25 +1450,25 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
% Create AddWell1RowButton
|
% Create AddWell1RowButton
|
||||||
app.AddWell1RowButton = uibutton(app.WellsTab, 'push');
|
app.AddWell1RowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.AddWell1RowButton.ButtonPushedFcn = createCallbackFcn(app, @AddWell1RowButtonPushed, true);
|
app.AddWell1RowButton.ButtonPushedFcn = createCallbackFcn(app, @AddWell1RowButtonPushed, true);
|
||||||
app.AddWell1RowButton.Position = [136 420 100 23];
|
app.AddWell1RowButton.Position = [144 128 100 41];
|
||||||
app.AddWell1RowButton.Text = 'AddWell1Row';
|
app.AddWell1RowButton.Text = 'AddWell1Row';
|
||||||
|
|
||||||
% Create DeleteWell1RowButton
|
% Create DeleteWell1RowButton
|
||||||
app.DeleteWell1RowButton = uibutton(app.WellsTab, 'push');
|
app.DeleteWell1RowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.DeleteWell1RowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteWell1RowButtonPushed, true);
|
app.DeleteWell1RowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteWell1RowButtonPushed, true);
|
||||||
app.DeleteWell1RowButton.Position = [301 418 103 23];
|
app.DeleteWell1RowButton.Position = [309 126 103 41];
|
||||||
app.DeleteWell1RowButton.Text = 'DeleteWell1Row';
|
app.DeleteWell1RowButton.Text = 'DeleteWell1Row';
|
||||||
|
|
||||||
% Create DeleteWell2RowButton
|
% Create DeleteWell2RowButton
|
||||||
app.DeleteWell2RowButton = uibutton(app.WellsTab, 'push');
|
app.DeleteWell2RowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.DeleteWell2RowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteWell2RowButtonPushed, true);
|
app.DeleteWell2RowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteWell2RowButtonPushed, true);
|
||||||
app.DeleteWell2RowButton.Position = [931 421 103 23];
|
app.DeleteWell2RowButton.Position = [414 425 103 23];
|
||||||
app.DeleteWell2RowButton.Text = 'DeleteWell2Row';
|
app.DeleteWell2RowButton.Text = 'DeleteWell2Row';
|
||||||
|
|
||||||
% Create AddWell2RowButton
|
% Create AddWell2RowButton
|
||||||
app.AddWell2RowButton = uibutton(app.WellsTab, 'push');
|
app.AddWell2RowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.AddWell2RowButton.ButtonPushedFcn = createCallbackFcn(app, @AddWell2RowButtonPushed, true);
|
app.AddWell2RowButton.ButtonPushedFcn = createCallbackFcn(app, @AddWell2RowButtonPushed, true);
|
||||||
app.AddWell2RowButton.Position = [705 423 100 23];
|
app.AddWell2RowButton.Position = [188 427 100 23];
|
||||||
app.AddWell2RowButton.Text = 'AddWell2Row';
|
app.AddWell2RowButton.Text = 'AddWell2Row';
|
||||||
|
|
||||||
% Create AddScheduleRowButton
|
% Create AddScheduleRowButton
|
||||||
@@ -1485,13 +1486,13 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
% Create DeleteFracLocRowButton
|
% Create DeleteFracLocRowButton
|
||||||
app.DeleteFracLocRowButton = uibutton(app.WellsTab, 'push');
|
app.DeleteFracLocRowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.DeleteFracLocRowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteFracLocRowButtonPushed, true);
|
app.DeleteFracLocRowButton.ButtonPushedFcn = createCallbackFcn(app, @DeleteFracLocRowButtonPushed, true);
|
||||||
app.DeleteFracLocRowButton.Position = [288 123 117 23];
|
app.DeleteFracLocRowButton.Position = [1096 466 117 23];
|
||||||
app.DeleteFracLocRowButton.Text = 'DeleteFracLocRow';
|
app.DeleteFracLocRowButton.Text = 'DeleteFracLocRow';
|
||||||
|
|
||||||
% Create AddFracLocRowButton
|
% Create AddFracLocRowButton
|
||||||
app.AddFracLocRowButton = uibutton(app.WellsTab, 'push');
|
app.AddFracLocRowButton = uibutton(app.WellsTab, 'push');
|
||||||
app.AddFracLocRowButton.ButtonPushedFcn = createCallbackFcn(app, @AddFracLocRowButtonPushed, true);
|
app.AddFracLocRowButton.ButtonPushedFcn = createCallbackFcn(app, @AddFracLocRowButtonPushed, true);
|
||||||
app.AddFracLocRowButton.Position = [149 123 104 23];
|
app.AddFracLocRowButton.Position = [954 464 104 23];
|
||||||
app.AddFracLocRowButton.Text = 'AddFracLocRow';
|
app.AddFracLocRowButton.Text = 'AddFracLocRow';
|
||||||
|
|
||||||
% Create ScheduleTimeConfigLabel
|
% Create ScheduleTimeConfigLabel
|
||||||
@@ -1499,26 +1500,33 @@ classdef EDFM_Simulator_App < matlab.apps.AppBase
|
|||||||
app.ScheduleTimeConfigLabel.Position = [1003 331 131 30];
|
app.ScheduleTimeConfigLabel.Position = [1003 331 131 30];
|
||||||
app.ScheduleTimeConfigLabel.Text = 'Schedule Time Config';
|
app.ScheduleTimeConfigLabel.Text = 'Schedule Time Config';
|
||||||
|
|
||||||
% Create Well1Label
|
% Create ConventionalWellsLabel
|
||||||
app.Well1Label = uilabel(app.WellsTab);
|
app.ConventionalWellsLabel = uilabel(app.WellsTab);
|
||||||
app.Well1Label.Position = [41 422 35 22];
|
app.ConventionalWellsLabel.Position = [27 130 108 40];
|
||||||
app.Well1Label.Text = 'Well1';
|
app.ConventionalWellsLabel.Text = 'Conventional Wells';
|
||||||
|
|
||||||
% Create Well2Label
|
% Create FractureWellsLabel
|
||||||
app.Well2Label = uilabel(app.WellsTab);
|
app.FractureWellsLabel = uilabel(app.WellsTab);
|
||||||
app.Well2Label.Position = [560 424 35 22];
|
app.FractureWellsLabel.Position = [43 428 82 22];
|
||||||
app.Well2Label.Text = 'Well2';
|
app.FractureWellsLabel.Text = 'Fracture Wells';
|
||||||
|
|
||||||
% Create WellLocationLabel
|
|
||||||
app.WellLocationLabel = uilabel(app.WellsTab);
|
|
||||||
app.WellLocationLabel.Position = [48 125 73 22];
|
|
||||||
app.WellLocationLabel.Text = 'WellLocation';
|
|
||||||
|
|
||||||
% Create ScheduleLabel
|
% Create ScheduleLabel
|
||||||
app.ScheduleLabel = uilabel(app.WellsTab);
|
app.ScheduleLabel = uilabel(app.WellsTab);
|
||||||
app.ScheduleLabel.Position = [555 127 55 22];
|
app.ScheduleLabel.Position = [555 127 55 22];
|
||||||
app.ScheduleLabel.Text = 'Schedule';
|
app.ScheduleLabel.Text = 'Schedule';
|
||||||
|
|
||||||
|
% Create SelectedFractureWellLabel
|
||||||
|
app.SelectedFractureWellLabel = uilabel(app.WellsTab);
|
||||||
|
app.SelectedFractureWellLabel.HorizontalAlignment = 'right';
|
||||||
|
app.SelectedFractureWellLabel.Position = [723 464 126 22];
|
||||||
|
app.SelectedFractureWellLabel.Text = 'Selected Fracture Well';
|
||||||
|
|
||||||
|
% Create SelectedFracWellDropDown
|
||||||
|
app.SelectedFracWellDropDown = uidropdown(app.WellsTab);
|
||||||
|
app.SelectedFracWellDropDown.Items = {''};
|
||||||
|
app.SelectedFracWellDropDown.Position = [864 464 70 22];
|
||||||
|
app.SelectedFracWellDropDown.Value = '';
|
||||||
|
|
||||||
% Create SolverRunTab
|
% Create SolverRunTab
|
||||||
app.SolverRunTab = uitab(app.TabGroup);
|
app.SolverRunTab = uitab(app.TabGroup);
|
||||||
app.SolverRunTab.Title = 'Solver&Run';
|
app.SolverRunTab.Title = 'Solver&Run';
|
||||||
|
|||||||
@@ -48,29 +48,32 @@ switch metric
|
|||||||
case "GPR"
|
case "GPR"
|
||||||
field_name = 'qg';
|
field_name = 'qg';
|
||||||
y_label = 'Gas production rate, m^3/d';
|
y_label = 'Gas production rate, m^3/d';
|
||||||
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
|
for i = 1:n, data(i) = get_well_metric_value(wellpara, i, well_index, field_name); end
|
||||||
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
||||||
case "OPR"
|
case "OPR"
|
||||||
field_name = 'qo';
|
field_name = 'qo';
|
||||||
y_label = 'Oil production rate, m^3/d';
|
y_label = 'Oil production rate, m^3/d';
|
||||||
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
|
for i = 1:n, data(i) = get_well_metric_value(wellpara, i, well_index, field_name); end
|
||||||
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
||||||
case "WPR"
|
case "WPR"
|
||||||
field_name = 'qw';
|
field_name = 'qw';
|
||||||
y_label = 'Water production rate, m^3/d';
|
y_label = 'Water production rate, m^3/d';
|
||||||
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
|
for i = 1:n, data(i) = get_well_metric_value(wellpara, i, well_index, field_name); end
|
||||||
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
||||||
case "DPWF"
|
case "DPWF"
|
||||||
y_label = 'Pressure derivative, MPa';
|
y_label = 'Pressure derivative, MPa';
|
||||||
for i = 1:n
|
for i = 1:n
|
||||||
if i == 1
|
if i == 1
|
||||||
data(i) = (wellpara{i + 1}{1, well_index}.pwf - wellpara{i}{1, well_index}.pwf) / ...
|
data(i) = (get_well_metric_value(wellpara, i + 1, well_index, 'pwf') - ...
|
||||||
|
get_well_metric_value(wellpara, i, well_index, 'pwf')) / ...
|
||||||
(log(times(i + 1)) - log(times(i)));
|
(log(times(i + 1)) - log(times(i)));
|
||||||
elseif i == n
|
elseif i == n
|
||||||
data(i) = (wellpara{i}{1, well_index}.pwf - wellpara{i - 1}{1, well_index}.pwf) / ...
|
data(i) = (get_well_metric_value(wellpara, i, well_index, 'pwf') - ...
|
||||||
|
get_well_metric_value(wellpara, i - 1, well_index, 'pwf')) / ...
|
||||||
(log(times(i)) - log(times(i - 1)));
|
(log(times(i)) - log(times(i - 1)));
|
||||||
else
|
else
|
||||||
data(i) = (wellpara{i + 1}{1, well_index}.pwf - wellpara{i - 1}{1, well_index}.pwf) / ...
|
data(i) = (get_well_metric_value(wellpara, i + 1, well_index, 'pwf') - ...
|
||||||
|
get_well_metric_value(wellpara, i - 1, well_index, 'pwf')) / ...
|
||||||
(log(times(i + 1)) - log(times(i - 1)));
|
(log(times(i + 1)) - log(times(i - 1)));
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -79,7 +82,7 @@ switch metric
|
|||||||
otherwise
|
otherwise
|
||||||
field_name = 'pwf';
|
field_name = 'pwf';
|
||||||
y_label = 'BHP, MPa';
|
y_label = 'BHP, MPa';
|
||||||
for i = 1:n, data(i) = wellpara{i}{1, well_index}.(field_name); end
|
for i = 1:n, data(i) = get_well_metric_value(wellpara, i, well_index, field_name); end
|
||||||
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
plot(ax, times, data, 'k^-', 'LineWidth', 1.2);
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -89,6 +92,47 @@ ylabel(ax, y_label);
|
|||||||
title(ax, sprintf('Well Response: well %d, %s', well_index, metric));
|
title(ax, sprintf('Well Response: well %d, %s', well_index, metric));
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function value = get_well_metric_value(wellpara, time_index, well_index, field_name)
|
||||||
|
well_entry = get_well_entry(wellpara, time_index, well_index);
|
||||||
|
if isempty(well_entry)
|
||||||
|
value = NaN;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if ~isstruct(well_entry)
|
||||||
|
value = NaN;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if ~isfield(well_entry, field_name)
|
||||||
|
value = NaN;
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
value = well_entry.(field_name);
|
||||||
|
end
|
||||||
|
|
||||||
|
function well_entry = get_well_entry(wellpara, time_index, well_index)
|
||||||
|
time_entry = wellpara{time_index};
|
||||||
|
|
||||||
|
if iscell(time_entry)
|
||||||
|
if isempty(time_entry)
|
||||||
|
well_entry = [];
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
if well_index > numel(time_entry)
|
||||||
|
well_entry = [];
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
well_entry = time_entry{well_index};
|
||||||
|
elseif isstruct(time_entry)
|
||||||
|
if numel(time_entry) < well_index
|
||||||
|
well_entry = [];
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
well_entry = time_entry(well_index);
|
||||||
|
else
|
||||||
|
well_entry = [];
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function render_2d_layer(ax, config, results, options)
|
function render_2d_layer(ax, config, results, options)
|
||||||
r = results.r;
|
r = results.r;
|
||||||
output = results.OutputRs{get_option(options, 'time_step', numel(results.OutputRs))};
|
output = results.OutputRs{get_option(options, 'time_step', numel(results.OutputRs))};
|
||||||
|
|||||||
Reference in New Issue
Block a user