int main(int argc, char** argv) { // Set output precision int decimals = 16; // Process input parameters if (argc != 3) { std::cout << "Usage: mirror_grid filename.grdecl direction" << std::endl; std::cout << "(replace direction with either x or y)" << std::endl; exit(1); } const char* eclipsefilename = argv[1]; std::string direction(argv[2]); if ( ! ((direction == "x") || (direction == "y")) ) { std::cerr << "Unrecognized input parameter for direction: '" << direction << "'. Should be either x or y (maybe also z later)." << std::endl; exit(1); } // Parse grdecl file std::cout << "Parsing grid file '" << eclipsefilename << "' ..." << std::endl; Opm::Parser parser; Opm::ParseContext parseContext; const Opm::Deck deck(parser.parseFile(eclipsefilename , parseContext)); if ( ! (deck.hasKeyword("SPECGRID") && deck.hasKeyword("COORD") && deck.hasKeyword("ZCORN")) ) { std::cerr << "Grid file " << eclipsefilename << "are missing keywords SPECGRID, COORD or ZCORN!" << std::endl; exit(1); } // Create new grid file std::string mirrored_eclipsefilename = std::string(eclipsefilename); std::string::size_type last_dot = mirrored_eclipsefilename.find_last_of('.'); mirrored_eclipsefilename = mirrored_eclipsefilename.substr(0, last_dot) + "_mirrored-" + direction + ".grdecl"; std::ofstream outfile; outfile.open(mirrored_eclipsefilename.c_str(), std::ios::out | std::ios::trunc); if (!outfile) { std::cerr << "Can't open output file " << mirrored_eclipsefilename << std::endl; exit(1); } outfile.precision(decimals); outfile.setf(std::ios::fixed); // Print init message printInitMessage(outfile, eclipsefilename, direction); // Mirror keywords mirror_mapaxes(deck, direction, outfile); mirror_specgrid(deck, direction, outfile); mirror_coord(deck, direction, outfile); mirror_zcorn(deck, direction, outfile); mirror_celldata<int>("ACTNUM", deck, direction, outfile); mirror_celldata<double>("PERMX", deck, direction, outfile); mirror_celldata<double>("PERMY", deck, direction, outfile); mirror_celldata<double>("PERMZ", deck, direction, outfile); mirror_celldata<double>("PORO", deck, direction, outfile); mirror_celldata<int>("SATNUM", deck, direction, outfile); mirror_celldata<double>("NTG", deck, direction, outfile); mirror_celldata<double>("SWCR", deck, direction, outfile); mirror_celldata<double>("SOWCR", deck, direction, outfile); }
IncompPropertiesSinglePhase::IncompPropertiesSinglePhase(const Opm::Deck& deck, const Opm::EclipseState& eclState, const UnstructuredGrid& grid) { rock_.init(eclState, grid.number_of_cells, grid.global_cell, grid.cartdims); if (deck.hasKeyword("DENSITY")) { const auto& densityRecord = deck.getKeyword("DENSITY").getRecord(0); surface_density_ = densityRecord.getItem("OIL").getSIDouble(0); } else { surface_density_ = 1000.0; OPM_MESSAGE("Input is missing DENSITY -- using a standard density of " << surface_density_ << ".\n"); } // This will be modified if we have a PVCDO specification. reservoir_density_ = surface_density_; if (deck.hasKeyword("PVCDO")) { const auto& pvcdoRecord = deck.getKeyword("PVCDO").getRecord(0); if (pvcdoRecord.getItem("OIL_COMPRESSIBILITY").getSIDouble(0) != 0.0 || pvcdoRecord.getItem("OIL_VISCOSIBILITY").getSIDouble(0) != 0.0) { OPM_MESSAGE("Compressibility effects in PVCDO are ignored."); } reservoir_density_ /= pvcdoRecord.getItem("OIL_VOL_FACTOR").getSIDouble(0); viscosity_ = pvcdoRecord.getItem("OIL_VISCOSITY").getSIDouble(0); } else { viscosity_ = 1.0 * prefix::centi*unit::Poise; OPM_MESSAGE("Input is missing PVCDO -- using a standard viscosity of " << viscosity_ << " and reservoir density equal to surface density.\n"); } }
void GridManager::createGrdecl(const Opm::Deck& deck, struct grdecl &grdecl) { // Extract data from deck. const std::vector<double>& zcorn = deck.getKeyword("ZCORN").getSIDoubleData(); const std::vector<double>& coord = deck.getKeyword("COORD").getSIDoubleData(); const int* actnum = NULL; if (deck.hasKeyword("ACTNUM")) { actnum = &(deck.getKeyword("ACTNUM").getIntData()[0]); } std::array<int, 3> dims; if (deck.hasKeyword("DIMENS")) { const auto& dimensKeyword = deck.getKeyword("DIMENS"); dims[0] = dimensKeyword.getRecord(0).getItem(0).get< int >(0); dims[1] = dimensKeyword.getRecord(0).getItem(1).get< int >(0); dims[2] = dimensKeyword.getRecord(0).getItem(2).get< int >(0); } else if (deck.hasKeyword("SPECGRID")) { const auto& specgridKeyword = deck.getKeyword("SPECGRID"); dims[0] = specgridKeyword.getRecord(0).getItem(0).get< int >(0); dims[1] = specgridKeyword.getRecord(0).getItem(1).get< int >(0); dims[2] = specgridKeyword.getRecord(0).getItem(2).get< int >(0); } else { OPM_THROW(std::runtime_error, "Deck must have either DIMENS or SPECGRID."); } // Collect in input struct for preprocessing. grdecl.zcorn = &zcorn[0]; grdecl.coord = &coord[0]; grdecl.actnum = actnum; grdecl.dims[0] = dims[0]; grdecl.dims[1] = dims[1]; grdecl.dims[2] = dims[2]; if (deck.hasKeyword("MAPAXES")) { const auto& mapaxesKeyword = deck.getKeyword("MAPAXES"); const auto& mapaxesRecord = mapaxesKeyword.getRecord(0); // memleak alert: here we need to make sure that C code // can properly take ownership of the grdecl.mapaxes // object. if it is not freed, it will result in a // memleak... double *cWtfMapaxes = static_cast<double*>(malloc(sizeof(double)*mapaxesRecord.size())); for (unsigned i = 0; i < mapaxesRecord.size(); ++i) cWtfMapaxes[i] = mapaxesRecord.getItem(i).getSIDouble(0); grdecl.mapaxes = cWtfMapaxes; } else grdecl.mapaxes = NULL; }
/// Mirror keyword MAPAXES in deck void mirror_mapaxes( const Opm::Deck& deck, std::string direction, std::ofstream& out) { // Assumes axis aligned with x/y-direction std::cout << "Warning: Keyword MAPAXES not fully understood. Result should be verified manually." << std::endl; if (deck.hasKeyword("MAPAXES")) { std::vector<double> mapaxes = getMapaxesValues(deck); std::vector<double> mapaxes_mirrored = mapaxes; // Double the length of the coordinate axis if (direction == "x") { mapaxes_mirrored[4] = (mapaxes[4]-mapaxes[2])*2 + mapaxes[2]; } else if (direction == "y") { mapaxes_mirrored[1] = (mapaxes[1]-mapaxes[3])*2 + mapaxes[3]; } printKeywordValues(out, "MAPAXES", mapaxes_mirrored, 2); } }
void mirror_celldata(std::string keyword, const Opm::Deck& deck, std::string direction, std::ofstream& out) { if ( ! deck.hasKeyword(keyword)) { std::cout << "Ignoring keyword " << keyword << " as it was not found." << std::endl; return; } // Get data from eclipse deck const auto& specgridRecord = deck.getKeyword("SPECGRID").getRecord(0); std::vector<int> dimensions(3); dimensions[0] = specgridRecord.getItem("NX").get< int >(0); dimensions[1] = specgridRecord.getItem("NY").get< int >(0); dimensions[2] = specgridRecord.getItem("NZ").get< int >(0); std::vector<T> values = getKeywordValues(keyword, deck, T(0.0)); std::vector<T> values_mirrored(2*dimensions[0]*dimensions[1]*dimensions[2], 0.0); // Handle the two directions differently due to ordering of the pillars. if (direction == "x") { typename std::vector<T>::iterator it_orig = values.begin(); typename std::vector<T>::iterator it_new = values_mirrored.begin(); // Loop through each line and copy old cell data and add new (which are the old reversed) for ( ; it_orig != values.end(); it_orig += dimensions[0]) { // Copy old cell data copy(it_orig, it_orig + dimensions[0], it_new); it_new += dimensions[0]; // Add new cell data std::vector<double> next_vec(it_orig, it_orig + dimensions[0]); std::vector<double> next_reversed = next_vec; reverse(next_reversed.begin(), next_reversed.end()); copy(next_reversed.begin(), next_reversed.end(), it_new); it_new += dimensions[0]; } } else if (direction =="y") { typename std::vector<T>::iterator it_orig = values.begin(); typename std::vector<T>::iterator it_new = values_mirrored.begin(); // Entries per layer const int entries_per_layer = dimensions[0]*dimensions[1]; // Loop through each layer and copy old cell data and add new (which are the old reordered) for ( ; it_orig != values.end(); it_orig += entries_per_layer) { // Copy old cell data copy(it_orig, it_orig + entries_per_layer, it_new); it_new += entries_per_layer; // Add new cell data std::vector<T> next_vec(it_orig, it_orig + entries_per_layer); std::vector<T> next_reordered(entries_per_layer, 0.0); typename std::vector<T>::iterator it_next = next_vec.end(); typename std::vector<T>::iterator it_reordered = next_reordered.begin(); // Reorder next entries for ( ; it_reordered != next_reordered.end(); it_reordered += dimensions[0]) { copy(it_next - dimensions[0], it_next, it_reordered); it_next -= dimensions[0]; } copy(next_reordered.begin(), next_reordered.end(), it_new); it_new += entries_per_layer; } } else { std::cerr << "Direction should be either x or y" << std::endl; exit(1); } // Write new keyword values to output file printKeywordValues(out, keyword, values_mirrored, 8); }
// ----------------- Main program ----------------- int main(int argc, char** argv) try { using namespace Opm; std::cout << "\n================ Test program for weakly compressible two-phase flow with polymer ===============\n\n"; parameter::ParameterGroup param(argc, argv, false); std::cout << "--------------- Reading parameters ---------------" << std::endl; // If we have a "deck_filename", grid and props will be read from that. bool use_deck = param.has("deck_filename"); boost::scoped_ptr<GridManager> grid; boost::scoped_ptr<BlackoilPropertiesInterface> props; boost::scoped_ptr<RockCompressibility> rock_comp; std::unique_ptr<PolymerBlackoilState> state; Opm::PolymerProperties poly_props; Opm::Deck deck; std::unique_ptr< EclipseState > eclipseState; // bool check_well_controls = false; // int max_well_control_iterations = 0; double gravity[3] = { 0.0 }; if (use_deck) { std::string deck_filename = param.get<std::string>("deck_filename"); Parser parser; Opm::ParseContext parseContext({{ ParseContext::PARSE_RANDOM_SLASH , InputError::IGNORE }}); deck = parser.parseFile(deck_filename , parseContext); eclipseState.reset( new EclipseState(deck , parseContext) ); // Grid init grid.reset(new GridManager(eclipseState->getInputGrid())); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init props.reset(new BlackoilPropertiesFromDeck(deck, *eclipseState, ug_grid)); // check_well_controls = param.getDefault("check_well_controls", false); // max_well_control_iterations = param.getDefault("max_well_control_iterations", 10); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ), 2)); // Rock compressibility. rock_comp.reset(new RockCompressibility(*eclipseState)); // Gravity. gravity[2] = deck.hasKeyword("NOGRAV") ? 0.0 : unit::gravity; // Init state variables (saturation and pressure). if (param.has("init_saturation")) { initStateBasic(ug_grid, *props, param, gravity[2], *state); } else { initStateFromDeck(ug_grid, *props, deck, gravity[2], *state); } initBlackoilSurfvol(ug_grid, *props, *state); // Init polymer properties. poly_props.readFromDeck(deck, *eclipseState); } } else { // Grid init. const int nx = param.getDefault("nx", 100); const int ny = param.getDefault("ny", 100); const int nz = param.getDefault("nz", 1); const double dx = param.getDefault("dx", 1.0); const double dy = param.getDefault("dy", 1.0); const double dz = param.getDefault("dz", 1.0); grid.reset(new GridManager(nx, ny, nz, dx, dy, dz)); { const UnstructuredGrid& ug_grid = *(grid->c_grid()); // Rock and fluid init. props.reset(new BlackoilPropertiesBasic(param, ug_grid.dimensions, UgGridHelpers::numCells( ug_grid ))); state.reset( new PolymerBlackoilState( UgGridHelpers::numCells( ug_grid ) , UgGridHelpers::numFaces( ug_grid ) , 2)); // Rock compressibility. rock_comp.reset(new RockCompressibility(param)); // Gravity. gravity[2] = param.getDefault("gravity", 0.0); // Init state variables (saturation and pressure). initStateBasic(ug_grid, *props, param, gravity[2], *state); initBlackoilSurfvol(ug_grid, *props, *state); // Init Polymer state if (param.has("poly_init")) { double poly_init = param.getDefault("poly_init", 0.0); for (int cell = 0; cell < UgGridHelpers::numCells( ug_grid ); ++cell) { double smin[2], smax[2]; auto& saturation = state->saturation(); auto& concentration = state->getCellData( state->CONCENTRATION ); auto& max_concentration = state->getCellData( state->CMAX ); props->satRange(1, &cell, smin, smax); if (saturation[2*cell] > 0.5*(smin[0] + smax[0])) { concentration[cell] = poly_init; max_concentration[cell] = poly_init; } else { saturation[2*cell + 0] = 0.; saturation[2*cell + 1] = 1.; concentration[cell] = 0.; max_concentration[cell] = 0.; } } } } // Init polymer properties. // Setting defaults to provide a simple example case. double c_max = param.getDefault("c_max_limit", 5.0); double mix_param = param.getDefault("mix_param", 1.0); double rock_density = param.getDefault("rock_density", 1000.0); double dead_pore_vol = param.getDefault("dead_pore_vol", 0.15); double res_factor = param.getDefault("res_factor", 1.) ; // res_factor = 1 gives no change in permeability double c_max_ads = param.getDefault("c_max_ads", 1.); int ads_index = param.getDefault<int>("ads_index", Opm::PolymerProperties::NoDesorption); std::vector<double> c_vals_visc(2, -1e100); c_vals_visc[0] = 0.0; c_vals_visc[1] = 7.0; std::vector<double> visc_mult_vals(2, -1e100); visc_mult_vals[0] = 1.0; // poly_props.visc_mult_vals[1] = param.getDefault("c_max_viscmult", 30.0); visc_mult_vals[1] = 20.0; std::vector<double> c_vals_ads(3, -1e100); c_vals_ads[0] = 0.0; c_vals_ads[1] = 2.0; c_vals_ads[2] = 8.0; std::vector<double> ads_vals(3, -1e100); ads_vals[0] = 0.0; ads_vals[1] = 0.0015; ads_vals[2] = 0.0025; // ads_vals[1] = 0.0; // ads_vals[2] = 0.0; std::vector<double> water_vel_vals(2, -1e100); water_vel_vals[0] = 0.0; water_vel_vals[1] = 10.0; std::vector<double> shear_vrf_vals(2, -1e100); shear_vrf_vals[0] = 1.0; shear_vrf_vals[1] = 1.0; poly_props.set(c_max, mix_param, rock_density, dead_pore_vol, res_factor, c_max_ads, static_cast<Opm::PolymerProperties::AdsorptionBehaviour>(ads_index), c_vals_visc, visc_mult_vals, c_vals_ads, ads_vals, water_vel_vals, shear_vrf_vals); } bool use_gravity = (gravity[0] != 0.0 || gravity[1] != 0.0 || gravity[2] != 0.0); const double *grav = use_gravity ? &gravity[0] : 0; // Linear solver. LinearSolverFactory linsolver(param); // Write parameters used for later reference. bool output = param.getDefault("output", true); if (output) { std::string output_dir = param.getDefault("output_dir", std::string("output")); boost::filesystem::path fpath(output_dir); try { create_directories(fpath); } catch (...) { OPM_THROW(std::runtime_error, "Creating directories failed: " << fpath); } param.writeParam(output_dir + "/simulation.param"); } std::cout << "\n\n================ Starting main simulation loop ===============\n" << std::flush; SimulatorReport rep; if (!use_deck) { // Simple simulation without a deck. PolymerInflowBasic polymer_inflow(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax())); WellsManager wells; SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, polymer_inflow, linsolver, grav); SimulatorTimer simtimer; simtimer.init(param); warnIfUnusedParams(param); WellState well_state; well_state.init(0, *state); rep = simulator.run(simtimer, *state, well_state); } else { // With a deck, we may have more epochs etc. WellState well_state; int step = 0; Opm::TimeMap timeMap(deck); SimulatorTimer simtimer; simtimer.init(timeMap); // Check for WPOLYMER presence in last report step to decide // polymer injection control type. const bool use_wpolymer = deck.hasKeyword("WPOLYMER"); if (use_wpolymer) { if (param.has("poly_start_days")) { OPM_MESSAGE("Warning: Using WPOLYMER to control injection since it was found in deck. " "You seem to be trying to control it via parameter poly_start_days (etc.) as well."); } } for (size_t reportStepIdx = 0; reportStepIdx < timeMap.numTimesteps(); ++reportStepIdx) { simtimer.setCurrentStepNum(reportStepIdx); // Report on start of report step. std::cout << "\n\n-------------- Starting report step " << reportStepIdx << " --------------" << "\n (number of remaining steps: " << simtimer.numSteps() - step << ")\n\n" << std::flush; // Create new wells, polymer inflow controls. eclipseState.reset( new EclipseState( deck ) ); WellsManager wells(*eclipseState , reportStepIdx , *grid->c_grid(), props->permeability()); boost::scoped_ptr<PolymerInflowInterface> polymer_inflow; if (use_wpolymer) { if (wells.c_wells() == 0) { OPM_THROW(std::runtime_error, "Cannot control polymer injection via WPOLYMER without wells."); } polymer_inflow.reset(new PolymerInflowFromDeck(*eclipseState, *wells.c_wells(), props->numCells(), simtimer.currentStepNum())); } else { polymer_inflow.reset(new PolymerInflowBasic(param.getDefault("poly_start_days", 300.0)*Opm::unit::day, param.getDefault("poly_end_days", 800.0)*Opm::unit::day, param.getDefault("poly_amount", poly_props.cMax()))); } // @@@ HACK: we should really make a new well state and // properly transfer old well state to it every report step, // since number of wells may change etc. if (reportStepIdx == 0) { well_state.init(wells.c_wells(), *state); } // Create and run simulator. SimulatorCompressiblePolymer simulator(param, *grid->c_grid(), *props, poly_props, rock_comp->isActive() ? rock_comp.get() : 0, wells, *polymer_inflow, linsolver, grav); if (reportStepIdx == 0) { warnIfUnusedParams(param); } SimulatorReport epoch_rep = simulator.run(simtimer, *state, well_state); // Update total timing report and remember step number. rep += epoch_rep; step = simtimer.currentStepNum(); } } std::cout << "\n\n================ End of simulation ===============\n\n"; rep.report(std::cout); } catch (const std::exception &e) { std::cerr << "Program threw an exception: " << e.what() << "\n"; throw; }
/// set the tables which specify the temperature dependence of the oil viscosity void initFromDeck(std::shared_ptr<const PvtInterface> isothermalPvt, const Opm::Deck& deck, const Opm::EclipseState& eclipseState) { isothermalPvt_ = isothermalPvt; int numRegions; auto tables = eclipseState->getTableManager(); if (deck->hasKeyword("PVTO")) numRegions = tables->getPvtoTables().size(); else if (deck->hasKeyword("PVDO")) numRegions = tables->getPvdoTables().size(); else if (deck->hasKeyword("PVCDO")) numRegions = deck->getKeyword("PVCDO").size(); else OPM_THROW(std::runtime_error, "Oil phase was not initialized using a known way"); // viscosity if (deck->hasKeyword("VISCREF")) { oilvisctTables_ = &tables->getOilvisctTables(); const auto& viscrefKeyword = deck->getKeyword("VISCREF"); assert(int(oilvisctTables_->size()) == numRegions); assert(int(viscrefKeyword.size()) == numRegions); viscrefPress_.resize(numRegions); viscrefRs_.resize(numRegions); muRef_.resize(numRegions); for (int regionIdx = 0; regionIdx < numRegions; ++regionIdx) { const auto& viscrefRecord = viscrefKeyword.getRecord(regionIdx); viscrefPress_[regionIdx] = viscrefRecord.getItem("REFERENCE_PRESSURE").getSIDouble(0); viscrefRs_[regionIdx] = viscrefRecord.getItem("REFERENCE_RS").getSIDouble(0); // temperature used to calculate the reference viscosity [K]. the // value does not really matter if the underlying PVT object really // is isothermal... double Tref = 273.15 + 20; // compute the reference viscosity using the isothermal PVT object. double tmp1, tmp2; isothermalPvt_->mu(1, ®ionIdx, &viscrefPress_[regionIdx], &Tref, &viscrefRs_[regionIdx], &muRef_[regionIdx], &tmp1, &tmp2); } } // quantities required for density. note that we just always use the values // for the first EOS. (since EOS != PVT region.) tref_ = 0.0; if (deck->hasKeyword("THERMEX1")) { oilCompIdx_ = deck->getKeyword("OCOMPIDX").getRecord(0).getItem("OIL_COMPONENT_INDEX").get< int >(0) - 1; // always use the values of the first EOS tref_ = deck->getKeyword("TREF").getRecord(0).getItem("TEMPERATURE").getSIDouble(oilCompIdx_); pref_ = deck->getKeyword("PREF").getRecord(0).getItem("PRESSURE").getSIDouble(oilCompIdx_); cref_ = deck->getKeyword("CREF").getRecord(0).getItem("COMPRESSIBILITY").getSIDouble(oilCompIdx_); thermex1_ = deck->getKeyword("THERMEX1").getRecord(0).getItem("EXPANSION_COEFF").getSIDouble(oilCompIdx_); } }