diff --git a/docs/app_quickstart.md b/docs/app_quickstart.md new file mode 100644 index 0000000..d5f16e9 --- /dev/null +++ b/docs/app_quickstart.md @@ -0,0 +1,44 @@ +# App Quickstart + +## Launch + +From MATLAB, open the app with: + +```matlab +launch_edfm_simulator_app +``` + +File: + +- `gui_support/app/launch_edfm_simulator_app.m` + +## Current Scope + +The app currently provides: + +- tab layout by Part +- template loading +- config import/export as MAT +- result import/export as MAT +- unified run entry through `run_case(config)` +- run log capture +- basic result summary and plotting + +## Current Editing Style + +For large arrays and cell data, the app currently uses MATLAB literal text areas. + +Examples: + +- numeric arrays: `[1 2 3]` +- matrices: `[1 2; 3 4]` +- cell arrays: `{'w1', 1, [1 1 1], 0.089, 0, 1}` + +This keeps the first App Designer style version flexible while the parameter schema is still being stabilized. + +## Notes + +- The flow models are mutually exclusive. +- The GUI only uses the active `flow_model` branch at runtime. +- The current result page is a first-pass implementation. +- The current app class is written in App Designer style (`matlab.apps.AppBase`) as a text `.m` class instead of a binary `.mlapp`. diff --git a/docs/case_template_summary.md b/docs/case_template_summary.md new file mode 100644 index 0000000..6d1f09e --- /dev/null +++ b/docs/case_template_summary.md @@ -0,0 +1,90 @@ +# Case Template Summary + +This file captures the first-pass differences among the 7 existing example cases. +The goal is to turn these into GUI presets later instead of keeping 7 separate hard-coded entry scripts. + +## Shared Structure + +All current example `main1.m` scripts follow the same 8-part flow: + +1. model selection +2. matrix grid definition +3. fracture input +4. discretization +5. flow model and initial state +6. well and schedule +7. solver setup +8. execution + +## Template Summary + +| Case | Folder | Grid | Grid Model | Flow Model | Fracture Wells | Schedule Phases | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | +| 1 | `算例1-组分流-不规则非均质计算域` | `20 x 10 x 1` | SP | Multi-component | 1 | 3 | Component transport, surfactant/salt injection | +| 2 | `算例2-组分流-无效网格` | `20 x 20 x 3` | SP | Multi-component | 1 | 3 | Similar to case 1, but multi-layer and invalid-grid theme | +| 3 | `算例3-单重双重离散裂缝混合模型` | `20 x 10 x 1` | DP | Multi-component | 1 | 3 | Dual-medium setup | +| 4 | `算例4-气水两相流-压力导数曲线` | `20 x 10 x 1` | SP | Gas-water | 1 | 5 | Pressure derivative style schedule with very small early timesteps | +| 5 | `算例5-油水两相流-注焖采` | `20 x 10 x 1` | SP | Oil-water | 1 | 3 | Injection-soak-production schedule | +| 6 | `算例6-油水两相流-直井注压裂水平井采` | `20 x 10 x 2` | SP | Oil-water | 1 | 1 | Contains both conventional wells and fracture well | +| 7 | `算例7-油水两相流-不同裂缝制度不同` | `20 x 10 x 1` | SP | Oil-water | 6 | 4 | Multiple fracture-well controls with different stages | + +## Key Template Differences + +### Grid + +- Most cases use `dx = 50 * ones(1, 20)`. +- Most cases use `dy = 50 * ones(1, 10)`. +- `dz` differs by case: + - case 1: 1 layer + - case 2: 3 layers + - case 3: 1 layer + - case 4: 1 layer + - case 5: 1 layer + - case 6: 2 layers + - case 7: 1 layer + +### Discretization + +- Cases 1, 2, 4, 5, 6, 7 use single-medium mode. +- Case 3 uses dual-medium mode. + +### Flow Physics + +- Cases 1, 2, 3 use multi-component flow. +- Case 4 uses gas-water flow. +- Cases 5, 6, 7 use oil-water flow. + +### Wells + +- Cases 1 to 5 mainly define fracture well completions through `welloc`. +- Case 6 includes conventional wells in `well1` plus one fracture well in `well2`. +- Case 7 splits six fracture locations into six separately controlled fracture wells. + +### Schedule + +- Cases 1, 2, 3: 3-stage schedule +- Case 4: 5-stage schedule with aggressive timestep ramping +- Case 5: 3-stage schedule +- Case 6: 1-stage schedule +- Case 7: 4-stage schedule + +## Preset Design Recommendation + +Later, each GUI preset should contain: + +- display name +- source folder +- default `config` +- supported flow model +- supported grid model +- notes for the result page + +Suggested preset IDs: + +- `case01_multicomponent_irregular` +- `case02_multicomponent_invalid_grid` +- `case03_multicomponent_dp` +- `case04_gas_water_pressure_derivative` +- `case05_oil_water_huff_n_puff` +- `case06_oil_water_vertical_inj_fractured_horizontal_prod` +- `case07_oil_water_multi_fracture_regimes` diff --git a/docs/gui_parameter_plan.md b/docs/gui_parameter_plan.md new file mode 100644 index 0000000..da1b326 --- /dev/null +++ b/docs/gui_parameter_plan.md @@ -0,0 +1,386 @@ +# GUI Parameter And Run Architecture + +## Decision + +Use MATLAB `struct` as the primary runtime and persistence format. + +Reasons: + +- App Designer works naturally with nested `struct` data. +- Existing solver code already expects MATLAB variables and structs, not object models. +- `struct` avoids unnecessary conversion while the GUI is being built. +- JSON can still be added later as an optional export/import layer. + +Recommended flow: + +1. Load a case template into `config` as a MATLAB struct. +2. Let the GUI edit `config`. +3. Save or load `config` directly as MATLAB data first. +4. Convert `config` into solver inputs through a single entry point later: `run_case(config)`. + +## Unified Config Layout + +The GUI should be built around the following top-level fields: + +```matlab +config.meta +config.model +config.grid +config.fracture +config.discretization +config.flow +config.initial +config.wells +config.schedule +config.solver +config.output +``` + +## Part Mapping + +The existing `main1.m` scripts are structurally consistent and map well to the GUI: + +1. `model`: model selection +2. `grid`: matrix grid definition +3. `fracture`: fracture input and geometry +4. `discretization`: SP/DP discretization and medium setup +5. `flow` + `initial`: flow model, PVT, relative permeability, initial state +6. `wells` + `schedule`: wells, perforations, stage schedule +7. `solver`: nonlinear and timestep controls +8. `output`: run execution and result presentation + +## Parameter Layers + +The previous inventory mixed together direct inputs and fields created during preprocessing. +The GUI should distinguish them explicitly. + +### Layer A: Editable Input Parameters + +These are the parameters the GUI should expose directly or through advanced panels. + +### Layer B: Derived Or Runtime Fields + +These are generated by preprocessing, discretization, solver assembly, or postprocessing. +They should usually be displayed or logged, but not edited directly. + +## Core Parameter Inventory + +This is the revised first-pass grouping of parameters already identified in the repository. + +### `meta` + +- `case_name` +- `case_id` +- `description` +- `source_case_folder` +- `created_from_template` +- `version` + +### `model` + +- `modelflag` +- `grid_model` +- `flow_model` + +Known values: + +- `modelflag`: currently fixed to classical EDFM in all 7 examples +- `grid_model`: `1` single-medium, `2` dual-medium +- `flow_model`: `1` gas-water, `2` oil-water, `3` multi-component + +### `grid` + +Editable inputs: + +- `dx` +- `dy` +- `dz` +- `nx` +- `ny` +- `nz` +- `NTG` + +Derived by `GridProp_pre` and later preprocessing: + +- `coordinates` +- `nodes` +- `nP` +- `nmc` +- `dxv` +- `dyv` +- `dzv` +- `vm` +- `zm` +- `xrao` +- `yrao` +- `zrao` +- `cell_mid_coords` + +Important correction: + +- `boundary` is not defined in `GridProp_pre.m`. +- In case 1 it is defined in `grid_discretization_SP_model.m`. +- This means geometric grid size and reservoir validity boundary are separate parameter groups and should not be merged. + +### `fracture` + +- `input_style` +- `input_content` +- `f` +- `fellip` +- `fractureLines` +- `fractureHeights` +- `flowBarrierFlags` +- `frac_information` +- `nf` + +Observed input modes: + +- Engineering input table +- Vector input +- 2D line input +- Reserved `.fab` import mode + +### `discretization` + +Editable inputs: + +- `grid_model` +- `boundary` +- `invalid_layer` +- `valid_grids` generation rule +- matrix permeability definition rule +- matrix porosity definition rule +- matrix reference pressure `prpor` +- matrix compressibility `cpor` +- rock density `rock_density` +- fracture permeability `Kf` +- fracture aperture `Wf` +- fracture porosity `Porf` +- fracture reference pressure `prporf` +- fracture compressibility `cporf` +- `stress_factor_fracture` +- `stress_factor_matrix` +- `stress_factor_ref_pressure` +- dual-medium only: + - `kx_matrixLayer` + - `ky_matrixLayer` + - `kz_matrixLayer` + - `pori_matrixLayer` + - `sigma` + +Observed editable rock-property expressions in current cases: + +- case 1 SP: + - polygon `boundary` + - `kx/ky/kz` as spatial functions of `cell_mid_coords` + - `pori` as spatial function of `cell_mid_coords` +- cases 2/4/6: + - constant matrix properties + - `invalid_layer` controls inactive layers +- cases 5/7: + - constant matrix properties, different values from cases 2/4/6 +- case 3 DP: + - fracture-layer permeability set + - matrix-layer permeability set + - matrix-layer porosity + - shape factor `sigma` + +Derived fields: + +- fracture-matrix discretization result +- fracture-fracture discretization result +- EDFM connection data +- operator generation inputs for `OperatorRS` +- runtime fields added onto `r`, including `rock_density`, `valid_grids`, stress-factor fields, and dual-medium derived fields + +### `flow` + +Shared categories: + +- phase density +- reference pressure +- formation volume factor +- compressibility +- viscosity +- viscosity-pressure coefficient +- capillary pressure switch +- non-Darcy coefficient +- threshold pressure gradient +- matrix relative permeability table +- fracture relative permeability table + +Additional note: + +- Many of these are hidden inside helper files such as `gas_water_flow.m`, `oil_water_flow.m`, and `multi_component_flow.m`, so they must be treated as editable physics parameters even though they are not visible in `main1.m`. + +Gas-water specific: + +- `gas_prop.VL` +- `gas_prop.PL` +- `gas_prop.Kn` +- `gas_prop.Kn_modified_factor` +- `gas_prop.beta_non_Darcy_flow` +- `density_g_sc` +- `gas_model` +- gas PVT table or generated PVT parameters + +Oil-water specific: + +- `density_o_sc` +- `oil_model` +- oil PVT table or generated PVT parameters + +Multi-component specific: + +- `Ds` +- `Db` +- adsorption table `c_ca_table` +- chemistry parameters `R`, `Vm`, `Temperature`, `chemistry_cof` +- dynamic relative permeability tables `cs_Nc`, `kr_nosurf`, `kr_surf`, `PC` +- fracture counterparts of the same tables +- `number_state_variables` + +### `initial` + +Gas-water and oil-water: + +- `P` +- `Sw` + +Multi-component: + +- `P` +- `Sw` +- `Cs` +- `Cb` + +### `wells` + +Conventional well definition: + +- `well1` +- well name +- perforation count +- perforation grid indices +- wellbore radius +- skin +- well type + +Fracture-well definition: + +- `num_fracture_wells` +- `welloc` +- `perfnum` +- `well2` + +Combined runtime well structure: + +- `Wellc` + +### `schedule` + +- `number_phases` +- `well_schedules` +- `time` +- `dtmax` +- `dtmin` + +Per-row schedule fields observed: + +- well name +- well state +- well role +- control mode +- target value 1 +- target value 2 +- optional `Cs_inj` +- optional `Cb_inj` + +### `solver` + +- `yitap` +- `yitas` +- `omega` +- `Nmax` +- `epsave` +- `epsmax` + +### `output` + +Runtime outputs observed: + +- `Times` +- `OutputRs` +- `Wellpara` +- `trun` + +Useful progress fields for the future GUI: + +- current stage index +- current simulation time +- current `dt` +- Newton iteration count +- cumulative linear solve time +- cumulative Jacobian assembly time + +## Preset Strategy + +The seven example folders should be treated as presets, not as independent long-term entry points. + +Recommended future structure: + +- `load_case_template(1)` ... `load_case_template(7)` +- each template returns a complete `config` +- GUI edits the `config` +- `run_case(config)` executes the solver + +This avoids binding the GUI to seven different hard-coded scripts. + +## Run Page Scope + +Pause and abort are explicitly deferred for now. + +Current target for the run page: + +- show active case name +- show active stage +- show current simulation time +- show current timestep +- show Newton count +- show text log +- optionally plot selected well response during run + +The existing solver already prints progress with `fprintf`, but the proper GUI solution will be to add a progress callback later. + +## File-Level Sources Confirmed So Far + +Primary input sources already confirmed: + +- `main1.m` +- `GridProp_pre.m` +- `grid_discretization_SP_model.m` +- `grid_discretization_DP_model.m` +- `preprocess_heterogeneous.m` +- `gas_water_flow.m` +- `oil_water_flow.m` +- `multi_component_flow.m` + +This matters because not all user-facing parameters live in `main1.m`. +The parameter registry must therefore be built from the full call chain, not from entry scripts only. + +## Next Implementation Steps + +1. Build a complete field-level parameter registry file from the 7 case folders. +2. Keep `create_empty_config` as the primary configuration skeleton. +3. Use `run_case(config)` as the single runtime entry point for all three flow models. +4. Build App Designer pages around model-specific parameter panels. + +## Runtime Status + +Current runtime direction: + +- `run_case(config)` is now the unified execution entry. +- `flow_model` is treated as mutually exclusive. +- GUI should only show the active model's parameter set. +- Non-active model parameters should remain hidden and unused. diff --git a/docs/parameter_registry.md b/docs/parameter_registry.md new file mode 100644 index 0000000..ed29f84 --- /dev/null +++ b/docs/parameter_registry.md @@ -0,0 +1,351 @@ +# Parameter Registry + +This registry maps the current hard-coded parameters to a unified `config` structure. +It is intentionally field-oriented so the future GUI can be built against a stable schema. + +## Conventions + +- `Config Path`: target field in the unified config +- `Source Variable`: current variable name in code +- `Source File`: current MATLAB file where the parameter is defined +- `Cases`: which example folders currently use the field +- `Editable`: whether the GUI should expose the field +- `Notes`: behavior, enum meaning, or shape + +## Meta + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `meta.case_name` | none | template-level | all | yes | Display name | +| `meta.case_id` | none | template-level | all | yes | Stable preset ID | +| `meta.description` | none | template-level | all | yes | Free text | +| `meta.source_case_folder` | folder name | template-level | all | no | Traceability | +| `meta.created_from_template` | none | template-level | all | no | Preset source | +| `meta.version` | none | config system | all | no | Schema version | + +## Model + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `model.modelflag` | `modelflag` | `main1.m` | 1-7 | yes | Current examples use `1` | +| `model.grid_model` | `grid_model` | `main1.m` | 1-7 | yes | `1` SP, `2` DP | +| `model.flow_model` | `flow_model` | `main1.m` | 1-7 | yes | `1` gas-water, `2` oil-water, `3` multi-component | + +## Grid Geometry + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `grid.dx` | `dx` | `main1.m` | 1-7 | yes | Vector | +| `grid.dy` | `dy` | `main1.m` | 1-7 | yes | Vector | +| `grid.dz` | `dz` | `main1.m` | 1-7 | yes | Vector | +| `grid.nx` | `nx` | `main1.m` | 1-7 | no | Derived from `dx` | +| `grid.ny` | `ny` | `main1.m` | 1-7 | no | Derived from `dy` | +| `grid.nz` | `nz` | `main1.m` | 1-7 | no | Derived from `dz` | +| `grid.NTG` | `NTG` | `main1.m` | 1-7 | no | Array sized `nx*ny*nz` | + +Derived fields produced by `GridProp_pre.m`: + +- `grid.coordinates` +- `grid.nodes` +- `grid.nP` +- `grid.nmc` +- `grid.dxv` +- `grid.dyv` +- `grid.dzv` +- `grid.zm` +- `grid.vm` +- `grid.xrao` +- `grid.yrao` +- `grid.zrao` +- `grid.cell_mid_coords` + +## Fracture Input + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `fracture.input_style` | `input_style` | `main1.m` | 1-7 | yes | 1 engineering, 2 vector, 3 2D lines, 4 fab reserved | +| `fracture.input_content` | `input_content` | `main1.m` | 1-7 | yes | Engineering fracture table | +| `fracture.f` | `f` | `main1.m` / helper outputs | 1-7 | advanced | Vector fracture representation | +| `fracture.fellip` | `fellip` | `main1.m` / helper outputs | 1-7 | advanced | Elliptic fractures | +| `fracture.fractureLines` | `fractureLines` | `main1.m` | 1-7 | yes | Used in mode 3 | +| `fracture.fractureHeights` | `fractureHeights` | `main1.m` | 1-7 | yes | Used in mode 3 | +| `fracture.flowBarrierFlags` | `flowBarrierFlags` | `main1.m` | 1-7 | yes | Optional | +| `fracture.frac_information` | `frac_information` | helper outputs | 1-7 | no | Generated by helper functions | +| `fracture.nf` | `nf` | `main1.m` | 1-7 | no | Derived fracture count | + +## Discretization Selection + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `discretization.mode` | `grid_model` | `main1.m` | 1-7 | yes | Mirrors `model.grid_model` | +| `discretization.use_operator` | implicit | `main1.m` | 1-7 | no | `OperatorRS` is always called currently | + +## SP Rock And Boundary Parameters + +These are critical editable parameters and were previously undercounted. + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `discretization.sp.boundary_polygon` | `boundary` | `grid_discretization_SP_model.m` | 1 | yes | Irregular domain polygon | +| `discretization.sp.invalid_layer` | `invalid_layer` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Layer indices disabled from simulation | +| `discretization.sp.valid_grids` | `valid_grids` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | no | Derived from boundary/layers | +| `discretization.sp.matrix.kx` | `kx` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Array or generated field | +| `discretization.sp.matrix.ky` | `ky` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Array or generated field | +| `discretization.sp.matrix.kz` | `kz` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Array or generated field | +| `discretization.sp.matrix.pori` | `pori` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Array or generated field | +| `discretization.sp.matrix.prpor` | `prpor` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Matrix reference pressure | +| `discretization.sp.matrix.cpor` | `cpor` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Matrix compressibility | +| `discretization.sp.matrix.rock_density` | `rock_density` / `density_rock` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Naming is inconsistent across cases | +| `discretization.sp.fracture.Kf` | `Kf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Fracture permeability | +| `discretization.sp.fracture.Wf` | `Wf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Fracture aperture | +| `discretization.sp.fracture.Porf` | `Porf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Fracture porosity | +| `discretization.sp.fracture.prporf` | `prporf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Fracture reference pressure | +| `discretization.sp.fracture.cporf` | `cporf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Fracture compressibility | +| `discretization.sp.stress.fracture_factor` | `stress_factor_fracture` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Stress sensitivity | +| `discretization.sp.stress.matrix_factor` | `stress_factor_matrix` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Stress sensitivity | +| `discretization.sp.stress.ref_pressure` | `stress_factor_ref_pressure` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Reference pressure | +| `discretization.sp.stress.Rpt` | `Rpt` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Unknown | +| `discretization.sp.stress.cf` | `cf` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Unknown | +| `discretization.sp.stress.ca` | `ca` | `grid_discretization_SP_model.m` | 1,2,4,5,6,7 | yes | Unknown | + +Case-specific notes: + +- Case 1 uses spatial functions and an irregular polygon boundary. +- Cases 2 and 4 use `invalid_layer` to deactivate the second layer when applicable. +- Cases 5 and 7 use different constant matrix properties than cases 2, 4, and 6. + +## DP Rock Parameters + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `discretization.dp.fracture_layer.kx` | `kx` | `grid_discretization_DP_model.m` | 3 | yes | Dual-medium fracture layer | +| `discretization.dp.fracture_layer.ky` | `ky` | `grid_discretization_DP_model.m` | 3 | yes | Dual-medium fracture layer | +| `discretization.dp.fracture_layer.kz` | `kz` | `grid_discretization_DP_model.m` | 3 | yes | Dual-medium fracture layer | +| `discretization.dp.fracture_layer.pori` | `pori` | `grid_discretization_DP_model.m` | 3 | no | Dual-medium fracture layer porosity | +| `discretization.dp.matrix_layer.kx` | `kx_matrixLayer` | `grid_discretization_DP_model.m` | 3 | yes | Matrix layer permeability | +| `discretization.dp.matrix_layer.ky` | `ky_matrixLayer` | `grid_discretization_DP_model.m` | 3 | yes | Matrix layer permeability | +| `discretization.dp.matrix_layer.kz` | `kz_matrixLayer` | `grid_discretization_DP_model.m` | 3 | yes | Matrix layer permeability | +| `discretization.dp.matrix_layer.pori` | `pori_matrixLayer` | `grid_discretization_DP_model.m` | 3 | yes | Matrix layer porosity | +| `discretization.dp.shape_factor` | `sigma` | `grid_discretization_DP_model.m` | 3 | yes | Interporosity shape factor | +| `discretization.dp.valid_grids` | `valid_grids` | `grid_discretization_DP_model.m` | 3 | no | All ones in current example | +| `discretization.dp.NTG` | `NTG` | `grid_discretization_DP_model.m` | 3 | yes | Reassigned in file | +| `discretization.dp.prpor` | `prpor` | `grid_discretization_DP_model.m` | 3 | yes | Reference pressure | +| `discretization.dp.cpor` | `cpor` | `grid_discretization_DP_model.m` | 3 | yes | Compressibility | +| `discretization.dp.fracture.Kf` | `Kf` | `grid_discretization_DP_model.m` | 3 | yes | Fracture permeability | +| `discretization.dp.fracture.Wf` | `Wf` | `grid_discretization_DP_model.m` | 3 | yes | Fracture aperture | +| `discretization.dp.fracture.Porf` | `Porf` | `grid_discretization_DP_model.m` | 3 | yes | Fracture porosity | +| `discretization.dp.fracture.prporf` | `prporf` | `grid_discretization_DP_model.m` | 3 | yes | Fracture reference pressure | +| `discretization.dp.fracture.cporf` | `cporf` | `grid_discretization_DP_model.m` | 3 | yes | Fracture compressibility | +| `discretization.dp.stress.fracture_factor` | `stress_factor_fracture` | `grid_discretization_DP_model.m` | 3 | yes | Stress sensitivity | +| `discretization.dp.stress.matrix_factor` | `stress_factor_matrix` | `grid_discretization_DP_model.m` | 3 | yes | Stress sensitivity | +| `discretization.dp.stress.ref_pressure` | `stress_factor_ref_pressure` | `grid_discretization_DP_model.m` | 3 | yes | Reference pressure | +| `discretization.dp.rock_density` | `rock_density` | `grid_discretization_DP_model.m` | 3 | yes | Rock density | +| `discretization.dp.Rpt` | `Rpt` | `grid_discretization_DP_model.m` | 3 | yes | Unknown | +| `discretization.dp.cf` | `cf` | `grid_discretization_DP_model.m` | 3 | yes | Unknown | +| `discretization.dp.ca` | `ca` | `grid_discretization_DP_model.m` | 3 | yes | Unknown | + +## Flow: Gas-Water + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `flow.gas_water.gas_prop.VL` | `gas_prop.VL` | `gas_water_flow.m` | 4 | yes | Langmuir volume | +| `flow.gas_water.gas_prop.PL` | `gas_prop.PL` | `gas_water_flow.m` | 4 | yes | Langmuir pressure | +| `flow.gas_water.gas_prop.Kn` | `gas_prop.Kn` | `gas_water_flow.m` | 4 | yes | Knudsen number | +| `flow.gas_water.gas_prop.Kn_modified_factor` | `gas_prop.Kn_modified_factor` | `gas_water_flow.m` | 4 | no | Derived | +| `flow.gas_water.gas_prop.beta_non_darcy_flow` | `gas_prop.beta_non_Darcy_flow` | `gas_water_flow.m` | 4 | yes | High-velocity term | +| `flow.gas_water.p_grad_threshold` | `p_grad_threshold` | `gas_water_flow.m` | 4 | yes | Threshold pressure gradient | +| `flow.gas_water.density_g_sc` | `density_g_sc` | `gas_water_flow.m` | 4 | yes | Gas density at standard conditions | +| `flow.gas_water.density_w_sc` | `density_w_sc` | `gas_water_flow.m` | 4 | yes | Water density at standard conditions | +| `flow.gas_water.gas_model` | `gas_model` | `gas_water_flow.m` | 4 | yes | 1 generated PVT, 2 tabulated PVT | +| `flow.gas_water.prg` | `prg` | `gas_water_flow.m` | 4 | yes | Only in model 1 | +| `flow.gas_water.Bgi` | `Bgi` | `gas_water_flow.m` | 4 | yes | Only in model 1 | +| `flow.gas_water.cg` | `cg` | `gas_water_flow.m` | 4 | yes | Only in model 1 | +| `flow.gas_water.vgi` | `vgi` | `gas_water_flow.m` | 4 | yes | Only in model 1 | +| `flow.gas_water.cvg` | `cvg` | `gas_water_flow.m` | 4 | yes | Only in model 1 | +| `flow.gas_water.Ppr` | `Ppr` | `gas_water_flow.m` | 4 | yes | Table or generated | +| `flow.gas_water.BG` | `BG` | `gas_water_flow.m` | 4 | yes | Table or generated | +| `flow.gas_water.MUG` | `MUG` | `gas_water_flow.m` | 4 | yes | Table or generated | +| `flow.gas_water.prw` | `prw` | `gas_water_flow.m` | 4 | yes | Water reference pressure | +| `flow.gas_water.Bwi` | `Bwi` | `gas_water_flow.m` | 4 | yes | Water volume factor | +| `flow.gas_water.cw` | `cw` | `gas_water_flow.m` | 4 | yes | Water compressibility | +| `flow.gas_water.vwi` | `vwi` | `gas_water_flow.m` | 4 | yes | Water viscosity | +| `flow.gas_water.cvw` | `cvw` | `gas_water_flow.m` | 4 | yes | Water viscosity-pressure coefficient | +| `flow.gas_water.ifpcgl` | `ifpcgl` | `gas_water_flow.m` | 4 | yes | Capillary pressure switch | +| `flow.gas_water.matrix_relperm_table` | `RPGW` | `gas_water_flow.m` | 4 | yes | Matrix table | +| `flow.gas_water.fracture_relperm_table` | `PRF` | `gas_water_flow.m` | 4 | yes | Fracture table | + +## Flow: Oil-Water + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `flow.oil_water.density_o_sc` | `density_o_sc` | `oil_water_flow.m` | 5,6,7 | yes | Oil density | +| `flow.oil_water.density_w_sc` | `density_w_sc` | `oil_water_flow.m` | 5,6,7 | yes | Water density | +| `flow.oil_water.oil_model` | `oil_model` | `oil_water_flow.m` | 5,6,7 | yes | 1 generated PVT, 2 tabulated | +| `flow.oil_water.pro` | `pro` | `oil_water_flow.m` | 5,6,7 | yes | Oil ref pressure | +| `flow.oil_water.Boi` | `Boi` | `oil_water_flow.m` | 5,6,7 | yes | Oil volume factor | +| `flow.oil_water.co` | `co` | `oil_water_flow.m` | 5,6,7 | yes | Oil compressibility | +| `flow.oil_water.voi` | `voi` | `oil_water_flow.m` | 5,6,7 | yes | Oil viscosity | +| `flow.oil_water.cvo` | `cvo` | `oil_water_flow.m` | 5,6,7 | yes | Oil viscosity-pressure coefficient | +| `flow.oil_water.Ppr` | `Ppr` | `oil_water_flow.m` | 5,6,7 | yes | Table or generated | +| `flow.oil_water.BO` | `BO` | `oil_water_flow.m` | 5,6,7 | yes | Table or generated | +| `flow.oil_water.MUO` | `MUO` | `oil_water_flow.m` | 5,6,7 | yes | Table or generated | +| `flow.oil_water.prw` | `prw` | `oil_water_flow.m` | 5,6,7 | yes | Water ref pressure | +| `flow.oil_water.Bwi` | `Bwi` | `oil_water_flow.m` | 5,6,7 | yes | Water volume factor | +| `flow.oil_water.cw` | `cw` | `oil_water_flow.m` | 5,6,7 | yes | Water compressibility | +| `flow.oil_water.vwi` | `vwi` | `oil_water_flow.m` | 5,6,7 | yes | Water viscosity | +| `flow.oil_water.cvw` | `cvw` | `oil_water_flow.m` | 5,6,7 | yes | Water viscosity-pressure coefficient | +| `flow.oil_water.ifpcow` | `ifpcow` | `oil_water_flow.m` | 5,6,7 | yes | Capillary pressure switch | +| `flow.oil_water.matrix_relperm_table` | `PRM` | `oil_water_flow.m` | 5,6,7 | yes | Matrix table | +| `flow.oil_water.fracture_relperm_table` | `PRF` | `oil_water_flow.m` | 5,6,7 | yes | Fracture table | +| `flow.oil_water.beta_non_darcy_flow` | `beta_non_Darcy_flow` | `oil_water_flow.m` | 5,6,7 | yes | Non-Darcy coefficient | +| `flow.oil_water.p_grad_threshold` | `p_grad_threshold` | `oil_water_flow.m` | 5,6,7 | yes | Threshold gradient | + +## Flow: Multi-Component + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `flow.multi_component.Ds` | `Ds` | `multi_component_flow.m` | 1,2,3 | yes | Surfactant diffusion | +| `flow.multi_component.Db` | `Db` | `multi_component_flow.m` | 1,2,3 | yes | Salt diffusion | +| `flow.multi_component.c_ca_table` | `c_ca_table` | `multi_component_flow.m` | 1,2,3 | yes | Adsorption table | +| `flow.multi_component.cs_data` | `cs_data` | `multi_component_flow.m` | 1,2,3 | no | Derived from table | +| `flow.multi_component.cb_data` | `cb_data` | `multi_component_flow.m` | 1,2,3 | no | Derived from table | +| `flow.multi_component.csa_data` | `csa_data` | `multi_component_flow.m` | 1,2,3 | no | Derived from table | +| `flow.multi_component.cba_data` | `cba_data` | `multi_component_flow.m` | 1,2,3 | no | Derived from table | +| `flow.multi_component.R` | `R` | `multi_component_flow.m` | 1,2,3 | yes | Chemistry constant | +| `flow.multi_component.Vm` | `Vm` | `multi_component_flow.m` | 1,2,3 | yes | Molar volume | +| `flow.multi_component.Temperature` | `Temperature` | `multi_component_flow.m` | 1,2,3 | yes | Temperature | +| `flow.multi_component.chemistry_cof` | `chemistry_cof` | `multi_component_flow.m` | 1,2,3 | no | Derived | +| `flow.multi_component.x_matrix!!!!` | `xm` | `multi_component_flow.m` | 1,2,3 | yes | Chemistry activity factor for matrix cells | +| `flow.multi_component.x_fracture!!!` | `xf` | `multi_component_flow.m` | 1,2,3 | yes | Chemistry activity factor for fracture cells | +| `flow.multi_component.cs_Nc` | `cs_Nc` | `multi_component_flow.m` | 1,2,3 | yes | Dynamic relative permeability mapping | +| `flow.multi_component.Nc_nosurf` | `Nc_nosurf` | `multi_component_flow.m` | 1,2,3 | no | Derived | +| `flow.multi_component.Nc_surf` | `Nc_surf` | `multi_component_flow.m` | 1,2,3 | no | Derived | +| `flow.multi_component.kr_nosurf` | `kr_nosurf` | `multi_component_flow.m` | 1,2,3 | yes | Relative permeability table | +| `flow.multi_component.kr_surf` | `kr_surf` | `multi_component_flow.m` | 1,2,3 | yes | Relative permeability table | +| `flow.multi_component.PC` | `PC` | `multi_component_flow.m` | 1,2,3 | yes | Capillary pressure table | +| `flow.multi_component.cs_Nc_fracture` | `cs_Nc_fracture` | `multi_component_flow.m` | 1,2,3 | yes | Fracture table | +| `flow.multi_component.kr_nosurf_fracture` | `kr_nosurf_fracture` | `multi_component_flow.m` | 1,2,3 | yes | Fracture table | +| `flow.multi_component.kr_surf_fracture` | `kr_surf_fracture` | `multi_component_flow.m` | 1,2,3 | yes | Fracture table | +| `flow.multi_component.PC_fracture` | `PC_fracture` | `multi_component_flow.m` | 1,2,3 | yes | Fracture capillary table | +| `flow.multi_component.ifpcgl` | `ifpcgl` | `multi_component_flow.m` | 1,2,3 | yes | Capillary pressure switch | +| `flow.multi_component.p_grad_threshold` | `p_grad_threshold` | `multi_component_flow.m` | 1,2,3 | yes | Threshold gradient | +| `flow.multi_component.gas_prop.VL` | `gas_prop.VL` | `multi_component_flow.m` | 1,2,3 | yes | Often zero for oil-like use | +| `flow.multi_component.gas_prop.PL` | `gas_prop.PL` | `multi_component_flow.m` | 1,2,3 | yes | Langmuir pressure | +| `flow.multi_component.gas_prop.Kn` | `gas_prop.Kn` | `multi_component_flow.m` | 1,2,3 | yes | Knudsen number | +| `flow.multi_component.gas_prop.Kn_modified_factor` | `gas_prop.Kn_modified_factor` | `multi_component_flow.m` | 1,2,3 | no | Derived | +| `flow.multi_component.gas_prop.stress_factor_fracture` | `gas_prop.stress_factor_fracture` | `multi_component_flow.m` | 1,2,3 | yes | Present in case-specific flow files | +| `flow.multi_component.gas_prop.stress_factor_matrix` | `gas_prop.stress_factor_matrix` | `multi_component_flow.m` | 1,2,3 | yes | Present in case-specific flow files | +| `flow.multi_component.gas_prop.stress_factor_ref_pressure` | `gas_prop.stress_factor_ref_pressure` | `multi_component_flow.m` | 1,2,3 | yes | Present in case-specific flow files | +| `flow.multi_component.gas_prop.beta_non_darcy_flow` | `gas_prop.beta_non_Darcy_flow` | `multi_component_flow.m` | 1,2,3 | yes | Non-Darcy coefficient | +| `flow.multi_component.density_g_sc` | `density_g_sc` | `multi_component_flow.m` | 1,2,3 | yes | Standard density | +| `flow.multi_component.gas_model` | `gas_model` | `multi_component_flow.m` | 1,2,3 | yes | 1 generated, 2 tabulated | +| `flow.multi_component.prg` | `prg` | `multi_component_flow.m` | 1,2,3 | yes | Ref pressure | +| `flow.multi_component.Bgi` | `Bgi` | `multi_component_flow.m` | 1,2,3 | yes | Volume factor | +| `flow.multi_component.cg` | `cg` | `multi_component_flow.m` | 1,2,3 | yes | Compressibility | +| `flow.multi_component.vgi` | `vgi` | `multi_component_flow.m` | 1,2,3 | yes | Viscosity | +| `flow.multi_component.cvg` | `cvg` | `multi_component_flow.m` | 1,2,3 | yes | Viscosity-pressure coefficient | +| `flow.multi_component.Ppr` | `Ppr` | `multi_component_flow.m` | 1,2,3 | yes | Table or generated | +| `flow.multi_component.BG` | `BG` | `multi_component_flow.m` | 1,2,3 | yes | Table or generated | +| `flow.multi_component.MUG` | `MUG` | `multi_component_flow.m` | 1,2,3 | yes | Table or generated | +| `flow.multi_component.density_w_sc` | `density_w_sc` | `multi_component_flow.m` | 1,2,3 | yes | Water density | +| `flow.multi_component.prw` | `prw` | `multi_component_flow.m` | 1,2,3 | yes | Water ref pressure | +| `flow.multi_component.Bwi` | `Bwi` | `multi_component_flow.m` | 1,2,3 | yes | Water volume factor | +| `flow.multi_component.cw` | `cw` | `multi_component_flow.m` | 1,2,3 | yes | Water compressibility | +| `flow.multi_component.vwi` | `vwi` | `multi_component_flow.m` | 1,2,3 | yes | Water viscosity | +| `flow.multi_component.cvw` | `cvw` | `multi_component_flow.m` | 1,2,3 | yes | Water viscosity-pressure coefficient | +| `flow.multi_component.number_state_variables` | `number_state_variables` | `multi_component_flow.m` | 1,2,3 | no | Fixed as 4 in current code | + +## Initial Conditions + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `initial.pressure` | `P` | flow files | 1-7 | yes | Initial pressure array | +| `initial.sw` | `Sw` | flow files | 1-7 | yes | Initial water saturation array | +| `initial.cs` | `Cs` | `multi_component_flow.m` | 1,2,3 | yes | Initial surfactant concentration | +| `initial.cb` | `Cb` | `multi_component_flow.m` | 1,2,3 | yes | Initial salt concentration | + +## Wells + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `wells.well1` | `well1` | `main1.m` | 1-7 | yes | Conventional wells table | +| `wells.num_fracture_wells` | `num_fracture_wells` | `main1.m` | 1-7 | yes | Count | +| `wells.welloc` | `welloc` | `main1.m` | 1-7 | yes | Fracture well coordinates | +| `wells.perfnum` | `perfnum` | helper output | 1-7 | no | Derived from `findWelloc` | +| `wells.well2` | `well2` | `main1.m` | 1-7 | yes | Fracture-well table | +| `wells.Wellc` | `Wellc` | helper output | 1-7 | no | Combined runtime structure | + +Conventional well row shape: + +- well name +- perforation count +- perforation grid index matrix +- well radius +- skin +- well type + +Fracture-well row shape: + +- well name +- perforation count +- perforation indices +- well radius +- skin +- well type + +## Schedule + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `schedule.number_phases` | `number_phases` | `main1.m` | 1-7 | yes | Stage count | +| `schedule.well_schedules` | `well_schedules` | `main1.m` | 1-7 | yes | Per-stage control rows | +| `schedule.time` | `time` | `main1.m` | 1-7 | yes | Stage duration vector | +| `schedule.dtmax` | `dtmax` | `main1.m` | 1-7 | yes | Max timestep per stage | +| `schedule.dtmin` | `dtmin` | `main1.m` | 1-7 | yes | Min timestep per stage | + +Schedule row fields observed: + +- well name +- well state +- well role +- control mode +- target value 1 +- target value 2 +- optional `Cs_inj` +- optional `Cb_inj` + +## Solver + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `solver.yitap` | `yitap` | `main1.m` | 1-7 | yes | Adaptive timestep control | +| `solver.yitas` | `yitas` | `main1.m` | 1-7 | yes | Adaptive timestep control | +| `solver.omega` | `omega` | `main1.m` | 1-7 | yes | Adaptive timestep control | +| `solver.Nmax` | `Nmax` | `main1.m` | 1-7 | yes | Max Newton iterations | +| `solver.epsave` | `epsave` | `main1.m` | 1-7 | yes | Average residual tolerance | +| `solver.epsmax` | `epsmax` | `main1.m` | 1-7 | yes | Max residual tolerance | + +## Runtime / Output + +| Config Path | Source Variable | Source File | Cases | Editable | Notes | +| --- | --- | --- | --- | --- | --- | +| `output.result_name` | none | future GUI | all | yes | Result label | +| `output.save_input_path` | none | future GUI | all | yes | Save location | +| `output.save_result_path` | none | future GUI | all | yes | Save location | +| `output.plot_requests` | none | future GUI | all | yes | Requested result plots | + +Runtime-only outputs: + +- `Times` +- `OutputRs` +- `Wellpara` +- `trun` + +## Known Inconsistencies To Normalize + +- `rock_density` and `density_rock` are both used in SP discretization files. +- Some fields are editable expressions in one case and constant arrays in another, especially `kx`, `ky`, `kz`, and `pori`. +- `boundary` exists in case 1 but not in the other SP examples. +- DP mode introduces extra fields absent from SP mode. + +These inconsistencies are exactly why the GUI must be built on a normalized `config` instead of the raw scripts. diff --git a/docs/project_status_overview.md b/docs/project_status_overview.md new file mode 100644 index 0000000..d76ee15 --- /dev/null +++ b/docs/project_status_overview.md @@ -0,0 +1,369 @@ +# EDFM GUI Project Status Overview + +## Project Goal + +Build a MATLAB GUI for the existing EDFM simulator so that users can: + +- edit simulation parameters through a graphical interface +- load and save input configurations +- run the solver from the GUI +- view run status and basic results +- import and export results +- eventually call existing plotting functions from the result page + +The current solver code already exists. +The main work is: + +1. extracting and organizing parameters from the existing case scripts and helper files +2. building a GUI around a unified configuration format and runtime entry point + +## Functional Scope Agreed So Far + +### GUI Structure + +The GUI should be organized by the `Part1` to `Part8` logic already present in the case `main1.m` files. + +Recommended page split: + +- `Part1 模型` +- `Part2 网格` +- `Part3 裂缝` +- `Part4 离散化/岩性` +- `Part5 流动模型` +- `Part6 井与制度` +- `Part7 求解器` +- `结果` + +There is also a top toolbar for: + +- template loading +- config import/export +- run +- result import/export + +### Data Management + +The main editable runtime format is now defined as MATLAB `struct`. + +Current persistence direction: + +- config import/export: `.mat` +- result import/export: `.mat` + +JSON is not the current priority. +It can be added later if needed. + +### Runtime Model Handling + +The three flow models are mutually exclusive: + +- `1`: gas-water +- `2`: oil-water +- `3`: multi-component + +The GUI should only show the active flow model's parameters. +Inactive model parameters should not be shown or used during runtime. + +## What Has Been Completed + +## 1. Parameter extraction and organization + +The repository has been reviewed across: + +- all 7 example case folders +- `main1.m` +- `GridProp_pre.m` +- `grid_discretization_SP_model.m` +- `grid_discretization_DP_model.m` +- `gas_water_flow.m` +- `oil_water_flow.m` +- `multi_component_flow.m` +- solver entry and main runtime functions + +Completed documents: + +- [`gui_parameter_plan.md`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\docs\gui_parameter_plan.md) +- [`case_template_summary.md`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\docs\case_template_summary.md) +- [`parameter_registry.md`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\docs\parameter_registry.md) + +These already cover: + +- overall GUI architecture +- 7-case summary +- field-level parameter registry +- separation of editable parameters and derived runtime fields + +## 2. Unified config skeleton + +Created: + +- [`create_empty_config.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\config\create_empty_config.m) + +Current config structure includes: + +- `meta` +- `model` +- `grid` +- `fracture` +- `discretization` +- `flow` +- `initial` +- `wells` +- `schedule` +- `solver` +- `output` + +It now includes all major editable parameter groups identified so far. + +## 3. Config and result MAT import/export helpers + +Created: + +- [`load_config_mat.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\config\load_config_mat.m) +- [`save_config_mat.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\config\save_config_mat.m) +- [`load_results_mat.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\results\load_results_mat.m) +- [`save_results_mat.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\results\save_results_mat.m) + +## 4. Unified runtime entry point + +Created: + +- [`run_case.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\runtime\run_case.m) + +This is now the intended runtime entry point instead of calling each case's `main1.m` directly. + +Current status: + +- grid preprocessing is routed through unified config +- fracture inputs are routed through unified config +- well and schedule setup are routed through unified config +- solver invocation is routed through unified config +- all three `flow_model` branches are now supported through the unified runtime path + +## 5. First template support + +Created: + +- [`load_case_template.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\templates\load_case_template.m) +- [`load_case_template_case01.m`](C:\Users\Administrator\Videos\3D_EDFM_simulator_20260305\gui_support\templates\load_case_template_case01.m) + +Current template coverage: + +- `case01` + +This is enough to validate the unified config and runtime direction, but not yet enough for final GUI delivery. + +## 6. App Designer direction clarified + +Important conclusion: + +- a pure `.m` app class is not the right final form for this project if the user wants to edit the GUI in MATLAB App Designer +- the final GUI should be built as a real `.mlapp` +- the current recommended workflow is: + - manually draw the layout in App Designer + - keep stable component names + - then bind code to those components + +## What Still Needs To Be Done + +## 1. Draw the real `.mlapp` interface + +This is the next main task. + +Need to complete: + +- top toolbar +- all main tabs +- model-specific sub-tabs or dynamic sections +- all editable controls +- result page layout + +The drawing guide has been prepared conceptually, but the actual `.mlapp` still needs to be built manually in App Designer. + +## 2. Bind `.mlapp` code to existing backend + +After the UI is drawn, code still needs to be connected for: + +- startup default config loading +- template switching +- config import/export +- result import/export +- reading values from controls into `config` +- writing `config` back into controls +- calling `run_case(config)` +- refreshing result plots + +## 3. Expand template coverage + +Currently only `case01` has a template loader. + +Still needed: + +- `case02` +- `case03` +- `case04` +- `case05` +- `case06` +- `case07` + +These should eventually all become presets in the GUI. + +## 4. Strengthen flow-model config coverage + +The unified runtime path now supports all three model branches, but more practical work is still needed: + +- make sure default template values are complete for each model +- validate that GUI fields map cleanly to backend config fields +- ensure inactive model fields do not interfere with execution + +## 5. Improve editing experience for complex parameters + +Right now, many large arrays or nested cell structures are still best represented as text expressions. +This is acceptable for early integration but not ideal for end users. + +Likely future upgrades: + +- use `UITable` for well definitions +- use `UITable` for schedules +- use `UITable` for fracture engineering input +- use dedicated table editors for relperm/PVT/adsorption data + +## 6. Results page enhancement + +The final result page should support: + +- summary text +- well response curves +- Newton-vs-time or runtime diagnostics +- 2D and 3D plots +- selected existing post-processing functions + +This is only partially addressed so far. + +## Hard Parts + +## 1. Parameters are not only in `main1.m` + +This is the biggest structural difficulty. + +Important parameters are scattered across: + +- case `main1.m` +- grid preprocessing files +- SP/DP discretization files +- physics setup files +- solver-related helper files + +This makes GUI extraction error-prone if only entry scripts are inspected. + +## 2. Same function names appear in multiple case folders + +Several case folders contain same-named files such as: + +- `grid_discretization_SP_model.m` +- `multi_component_flow.m` + +This means runtime path resolution can break if the working directory or MATLAB path is not controlled carefully. + +The current backend addresses this by resolving case folders explicitly before running. + +## 3. MATLAB App Designer is not Qt-style UI-file driven + +Unlike Qt `.ui`, MATLAB `.mlapp` is not a simple open text UI definition format. +This means: + +- generating App Designer GUI purely by external text editing is unreliable +- manual drawing inside App Designer is the safer workflow + +## 4. Complex data entry is awkward in basic controls + +Several parameters are naturally tabular or nested: + +- fracture engineering input +- well definitions +- well schedules +- relperm tables +- adsorption tables + +These need careful component choice. +If handled with only `TextArea`, the app will work but will not be user-friendly. + +## 5. Encoding and Chinese path issues + +Some files and folders contain Chinese names. +Some comments also appear to have encoding issues. + +Risks: + +- MATLAB string/path handling may be fine, but external tooling and generated code may display garbage text +- hardcoding such paths in generated code is risky + +ASCII-safe identifiers and runtime folder resolution are preferred where possible. + +## Easy-To-Break Areas + +## 1. Component naming drift + +If the `.mlapp` component names are changed casually after backend code is written, callbacks and data binding will break quickly. + +Recommendation: + +- finalize component names early +- keep names stable + +## 2. Array/cell parsing from UI text + +If text areas are used for MATLAB expressions, malformed input can break parsing. + +Examples: + +- missing brackets +- malformed cell arrays +- inconsistent row widths + +Recommendation: + +- use text areas only as an intermediate step +- replace key inputs with `UITable` later + +## 3. Flow-model mutual exclusivity + +Gas-water, oil-water, and multi-component are mutually exclusive. +If the GUI keeps unrelated fields visible and active, users may edit values that are silently ignored or accidentally used. + +Recommendation: + +- show only the active model section +- hide or disable the others + +## 4. Schedule and well table consistency + +The solver expects consistent relations among: + +- `well1` +- `welloc` +- `well2` +- `well_schedules` +- `time` +- `dtmin` +- `dtmax` + +If the user edits one without updating the others, runtime errors are likely. + +## 5. Case-specific discretization assumptions + +Case 1 includes irregular boundary polygon logic. +Case 3 uses DP-specific fields. +Other cases use different rock defaults. + +If the GUI over-generalizes too early, it may hide case-specific assumptions that are required for correct runs. + +## Recommended Next Step + +The highest-value next step is: + +1. manually build the `.mlapp` layout in App Designer +2. keep component names fixed +3. then connect each page to the existing backend functions and config structure + +That is the safest path to a maintainable App Designer application. diff --git a/gui_support/app/EDFMSimulatorApp.m b/gui_support/app/EDFMSimulatorApp.m new file mode 100644 index 0000000..6eadafa --- /dev/null +++ b/gui_support/app/EDFMSimulatorApp.m @@ -0,0 +1,332 @@ +classdef EDFMSimulatorApp < matlab.apps.AppBase + properties (Access = public) + UIFigure matlab.ui.Figure + end + + properties (Access = private) + UI struct + Config struct + Results struct + end + + methods (Access = private) + function startup(app) + app.Config = load_case_template('case01'); + app.Results = struct(); + app.refreshUIFromConfig(); + app.appendLog('Loaded default template: case01'); + app.updateStatus('Ready'); + end + + function createComponents(app) + app.UIFigure = uifigure('Visible', 'off', 'Name', 'EDFM Simulator App'); + app.UIFigure.Position = [100 100 1500 900]; + + root = uigridlayout(app.UIFigure, [2 1]); + root.RowHeight = {44, '1x'}; + + top = uigridlayout(root, [1 9]); + top.ColumnWidth = {140, 110, 110, 110, 90, 110, 110, '1x', 180}; + app.UI.TemplateDropDown = uidropdown(top, 'Items', {'case01'}, 'Value', 'case01'); + app.UI.TemplateDropDown.Layout.Column = 1; + app.UI.LoadTemplateButton = uibutton(top, 'push', 'Text', 'Load Template', ... + 'ButtonPushedFcn', @(~,~) app.onLoadTemplate()); + app.UI.LoadTemplateButton.Layout.Column = 2; + app.UI.ImportConfigButton = uibutton(top, 'push', 'Text', 'Import Config', ... + 'ButtonPushedFcn', @(~,~) app.onImportConfig()); + app.UI.ImportConfigButton.Layout.Column = 3; + app.UI.ExportConfigButton = uibutton(top, 'push', 'Text', 'Export Config', ... + 'ButtonPushedFcn', @(~,~) app.onExportConfig()); + app.UI.ExportConfigButton.Layout.Column = 4; + app.UI.RunButton = uibutton(top, 'push', 'Text', 'Run', ... + 'ButtonPushedFcn', @(~,~) app.onRun()); + app.UI.RunButton.Layout.Column = 5; + app.UI.ImportResultButton = uibutton(top, 'push', 'Text', 'Import Result', ... + 'ButtonPushedFcn', @(~,~) app.onImportResult()); + app.UI.ImportResultButton.Layout.Column = 6; + app.UI.ExportResultButton = uibutton(top, 'push', 'Text', 'Export Result', ... + 'ButtonPushedFcn', @(~,~) app.onExportResult()); + app.UI.ExportResultButton.Layout.Column = 7; + app.UI.StatusLabel = uilabel(top, 'Text', 'Status: Ready', 'HorizontalAlignment', 'right'); + app.UI.StatusLabel.Layout.Column = 9; + + tabs = uitabgroup(root); + tabs.Layout.Row = 2; + app.UI.Tabs = tabs; + app.UI.Part1Tab = uitab(tabs, 'Title', 'Part1 Model'); + app.UI.Part2Tab = uitab(tabs, 'Title', 'Part2 Grid'); + app.UI.Part3Tab = uitab(tabs, 'Title', 'Part3 Fracture'); + app.UI.Part4Tab = uitab(tabs, 'Title', 'Part4 Discretization'); + app.UI.Part5Tab = uitab(tabs, 'Title', 'Part5 Flow'); + app.UI.Part6Tab = uitab(tabs, 'Title', 'Part6 Wells'); + app.UI.Part7Tab = uitab(tabs, 'Title', 'Part7 Solver'); + app.UI.RunTab = uitab(tabs, 'Title', 'Run'); + app.UI.ResultTab = uitab(tabs, 'Title', 'Results'); + + app.buildPart1(); + app.buildPart2(); + app.buildPart3(); + app.buildPart4(); + app.buildPart5(); + app.buildPart6(); + app.buildPart7(); + app.buildRunTab(); + app.buildResultTab(); + + app.UIFigure.Visible = 'on'; + end + + function buildPart1(app) + g = uigridlayout(app.UI.Part1Tab, [4 2]); g.RowHeight = {30,30,30,'1x'}; g.ColumnWidth = {160,'1x'}; + uilabel(g, 'Text', 'Model Flag'); app.UI.ModelFlag = uidropdown(g, 'Items', {'1'}); app.UI.ModelFlag.Layout.Column = 2; + lbl = uilabel(g, 'Text', 'Grid Model'); lbl.Layout.Row = 2; lbl.Layout.Column = 1; + app.UI.GridModel = uidropdown(g, 'Items', {'1','2'}); app.UI.GridModel.Layout.Row = 2; app.UI.GridModel.Layout.Column = 2; + lbl = uilabel(g, 'Text', 'Flow Model'); lbl.Layout.Row = 3; lbl.Layout.Column = 1; + app.UI.FlowModel = uidropdown(g, 'Items', {'1','2','3'}); app.UI.FlowModel.Layout.Row = 3; app.UI.FlowModel.Layout.Column = 2; + end + + function buildPart2(app) + g = uigridlayout(app.UI.Part2Tab, [4 2]); g.ColumnWidth = {130,'1x'}; g.RowHeight = {100,100,100,'1x'}; + app.createAreaField(g, 1, 'dx', 'Dx'); + app.createAreaField(g, 2, 'dy', 'Dy'); + app.createAreaField(g, 3, 'dz', 'Dz'); + app.createAreaField(g, 4, 'ntg', 'NTG'); + end + + function buildPart3(app) + g = uigridlayout(app.UI.Part3Tab, [5 2]); g.ColumnWidth = {160,'1x'}; g.RowHeight = {30,120,120,120,80}; + uilabel(g, 'Text', 'Input Style'); app.UI.InputStyle = uidropdown(g, 'Items', {'1','2','3','4'}); app.UI.InputStyle.Layout.Column = 2; + app.createAreaField(g, 2, 'fractureInput', 'Input Content'); + app.createAreaField(g, 3, 'fractureLines', 'Fracture Lines'); + app.createAreaField(g, 4, 'fractureHeights', 'Fracture Heights'); + app.createAreaField(g, 5, 'flowBarrier', 'Flow Barrier Flags'); + end + + function buildPart4(app) + g = uigridlayout(app.UI.Part4Tab, [7 2]); g.ColumnWidth = {180,'1x'}; g.RowHeight = {90,60,70,70,70,30,'1x'}; + app.createAreaField(g, 1, 'boundary', 'Boundary Polygon'); + app.createAreaField(g, 2, 'invalidLayer', 'Invalid Layer'); + app.createAreaField(g, 3, 'matrixKx', 'Matrix kx'); + app.createAreaField(g, 4, 'matrixKy', 'Matrix ky'); + app.createAreaField(g, 5, 'matrixKz', 'Matrix kz'); + app.createAreaField(g, 6, 'matrixPori', 'Matrix pori'); + panel = uipanel(g); panel.Layout.Row = 7; panel.Layout.Column = 2; sub = uigridlayout(panel, [1 6]); + uilabel(sub, 'Text', 'prpor'); app.UI.MatrixPrpor = uieditfield(sub, 'numeric'); + uilabel(sub, 'Text', 'cpor'); app.UI.MatrixCpor = uieditfield(sub, 'numeric'); + uilabel(sub, 'Text', 'rho'); app.UI.RockDensity = uieditfield(sub, 'numeric'); + end + + function buildPart5(app) + g = uigridlayout(app.UI.Part5Tab, [3 1]); g.RowHeight = {100,'1x',120}; + init = uipanel(g, 'Title', 'Initial State'); initg = uigridlayout(init, [2 4]); + uilabel(initg, 'Text', 'Pressure'); uilabel(initg, 'Text', 'Sw'); uilabel(initg, 'Text', 'Cs'); uilabel(initg, 'Text', 'Cb'); + app.UI.InitialPressure = uieditfield(initg, 'numeric'); app.UI.InitialPressure.Layout.Row = 2; + app.UI.InitialSw = uieditfield(initg, 'numeric'); app.UI.InitialSw.Layout.Row = 2; + app.UI.InitialCs = uieditfield(initg, 'numeric'); app.UI.InitialCs.Layout.Row = 2; + app.UI.InitialCb = uieditfield(initg, 'numeric'); app.UI.InitialCb.Layout.Row = 2; + subtabs = uitabgroup(g); + app.UI.GasTab = uitab(subtabs, 'Title', 'Gas-Water'); + app.UI.OilTab = uitab(subtabs, 'Title', 'Oil-Water'); + app.UI.MultiTab = uitab(subtabs, 'Title', 'Multi-Component'); + app.UI.GasArea = uitextarea(uigridlayout(app.UI.GasTab, [1 1])); + app.UI.OilArea = uitextarea(uigridlayout(app.UI.OilTab, [1 1])); + mg = uigridlayout(app.UI.MultiTab, [2 3]); + app.UI.MultiAdsorption = uitextarea(mg); app.UI.MultiNc = uitextarea(mg); app.UI.MultiPc = uitextarea(mg); + app.UI.MultiKrNoSurf = uitextarea(mg); app.UI.MultiKrSurf = uitextarea(mg); app.UI.MultiNote = uitextarea(mg, 'Editable', 'off', 'Value', {'Active flow model only.'}); + hint = uitextarea(g, 'Editable', 'off', 'Value', {'Flow models are mutually exclusive.', 'Only the selected model will be used at run time.'}); + hint.Layout.Row = 3; + end + + function buildPart6(app) + g = uigridlayout(app.UI.Part6Tab, [4 2]); g.ColumnWidth = {160,'1x'}; g.RowHeight = {110,110,140,'1x'}; + app.createAreaField(g, 1, 'well1', 'Well1'); + app.createAreaField(g, 2, 'welloc', 'Well Locations'); + app.createAreaField(g, 3, 'well2', 'Well2'); + p = uipanel(g); p.Layout.Row = 4; p.Layout.Column = 2; sub = uigridlayout(p, [1 4]); + app.UI.ScheduleArea = uitextarea(sub); app.UI.TimeArea = uitextarea(sub); app.UI.DtMinArea = uitextarea(sub); app.UI.DtMaxArea = uitextarea(sub); + lbl = uilabel(g, 'Text', 'Schedule / Time / dt'); lbl.Layout.Row = 4; lbl.Layout.Column = 1; + end + + function buildPart7(app) + g = uigridlayout(app.UI.Part7Tab, [2 6]); + names = {'YitaP','YitaS','Omega','Nmax','EpsAve','EpsMax'}; + for i = 1:numel(names) + lbl = uilabel(g, 'Text', names{i}); lbl.Layout.Row = 1; lbl.Layout.Column = i; + app.UI.(names{i}) = uieditfield(g, 'numeric'); + app.UI.(names{i}).Layout.Row = 2; app.UI.(names{i}).Layout.Column = i; + end + end + + function buildRunTab(app) + g = uigridlayout(app.UI.RunTab, [2 1]); g.RowHeight = {40,'1x'}; + uibutton(g, 'push', 'Text', 'Run Current Config', 'ButtonPushedFcn', @(~,~) app.onRun()); + app.UI.RunLog = uitextarea(g, 'Editable', 'off'); app.UI.RunLog.Layout.Row = 2; + end + + function buildResultTab(app) + g = uigridlayout(app.UI.ResultTab, [1 2]); g.ColumnWidth = {360,'1x'}; + app.UI.ResultSummary = uitextarea(g, 'Editable', 'off'); + app.UI.ResultAxes = uiaxes(g); title(app.UI.ResultAxes, 'Results'); + end + + function createAreaField(app, parent, row, field_name, label_text) + lbl = uilabel(parent, 'Text', label_text); + lbl.Layout.Row = row; + lbl.Layout.Column = 1; + app.UI.(field_name) = uitextarea(parent); + app.UI.(field_name).Layout.Row = row; + app.UI.(field_name).Layout.Column = 2; + end + + function onLoadTemplate(app) + app.Config = load_case_template(app.UI.TemplateDropDown.Value); + app.refreshUIFromConfig(); + app.appendLog(['Loaded template: ', app.UI.TemplateDropDown.Value]); + app.updateStatus('Template loaded'); + end + + function onImportConfig(app) + [f, p] = uigetfile('*.mat', 'Import Config'); if isequal(f, 0), return; end + app.Config = load_config_mat(fullfile(p, f)); + app.refreshUIFromConfig(); + app.appendLog(['Imported config: ', fullfile(p, f)]); + app.updateStatus('Config imported'); + end + + function onExportConfig(app) + app.pushUIToConfig(); + [f, p] = uiputfile('*.mat', 'Export Config', 'config.mat'); if isequal(f, 0), return; end + save_config_mat(app.Config, fullfile(p, f)); + app.appendLog(['Exported config: ', fullfile(p, f)]); + app.updateStatus('Config exported'); + end + + function onImportResult(app) + [f, p] = uigetfile('*.mat', 'Import Result'); if isequal(f, 0), return; end + app.Results = load_results_mat(fullfile(p, f)); + app.refreshResults(); + app.updateStatus('Result imported'); + end + + function onExportResult(app) + if isempty(fieldnames(app.Results)), uialert(app.UIFigure, 'No results available.', 'Export Result'); return; end + [f, p] = uiputfile('*.mat', 'Export Result', 'results.mat'); if isequal(f, 0), return; end + save_results_mat(app.Results, fullfile(p, f)); + app.appendLog(['Exported result: ', fullfile(p, f)]); + app.updateStatus('Result exported'); + end + + function onRun(app) + app.pushUIToConfig(); + app.updateStatus('Running'); + app.appendLog('Run started'); + drawnow; + try + log_text = evalc('[r, Times, OutputRs, Wellpara, trun] = run_case(app.Config);'); + app.Results = struct('r', r, 'Times', Times, 'OutputRs', {OutputRs}, 'Wellpara', {Wellpara}, 'trun', trun, 'captured_log', log_text); + app.appendLog(log_text); + app.refreshResults(); + app.UI.Tabs.SelectedTab = app.UI.ResultTab; + app.updateStatus('Run completed'); + catch ME + app.appendLog(getReport(ME, 'extended', 'hyperlinks', 'off')); + app.updateStatus('Run failed'); + uialert(app.UIFigure, ME.message, 'Run Failed'); + end + end + + function refreshUIFromConfig(app) + c = app.Config; + app.UI.ModelFlag.Value = string(c.model.modelflag); + app.UI.GridModel.Value = string(c.model.grid_model); + app.UI.FlowModel.Value = string(c.model.flow_model); + app.UI.Dx.Value = splitlines(app.toExpr(c.grid.dx)); app.UI.Dy.Value = splitlines(app.toExpr(c.grid.dy)); app.UI.Dz.Value = splitlines(app.toExpr(c.grid.dz)); app.UI.Ntg.Value = splitlines(app.toExpr(c.grid.NTG)); + app.UI.InputStyle.Value = string(c.fracture.input_style); app.UI.fractureInput.Value = splitlines(app.toExpr(c.fracture.input_content)); app.UI.fractureLines.Value = splitlines(app.toExpr(c.fracture.fractureLines)); app.UI.fractureHeights.Value = splitlines(app.toExpr(c.fracture.fractureHeights)); app.UI.flowBarrier.Value = splitlines(app.toExpr(c.fracture.flowBarrierFlags)); + app.UI.boundary.Value = splitlines(app.toExpr(c.discretization.sp.boundary_polygon)); app.UI.invalidLayer.Value = splitlines(app.toExpr(c.discretization.sp.invalid_layer)); app.UI.matrixKx.Value = splitlines(app.toExpr(c.discretization.sp.matrix.kx)); app.UI.matrixKy.Value = splitlines(app.toExpr(c.discretization.sp.matrix.ky)); app.UI.matrixKz.Value = splitlines(app.toExpr(c.discretization.sp.matrix.kz)); app.UI.matrixPori.Value = splitlines(app.toExpr(c.discretization.sp.matrix.pori)); + app.UI.MatrixPrpor.Value = c.discretization.sp.matrix.prpor; app.UI.MatrixCpor.Value = c.discretization.sp.matrix.cpor; app.UI.RockDensity.Value = c.discretization.sp.matrix.rock_density; + app.UI.GasArea.Value = splitlines(app.toExpr(c.flow.gas_water.matrix_relperm_table)); app.UI.OilArea.Value = splitlines(app.toExpr(c.flow.oil_water.matrix_relperm_table)); app.UI.MultiAdsorption.Value = splitlines(app.toExpr(c.flow.multi_component.c_ca_table)); app.UI.MultiNc.Value = splitlines(app.toExpr(c.flow.multi_component.cs_Nc)); app.UI.MultiPc.Value = splitlines(app.toExpr(c.flow.multi_component.PC)); app.UI.MultiKrNoSurf.Value = splitlines(app.toExpr(c.flow.multi_component.kr_nosurf)); app.UI.MultiKrSurf.Value = splitlines(app.toExpr(c.flow.multi_component.kr_surf)); + app.UI.InitialPressure.Value = app.scalarValue(c.initial.pressure); app.UI.InitialSw.Value = app.scalarValue(c.initial.sw); app.UI.InitialCs.Value = app.scalarValue(c.initial.cs); app.UI.InitialCb.Value = app.scalarValue(c.initial.cb); + app.UI.well1.Value = splitlines(app.toExpr(c.wells.well1)); app.UI.welloc.Value = splitlines(app.toExpr(c.wells.welloc)); app.UI.well2.Value = splitlines(app.toExpr(c.wells.well2)); app.UI.ScheduleArea.Value = splitlines(app.toExpr(c.schedule.well_schedules)); app.UI.TimeArea.Value = splitlines(app.toExpr(c.schedule.time)); app.UI.DtMinArea.Value = splitlines(app.toExpr(c.schedule.dtmin)); app.UI.DtMaxArea.Value = splitlines(app.toExpr(c.schedule.dtmax)); + app.UI.YitaP.Value = c.solver.yitap; app.UI.YitaS.Value = c.solver.yitas; app.UI.Omega.Value = c.solver.omega; app.UI.Nmax.Value = c.solver.Nmax; app.UI.EpsAve.Value = c.solver.epsave; app.UI.EpsMax.Value = c.solver.epsmax; + end + + function pushUIToConfig(app) + c = app.Config; + c.model.modelflag = str2double(app.UI.ModelFlag.Value); c.model.grid_model = str2double(app.UI.GridModel.Value); c.model.flow_model = str2double(app.UI.FlowModel.Value); + c.grid.dx = app.parseExpr(app.UI.Dx.Value); c.grid.dy = app.parseExpr(app.UI.Dy.Value); c.grid.dz = app.parseExpr(app.UI.Dz.Value); c.grid.NTG = app.parseExpr(app.UI.Ntg.Value); c.grid.nx = numel(c.grid.dx); c.grid.ny = numel(c.grid.dy); c.grid.nz = numel(c.grid.dz); + c.fracture.input_style = str2double(app.UI.InputStyle.Value); c.fracture.input_content = app.parseExpr(app.UI.fractureInput.Value); c.fracture.fractureLines = app.parseExpr(app.UI.fractureLines.Value); c.fracture.fractureHeights = app.parseExpr(app.UI.fractureHeights.Value); c.fracture.flowBarrierFlags = app.parseExpr(app.UI.flowBarrier.Value); + c.discretization.sp.boundary_polygon = app.parseExpr(app.UI.boundary.Value); c.discretization.sp.invalid_layer = app.parseExpr(app.UI.invalidLayer.Value); c.discretization.sp.matrix.kx = app.parseExpr(app.UI.matrixKx.Value); c.discretization.sp.matrix.ky = app.parseExpr(app.UI.matrixKy.Value); c.discretization.sp.matrix.kz = app.parseExpr(app.UI.matrixKz.Value); c.discretization.sp.matrix.pori = app.parseExpr(app.UI.matrixPori.Value); c.discretization.sp.matrix.prpor = app.UI.MatrixPrpor.Value; c.discretization.sp.matrix.cpor = app.UI.MatrixCpor.Value; c.discretization.sp.matrix.rock_density = app.UI.RockDensity.Value; + c.flow.gas_water.matrix_relperm_table = app.parseExpr(app.UI.GasArea.Value); c.flow.oil_water.matrix_relperm_table = app.parseExpr(app.UI.OilArea.Value); c.flow.multi_component.c_ca_table = app.parseExpr(app.UI.MultiAdsorption.Value); c.flow.multi_component.cs_Nc = app.parseExpr(app.UI.MultiNc.Value); c.flow.multi_component.PC = app.parseExpr(app.UI.MultiPc.Value); c.flow.multi_component.kr_nosurf = app.parseExpr(app.UI.MultiKrNoSurf.Value); c.flow.multi_component.kr_surf = app.parseExpr(app.UI.MultiKrSurf.Value); + c.initial.pressure = app.UI.InitialPressure.Value; c.initial.sw = app.UI.InitialSw.Value; c.initial.cs = app.UI.InitialCs.Value; c.initial.cb = app.UI.InitialCb.Value; + c.wells.well1 = app.parseExpr(app.UI.well1.Value); c.wells.welloc = app.parseExpr(app.UI.welloc.Value); c.wells.num_fracture_wells = numel(c.wells.welloc); c.wells.well2 = app.parseExpr(app.UI.well2.Value); + c.schedule.well_schedules = app.parseExpr(app.UI.ScheduleArea.Value); c.schedule.time = app.parseExpr(app.UI.TimeArea.Value); c.schedule.dtmin = app.parseExpr(app.UI.DtMinArea.Value); c.schedule.dtmax = app.parseExpr(app.UI.DtMaxArea.Value); c.schedule.number_phases = size(c.schedule.well_schedules, 1); + c.solver.yitap = app.UI.YitaP.Value; c.solver.yitas = app.UI.YitaS.Value; c.solver.omega = app.UI.Omega.Value; c.solver.Nmax = app.UI.Nmax.Value; c.solver.epsave = app.UI.EpsAve.Value; c.solver.epsmax = app.UI.EpsMax.Value; + app.Config = c; + end + + function refreshResults(app) + if isempty(fieldnames(app.Results)), app.UI.ResultSummary.Value = {'No results loaded.'}; cla(app.UI.ResultAxes); return; end + summary = {}; + if isfield(app.Results, 'Times'), summary{end+1} = ['Time steps: ', num2str(numel(app.Results.Times))]; end %#ok + if isfield(app.Results, 'trun') && isstruct(app.Results.trun) + if isfield(app.Results.trun, 'run_time'), summary{end+1} = ['Run time: ', num2str(app.Results.trun.run_time)]; end %#ok + if isfield(app.Results.trun, 'Newton_step'), summary{end+1} = ['Newton steps: ', num2str(app.Results.trun.Newton_step)]; end %#ok + end + app.UI.ResultSummary.Value = summary; + cla(app.UI.ResultAxes); + if isfield(app.Results, 'trun') && isfield(app.Results.trun, 'Newtons_vs_time') && ~isempty(app.Results.trun.Newtons_vs_time) + plot(app.UI.ResultAxes, app.Results.trun.Newtons_vs_time(:,1), app.Results.trun.Newtons_vs_time(:,2), '-o'); title(app.UI.ResultAxes, 'Newtons vs Time'); xlabel(app.UI.ResultAxes, 'Time'); ylabel(app.UI.ResultAxes, 'Newton Count'); + elseif isfield(app.Results, 'Times') && ~isempty(app.Results.Times) + plot(app.UI.ResultAxes, app.Results.Times, 1:numel(app.Results.Times), '-o'); title(app.UI.ResultAxes, 'Time Step Index'); xlabel(app.UI.ResultAxes, 'Time'); ylabel(app.UI.ResultAxes, 'Step'); + end + end + + function appendLog(app, txt) + lines = string(txt); lines = splitlines(lines); + current = string(app.UI.RunLog.Value); + if isequal(current, ""), app.UI.RunLog.Value = cellstr(lines); else, app.UI.RunLog.Value = cellstr([current; lines]); end + end + + function updateStatus(app, txt) + app.UI.StatusLabel.Text = ['Status: ', txt]; + end + + function out = parseExpr(app, lines) %#ok + txt = strtrim(strjoin(string(lines), newline)); + if txt == "", out = []; else, out = eval(txt); end %#ok + end + + function txt = toExpr(app, value) %#ok + if isempty(value), txt = '[]'; return; end + if isnumeric(value) || islogical(value), txt = mat2str(value); return; end + if ischar(value), txt = ['''', strrep(value, '''', ''''''), '''']; return; end + if isstring(value) && isscalar(value), txt = ['''', 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} = app.toExpr(value{i,j}); end + rows{i} = strjoin(cols, ', '); + end + txt = ['{', strjoin(rows, [';', newline]), '}']; return; + end + txt = evalc('disp(value)'); + end + + function value = scalarValue(app, raw) %#ok + if isempty(raw), value = 0; elseif isscalar(raw), value = raw; else, value = raw(1); end + end + end + + methods (Access = public) + function app = EDFMSimulatorApp() + createComponents(app); + registerApp(app, app.UIFigure); + runStartupFcn(app, @(~,~) app.startup()); + end + + function delete(app) + delete(app.UIFigure); + end + end +end diff --git a/gui_support/app/EDFM_Simulator_App.m b/gui_support/app/EDFM_Simulator_App.m new file mode 100644 index 0000000..0731d68 --- /dev/null +++ b/gui_support/app/EDFM_Simulator_App.m @@ -0,0 +1,917 @@ +classdef EDFM_Simulator_App < matlab.apps.AppBase + + % Properties that correspond to app components + properties (Access = public) + UIFigure matlab.ui.Figure + MainGrid matlab.ui.container.GridLayout + TopGrid matlab.ui.container.GridLayout + + TemplateDropDown matlab.ui.control.DropDown + LoadTemplateButton matlab.ui.control.Button + ImportConfigButton matlab.ui.control.Button + ExportConfigButton matlab.ui.control.Button + RunButton matlab.ui.control.Button + ImportResultButton matlab.ui.control.Button + ExportResultButton matlab.ui.control.Button + StatusLabel matlab.ui.control.Label + + MainTabGroup matlab.ui.container.TabGroup + Part1Tab matlab.ui.container.Tab + Part2Tab matlab.ui.container.Tab + Part3Tab matlab.ui.container.Tab + Part4Tab matlab.ui.container.Tab + Part5Tab matlab.ui.container.Tab + Part6Tab matlab.ui.container.Tab + Part7Tab matlab.ui.container.Tab + RunTab matlab.ui.container.Tab + ResultTab matlab.ui.container.Tab + + Part1Grid matlab.ui.container.GridLayout + ModelFlagLabel matlab.ui.control.Label + ModelFlagDropDown matlab.ui.control.DropDown + GridModelLabel matlab.ui.control.Label + GridModelDropDown matlab.ui.control.DropDown + FlowModelLabel matlab.ui.control.Label + FlowModelDropDown matlab.ui.control.DropDown + + Part2Grid matlab.ui.container.GridLayout + DxLabel matlab.ui.control.Label + DxTextArea matlab.ui.control.TextArea + DyLabel matlab.ui.control.Label + DyTextArea matlab.ui.control.TextArea + DzLabel matlab.ui.control.Label + DzTextArea matlab.ui.control.TextArea + NTGLabel matlab.ui.control.Label + NTGTextArea matlab.ui.control.TextArea + + Part3Grid matlab.ui.container.GridLayout + FractureInputStyleLabel matlab.ui.control.Label + FractureInputStyleDropDown matlab.ui.control.DropDown + FractureInputLabel matlab.ui.control.Label + FractureInputTextArea matlab.ui.control.TextArea + FractureLinesLabel matlab.ui.control.Label + FractureLinesTextArea matlab.ui.control.TextArea + FractureHeightsLabel matlab.ui.control.Label + FractureHeightsTextArea matlab.ui.control.TextArea + FlowBarrierFlagsLabel matlab.ui.control.Label + FlowBarrierFlagsTextArea matlab.ui.control.TextArea + + Part4Grid matlab.ui.container.GridLayout + BoundaryLabel matlab.ui.control.Label + BoundaryTextArea matlab.ui.control.TextArea + InvalidLayerLabel matlab.ui.control.Label + InvalidLayerTextArea matlab.ui.control.TextArea + MatrixKxLabel matlab.ui.control.Label + MatrixKxTextArea matlab.ui.control.TextArea + MatrixKyLabel matlab.ui.control.Label + MatrixKyTextArea matlab.ui.control.TextArea + MatrixKzLabel matlab.ui.control.Label + MatrixKzTextArea matlab.ui.control.TextArea + MatrixPoriLabel matlab.ui.control.Label + MatrixPoriTextArea matlab.ui.control.TextArea + MatrixPrporLabel matlab.ui.control.Label + MatrixPrporField matlab.ui.control.NumericEditField + MatrixCporLabel matlab.ui.control.Label + MatrixCporField matlab.ui.control.NumericEditField + RockDensityLabel matlab.ui.control.Label + RockDensityField matlab.ui.control.NumericEditField + FractureKfLabel matlab.ui.control.Label + FractureKfTextArea matlab.ui.control.TextArea + FractureWfLabel matlab.ui.control.Label + FractureWfTextArea matlab.ui.control.TextArea + FracturePorfLabel matlab.ui.control.Label + FracturePorfTextArea matlab.ui.control.TextArea + + Part5Grid matlab.ui.container.GridLayout + InitialPanel matlab.ui.container.Panel + InitialGrid matlab.ui.container.GridLayout + InitialPressureLabel matlab.ui.control.Label + InitialPressureField matlab.ui.control.NumericEditField + InitialSwLabel matlab.ui.control.Label + InitialSwField matlab.ui.control.NumericEditField + InitialCsLabel matlab.ui.control.Label + InitialCsField matlab.ui.control.NumericEditField + InitialCbLabel matlab.ui.control.Label + InitialCbField matlab.ui.control.NumericEditField + + FlowSubTabGroup matlab.ui.container.TabGroup + GasWaterTab matlab.ui.container.Tab + OilWaterTab matlab.ui.container.Tab + MultiComponentTab matlab.ui.container.Tab + + GasWaterGrid matlab.ui.container.GridLayout + GasRelPermLabel matlab.ui.control.Label + GasRelPermTextArea matlab.ui.control.TextArea + GasPVTLabel matlab.ui.control.Label + GasPVTTextArea matlab.ui.control.TextArea + + OilWaterGrid matlab.ui.container.GridLayout + OilRelPermLabel matlab.ui.control.Label + OilRelPermTextArea matlab.ui.control.TextArea + OilPVTLabel matlab.ui.control.Label + OilPVTTextArea matlab.ui.control.TextArea + + MultiComponentGrid matlab.ui.container.GridLayout + AdsorptionLabel matlab.ui.control.Label + AdsorptionTextArea matlab.ui.control.TextArea + NcTableLabel matlab.ui.control.Label + NcTableTextArea matlab.ui.control.TextArea + KrNoSurfLabel matlab.ui.control.Label + KrNoSurfTextArea matlab.ui.control.TextArea + KrSurfLabel matlab.ui.control.Label + KrSurfTextArea matlab.ui.control.TextArea + PcTableLabel matlab.ui.control.Label + PcTableTextArea matlab.ui.control.TextArea + + Part6Grid matlab.ui.container.GridLayout + Well1Label matlab.ui.control.Label + Well1Table matlab.ui.control.Table + FractureWellLocationLabel matlab.ui.control.Label + FractureWellLocationTable matlab.ui.control.Table + Well2Label matlab.ui.control.Label + Well2Table matlab.ui.control.Table + ScheduleLabel matlab.ui.control.Label + ScheduleTable matlab.ui.control.Table + TimeLabel matlab.ui.control.Label + TimeTextArea matlab.ui.control.TextArea + DtMinLabel matlab.ui.control.Label + DtMinTextArea matlab.ui.control.TextArea + DtMaxLabel matlab.ui.control.Label + DtMaxTextArea matlab.ui.control.TextArea + + Part7Grid matlab.ui.container.GridLayout + YitaPLabel matlab.ui.control.Label + YitaPField matlab.ui.control.NumericEditField + YitaSLabel matlab.ui.control.Label + YitaSField matlab.ui.control.NumericEditField + OmegaLabel matlab.ui.control.Label + OmegaField matlab.ui.control.NumericEditField + NmaxLabel matlab.ui.control.Label + NmaxField matlab.ui.control.NumericEditField + EpsAveLabel matlab.ui.control.Label + EpsAveField matlab.ui.control.NumericEditField + EpsMaxLabel matlab.ui.control.Label + EpsMaxField matlab.ui.control.NumericEditField + + RunGrid matlab.ui.container.GridLayout + RunCurrentConfigButton matlab.ui.control.Button + CurrentStageLabel matlab.ui.control.Label + CurrentTimeLabel matlab.ui.control.Label + CurrentDtLabel matlab.ui.control.Label + RunLogTextArea matlab.ui.control.TextArea + + ResultGrid matlab.ui.container.GridLayout + ResultLeftGrid matlab.ui.container.GridLayout + ResultSummaryLabel matlab.ui.control.Label + ResultSummaryTextArea matlab.ui.control.TextArea + ResultTypeLabel matlab.ui.control.Label + ResultTypeDropDown matlab.ui.control.DropDown + PlotResultButton matlab.ui.control.Button + ResultAxes matlab.ui.control.UIAxes + end + + % Component initialization + methods (Access = private) + + % Create UIFigure and components + function createComponents(app) + + % Create UIFigure and hide until all components are created + app.UIFigure = uifigure('Visible', 'off'); + app.UIFigure.Position = [100 100 1500 920]; + app.UIFigure.Name = 'EDFM Simulator App'; + + % Create MainGrid + app.MainGrid = uigridlayout(app.UIFigure); + app.MainGrid.ColumnWidth = {'1x'}; + app.MainGrid.RowHeight = {48, '1x'}; + app.MainGrid.Padding = [8 8 8 8]; + + % Create TopGrid + app.TopGrid = uigridlayout(app.MainGrid); + app.TopGrid.Layout.Row = 1; + app.TopGrid.Layout.Column = 1; + app.TopGrid.RowHeight = {'1x'}; + app.TopGrid.ColumnWidth = {140, 110, 110, 110, 90, 110, 110, '1x', 180}; + app.TopGrid.ColumnSpacing = 8; + app.TopGrid.Padding = [0 0 0 0]; + + % Create top controls + app.TemplateDropDown = uidropdown(app.TopGrid); + app.TemplateDropDown.Layout.Row = 1; + app.TemplateDropDown.Layout.Column = 1; + app.TemplateDropDown.Items = {'case01'}; + app.TemplateDropDown.Value = 'case01'; + + app.LoadTemplateButton = uibutton(app.TopGrid, 'push'); + app.LoadTemplateButton.Layout.Row = 1; + app.LoadTemplateButton.Layout.Column = 2; + app.LoadTemplateButton.Text = 'Load Template'; + + app.ImportConfigButton = uibutton(app.TopGrid, 'push'); + app.ImportConfigButton.Layout.Row = 1; + app.ImportConfigButton.Layout.Column = 3; + app.ImportConfigButton.Text = 'Import Config'; + + app.ExportConfigButton = uibutton(app.TopGrid, 'push'); + app.ExportConfigButton.Layout.Row = 1; + app.ExportConfigButton.Layout.Column = 4; + app.ExportConfigButton.Text = 'Export Config'; + + app.RunButton = uibutton(app.TopGrid, 'push'); + app.RunButton.Layout.Row = 1; + app.RunButton.Layout.Column = 5; + app.RunButton.Text = 'Run'; + + app.ImportResultButton = uibutton(app.TopGrid, 'push'); + app.ImportResultButton.Layout.Row = 1; + app.ImportResultButton.Layout.Column = 6; + app.ImportResultButton.Text = 'Import Result'; + + app.ExportResultButton = uibutton(app.TopGrid, 'push'); + app.ExportResultButton.Layout.Row = 1; + app.ExportResultButton.Layout.Column = 7; + app.ExportResultButton.Text = 'Export Result'; + + app.StatusLabel = uilabel(app.TopGrid); + app.StatusLabel.Layout.Row = 1; + app.StatusLabel.Layout.Column = 9; + app.StatusLabel.HorizontalAlignment = 'right'; + app.StatusLabel.Text = 'Status: Ready'; + + % Create MainTabGroup + app.MainTabGroup = uitabgroup(app.MainGrid); + app.MainTabGroup.Layout.Row = 2; + app.MainTabGroup.Layout.Column = 1; + + % Create tabs + app.Part1Tab = uitab(app.MainTabGroup); + app.Part1Tab.Title = 'Part1 Model'; + + app.Part2Tab = uitab(app.MainTabGroup); + app.Part2Tab.Title = 'Part2 Grid'; + + app.Part3Tab = uitab(app.MainTabGroup); + app.Part3Tab.Title = 'Part3 Fracture'; + + app.Part4Tab = uitab(app.MainTabGroup); + app.Part4Tab.Title = 'Part4 Discretization'; + + app.Part5Tab = uitab(app.MainTabGroup); + app.Part5Tab.Title = 'Part5 Flow'; + + app.Part6Tab = uitab(app.MainTabGroup); + app.Part6Tab.Title = 'Part6 Wells'; + + app.Part7Tab = uitab(app.MainTabGroup); + app.Part7Tab.Title = 'Part7 Solver'; + + app.RunTab = uitab(app.MainTabGroup); + app.RunTab.Title = 'Run'; + + app.ResultTab = uitab(app.MainTabGroup); + app.ResultTab.Title = 'Results'; + + % ---------------- Part1 ---------------- + app.Part1Grid = uigridlayout(app.Part1Tab); + app.Part1Grid.ColumnWidth = {180, '1x'}; + app.Part1Grid.RowHeight = {32, 32, 32, '1x'}; + app.Part1Grid.Padding = [12 12 12 12]; + + app.ModelFlagLabel = uilabel(app.Part1Grid); + app.ModelFlagLabel.Layout.Row = 1; + app.ModelFlagLabel.Layout.Column = 1; + app.ModelFlagLabel.Text = 'Model Flag'; + + app.ModelFlagDropDown = uidropdown(app.Part1Grid); + app.ModelFlagDropDown.Layout.Row = 1; + app.ModelFlagDropDown.Layout.Column = 2; + app.ModelFlagDropDown.Items = {'1'}; + app.ModelFlagDropDown.Value = '1'; + + app.GridModelLabel = uilabel(app.Part1Grid); + app.GridModelLabel.Layout.Row = 2; + app.GridModelLabel.Layout.Column = 1; + app.GridModelLabel.Text = 'Grid Model'; + + app.GridModelDropDown = uidropdown(app.Part1Grid); + app.GridModelDropDown.Layout.Row = 2; + app.GridModelDropDown.Layout.Column = 2; + app.GridModelDropDown.Items = {'1', '2'}; + app.GridModelDropDown.Value = '1'; + + app.FlowModelLabel = uilabel(app.Part1Grid); + app.FlowModelLabel.Layout.Row = 3; + app.FlowModelLabel.Layout.Column = 1; + app.FlowModelLabel.Text = 'Flow Model'; + + app.FlowModelDropDown = uidropdown(app.Part1Grid); + app.FlowModelDropDown.Layout.Row = 3; + app.FlowModelDropDown.Layout.Column = 2; + app.FlowModelDropDown.Items = {'1', '2', '3'}; + app.FlowModelDropDown.Value = '3'; + + % ---------------- Part2 ---------------- + app.Part2Grid = uigridlayout(app.Part2Tab); + app.Part2Grid.ColumnWidth = {140, '1x'}; + app.Part2Grid.RowHeight = {110, 110, 110, '1x'}; + app.Part2Grid.Padding = [12 12 12 12]; + + app.DxLabel = uilabel(app.Part2Grid); + app.DxLabel.Layout.Row = 1; + app.DxLabel.Layout.Column = 1; + app.DxLabel.Text = 'dx'; + + app.DxTextArea = uitextarea(app.Part2Grid); + app.DxTextArea.Layout.Row = 1; + app.DxTextArea.Layout.Column = 2; + + app.DyLabel = uilabel(app.Part2Grid); + app.DyLabel.Layout.Row = 2; + app.DyLabel.Layout.Column = 1; + app.DyLabel.Text = 'dy'; + + app.DyTextArea = uitextarea(app.Part2Grid); + app.DyTextArea.Layout.Row = 2; + app.DyTextArea.Layout.Column = 2; + + app.DzLabel = uilabel(app.Part2Grid); + app.DzLabel.Layout.Row = 3; + app.DzLabel.Layout.Column = 1; + app.DzLabel.Text = 'dz'; + + app.DzTextArea = uitextarea(app.Part2Grid); + app.DzTextArea.Layout.Row = 3; + app.DzTextArea.Layout.Column = 2; + + app.NTGLabel = uilabel(app.Part2Grid); + app.NTGLabel.Layout.Row = 4; + app.NTGLabel.Layout.Column = 1; + app.NTGLabel.Text = 'NTG'; + + app.NTGTextArea = uitextarea(app.Part2Grid); + app.NTGTextArea.Layout.Row = 4; + app.NTGTextArea.Layout.Column = 2; + + % ---------------- Part3 ---------------- + app.Part3Grid = uigridlayout(app.Part3Tab); + app.Part3Grid.ColumnWidth = {170, '1x'}; + app.Part3Grid.RowHeight = {32, 120, 120, 120, 90}; + app.Part3Grid.Padding = [12 12 12 12]; + + app.FractureInputStyleLabel = uilabel(app.Part3Grid); + app.FractureInputStyleLabel.Layout.Row = 1; + app.FractureInputStyleLabel.Layout.Column = 1; + app.FractureInputStyleLabel.Text = 'Input Style'; + + app.FractureInputStyleDropDown = uidropdown(app.Part3Grid); + app.FractureInputStyleDropDown.Layout.Row = 1; + app.FractureInputStyleDropDown.Layout.Column = 2; + app.FractureInputStyleDropDown.Items = {'1', '2', '3', '4'}; + app.FractureInputStyleDropDown.Value = '1'; + + app.FractureInputLabel = uilabel(app.Part3Grid); + app.FractureInputLabel.Layout.Row = 2; + app.FractureInputLabel.Layout.Column = 1; + app.FractureInputLabel.Text = 'Input Content'; + + app.FractureInputTextArea = uitextarea(app.Part3Grid); + app.FractureInputTextArea.Layout.Row = 2; + app.FractureInputTextArea.Layout.Column = 2; + + app.FractureLinesLabel = uilabel(app.Part3Grid); + app.FractureLinesLabel.Layout.Row = 3; + app.FractureLinesLabel.Layout.Column = 1; + app.FractureLinesLabel.Text = 'Fracture Lines'; + + app.FractureLinesTextArea = uitextarea(app.Part3Grid); + app.FractureLinesTextArea.Layout.Row = 3; + app.FractureLinesTextArea.Layout.Column = 2; + + app.FractureHeightsLabel = uilabel(app.Part3Grid); + app.FractureHeightsLabel.Layout.Row = 4; + app.FractureHeightsLabel.Layout.Column = 1; + app.FractureHeightsLabel.Text = 'Fracture Heights'; + + app.FractureHeightsTextArea = uitextarea(app.Part3Grid); + app.FractureHeightsTextArea.Layout.Row = 4; + app.FractureHeightsTextArea.Layout.Column = 2; + + app.FlowBarrierFlagsLabel = uilabel(app.Part3Grid); + app.FlowBarrierFlagsLabel.Layout.Row = 5; + app.FlowBarrierFlagsLabel.Layout.Column = 1; + app.FlowBarrierFlagsLabel.Text = 'Flow Barrier Flags'; + + app.FlowBarrierFlagsTextArea = uitextarea(app.Part3Grid); + app.FlowBarrierFlagsTextArea.Layout.Row = 5; + app.FlowBarrierFlagsTextArea.Layout.Column = 2; + + % ---------------- Part4 ---------------- + app.Part4Grid = uigridlayout(app.Part4Tab); + app.Part4Grid.ColumnWidth = {180, '1x', 180, '1x'}; + app.Part4Grid.RowHeight = {90, 60, 90, 90, 90, 32, '1x'}; + app.Part4Grid.Padding = [12 12 12 12]; + + app.BoundaryLabel = uilabel(app.Part4Grid); + app.BoundaryLabel.Layout.Row = 1; + app.BoundaryLabel.Layout.Column = 1; + app.BoundaryLabel.Text = 'Boundary Polygon'; + + app.BoundaryTextArea = uitextarea(app.Part4Grid); + app.BoundaryTextArea.Layout.Row = 1; + app.BoundaryTextArea.Layout.Column = [2 4]; + + app.InvalidLayerLabel = uilabel(app.Part4Grid); + app.InvalidLayerLabel.Layout.Row = 2; + app.InvalidLayerLabel.Layout.Column = 1; + app.InvalidLayerLabel.Text = 'Invalid Layer'; + + app.InvalidLayerTextArea = uitextarea(app.Part4Grid); + app.InvalidLayerTextArea.Layout.Row = 2; + app.InvalidLayerTextArea.Layout.Column = [2 4]; + + app.MatrixKxLabel = uilabel(app.Part4Grid); + app.MatrixKxLabel.Layout.Row = 3; + app.MatrixKxLabel.Layout.Column = 1; + app.MatrixKxLabel.Text = 'Matrix kx'; + + app.MatrixKxTextArea = uitextarea(app.Part4Grid); + app.MatrixKxTextArea.Layout.Row = 3; + app.MatrixKxTextArea.Layout.Column = 2; + + app.MatrixKyLabel = uilabel(app.Part4Grid); + app.MatrixKyLabel.Layout.Row = 3; + app.MatrixKyLabel.Layout.Column = 3; + app.MatrixKyLabel.Text = 'Matrix ky'; + + app.MatrixKyTextArea = uitextarea(app.Part4Grid); + app.MatrixKyTextArea.Layout.Row = 3; + app.MatrixKyTextArea.Layout.Column = 4; + + app.MatrixKzLabel = uilabel(app.Part4Grid); + app.MatrixKzLabel.Layout.Row = 4; + app.MatrixKzLabel.Layout.Column = 1; + app.MatrixKzLabel.Text = 'Matrix kz'; + + app.MatrixKzTextArea = uitextarea(app.Part4Grid); + app.MatrixKzTextArea.Layout.Row = 4; + app.MatrixKzTextArea.Layout.Column = 2; + + app.MatrixPoriLabel = uilabel(app.Part4Grid); + app.MatrixPoriLabel.Layout.Row = 4; + app.MatrixPoriLabel.Layout.Column = 3; + app.MatrixPoriLabel.Text = 'Matrix pori'; + + app.MatrixPoriTextArea = uitextarea(app.Part4Grid); + app.MatrixPoriTextArea.Layout.Row = 4; + app.MatrixPoriTextArea.Layout.Column = 4; + + app.FractureKfLabel = uilabel(app.Part4Grid); + app.FractureKfLabel.Layout.Row = 5; + app.FractureKfLabel.Layout.Column = 1; + app.FractureKfLabel.Text = 'Fracture Kf'; + + app.FractureKfTextArea = uitextarea(app.Part4Grid); + app.FractureKfTextArea.Layout.Row = 5; + app.FractureKfTextArea.Layout.Column = 2; + + app.FractureWfLabel = uilabel(app.Part4Grid); + app.FractureWfLabel.Layout.Row = 5; + app.FractureWfLabel.Layout.Column = 3; + app.FractureWfLabel.Text = 'Fracture Wf'; + + app.FractureWfTextArea = uitextarea(app.Part4Grid); + app.FractureWfTextArea.Layout.Row = 5; + app.FractureWfTextArea.Layout.Column = 4; + + app.MatrixPrporLabel = uilabel(app.Part4Grid); + app.MatrixPrporLabel.Layout.Row = 6; + app.MatrixPrporLabel.Layout.Column = 1; + app.MatrixPrporLabel.Text = 'Matrix prpor'; + + app.MatrixPrporField = uieditfield(app.Part4Grid, 'numeric'); + app.MatrixPrporField.Layout.Row = 6; + app.MatrixPrporField.Layout.Column = 2; + + app.MatrixCporLabel = uilabel(app.Part4Grid); + app.MatrixCporLabel.Layout.Row = 6; + app.MatrixCporLabel.Layout.Column = 3; + app.MatrixCporLabel.Text = 'Matrix cpor'; + + app.MatrixCporField = uieditfield(app.Part4Grid, 'numeric'); + app.MatrixCporField.Layout.Row = 6; + app.MatrixCporField.Layout.Column = 4; + + app.RockDensityLabel = uilabel(app.Part4Grid); + app.RockDensityLabel.Layout.Row = 7; + app.RockDensityLabel.Layout.Column = 1; + app.RockDensityLabel.Text = 'Rock Density'; + + app.RockDensityField = uieditfield(app.Part4Grid, 'numeric'); + app.RockDensityField.Layout.Row = 7; + app.RockDensityField.Layout.Column = 2; + + app.FracturePorfLabel = uilabel(app.Part4Grid); + app.FracturePorfLabel.Layout.Row = 7; + app.FracturePorfLabel.Layout.Column = 3; + app.FracturePorfLabel.Text = 'Fracture Porf'; + + app.FracturePorfTextArea = uitextarea(app.Part4Grid); + app.FracturePorfTextArea.Layout.Row = 7; + app.FracturePorfTextArea.Layout.Column = 4; + + % ---------------- Part5 ---------------- + app.Part5Grid = uigridlayout(app.Part5Tab); + app.Part5Grid.ColumnWidth = {'1x'}; + app.Part5Grid.RowHeight = {110, '1x'}; + app.Part5Grid.Padding = [12 12 12 12]; + + app.InitialPanel = uipanel(app.Part5Grid); + app.InitialPanel.Layout.Row = 1; + app.InitialPanel.Layout.Column = 1; + app.InitialPanel.Title = 'Initial State'; + + app.InitialGrid = uigridlayout(app.InitialPanel); + app.InitialGrid.ColumnWidth = {120, 120, 120, 120}; + app.InitialGrid.RowHeight = {26, 32}; + + app.InitialPressureLabel = uilabel(app.InitialGrid); + app.InitialPressureLabel.Layout.Row = 1; + app.InitialPressureLabel.Layout.Column = 1; + app.InitialPressureLabel.Text = 'Pressure'; + + app.InitialPressureField = uieditfield(app.InitialGrid, 'numeric'); + app.InitialPressureField.Layout.Row = 2; + app.InitialPressureField.Layout.Column = 1; + + app.InitialSwLabel = uilabel(app.InitialGrid); + app.InitialSwLabel.Layout.Row = 1; + app.InitialSwLabel.Layout.Column = 2; + app.InitialSwLabel.Text = 'Sw'; + + app.InitialSwField = uieditfield(app.InitialGrid, 'numeric'); + app.InitialSwField.Layout.Row = 2; + app.InitialSwField.Layout.Column = 2; + + app.InitialCsLabel = uilabel(app.InitialGrid); + app.InitialCsLabel.Layout.Row = 1; + app.InitialCsLabel.Layout.Column = 3; + app.InitialCsLabel.Text = 'Cs'; + + app.InitialCsField = uieditfield(app.InitialGrid, 'numeric'); + app.InitialCsField.Layout.Row = 2; + app.InitialCsField.Layout.Column = 3; + + app.InitialCbLabel = uilabel(app.InitialGrid); + app.InitialCbLabel.Layout.Row = 1; + app.InitialCbLabel.Layout.Column = 4; + app.InitialCbLabel.Text = 'Cb'; + + app.InitialCbField = uieditfield(app.InitialGrid, 'numeric'); + app.InitialCbField.Layout.Row = 2; + app.InitialCbField.Layout.Column = 4; + + app.FlowSubTabGroup = uitabgroup(app.Part5Grid); + app.FlowSubTabGroup.Layout.Row = 2; + app.FlowSubTabGroup.Layout.Column = 1; + + app.GasWaterTab = uitab(app.FlowSubTabGroup); + app.GasWaterTab.Title = 'Gas-Water'; + + app.OilWaterTab = uitab(app.FlowSubTabGroup); + app.OilWaterTab.Title = 'Oil-Water'; + + app.MultiComponentTab = uitab(app.FlowSubTabGroup); + app.MultiComponentTab.Title = 'Multi-Component'; + + app.GasWaterGrid = uigridlayout(app.GasWaterTab); + app.GasWaterGrid.ColumnWidth = {180, '1x'}; + app.GasWaterGrid.RowHeight = {140, '1x'}; + app.GasWaterGrid.Padding = [12 12 12 12]; + + app.GasRelPermLabel = uilabel(app.GasWaterGrid); + app.GasRelPermLabel.Layout.Row = 1; + app.GasRelPermLabel.Layout.Column = 1; + app.GasRelPermLabel.Text = 'Gas RelPerm Table'; + + app.GasRelPermTextArea = uitextarea(app.GasWaterGrid); + app.GasRelPermTextArea.Layout.Row = 1; + app.GasRelPermTextArea.Layout.Column = 2; + + app.GasPVTLabel = uilabel(app.GasWaterGrid); + app.GasPVTLabel.Layout.Row = 2; + app.GasPVTLabel.Layout.Column = 1; + app.GasPVTLabel.Text = 'Gas PVT Text'; + + app.GasPVTTextArea = uitextarea(app.GasWaterGrid); + app.GasPVTTextArea.Layout.Row = 2; + app.GasPVTTextArea.Layout.Column = 2; + + app.OilWaterGrid = uigridlayout(app.OilWaterTab); + app.OilWaterGrid.ColumnWidth = {180, '1x'}; + app.OilWaterGrid.RowHeight = {140, '1x'}; + app.OilWaterGrid.Padding = [12 12 12 12]; + + app.OilRelPermLabel = uilabel(app.OilWaterGrid); + app.OilRelPermLabel.Layout.Row = 1; + app.OilRelPermLabel.Layout.Column = 1; + app.OilRelPermLabel.Text = 'Oil RelPerm Table'; + + app.OilRelPermTextArea = uitextarea(app.OilWaterGrid); + app.OilRelPermTextArea.Layout.Row = 1; + app.OilRelPermTextArea.Layout.Column = 2; + + app.OilPVTLabel = uilabel(app.OilWaterGrid); + app.OilPVTLabel.Layout.Row = 2; + app.OilPVTLabel.Layout.Column = 1; + app.OilPVTLabel.Text = 'Oil PVT Text'; + + app.OilPVTTextArea = uitextarea(app.OilWaterGrid); + app.OilPVTTextArea.Layout.Row = 2; + app.OilPVTTextArea.Layout.Column = 2; + + app.MultiComponentGrid = uigridlayout(app.MultiComponentTab); + app.MultiComponentGrid.ColumnWidth = {180, '1x', 180, '1x'}; + app.MultiComponentGrid.RowHeight = {120, 120, 120}; + app.MultiComponentGrid.Padding = [12 12 12 12]; + + app.AdsorptionLabel = uilabel(app.MultiComponentGrid); + app.AdsorptionLabel.Layout.Row = 1; + app.AdsorptionLabel.Layout.Column = 1; + app.AdsorptionLabel.Text = 'Adsorption Table'; + + app.AdsorptionTextArea = uitextarea(app.MultiComponentGrid); + app.AdsorptionTextArea.Layout.Row = 1; + app.AdsorptionTextArea.Layout.Column = 2; + + app.NcTableLabel = uilabel(app.MultiComponentGrid); + app.NcTableLabel.Layout.Row = 1; + app.NcTableLabel.Layout.Column = 3; + app.NcTableLabel.Text = 'Nc Table'; + + app.NcTableTextArea = uitextarea(app.MultiComponentGrid); + app.NcTableTextArea.Layout.Row = 1; + app.NcTableTextArea.Layout.Column = 4; + + app.KrNoSurfLabel = uilabel(app.MultiComponentGrid); + app.KrNoSurfLabel.Layout.Row = 2; + app.KrNoSurfLabel.Layout.Column = 1; + app.KrNoSurfLabel.Text = 'Kr No Surf'; + + app.KrNoSurfTextArea = uitextarea(app.MultiComponentGrid); + app.KrNoSurfTextArea.Layout.Row = 2; + app.KrNoSurfTextArea.Layout.Column = 2; + + app.KrSurfLabel = uilabel(app.MultiComponentGrid); + app.KrSurfLabel.Layout.Row = 2; + app.KrSurfLabel.Layout.Column = 3; + app.KrSurfLabel.Text = 'Kr Surf'; + + app.KrSurfTextArea = uitextarea(app.MultiComponentGrid); + app.KrSurfTextArea.Layout.Row = 2; + app.KrSurfTextArea.Layout.Column = 4; + + app.PcTableLabel = uilabel(app.MultiComponentGrid); + app.PcTableLabel.Layout.Row = 3; + app.PcTableLabel.Layout.Column = 1; + app.PcTableLabel.Text = 'Pc Table'; + + app.PcTableTextArea = uitextarea(app.MultiComponentGrid); + app.PcTableTextArea.Layout.Row = 3; + app.PcTableTextArea.Layout.Column = [2 4]; + + % ---------------- Part6 ---------------- + app.Part6Grid = uigridlayout(app.Part6Tab); + app.Part6Grid.ColumnWidth = {180, '1x'}; + app.Part6Grid.RowHeight = {120, 120, 120, 160, 80, 80, '1x'}; + app.Part6Grid.Padding = [12 12 12 12]; + + app.Well1Label = uilabel(app.Part6Grid); + app.Well1Label.Layout.Row = 1; + app.Well1Label.Layout.Column = 1; + app.Well1Label.Text = 'Well1'; + + app.Well1Table = uitable(app.Part6Grid); + app.Well1Table.Layout.Row = 1; + app.Well1Table.Layout.Column = 2; + + app.FractureWellLocationLabel = uilabel(app.Part6Grid); + app.FractureWellLocationLabel.Layout.Row = 2; + app.FractureWellLocationLabel.Layout.Column = 1; + app.FractureWellLocationLabel.Text = 'Fracture Well Locations'; + + app.FractureWellLocationTable = uitable(app.Part6Grid); + app.FractureWellLocationTable.Layout.Row = 2; + app.FractureWellLocationTable.Layout.Column = 2; + + app.Well2Label = uilabel(app.Part6Grid); + app.Well2Label.Layout.Row = 3; + app.Well2Label.Layout.Column = 1; + app.Well2Label.Text = 'Well2'; + + app.Well2Table = uitable(app.Part6Grid); + app.Well2Table.Layout.Row = 3; + app.Well2Table.Layout.Column = 2; + + app.ScheduleLabel = uilabel(app.Part6Grid); + app.ScheduleLabel.Layout.Row = 4; + app.ScheduleLabel.Layout.Column = 1; + app.ScheduleLabel.Text = 'Schedule'; + + app.ScheduleTable = uitable(app.Part6Grid); + app.ScheduleTable.Layout.Row = 4; + app.ScheduleTable.Layout.Column = 2; + + app.TimeLabel = uilabel(app.Part6Grid); + app.TimeLabel.Layout.Row = 5; + app.TimeLabel.Layout.Column = 1; + app.TimeLabel.Text = 'Time'; + + app.TimeTextArea = uitextarea(app.Part6Grid); + app.TimeTextArea.Layout.Row = 5; + app.TimeTextArea.Layout.Column = 2; + + app.DtMinLabel = uilabel(app.Part6Grid); + app.DtMinLabel.Layout.Row = 6; + app.DtMinLabel.Layout.Column = 1; + app.DtMinLabel.Text = 'dtmin'; + + app.DtMinTextArea = uitextarea(app.Part6Grid); + app.DtMinTextArea.Layout.Row = 6; + app.DtMinTextArea.Layout.Column = 2; + + app.DtMaxLabel = uilabel(app.Part6Grid); + app.DtMaxLabel.Layout.Row = 7; + app.DtMaxLabel.Layout.Column = 1; + app.DtMaxLabel.Text = 'dtmax'; + + app.DtMaxTextArea = uitextarea(app.Part6Grid); + app.DtMaxTextArea.Layout.Row = 7; + app.DtMaxTextArea.Layout.Column = 2; + + % ---------------- Part7 ---------------- + app.Part7Grid = uigridlayout(app.Part7Tab); + app.Part7Grid.ColumnWidth = {100, 100, 100, 100, 100, 100}; + app.Part7Grid.RowHeight = {26, 32, '1x'}; + app.Part7Grid.Padding = [12 12 12 12]; + + app.YitaPLabel = uilabel(app.Part7Grid); + app.YitaPLabel.Layout.Row = 1; + app.YitaPLabel.Layout.Column = 1; + app.YitaPLabel.Text = 'yitap'; + + app.YitaPField = uieditfield(app.Part7Grid, 'numeric'); + app.YitaPField.Layout.Row = 2; + app.YitaPField.Layout.Column = 1; + + app.YitaSLabel = uilabel(app.Part7Grid); + app.YitaSLabel.Layout.Row = 1; + app.YitaSLabel.Layout.Column = 2; + app.YitaSLabel.Text = 'yitas'; + + app.YitaSField = uieditfield(app.Part7Grid, 'numeric'); + app.YitaSField.Layout.Row = 2; + app.YitaSField.Layout.Column = 2; + + app.OmegaLabel = uilabel(app.Part7Grid); + app.OmegaLabel.Layout.Row = 1; + app.OmegaLabel.Layout.Column = 3; + app.OmegaLabel.Text = 'omega'; + + app.OmegaField = uieditfield(app.Part7Grid, 'numeric'); + app.OmegaField.Layout.Row = 2; + app.OmegaField.Layout.Column = 3; + + app.NmaxLabel = uilabel(app.Part7Grid); + app.NmaxLabel.Layout.Row = 1; + app.NmaxLabel.Layout.Column = 4; + app.NmaxLabel.Text = 'Nmax'; + + app.NmaxField = uieditfield(app.Part7Grid, 'numeric'); + app.NmaxField.Layout.Row = 2; + app.NmaxField.Layout.Column = 4; + + app.EpsAveLabel = uilabel(app.Part7Grid); + app.EpsAveLabel.Layout.Row = 1; + app.EpsAveLabel.Layout.Column = 5; + app.EpsAveLabel.Text = 'epsave'; + + app.EpsAveField = uieditfield(app.Part7Grid, 'numeric'); + app.EpsAveField.Layout.Row = 2; + app.EpsAveField.Layout.Column = 5; + + app.EpsMaxLabel = uilabel(app.Part7Grid); + app.EpsMaxLabel.Layout.Row = 1; + app.EpsMaxLabel.Layout.Column = 6; + app.EpsMaxLabel.Text = 'epsmax'; + + app.EpsMaxField = uieditfield(app.Part7Grid, 'numeric'); + app.EpsMaxField.Layout.Row = 2; + app.EpsMaxField.Layout.Column = 6; + + % ---------------- RunTab ---------------- + app.RunGrid = uigridlayout(app.RunTab); + app.RunGrid.ColumnWidth = {140, 180, 180, '1x'}; + app.RunGrid.RowHeight = {36, '1x'}; + app.RunGrid.Padding = [12 12 12 12]; + + app.RunCurrentConfigButton = uibutton(app.RunGrid, 'push'); + app.RunCurrentConfigButton.Layout.Row = 1; + app.RunCurrentConfigButton.Layout.Column = 1; + app.RunCurrentConfigButton.Text = 'Run Current Config'; + + app.CurrentStageLabel = uilabel(app.RunGrid); + app.CurrentStageLabel.Layout.Row = 1; + app.CurrentStageLabel.Layout.Column = 2; + app.CurrentStageLabel.Text = 'Stage: -'; + + app.CurrentTimeLabel = uilabel(app.RunGrid); + app.CurrentTimeLabel.Layout.Row = 1; + app.CurrentTimeLabel.Layout.Column = 3; + app.CurrentTimeLabel.Text = 'Time: -'; + + app.CurrentDtLabel = uilabel(app.RunGrid); + app.CurrentDtLabel.Layout.Row = 1; + app.CurrentDtLabel.Layout.Column = 4; + app.CurrentDtLabel.Text = 'dt: -'; + + app.RunLogTextArea = uitextarea(app.RunGrid); + app.RunLogTextArea.Layout.Row = 2; + app.RunLogTextArea.Layout.Column = [1 4]; + app.RunLogTextArea.Editable = 'off'; + + % ---------------- ResultTab ---------------- + app.ResultGrid = uigridlayout(app.ResultTab); + app.ResultGrid.ColumnWidth = {360, '1x'}; + app.ResultGrid.RowHeight = {'1x'}; + app.ResultGrid.Padding = [12 12 12 12]; + + app.ResultLeftGrid = uigridlayout(app.ResultGrid); + app.ResultLeftGrid.Layout.Row = 1; + app.ResultLeftGrid.Layout.Column = 1; + app.ResultLeftGrid.ColumnWidth = {'1x'}; + app.ResultLeftGrid.RowHeight = {24, 180, 24, 32, 36, '1x'}; + + app.ResultSummaryLabel = uilabel(app.ResultLeftGrid); + app.ResultSummaryLabel.Layout.Row = 1; + app.ResultSummaryLabel.Layout.Column = 1; + app.ResultSummaryLabel.Text = 'Result Summary'; + + app.ResultSummaryTextArea = uitextarea(app.ResultLeftGrid); + app.ResultSummaryTextArea.Layout.Row = 2; + app.ResultSummaryTextArea.Layout.Column = 1; + app.ResultSummaryTextArea.Editable = 'off'; + + app.ResultTypeLabel = uilabel(app.ResultLeftGrid); + app.ResultTypeLabel.Layout.Row = 3; + app.ResultTypeLabel.Layout.Column = 1; + app.ResultTypeLabel.Text = 'Result Type'; + + app.ResultTypeDropDown = uidropdown(app.ResultLeftGrid); + app.ResultTypeDropDown.Layout.Row = 4; + app.ResultTypeDropDown.Layout.Column = 1; + app.ResultTypeDropDown.Items = {'Time Steps', 'Newtons vs Time'}; + app.ResultTypeDropDown.Value = 'Time Steps'; + + app.PlotResultButton = uibutton(app.ResultLeftGrid, 'push'); + app.PlotResultButton.Layout.Row = 5; + app.PlotResultButton.Layout.Column = 1; + app.PlotResultButton.Text = 'Plot Result'; + + app.ResultAxes = uiaxes(app.ResultGrid); + app.ResultAxes.Layout.Row = 1; + app.ResultAxes.Layout.Column = 2; + title(app.ResultAxes, 'Results'); + xlabel(app.ResultAxes, 'X'); + ylabel(app.ResultAxes, 'Y'); + + % Show the figure after all components are created + app.UIFigure.Visible = 'on'; + end + end + + % App creation and deletion + methods (Access = public) + + % Construct app + function app = EDFM_Simulator_App + + % Create UIFigure and components + createComponents(app) + + % Register the app with App Designer + registerApp(app, app.UIFigure) + + if nargout == 0 + clear app + end + end + + % Code that executes before app deletion + function delete(app) + + % Delete UIFigure when app is deleted + delete(app.UIFigure) + end + end +end diff --git a/gui_support/app/EDFM_Simulator_App.mlapp b/gui_support/app/EDFM_Simulator_App.mlapp new file mode 100644 index 0000000..cbb9179 Binary files /dev/null and b/gui_support/app/EDFM_Simulator_App.mlapp differ diff --git a/gui_support/app/launch_edfm_simulator_app.m b/gui_support/app/launch_edfm_simulator_app.m new file mode 100644 index 0000000..8d5e7cf --- /dev/null +++ b/gui_support/app/launch_edfm_simulator_app.m @@ -0,0 +1,5 @@ +function app = launch_edfm_simulator_app() +%LAUNCH_EDFM_SIMULATOR_APP Launch the EDFM Simulator App. + +startup; +app = EDFMSimulatorApp(); diff --git a/gui_support/config/create_empty_config.m b/gui_support/config/create_empty_config.m new file mode 100644 index 0000000..76c7c50 --- /dev/null +++ b/gui_support/config/create_empty_config.m @@ -0,0 +1,248 @@ +function config = create_empty_config() +%CREATE_EMPTY_CONFIG Create a unified GUI/runtime config skeleton. + +config = struct(); + +config.meta = struct( ... + 'case_name', '', ... + 'case_id', '', ... + 'description', '', ... + 'source_case_folder', '', ... + 'created_from_template', '', ... + 'version', '0.1'); + +config.model = struct( ... + 'modelflag', 1, ... + 'grid_model', 1, ... + 'flow_model', 1); + +config.grid = struct( ... + 'dx', [], ... + 'dy', [], ... + 'dz', [], ... + 'nx', 0, ... + 'ny', 0, ... + 'nz', 0, ... + 'NTG', [], ... + 'coordinates', [], ... + 'nodes', [], ... + 'nP', 0, ... + 'nmc', 0, ... + 'dxv', [], ... + 'dyv', [], ... + 'dzv', [], ... + 'zm', [], ... + 'vm', [], ... + 'xrao', [], ... + 'yrao', [], ... + 'zrao', [], ... + 'cell_mid_coords', []); + +config.fracture = struct( ... + 'input_style', 1, ... + 'input_content', {{}}, ... + 'f', [], ... + 'fellip', [], ... + 'fractureLines', [], ... + 'fractureHeights', [], ... + 'flowBarrierFlags', [], ... + 'frac_information', [], ... + 'nf', 0); + +config.discretization = struct( ... + 'mode', 1, ... + 'use_operator', true, ... + 'sp', struct( ... + 'boundary_polygon', [], ... + 'invalid_layer', [], ... + 'valid_grids', [], ... + 'matrix', struct( ... + 'kx', [], ... + 'ky', [], ... + 'kz', [], ... + 'pori', [], ... + 'prpor', 20, ... + 'cpor', 1.0e-5, ... + 'rock_density', 2700), ... + 'fracture', struct( ... + 'Kf', [], ... + 'Wf', [], ... + 'Porf', [], ... + 'prporf', 20, ... + 'cporf', 1.0e-5), ... + 'stress', struct( ... + 'fracture_factor', 0, ... + 'matrix_factor', 0, ... + 'ref_pressure', 20)), ... + 'dp', struct( ... + 'valid_grids', [], ... + 'NTG', [], ... + 'prpor', 20, ... + 'cpor', 1.0e-5, ... + 'rock_density', 2700, ... + 'fracture_layer', struct( ... + 'kx', [], ... + 'ky', [], ... + 'kz', [], ... + 'pori', []), ... + 'matrix_layer', struct( ... + 'kx', [], ... + 'ky', [], ... + 'kz', [], ... + 'pori', []), ... + 'shape_factor', [], ... + 'fracture', struct( ... + 'Kf', [], ... + 'Wf', [], ... + 'Porf', [], ... + 'prporf', 20, ... + 'cporf', 1.0e-5), ... + 'stress', struct( ... + 'fracture_factor', 0, ... + 'matrix_factor', 0, ... + 'ref_pressure', 20)), ... + 'notes', ''); + +config.flow = struct( ... + 'gas_water', struct( ... + 'gas_prop', struct( ... + 'VL', 0, ... + 'PL', 0, ... + 'Kn', 0, ... + 'Kn_modified_factor', 1, ... + 'beta_non_darcy_flow', 0), ... + 'p_grad_threshold', 0, ... + 'density_g_sc', 0, ... + 'density_w_sc', 1000, ... + 'gas_model', 1, ... + 'prg', [], ... + 'Bgi', [], ... + 'cg', [], ... + 'vgi', [], ... + 'cvg', [], ... + 'Ppr', [], ... + 'BG', [], ... + 'MUG', [], ... + 'prw', [], ... + 'Bwi', [], ... + 'cw', [], ... + 'vwi', [], ... + 'cvw', [], ... + 'ifpcgl', 0, ... + 'Ppr', [], ... + 'BG', [], ... + 'MUG', [], ... + 'matrix_relperm_table', [], ... + 'fracture_relperm_table', []), ... + 'oil_water', struct( ... + 'density_o_sc', 0, ... + 'density_w_sc', 1000, ... + 'oil_model', 1, ... + 'pro', [], ... + 'Boi', [], ... + 'co', [], ... + 'voi', [], ... + 'cvo', [], ... + 'Ppr', [], ... + 'BO', [], ... + 'MUO', [], ... + 'prw', [], ... + 'Bwi', [], ... + 'cw', [], ... + 'vwi', [], ... + 'cvw', [], ... + 'ifpcow', 0, ... + 'Ppr', [], ... + 'BO', [], ... + 'MUO', [], ... + 'matrix_relperm_table', [], ... + 'fracture_relperm_table', [], ... + 'beta_non_darcy_flow', 0, ... + 'p_grad_threshold', 0), ... + 'multi_component', struct( ... + 'Ds', [], ... + 'Db', [], ... + 'c_ca_table', [], ... + 'cs_data', [], ... + 'cb_data', [], ... + 'csa_data', [], ... + 'cba_data', [], ... + 'R', [], ... + 'Vm', [], ... + 'Temperature', [], ... + 'chemistry_cof', [], ... + 'x_matrix', 0.9, ... + 'x_fracture', 1.0, ... + 'cs_Nc', [], ... + 'Nc_nosurf', [], ... + 'Nc_surf', [], ... + 'kr_nosurf', [], ... + 'kr_surf', [], ... + 'PC', [], ... + 'cs_Nc_fracture', [], ... + 'kr_nosurf_fracture', [], ... + 'kr_surf_fracture', [], ... + 'PC_fracture', [], ... + 'ifpcgl', 0, ... + 'p_grad_threshold', 0, ... + 'gas_prop', struct( ... + 'VL', 0, ... + 'PL', 0, ... + 'Kn', 0, ... + 'Kn_modified_factor', 1, ... + 'stress_factor_fracture', 0, ... + 'stress_factor_matrix', 0, ... + 'stress_factor_ref_pressure', 20, ... + 'beta_non_darcy_flow', 0), ... + 'density_g_sc', 0, ... + 'density_w_sc', 1000, ... + 'gas_model', 1, ... + 'prg', [], ... + 'Bgi', [], ... + 'cg', [], ... + 'vgi', [], ... + 'cvg', [], ... + 'Ppr', [], ... + 'BG', [], ... + 'MUG', [], ... + 'prw', [], ... + 'Bwi', [], ... + 'cw', [], ... + 'vwi', [], ... + 'cvw', [], ... + 'number_state_variables', 4)); + +config.initial = struct( ... + 'pressure', [], ... + 'sw', [], ... + 'cs', [], ... + 'cb', []); + +config.wells = struct( ... + 'well1', {{}}, ... + 'num_fracture_wells', 0, ... + 'welloc', {{}}, ... + 'perfnum', {{}}, ... + 'well2', {{}}, ... + 'Wellc', []); + +config.schedule = struct( ... + 'number_phases', 0, ... + 'well_schedules', {{}}, ... + 'time', [], ... + 'dtmax', [], ... + 'dtmin', []); + +config.solver = struct( ... + 'yitap', 5, ... + 'yitas', 0.04, ... + 'omega', 0.5, ... + 'Nmax', 50, ... + 'epsave', 1e-6, ... + 'epsmax', 1e-6); + +config.output = struct( ... + 'save_input_path', '', ... + 'save_result_path', '', ... + 'result_name', '', ... + 'plot_requests', {{}}); diff --git a/gui_support/config/load_config_json.m b/gui_support/config/load_config_json.m new file mode 100644 index 0000000..a2ea820 --- /dev/null +++ b/gui_support/config/load_config_json.m @@ -0,0 +1,13 @@ +function config = load_config_json(file_path) +%LOAD_CONFIG_JSON Load a config struct from a JSON file. + +arguments + file_path (1,:) char +end + +if ~exist(file_path, 'file') + error('load_config_json:FileNotFound', 'Config file not found: %s', file_path); +end + +json_text = fileread(file_path); +config = jsondecode(json_text); diff --git a/gui_support/config/load_config_mat.m b/gui_support/config/load_config_mat.m new file mode 100644 index 0000000..f77948a --- /dev/null +++ b/gui_support/config/load_config_mat.m @@ -0,0 +1,18 @@ +function config = load_config_mat(file_path) +%LOAD_CONFIG_MAT Load config struct from a MAT file. + +arguments + file_path (1,:) char +end + +if ~exist(file_path, 'file') + error('load_config_mat:FileNotFound', 'Config file not found: %s', file_path); +end + +data = load(file_path, '-mat'); +if ~isfield(data, 'config') + error('load_config_mat:MissingConfigVariable', ... + 'MAT file does not contain variable ''config'': %s', file_path); +end + +config = data.config; diff --git a/gui_support/config/save_config_json.m b/gui_support/config/save_config_json.m new file mode 100644 index 0000000..c38312b --- /dev/null +++ b/gui_support/config/save_config_json.m @@ -0,0 +1,21 @@ +function save_config_json(config, file_path) +%SAVE_CONFIG_JSON Save a config struct to a JSON file. + +arguments + config (1,1) struct + file_path (1,:) char +end + +folder_path = fileparts(file_path); +if ~isempty(folder_path) && ~exist(folder_path, 'dir') + mkdir(folder_path); +end + +json_text = jsonencode(config, PrettyPrint=true); +fid = fopen(file_path, 'w'); +if fid == -1 + error('save_config_json:FileOpenFailed', 'Cannot open file: %s', file_path); +end + +cleanup_obj = onCleanup(@() fclose(fid)); +fprintf(fid, '%s', json_text); diff --git a/gui_support/config/save_config_mat.m b/gui_support/config/save_config_mat.m new file mode 100644 index 0000000..e61b435 --- /dev/null +++ b/gui_support/config/save_config_mat.m @@ -0,0 +1,14 @@ +function save_config_mat(config, file_path) +%SAVE_CONFIG_MAT Save config struct to a MAT file. + +arguments + config (1,1) struct + file_path (1,:) char +end + +folder_path = fileparts(file_path); +if ~isempty(folder_path) && ~exist(folder_path, 'dir') + mkdir(folder_path); +end + +save(file_path, 'config', '-mat'); diff --git a/gui_support/results/load_results_mat.m b/gui_support/results/load_results_mat.m new file mode 100644 index 0000000..79fe0c9 --- /dev/null +++ b/gui_support/results/load_results_mat.m @@ -0,0 +1,18 @@ +function results = load_results_mat(file_path) +%LOAD_RESULTS_MAT Load results struct from a MAT file. + +arguments + file_path (1,:) char +end + +if ~exist(file_path, 'file') + error('load_results_mat:FileNotFound', 'Results file not found: %s', file_path); +end + +data = load(file_path, '-mat'); +if ~isfield(data, 'results') + error('load_results_mat:MissingResultsVariable', ... + 'MAT file does not contain variable ''results'': %s', file_path); +end + +results = data.results; diff --git a/gui_support/results/save_results_mat.m b/gui_support/results/save_results_mat.m new file mode 100644 index 0000000..ccd8e72 --- /dev/null +++ b/gui_support/results/save_results_mat.m @@ -0,0 +1,14 @@ +function save_results_mat(results, file_path) +%SAVE_RESULTS_MAT Save results struct to a MAT file. + +arguments + results (1,1) struct + file_path (1,:) char +end + +folder_path = fileparts(file_path); +if ~isempty(folder_path) && ~exist(folder_path, 'dir') + mkdir(folder_path); +end + +save(file_path, 'results', '-mat'); diff --git a/gui_support/runtime/run_case.m b/gui_support/runtime/run_case.m new file mode 100644 index 0000000..01d8eef --- /dev/null +++ b/gui_support/runtime/run_case.m @@ -0,0 +1,413 @@ +function [r, Times, OutputRs, Wellpara, trun] = run_case(config) +%RUN_CASE Execute a simulation from unified config. +% +% This is the first migration step from hard-coded main1.m scripts toward a +% unified GUI entry point. The current implementation is intended to +% reproduce case-template runs while the remaining flow-model internals are +% still being normalized. + +arguments + config (1,1) struct +end + +project_root = fileparts(fileparts(fileparts(mfilename('fullpath')))); +source_case_folder = resolve_source_case_folder(project_root, config.meta); +if isempty(source_case_folder) + error('run_case:MissingSourceCaseFolder', ... + 'config.meta.source_case_folder is required.'); +end + +if ~isfolder(source_case_folder) + error('run_case:SourceCaseFolderNotFound', ... + 'Source case folder not found: %s', source_case_folder); +end + +addpath(genpath(project_root)); + +old_dir = pwd; +cleanup_obj = onCleanup(@() cd(old_dir)); +cd(source_case_folder); + +overall_tic = tic; + +config.grid.nx = numel(config.grid.dx); +config.grid.ny = numel(config.grid.dy); +config.grid.nz = numel(config.grid.dz); + +r = GridProp_pre( ... + config.grid.dx, ... + config.grid.dy, ... + config.grid.dz, ... + config.grid.nx, ... + config.grid.ny, ... + config.grid.nz, ... + config.grid.NTG); + +[f, fellip, frac_information, nf] = build_fracture_inputs(config.fracture); + +modelflag = config.model.modelflag; +grid_model = config.model.grid_model; +flow_model = config.model.flow_model; + +if grid_model == 1 + r = grid_discretization_SP_model(modelflag, r, f, frac_information, fellip, nf); +elseif grid_model == 2 + r = grid_discretization_DP_model(modelflag, r, f, frac_information, fellip, nf); +else + error('run_case:UnsupportedGridModel', 'Unsupported grid model: %d', grid_model); +end + +os = OperatorRS(r.N, r.nex, r.nc); + +[fluid_model, state0] = build_flow_inputs(flow_model, r, config); + +well1 = handle_well1(config.wells.well1, r); +[well2, welloc] = build_fracture_wells(config.wells, r); +Wellc = handle_well1_well2(well1, well2, welloc, r); + +[Times, OutputRs, Wellpara, trun] = solver_NR( ... + r, ... + flow_model, ... + fluid_model, ... + os, ... + state0, ... + config.solver.yitap, ... + config.solver.yitas, ... + config.solver.omega, ... + config.solver.Nmax, ... + config.solver.epsave, ... + config.solver.epsmax, ... + config.schedule.dtmin, ... + config.schedule.dtmax, ... + Wellc, ... + config.schedule.time, ... + config.schedule.well_schedules); + +trun.run_time = toc(overall_tic); +end + +function source_case_folder = resolve_source_case_folder(project_root, meta_config) +source_case_folder = meta_config.source_case_folder; + +if isfolder(source_case_folder) + return; +end + +case_id = lower(string(meta_config.case_id)); +switch case_id + case "case01" + source_case_folder = find_case_folder_by_index(project_root, 1); + otherwise + if isempty(source_case_folder) || startsWith(string(source_case_folder), "CASE") + source_case_folder = ''; + end +end +end + +function source_case_folder = find_case_folder_by_index(project_root, case_index) +folder_candidates = dir(fullfile(project_root, sprintf('*%d-*', case_index))); +folder_candidates = folder_candidates([folder_candidates.isdir]); + +if isempty(folder_candidates) + source_case_folder = ''; +else + source_case_folder = fullfile(project_root, folder_candidates(1).name); +end +end + +function [f, fellip, frac_information, nf] = build_fracture_inputs(fracture_config) +input_style = fracture_config.input_style; + +switch input_style + case 1 + [f, fellip] = sort_fracture(fracture_config.input_content); + m = size(f, 1); + n = size(fellip, 1); + nf = (m + n) / 5; + frac_information = fractureInformation_input_engineering_vector( ... + f, fellip, fracture_config.flowBarrierFlags); + case 2 + f = fracture_config.f; + fellip = fracture_config.fellip; + m = size(f, 1); + n = size(fellip, 1); + nf = (m + n) / 5; + frac_information = fractureInformation_input_engineering_vector(f, fellip); + case 3 + fellip = fracture_config.fellip; + [f, frac_information] = input_fracture_2D( ... + fracture_config.fractureLines, ... + fracture_config.fractureHeights, ... + fracture_config.flowBarrierFlags); + m = size(f, 1); + n = size(fellip, 1); + nf = (m + n) / 5; + otherwise + error('run_case:UnsupportedFractureInputStyle', ... + 'Unsupported fracture input style: %d', input_style); +end +end + +function [fluid_model, state0] = build_flow_inputs(flow_model, r, config) +switch flow_model + case 1 + if has_complete_gas_water_config(config.flow.gas_water) + [fluid_model, state0] = build_gas_water_flow_from_config( ... + config.flow.gas_water, config.initial, r); + else + [fluid_model, state0] = gas_water_flow(r); + end + case 2 + if has_complete_oil_water_config(config.flow.oil_water) + [fluid_model, state0] = build_oil_water_flow_from_config( ... + config.flow.oil_water, config.initial, r); + else + [fluid_model, state0] = oil_water_flow(r); + end + case 3 + if has_complete_multicomponent_config(config.flow.multi_component) + [fluid_model, state0] = build_multi_component_flow_from_config( ... + config.flow.multi_component, config.initial, r); + else + [fluid_model, state0] = multi_component_flow(r); + end + otherwise + error('run_case:UnsupportedFlowModel', ... + 'Unsupported flow model: %d', flow_model); +end +end + +function tf = has_complete_gas_water_config(gw) +required_fields = { ... + 'density_g_sc', 'density_w_sc', 'gas_model', 'prw', 'Bwi', 'cw', ... + 'vwi', 'cvw', 'ifpcgl', 'matrix_relperm_table', 'fracture_relperm_table', ... + 'p_grad_threshold'}; + +tf = true; +for i = 1:numel(required_fields) + field_name = required_fields{i}; + if ~isfield(gw, field_name) || isempty(gw.(field_name)) + tf = false; + return; + end +end + +if ~isfield(gw, 'gas_prop') || isempty(gw.gas_prop) + tf = false; + return; +end + +if gw.gas_model == 1 + tf = all(isfield(gw, {'prg', 'Bgi', 'cg', 'vgi', 'cvg'})) && ... + ~isempty(gw.prg) && ~isempty(gw.Bgi) && ~isempty(gw.cg) && ... + ~isempty(gw.vgi) && ~isempty(gw.cvg); +else + tf = all(isfield(gw, {'Ppr', 'BG', 'MUG'})) && ... + ~isempty(gw.Ppr) && ~isempty(gw.BG) && ~isempty(gw.MUG); +end +end + +function tf = has_complete_oil_water_config(ow) +required_fields = { ... + 'density_o_sc', 'density_w_sc', 'oil_model', 'prw', 'Bwi', 'cw', ... + 'vwi', 'cvw', 'ifpcow', 'matrix_relperm_table', 'fracture_relperm_table', ... + 'beta_non_darcy_flow', 'p_grad_threshold'}; + +tf = true; +for i = 1:numel(required_fields) + field_name = required_fields{i}; + if ~isfield(ow, field_name) || isempty(ow.(field_name)) + tf = false; + return; + end +end + +if ow.oil_model == 1 + tf = all(isfield(ow, {'pro', 'Boi', 'co', 'voi', 'cvo'})) && ... + ~isempty(ow.pro) && ~isempty(ow.Boi) && ~isempty(ow.co) && ... + ~isempty(ow.voi) && ~isempty(ow.cvo); +else + tf = all(isfield(ow, {'Ppr', 'BO', 'MUO'})) && ... + ~isempty(ow.Ppr) && ~isempty(ow.BO) && ~isempty(ow.MUO); +end +end + +function [f, state0] = build_gas_water_flow_from_config(gw, initial_config, r) +if gw.gas_model == 1 + [Ppr, BG, MUG] = cal_gas_prop(gw.prg, gw.Bgi, gw.cg, gw.vgi, gw.cvg); +else + Ppr = gw.Ppr; + BG = gw.BG; + MUG = gw.MUG; +end + +matrix_table = gw.matrix_relperm_table; +fracture_table = gw.fracture_relperm_table; + +f = fluidPVT_gas_water_flow( ... + Ppr, BG, MUG, gw.Bwi, gw.prw, gw.cw, gw.vwi, gw.cvw, ... + matrix_table(:, 1), matrix_table(:, 3), matrix_table(:, 2), matrix_table(:, 4), ... + fracture_table(:, 1), fracture_table(:, 3), fracture_table(:, 2), fracture_table(:, 4), ... + gw.density_g_sc, gw.ifpcgl, r.rpt); + +gas_prop = gw.gas_prop; +c1 = 1; +c2 = 8 / 9; +gas_prop.Kn_modified_factor = 1 + 8 * c1 * gas_prop.Kn + 16 * c2 * gas_prop.Kn^2; + +f.Dwsi = gw.density_w_sc; +f.Dgsi = gw.density_g_sc; +f.gas_prop = gas_prop; +f.p_grad_threshold = gw.p_grad_threshold; + +total_cells = r.nmc + r.nfc; +pressure0 = expand_initial_value(initial_config.pressure, total_cells); +sw0 = expand_initial_value(initial_config.sw, total_cells); +state0 = initialRS_gas_water_flow(pressure0, sw0); +end + +function [f, state0] = build_oil_water_flow_from_config(ow, initial_config, r) +if ow.oil_model == 1 + [Ppr, BO, MUO] = cal_oil_prop(ow.pro, ow.Boi, ow.co, ow.voi, ow.cvo); +else + Ppr = ow.Ppr; + BO = ow.BO; + MUO = ow.MUO; +end + +matrix_table = ow.matrix_relperm_table; +fracture_table = ow.fracture_relperm_table; + +f = fluidPVT_oil_water_flow( ... + Ppr, BO, MUO, ow.Bwi, ow.prw, ow.cw, ow.vwi, ow.cvw, ... + matrix_table(:, 1), matrix_table(:, 3), matrix_table(:, 2), matrix_table(:, 4), ... + fracture_table(:, 1), fracture_table(:, 3), fracture_table(:, 2), fracture_table(:, 4), ... + ow.density_o_sc, ow.ifpcow, r.rpt); + +f.beta_non_Darcy_flow = ow.beta_non_darcy_flow; +f.Dwsi = ow.density_w_sc; +f.Dosi = ow.density_o_sc; +f.p_grad_threshold = ow.p_grad_threshold; + +total_cells = r.nmc + r.nfc; +pressure0 = expand_initial_value(initial_config.pressure, total_cells); +sw0 = expand_initial_value(initial_config.sw, total_cells); +state0 = initialRS_oil_water_flow(pressure0, sw0); +end + +function tf = has_complete_multicomponent_config(mc) +required_fields = { ... + 'Ds', 'Db', 'c_ca_table', 'R', 'Vm', 'Temperature', ... + 'cs_Nc', 'kr_nosurf', 'kr_surf', 'PC', ... + 'cs_Nc_fracture', 'kr_nosurf_fracture', 'kr_surf_fracture', 'PC_fracture', ... + 'density_g_sc', 'density_w_sc', 'gas_model', 'prw', 'Bwi', 'cw', 'vwi', 'cvw'}; + +tf = true; +for i = 1:numel(required_fields) + field_name = required_fields{i}; + if ~isfield(mc, field_name) || isempty(mc.(field_name)) + tf = false; + return; + end +end + +if ~isfield(mc, 'gas_prop') || isempty(mc.gas_prop) + tf = false; + return; +end + +if mc.gas_model == 1 + tf = all(isfield(mc, {'prg', 'Bgi', 'cg', 'vgi', 'cvg'})) && ... + ~isempty(mc.prg) && ~isempty(mc.Bgi) && ~isempty(mc.cg) && ... + ~isempty(mc.vgi) && ~isempty(mc.cvg); +else + tf = all(isfield(mc, {'Ppr', 'BG', 'MUG'})) && ... + ~isempty(mc.Ppr) && ~isempty(mc.BG) && ~isempty(mc.MUG); +end +end + +function [f, state0] = build_multi_component_flow_from_config(mc, initial_config, r) +cs_data = mc.c_ca_table(:, 1); +cb_data = mc.c_ca_table(:, 1); +csa_data = mc.c_ca_table(:, 2) * 1e-3; +cba_data = mc.c_ca_table(:, 3) * 1e-3; +Nc_nosurf = min(mc.cs_Nc(:, 2)); +Nc_surf = max(mc.cs_Nc(:, 2)); + +if mc.gas_model == 1 + [Ppr, BG, MUG] = cal_gas_prop(mc.prg, mc.Bgi, mc.cg, mc.vgi, mc.cvg); +else + Ppr = mc.Ppr; + BG = mc.BG; + MUG = mc.MUG; +end + +f = fluidPVT_new( ... + Ppr, BG, MUG, mc.Bwi, mc.prw, mc.cw, mc.vwi, mc.cvw, ... + mc.ifpcgl, r.rpt, cs_data, csa_data, cb_data, cba_data, ... + mc.cs_Nc, Nc_nosurf, Nc_surf, mc.kr_nosurf, mc.kr_surf, mc.PC, ... + mc.cs_Nc_fracture, mc.kr_nosurf_fracture, mc.kr_surf_fracture, mc.PC_fracture); + +gas_prop = mc.gas_prop; +c1 = 1; +c2 = 8 / 9; +gas_prop.Kn_modified_factor = 1 + 8 * c1 * gas_prop.Kn + 16 * c2 * gas_prop.Kn^2; + +f.Dwsi = mc.density_w_sc; +f.Dgsi = mc.density_g_sc; +f.gas_prop = gas_prop; +f.Ds = mc.Ds; +f.Db = mc.Db; +f.chemistry_cof = mc.R * mc.Temperature / mc.Vm * 1e-3 / 10; +f.p_grad_threshold = mc.p_grad_threshold; +f.number_state_variables = mc.number_state_variables; + +total_cells = r.nmc + r.nfc; +matrix_x = default_if_empty(mc, 'x_matrix', 0.9); +fracture_x = default_if_empty(mc, 'x_fracture', 1.0); +f.x_total = [matrix_x * ones(r.nmc, 1); fracture_x * ones(r.nfc, 1)]; +f.chemistry_potential = f.chemistry_cof * log(f.x_total); + +pressure0 = expand_initial_value(initial_config.pressure, total_cells); +sw0 = expand_initial_value(initial_config.sw, total_cells); +cs0 = expand_initial_value(initial_config.cs, total_cells); +cb0 = expand_initial_value(initial_config.cb, total_cells); +state0 = initialRS(pressure0, sw0, cs0, cb0); +end + +function value = expand_initial_value(raw_value, total_cells) +if isscalar(raw_value) + value = raw_value * ones(total_cells, 1); +else + value = raw_value; +end +end + +function value = default_if_empty(s, field_name, fallback) +if isfield(s, field_name) && ~isempty(s.(field_name)) + value = s.(field_name); +else + value = fallback; +end +end + +function [well2, welloc] = build_fracture_wells(wells_config, r) +num_fracture_wells = wells_config.num_fracture_wells; +welloc = wells_config.welloc; +perfnum = cell(num_fracture_wells, 1); + +for i = 1:num_fracture_wells + perfnum{i, 1} = findWelloc(r, welloc{i, 1}); +end + +well2 = cell(num_fracture_wells, 6); +for i = 1:num_fracture_wells + if numel(wells_config.well2) >= i && ~isempty(wells_config.well2{i, 1}) + well2(i, :) = wells_config.well2(i, :); + well2{i, 2} = length(perfnum{i, 1}); + well2{i, 3} = perfnum{i, 1}; + else + well2(i, :) = {sprintf('wf%d', i), length(perfnum{i, 1}), perfnum{i, 1}, 0.178/2, 0, 4}; + end +end +end diff --git a/gui_support/templates/load_case_template.m b/gui_support/templates/load_case_template.m new file mode 100644 index 0000000..a6d9696 --- /dev/null +++ b/gui_support/templates/load_case_template.m @@ -0,0 +1,25 @@ +function config = load_case_template(case_id) +%LOAD_CASE_TEMPLATE Load a predefined case template into unified config. + +arguments + case_id +end + +case_key = normalize_case_id(case_id); + +switch case_key + case "case01" + config = load_case_template_case01(); + otherwise + error('load_case_template:UnsupportedCase', ... + 'Unsupported case template: %s', string(case_key)); +end +end + +function case_key = normalize_case_id(case_id) +if isnumeric(case_id) + case_key = "case" + compose("%02d", case_id); +else + case_key = lower(string(case_id)); +end +end diff --git a/gui_support/templates/load_case_template_case01.m b/gui_support/templates/load_case_template_case01.m new file mode 100644 index 0000000..48b78ba --- /dev/null +++ b/gui_support/templates/load_case_template_case01.m @@ -0,0 +1,189 @@ +function config = load_case_template_case01() +%LOAD_CASE_TEMPLATE_CASE01 Build config for case 1. + +config = create_empty_config(); + +config.meta.case_name = 'case01_multicomponent_irregular_domain'; +config.meta.case_id = 'case01'; +config.meta.description = 'Multicomponent flow with irregular heterogeneous domain'; +config.meta.source_case_folder = 'CASE01_FOLDER'; +config.meta.created_from_template = 'case01'; + +config.model.modelflag = 1; +config.model.grid_model = 1; +config.model.flow_model = 3; + +config.grid.dx = 50 * ones(1, 20); +config.grid.dy = 50 * ones(1, 10); +config.grid.dz = 10 * ones(1, 1); +config.grid.nx = numel(config.grid.dx); +config.grid.ny = numel(config.grid.dy); +config.grid.nz = numel(config.grid.dz); +config.grid.NTG = ones(config.grid.nx * config.grid.ny * config.grid.nz, 1); + +config.fracture.input_style = 1; +config.fracture.input_content = { + [255,255,5],90,90,0,200,10,1; + [305,255,5],90,90,0,200,10,1; + [355,255,5],90,90,0,200,10,1; + [405,255,5],90,90,0,200,10,1; + [455,255,5],90,90,0,200,10,1; + [505,255,5],90,90,0,200,10,1; + [555,255,5],90,90,0,200,10,1; + [605,255,5],90,90,0,200,10,1; + [655,255,5],90,90,0,200,10,1; + [705,255,5],90,90,0,200,10,1; + [755,255,5],90,90,0,200,10,1; + }; +config.fracture.flowBarrierFlags = []; + +config.discretization.mode = 1; +config.discretization.sp.boundary_polygon = [ + 0,100; + 800,50; + 1000,150; + 1000,350; + 800,450; + 0,400; + 0,100]; +config.discretization.sp.invalid_layer = []; +config.discretization.sp.matrix.prpor = 20; +config.discretization.sp.matrix.cpor = 1.0e-5; +config.discretization.sp.matrix.rock_density = 2700; +config.discretization.sp.fracture.prporf = 20; +config.discretization.sp.fracture.cporf = 1.0e-5; +config.discretization.sp.stress.fracture_factor = 0.0; +config.discretization.sp.stress.matrix_factor = 0.0; +config.discretization.sp.stress.ref_pressure = 20; + +config.flow.multi_component.Ds = 0.76e-9; +config.flow.multi_component.Db = 2.2e-9; +config.flow.multi_component.c_ca_table = [ + 0 0 0; + 0.05 1 0.075; + 0.1 1.75 0.125; + 0.2 3 0.2; + 0.5 4.35 0.325; + 1 4.55 0.365]; +config.flow.multi_component.R = 0.008314; +config.flow.multi_component.Vm = 18.02e-6; +config.flow.multi_component.Temperature = 293.15; +config.flow.multi_component.x_matrix = 0.9; +config.flow.multi_component.x_fracture = 1.0; +config.flow.multi_component.cs_Nc = [ + 0 1.6e-6 30; + 0.1 9.5e-6 10; + 0.2 2.5e-5 4; + 0.5 5.8e-5 1.4; + 1 8.2e-5 1; + 2 8.8e-5 0.75]; +config.flow.multi_component.kr_nosurf = [ + 0 0 0.64; + 0.25 0 0.64; + 0.30 0.072 0.6; + 0.40 0.14 0.56; + 0.50 0.26 0.48; + 0.60 0.4 0.38; + 0.70 0.56 0.26; + 0.85 0.88 0; + 1 0.88 0]; +config.flow.multi_component.kr_surf = [ + 0 0 0.6; + 0.18 0 0.6; + 0.25 0.056 0.56; + 0.30 0.1 0.52; + 0.40 0.19 0.42; + 0.50 0.29 0.3; + 0.60 0.37 0.19; + 0.75 0.46 0; + 1 0.46 0]; +config.flow.multi_component.PC = [ + 0 3.8916; + 0.2 3.8916; + 0.316 0.5796; + 0.435 0.3724; + 0.562 0.2425; + 0.614 0.0608; + 0.702 0.0372; + 0.812 0.0137; + 0.875 0.0104; + 0.906 0.009; + 0.937 0.0075; + 0.969 0.0059; + 1 0]; +config.flow.multi_component.cs_Nc_fracture = config.flow.multi_component.cs_Nc; +config.flow.multi_component.kr_nosurf_fracture = config.flow.multi_component.kr_nosurf; +config.flow.multi_component.kr_surf_fracture = config.flow.multi_component.kr_surf; +config.flow.multi_component.PC_fracture = config.flow.multi_component.PC; +config.flow.multi_component.ifpcgl = 0; +config.flow.multi_component.p_grad_threshold = 0.01; +config.flow.multi_component.gas_prop.VL = 0.0; +config.flow.multi_component.gas_prop.PL = 3.5; +config.flow.multi_component.gas_prop.Kn = 0; +config.flow.multi_component.gas_prop.stress_factor_fracture = 0.0; +config.flow.multi_component.gas_prop.stress_factor_matrix = 0.0; +config.flow.multi_component.gas_prop.stress_factor_ref_pressure = 20; +config.flow.multi_component.gas_prop.beta_non_darcy_flow = 0; +config.flow.multi_component.density_g_sc = 800; +config.flow.multi_component.gas_model = 1; +config.flow.multi_component.prg = 20; +config.flow.multi_component.Bgi = 1; +config.flow.multi_component.cg = 5e-4; +config.flow.multi_component.vgi = 6; +config.flow.multi_component.cvg = 0; +config.flow.multi_component.density_w_sc = 1000; +config.flow.multi_component.prw = 20; +config.flow.multi_component.Bwi = 1.0; +config.flow.multi_component.cw = 4.0e-4; +config.flow.multi_component.vwi = 1; +config.flow.multi_component.cvw = 0; +config.flow.multi_component.number_state_variables = 4; + +config.initial.pressure = 20; +config.initial.sw = 0.2; +config.initial.cs = 0.0; +config.initial.cb = 0.0; + +config.wells.well1 = {}; +config.wells.num_fracture_wells = 1; +config.wells.welloc = { + [ + 255,255,5; + 305,255,5; + 355,255,5; + 405,255,5; + 455,255,5; + 505,255,5; + 555,255,5; + 605,255,5; + 655,255,5; + 705,255,5; + 755,255,5 + ]}; +config.wells.well2 = { + 'w1', 0, [], 0.178/2, 0, 4 + }; + +config.schedule.number_phases = 3; +config.schedule.time = [10; 30; 200]; +config.schedule.dtmax = [0.1; 0.5; 0.5]; +config.schedule.dtmin = [0.001; 0.001; 0.001]; +config.schedule.well_schedules = { + { + 'w1','open','inj','const_pwf',40,40,'Cs_inj',0.5,'Cb_inj',1 + }; + { + 'w1','open','pro','const_q',0,0 + }; + { + 'w1','open','pro','const_pwf',10,10 + }}; + +config.solver.yitap = 5; +config.solver.yitas = 0.04; +config.solver.omega = 0.5; +config.solver.Nmax = 50; +config.solver.epsave = 1e-6; +config.solver.epsmax = 1e-6; + +config.output.result_name = 'case01_default_run';