//Method Name: AssignValues //Objectives: It gets the data from the ReadFile method and decides on which variables get values and which solvers are used or methods are called depending on these values //Inputs: a string to indicate file_name //Outputs: a variabe of type "SimulatorData" that contains the data read from the file in a form suitable for the use of the simulator public static SimulatorData ReadData(string file_name) { //a dictionary to store the data read from the file in a key-value pairs format Dictionary <string, string> dictionary = ReadFile(file_name + ".txt"); SimulatorData simulator_data = new SimulatorData(); if (dictionary.Count > 0) { // Chek if the input data file exists and it succefully was loaded. simulator_data.successfully_loaded_data = true; // Sets the file name. simulator_data.file_name = file_name; // This variable stores the size of the grid according to the numbering system used "active-blocks only, or all blocks". int size = 0; String key; #region Run Specs key = "natural_ordering"; if (dictionary.ContainsKey(key)) { if (dictionary[key] == "all_blocks") { simulator_data.natural_ordering = SimulatorData.NaturalOrdering.All_Blocks; } else if (dictionary[key] == "active_only") { simulator_data.natural_ordering = SimulatorData.NaturalOrdering.Active_Only; } } key = "single_phase_compressibility"; if (dictionary.ContainsKey(key)) { if (dictionary[key] == "incompressible") { simulator_data.compressibility = TypeDefinitions.Compressibility.Incompressible; } else if (dictionary[key] == "slightly_compressible") { simulator_data.compressibility = TypeDefinitions.Compressibility.Slightly_Compressible; } else if (dictionary[key] == "compressible") { simulator_data.compressibility = TypeDefinitions.Compressibility.Compressible; } } key = "grid_type"; if (dictionary.ContainsKey(key)) { if (dictionary[key] == "rectangular") { simulator_data.grid_type = Transmissibility.GridType.Rectangular; } else if (dictionary[key] == "cylindrical") { simulator_data.grid_type = Transmissibility.GridType.Cylindrical; } } key = "delta_t"; if (dictionary.ContainsKey(key)) { simulator_data.delta_t = double.Parse(dictionary[key]); } key = "time_max"; if (dictionary.ContainsKey(key)) { simulator_data.time_max = double.Parse(dictionary[key]); } key = "convergence_pressure"; if (dictionary.ContainsKey(key)) { simulator_data.convergence_pressure = double.Parse(dictionary[key]); } #endregion #region Output key = "what"; if (dictionary.ContainsKey(key)) { simulator_data.what = dictionary[key].Split(','); } key = "where"; if (dictionary.ContainsKey(key)) { string temp = dictionary[key]; simulator_data.where = temp == "console" ? SinglePhase.OutPut2D.Where.Console : SinglePhase.OutPut2D.Where.File; } key = "formatted"; if (dictionary.ContainsKey(key)) { string temp = dictionary[key]; simulator_data.formatted = temp == "yes" ? true : false; } key = "single_file"; if (dictionary.ContainsKey(key)) { string temp = dictionary[key]; simulator_data.single_file = temp == "yes" ? true : false; } #endregion #region Grid // For a rectangular grid if (simulator_data.grid_type == Transmissibility.GridType.Rectangular) { key = "homogeneous"; if (dictionary.ContainsKey(key)) { if (dictionary[key] == "yes") { simulator_data.homogeneous = true; } else { simulator_data.homogeneous = false; } } key = "inactive_blocks"; if (dictionary.ContainsKey(key)) { simulator_data.inactive_blocks = getArrayFromDictionary_int(dictionary[key]); } else { simulator_data.inactive_blocks = new int[0]; } key = "grid_dimensions"; if (dictionary.ContainsKey(key)) { string[] grid_dimensions = dictionary[key].Split(','); simulator_data.x = int.Parse(grid_dimensions[0]); simulator_data.y = int.Parse(grid_dimensions[1]); simulator_data.z = int.Parse(grid_dimensions[2]); } // Set the size of the grid according to the numbering system used "active-blocks only, or all blocks" if (simulator_data.natural_ordering == SimulatorData.NaturalOrdering.All_Blocks) { size = simulator_data.x * simulator_data.y * simulator_data.z; } else { size = simulator_data.x * simulator_data.y * simulator_data.z - simulator_data.inactive_blocks.Length; } ////////////////////////////////////////////////////////////////// //Block dimensions if (simulator_data.homogeneous == true) { simulator_data.delta_X = int.Parse(dictionary["delta_x"]); simulator_data.delta_Y = int.Parse(dictionary["delta_y"]); simulator_data.delta_Z = int.Parse(dictionary["delta_z"]); key = "depth_top"; if (dictionary.ContainsKey(key)) { simulator_data.depth_top = double.Parse(dictionary["depth_top"]); } } else { //Delta x values simulator_data.delta_X_array = getArrayFromDictionary_int(dictionary["delta_x"]); //Delta y values simulator_data.delta_Y_array = getArrayFromDictionary_int(dictionary["delta_y"]); //Heights values simulator_data.delta_Z_array = getArrayFromDictionary_int(dictionary["delta_z"]); key = "depth_top"; if (dictionary.ContainsKey(key)) { simulator_data.depth_top_array = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.depth_top_array = new double[size]; } } } // For a cylindrical grid // Single-well simulations. Always homogeneous rock properties. No in-active blocks else { } #endregion #region Rock Properties if (simulator_data.homogeneous) { if (dictionary.ContainsKey("Kz")) { // simulator_data.Kz_data = double.Parse(dictionary["Kz"]); } simulator_data.Kx_data = double.Parse(dictionary["Kx"]); simulator_data.Ky_data = double.Parse(dictionary["Ky"]); simulator_data.porosity = double.Parse(dictionary["porosity"]); //Rock Compressiblity key = "compressibility_rock"; if (dictionary.ContainsKey(key)) { simulator_data.compressibility_rock = double.Parse(dictionary["compressibility_rock"]); } } else { //Kx values simulator_data.Kx_data_array = getArrayFromDictionary_double(dictionary["Kx"]); //Ky values simulator_data.Ky_data_array = getArrayFromDictionary_double(dictionary["Ky"]); //Kz values if (dictionary.ContainsKey("Kz")) { simulator_data.Kz_data_array = getArrayFromDictionary_double(dictionary["Kz"]); } else { simulator_data.Kz_data_array = new double[size]; } //Porosity values simulator_data.porosity_array = getArrayFromDictionary_double(dictionary["porosity"]); //Rock Compressiblity key = "compressibility_rock"; if (dictionary.ContainsKey(key)) { simulator_data.compressibility_rock = double.Parse(dictionary["compressibility_rock"]); } } #endregion #region PVT key = "FVF"; if (dictionary.ContainsKey(key)) { simulator_data.FVF = double.Parse(dictionary[key]); } key = "density"; if (dictionary.ContainsKey(key)) { simulator_data.density = double.Parse(dictionary[key]); } key = "molecular_weight"; if (dictionary.ContainsKey(key)) { simulator_data.molecular_weight = double.Parse(dictionary[key]); } key = "temperature"; if (dictionary.ContainsKey(key)) { simulator_data.temperature = double.Parse(dictionary[key]); } key = "viscosity"; if (simulator_data.compressibility == TypeDefinitions.Compressibility.Incompressible) { if (dictionary.ContainsKey(key)) { simulator_data.viscosity = double.Parse(dictionary[key]); } } else { if (dictionary[key].Contains(',')) { double[][] temp = getTwoColumns(dictionary[key]); simulator_data.water_data[0] = temp[0]; simulator_data.water_data[2] = temp[1]; } else { simulator_data.viscosity = double.Parse(dictionary[key]); } } key = "compressibility_fluid"; if (dictionary.ContainsKey(key)) { simulator_data.compressibility_fluid = double.Parse(dictionary[key]); } key = "g_data_pressure"; if (dictionary.ContainsKey(key)) { ////pressure //simulator_data.g_data[0] = getArrayFromDictionary_double(dictionary, "g_data_pressure"); ////FVF //simulator_data.g_data[1] = getArrayFromDictionary_double(dictionary, "g_data_FVF"); ////Viscosity //simulator_data.g_data[2] = getArrayFromDictionary_double(dictionary, "g_data_viscosity"); ////Density //simulator_data.g_data[3] = getArrayFromDictionary_double(dictionary, "g_data_density"); } #endregion #region Boundary Conditions key = "initial_pressure"; if (dictionary.ContainsKey(key)) { simulator_data.initial_pressure = double.Parse(dictionary[key]); } key = "boundary_flow_rate"; if (dictionary.ContainsKey(key)) { simulator_data.boundary_flow_rate = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.boundary_flow_rate = new double[size]; } key = "boundary_pressure_x"; if (dictionary.ContainsKey(key)) { simulator_data.boundary_pressure_x = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.boundary_pressure_x = new double[size]; } key = "boundary_pressure_y"; if (dictionary.ContainsKey(key)) { simulator_data.boundary_pressure_y = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.boundary_pressure_y = new double[size]; } key = "boundary_pressure_gradient_x"; if (dictionary.ContainsKey(key)) { simulator_data.boundary_pressure_gradient_x = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.boundary_pressure_gradient_x = new double[size]; } key = "boundary_pressure_gradient_y"; if (dictionary.ContainsKey(key)) { simulator_data.boundary_pressure_gradient_y = getArrayFromDictionary_double(dictionary[key]); } else { simulator_data.boundary_pressure_gradient_y = new double[size]; } #endregion #region Well Data key = "well_locations"; if (dictionary.ContainsKey(key)) { simulator_data.well_locations = getArrayFromDictionary_int(dictionary[key]); } int number_of_wells = simulator_data.well_locations.Length; simulator_data.well_array = new Well[size]; for (int i = 1; i <= number_of_wells; i++) { Well well = new Well(); double[] well_data = getArrayFromDictionary_double(dictionary["well_" + i]); well.rw = well_data[0]; well.skin = well_data[1]; well.specified_BHP = well_data[2]; well.specified_flow_rate = well_data[3]; well.type_calculation = well_data[4] == 0 ? Well.TypeCalculation.Specified_BHP : Well.TypeCalculation.Specified_Flow_Rate; int location = simulator_data.well_locations[i - 1]; simulator_data.well_array[location] = well; } #endregion } return(simulator_data); }
//Method Name: initializeSimulationSinglePhase //Objectives: reads the data from the data object passed to it and assign the values to the blocks after constructing the grid and starting the solver //Inputs: a variable of type "SimulatorData" that contains all the simulation data read from the data input file //Outpits: N/A public static void initializeSimulationSinglePhase(SimulatorData data) { #region Initialize Rectangular grid dimensions bool homogeneous = data.homogeneous; int x = data.x, y = data.y, z = data.z; int[] inactive_blocks = data.inactive_blocks; //creates an array for the grid x, y and z dimensions int[] grid_dimensions = new int[] { x, y, z }; //calculates the size of the entire grid "including both active and non-active blocks" int size = x * y * z; //calculates the size of the active-blocks only int size_active = x * y * z - inactive_blocks.Length; //use this if the natural ordering excludes inactive blocks if (data.natural_ordering == SimulatorData.NaturalOrdering.Active_Only) { size = size_active; } GridBlock[] grid; #endregion #region Initialize grid data //The gridding technique used here is for only a single row or a single column int[] delta_X = new int[x]; int[] delta_Y = new int[y]; int[] delta_Z = new int[size]; double[] depth_top_array = new double[size]; double depth_top; //Homogeneous grid sizing if (homogeneous) { for (int i = 0; i < x; i++) { delta_X[i] = data.delta_X; } for (int i = 0; i < y; i++) { delta_Y[i] = data.delta_Y; } for (int i = 0; i < size; i++) { delta_Z[i] = data.delta_Z; } } //Heterogeneous grid sizing else { delta_X = data.delta_X_array; delta_Y = data.delta_Y_array; delta_Z = data.delta_Z_array; depth_top_array = data.depth_top_array; } #endregion #region Initialize Rock properties //The gridding technique used here is for the whole grid double[] Kx_data = new double[size]; double[] Ky_data = new double[size]; double[] Kz_data = new double[size]; double[] porosity = new double[size]; double compressibility_rock = 0; //Homogeneous grid if (homogeneous) { for (int i = 0; i < size; i++) { Kx_data[i] = data.Kx_data; Ky_data[i] = data.Ky_data; Kz_data[i] = data.Kz_data; porosity[i] = data.porosity; compressibility_rock = data.compressibility_rock; } } //Heterogeneous grid else { Kx_data = data.Kx_data_array; Ky_data = data.Ky_data_array; Kz_data = data.Kz_data_array; porosity = data.porosity_array; compressibility_rock = data.compressibility_rock; } #endregion #region Initialize PVT double FVF = data.FVF; double viscosity = data.viscosity; double density = data.density; double molecular_weight = data.molecular_weight; double temperature = data.temperature; PVT pvt = new PVT(us_w_d: data.water_data); //For slightly-compressibly fluid double compressibility_fluid = data.compressibility_fluid; //For compressible fluid, this data is used for constructing the PVT table double[][] g_data = data.g_data; //PVT pvt = new PVT(g_data: g_data); #endregion #region Initialize boundary conditions double initial_pressure = data.initial_pressure; double[] boundary_flow_rate = data.boundary_flow_rate; double[] boundary_pressure_x = data.boundary_pressure_x; double[] boundary_pressure_y = data.boundary_pressure_y; double[] boundary_pressure_gradient_x = data.boundary_pressure_gradient_x; double[] boundary_pressure_gradient_y = data.boundary_pressure_gradient_y; #endregion #region Initialize wells data Well[] wells = data.well_array; Well well; #endregion #region run specifications TypeDefinitions.Compressibility compressibility = data.compressibility; var phase = Transmissibility.Phase.Water; var grid_type = data.grid_type; //For compressible and slightly-compressible fluids, define the values for time steps and total simulation time double delta_t = data.delta_t; double time_max = data.time_max; //For compressible fluid problems double convergence_pressure = data.convergence_pressure; #endregion #region Initialize grid //######################################################################################### //Assign neighbouring blocks "according to the natural ordering procedure" //set numbering scheme var numbering_scheme = RectangularBlockNumbering.NumberingScheme.Active_Only; //contruct the grid grid = RectangularBlockNumbering.assignGridOrdering(grid_dimensions, inactive_blocks, numbering_scheme); GridBlock block; //assign properties to each block in the grid for (int counter = 0; counter < grid.Length; counter++) { block = grid[counter]; //######################################################################################### //Assign PVT and rock properties block.delta_x = delta_X[block.x]; block.delta_y = delta_Y[block.y]; block.h = delta_Z[counter]; block.depth = depth_top_array[counter] + block.h * 0.5; block.bulk_volume = block.delta_x * block.delta_y * block.h; block.Kx = Kx_data[counter]; block.Ky = Ky_data[counter]; block.Kz = Kz_data[counter]; //To-Do: initialize pressure through hydraulic equilibrium block.pressure = initial_pressure; if (compressibility == TypeDefinitions.Compressibility.Compressible) { block.water_viscosity = pvt.getWaterViscosity(initial_pressure); //For a single phase fluid, saturation and relative permeabilities of the fluid is equal to unity block.So = 0; block.Sg = 0; block.Sw = 1; block.Kro = 0; block.Krg = 0; block.Krw = 1; //For slightly compressible fluids, the values of rock compressibility "Cf" and fluid compressibility C_fluid are used to estimate the new values block.Cf = compressibility_rock; block.C = compressibility_fluid; const double a = 5.614583; double FVF_constants = 14.7 / (60 + 460) * (190 + 460) / 1; double z_factor = PVT.calculateZ(738.44, 418.38, initial_pressure, 190, 1); block.Bw = FVF_constants * z_factor / initial_pressure; density = PVT.calculateGasDensity(initial_pressure, molecular_weight, z_factor, temperature); block.water_density = density * gamma_c * gravity_c; block.porosity = Vp_Calculator.chord_slope_Vp(compressibility_rock, porosity[counter], block.pressure, initial_pressure); } else if (compressibility == TypeDefinitions.Compressibility.Slightly_Compressible) { //For slightly compressibly fluids, viscosity is independent of pressure if (viscosity > 0) { block.water_viscosity = viscosity; } else { block.water_viscosity = pvt.getWaterViscosity(initial_pressure); } //For a single phase fluid, saturation and relative permeabilities of the fluid is equal to unity block.So = 0; block.Sg = 0; block.Sw = 1; block.Kro = 0; block.Krg = 0; block.Krw = 1; //For slightly compressible fluids, the values of rock compressibility "Cf" and fluid compressibility C_fluid are used to estimate the new values block.Cf = compressibility_rock; block.C = compressibility_fluid; block.Bw = PVT.chord_slope_FVF(compressibility_fluid, 14.7); block.water_density = density * gamma_c * gravity_c; block.porosity = Vp_Calculator.chord_slope_Vp(compressibility_rock, porosity[counter], block.pressure, initial_pressure); } else { block.water_viscosity = viscosity; block.Bw = FVF; block.water_density = density * gamma_c * gravity_c; block.porosity = porosity[counter]; block.So = 0; block.Sg = 0; block.Sw = 1; block.Kro = 0; block.Krg = 0; block.Krw = 1; } //######################################################################################### //Calculate geometric factors "the part of the transmissibility that is always constant" only once in the initialization process var direction_x = Transmissibility.Direction.x; var direction_y = Transmissibility.Direction.y; if (homogeneous) { block.GF_x = Transmissibility.getGeometricFactor(block, grid_type, direction_x); block.GF_y = Transmissibility.getGeometricFactor(block, grid_type, direction_y); } //######################################################################################### //Set boundary conditions double boundary_transmissibility = 0; block.boundary_flow_rate = boundary_flow_rate[counter]; if (boundary_pressure_x[counter] != 0) { boundary_transmissibility = Transmissibility.getBoundaryTransmissibility(block, grid_type, direction_x, phase); block.boundary_pressure = boundary_pressure_x[counter]; block.boundary_pressure_times_transmissibility = boundary_transmissibility * boundary_pressure_x[counter]; block.boundary_transmissibility = boundary_transmissibility; } else if (boundary_pressure_y[counter] != 0) { boundary_transmissibility = Transmissibility.getBoundaryTransmissibility(block, grid_type, direction_y, phase); block.boundary_pressure = boundary_pressure_y[counter]; block.boundary_pressure_times_transmissibility = boundary_transmissibility * boundary_pressure_y[counter]; block.boundary_transmissibility = boundary_transmissibility; } //boundary pressure gradient is positive for depletion and negative for encroachment boundary_transmissibility = Transmissibility.getBoundaryTransmissibility(block, grid_type, direction_x, phase); block.boundary_pressure_gradient += boundary_pressure_gradient_x[counter] * block.delta_x / 2 * boundary_transmissibility; boundary_transmissibility = Transmissibility.getBoundaryTransmissibility(block, grid_type, direction_y, phase); block.boundary_pressure_gradient += boundary_pressure_gradient_y[counter] * block.delta_y / 2 * boundary_transmissibility; //######################################################################################### //Set well rates if (wells[counter] != null) { well = wells[counter]; block.type = GridBlock.Type.Well; //specified flow rate wells if (well.type_calculation == Well.TypeCalculation.Specified_Flow_Rate) { block.well_type = GridBlock.WellType.Specified_Flow_Rate; block.well_flow_rate = well.specified_flow_rate; block.specified_BHP = well.specified_BHP; block.skin = well.skin; block.rw = well.rw; double well_geometric_factor = Well.getGeometricFactor(block); block.well_geometric_factor = well_geometric_factor; block.well_transmissibility = Well.getTransmissibility(block, well_geometric_factor, Well.Phase.Water); } //specified BHP wells else { block.well_type = GridBlock.WellType.Specified_BHP; block.BHP = well.specified_BHP; block.rw = well.rw; block.skin = well.skin; double well_geometric_factor = Well.getGeometricFactor(block); block.well_geometric_factor = well_geometric_factor; block.well_transmissibility = Well.getTransmissibility(block, well_geometric_factor, Well.Phase.Water); } } } #region calculate the geometric factors of a heterogeneous grid if (!homogeneous) { var direction_x = Transmissibility.Direction.x; var direction_y = Transmissibility.Direction.y; int x_counter = 0; int y_counter = 0; //int z_counter = 0; for (int i = 0; i < grid.Length; i++) { block = grid[i]; if (x > 1) { x_counter = block.east_counter != -1 ? block.east_counter : block.west_counter; block.GF_x = x_counter != -1 ? Transmissibility.getGeometricFactor(block, grid[x_counter], grid_type, direction_x) : 0; } if (y > 1) { y_counter = block.north_counter != -1 ? block.north_counter : block.south_counter; block.GF_y = y_counter != -1 ? Transmissibility.getGeometricFactor(block, grid[y_counter], grid_type, direction_y) : 0; } } } #endregion #endregion #region Output SinglePhase.OutPut2D output = new SinglePhase.OutPut2D(grid, grid_dimensions, data.what, data.where, compressibility, data.file_name, data.formatted, data.single_file, inactive_blocks); #endregion #region Choose Solver if (compressibility == TypeDefinitions.Compressibility.Incompressible) { SolverSinglePhase.incompressible(grid, output); } else if (compressibility == TypeDefinitions.Compressibility.Slightly_Compressible) { SolverSinglePhase.slightly_compressible(grid, delta_t, time_max, output, pvt); } else { SolverSinglePhase.compressible(grid, delta_t, time_max, convergence_pressure, output, pvt); } #endregion }