diff --git a/README b/README index f0314100..ae7ebe30 100644 --- a/README +++ b/README @@ -88,10 +88,25 @@ If you want to contribute, fork OPM/opm-core on github. BUILDING -------- +There are two ways to build the opm-core library: + +1. As a stand-alone library. cd opm-core autoreconf -i ./configure make +If you want to install the library: + make install +or (if installing to /usr/local or similar) + sudo make install + +2. As a dune module. + - Put the opm-core directory in the same directory + as the other dune modules to be built (e.g. dune-commmon, + dune-grid). + - Run dunecontrol as normal. For more information on + the dune build system, see + http://www.dune-project.org/doc/installation-notes.html DOCUMENTATION diff --git a/opm/core/newwells.c b/opm/core/newwells.c index 68037201..c28e8566 100644 --- a/opm/core/newwells.c +++ b/opm/core/newwells.c @@ -468,6 +468,8 @@ add_well(enum WellType type , struct WellMgmt *m; + assert (W != NULL); + nw = W->number_of_wells; nperf_tot = W->well_connpos[nw]; @@ -532,6 +534,7 @@ append_well_controls(enum WellControlType type, struct WellControlMgmt *m; assert (W != NULL); + assert ((0 <= well_index) && (well_index < W->number_of_wells)); ctrl = W->ctrls[well_index]; np = W->number_of_phases; @@ -567,8 +570,13 @@ void set_current_control(int well_index, int current_control, struct Wells *W) /* ---------------------------------------------------------------------- */ { + assert (W != NULL); + assert ((0 <= well_index) && (well_index < W->number_of_wells)); + assert (W->ctrls[well_index] != NULL); + assert (current_control < W->ctrls[well_index]->num); + W->ctrls[well_index]->current = current_control; } @@ -578,7 +586,85 @@ void clear_well_controls(int well_index, struct Wells *W) /* ---------------------------------------------------------------------- */ { + assert (W != NULL); + assert ((0 <= well_index) && (well_index < W->number_of_wells)); + if (W->ctrls[well_index] != NULL) { W->ctrls[well_index]->num = 0; } } + + +/* ---------------------------------------------------------------------- */ +struct Wells * +clone_wells(const struct Wells *W) +/* ---------------------------------------------------------------------- */ +{ + int c, np, nperf, ok, pos, w; + double target; + const int *cells; + const double *WI, *comp_frac, *distr; + enum WellControlType type; + const struct WellControls *ctrls; + + struct Wells *ret; + + if (W == NULL) { + ret = NULL; + } + else { + np = W->number_of_phases; + ret = create_wells(W->number_of_phases, W->number_of_wells, + W->well_connpos[ W->number_of_wells ]); + + if (ret != NULL) { + pos = W->well_connpos[ 0 ]; + ok = 1; + + for (w = 0; ok && (w < W->number_of_wells); w++) { + nperf = W->well_connpos[w + 1] - pos; + cells = W->well_cells + pos; + + WI = W->WI != NULL ? W->WI + pos : NULL; + comp_frac = W->comp_frac != NULL ? W->comp_frac + w*np : NULL; + + ok = add_well(W->type[ w ], W->depth_ref[ w ], nperf, + comp_frac, cells, WI, W->name[ w ], ret); + + /* Capacity should be sufficient from create_wells() */ + assert (ok); + + ctrls = W->ctrls[ w ]; + + if (ok) { + ok = well_controls_reserve(ctrls->num, np, ret->ctrls[ w ]); + + for (c = 0; ok && (c < ctrls->num); c++) { + type = ctrls->type [ c ]; + target = ctrls->target[ c ]; + distr = & ctrls->distr [ c * np ]; + + ok = append_well_controls(type, target, distr, w, ret); + + /* Capacity *should* be sufficient from + * well_controls_reserve() */ + assert (ok); + } + } + + if (ok) { + set_current_control(w, ctrls->current, ret); + } + + pos = W->well_connpos[w + 1]; + } + + if (! ok) { + destroy_wells(ret); + ret = NULL; + } + } + } + + return ret; +} diff --git a/opm/core/newwells.h b/opm/core/newwells.h index f16675b6..20cfce61 100644 --- a/opm/core/newwells.h +++ b/opm/core/newwells.h @@ -235,6 +235,19 @@ void destroy_wells(struct Wells *W); +/** + * Create a deep-copy (i.e., clone) of an existing Wells object, including its + * controls. + * + * @param[in] W Existing Wells object. + * @return Complete clone of the input object. Dispose of resources using + * function destroy_wells() when no longer needed. Returns @c NULL in case of + * allocation failure. + */ +struct Wells * +clone_wells(const struct Wells *W); + + #ifdef __cplusplus } #endif diff --git a/opm/core/simulator/SimulatorCompressibleTwophase.cpp b/opm/core/simulator/SimulatorCompressibleTwophase.cpp index 6fb475e9..e921db87 100644 --- a/opm/core/simulator/SimulatorCompressibleTwophase.cpp +++ b/opm/core/simulator/SimulatorCompressibleTwophase.cpp @@ -407,8 +407,8 @@ namespace Opm // Optionally, check if well controls are satisfied. if (check_well_controls_) { Opm::computePhaseFlowRatesPerWell(*wells_, - fractional_flows, well_state.perfRates(), + fractional_flows, well_resflows_phase); std::cout << "Checking well conditions." << std::endl; // For testing we set surface := reservoir diff --git a/opm/core/wells/WellsGroup.cpp b/opm/core/wells/WellsGroup.cpp index afa17f12..c340c1ae 100644 --- a/opm/core/wells/WellsGroup.cpp +++ b/opm/core/wells/WellsGroup.cpp @@ -863,7 +863,7 @@ namespace Opm return; } // We're a producer, so we need to negate the input - double ntarget = target; + double ntarget = -target; double distr[3] = { 0.0, 0.0, 0.0 }; const int* phase_pos = phaseUsage().phase_pos; diff --git a/opm/core/wells/WellsManager.cpp b/opm/core/wells/WellsManager.cpp index 95cb0369..8aaca9e2 100644 --- a/opm/core/wells/WellsManager.cpp +++ b/opm/core/wells/WellsManager.cpp @@ -226,6 +226,14 @@ namespace Opm + /// Construct from existing wells object. + WellsManager::WellsManager(struct Wells* W) + : w_(clone_wells(W)) + { + } + + + /// Construct wells from deck. WellsManager::WellsManager(const Opm::EclipseGridParser& deck, const UnstructuredGrid& grid, diff --git a/opm/core/wells/WellsManager.hpp b/opm/core/wells/WellsManager.hpp index 6df65ec4..19289c5d 100644 --- a/opm/core/wells/WellsManager.hpp +++ b/opm/core/wells/WellsManager.hpp @@ -41,7 +41,14 @@ namespace Opm { public: /// Default constructor -- no wells. - WellsManager(); + WellsManager(); + + /// Construct from existing wells object. + /// WellsManager is not properly initialised in the sense that the logic to + /// manage control switching does not exist. + /// + /// @param[in] W Existing wells object. + WellsManager(struct Wells* W); /// Construct from input deck and grid. /// The permeability argument may be zero if the input contain diff --git a/tests/Makefile.am b/tests/Makefile.am index cb5935bb..6e69ae5c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -23,6 +23,7 @@ test_read_grid \ test_read_vag \ test_readpolymer \ test_sf2p \ +test_wells \ test_writeVtkData \ unit_test @@ -59,6 +60,9 @@ test_sf2p_SOURCES = test_sf2p.cpp test_writeVtkData_SOURCES = test_writeVtkData.cpp +test_wells_SOURCES = test_wells.cpp +test_wells_LDADD = $(LDADD) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) + unit_test_SOURCES = unit_test.cpp diff --git a/tests/test_wells.cpp b/tests/test_wells.cpp new file mode 100644 index 00000000..4f8ad7bb --- /dev/null +++ b/tests/test_wells.cpp @@ -0,0 +1,205 @@ +/* + Copyright 2012 SINTEF ICT, Applied Mathematics. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + +#include + +#if HAVE_DYNAMIC_BOOST_TEST +#define BOOST_TEST_DYN_LINK +#endif + +#define NVERBOSE // Suppress own messages when throw()ing + +#define BOOST_TEST_MODULE WellsModuleTest +#include + +#include + +#include +#include +#include + +BOOST_AUTO_TEST_CASE(Construction) +{ + const int nphases = 2; + const int nwells = 2; + const int nperfs = 2; + + boost::shared_ptr W(create_wells(nphases, nwells, nperfs), + destroy_wells); + + if (W) { + int cells[] = { 0, 9 }; + double WI = 1.0; + const double ifrac[] = { 1.0, 0.0 }; + + const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0], + &WI, "INJECTOR", W.get()); + + const double pfrac[] = { 0.0, 0.0 }; + const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1], + &WI, "PRODUCER", W.get()); + + if (ok0 && ok1) { + BOOST_CHECK_EQUAL(W->number_of_phases, nphases); + BOOST_CHECK_EQUAL(W->number_of_wells , nwells ); + + BOOST_CHECK_EQUAL(W->well_connpos[0], 0); + BOOST_CHECK_EQUAL(W->well_connpos[1], 1); + BOOST_CHECK_EQUAL(W->well_connpos[W->number_of_wells], nperfs); + + BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[0]], cells[0]); + BOOST_CHECK_EQUAL(W->well_cells[W->well_connpos[1]], cells[1]); + + BOOST_CHECK_EQUAL(W->WI[W->well_connpos[0]], WI); + BOOST_CHECK_EQUAL(W->WI[W->well_connpos[1]], WI); + + using std::string; + BOOST_CHECK_EQUAL(string(W->name[0]), string("INJECTOR")); + BOOST_CHECK_EQUAL(string(W->name[1]), string("PRODUCER")); + } + } +} + + +BOOST_AUTO_TEST_CASE(Controls) +{ + const int nphases = 2; + const int nwells = 1; + const int nperfs = 2; + + boost::shared_ptr W(create_wells(nphases, nwells, nperfs), + destroy_wells); + + if (W) { + int cells[] = { 0 , 9 }; + double WI [] = { 1.0, 1.0 }; + const double ifrac[] = { 1.0, 0.0 }; + + const bool ok = add_well(INJECTOR, 0.0, nperfs, &ifrac[0], &cells[0], + &WI[0], "INJECTOR", W.get()); + + if (ok) { + const double distr[] = { 1.0, 0.0 }; + const bool ok1 = append_well_controls(BHP, 1, &distr[0], + 0, W.get()); + const bool ok2 = append_well_controls(SURFACE_RATE, 1, + &distr[0], 0, W.get()); + + if (ok1 && ok2) { + WellControls* ctrls = W->ctrls[0]; + + BOOST_CHECK_EQUAL(ctrls->num , 2); + BOOST_CHECK_EQUAL(ctrls->current, -1); + + set_current_control(0, 0, W.get()); + BOOST_CHECK_EQUAL(ctrls->current, 0); + + set_current_control(0, 1, W.get()); + BOOST_CHECK_EQUAL(ctrls->current, 1); + + BOOST_CHECK_EQUAL(ctrls->type[0], BHP); + BOOST_CHECK_EQUAL(ctrls->type[1], SURFACE_RATE); + + BOOST_CHECK_EQUAL(ctrls->target[0], 1.0); + BOOST_CHECK_EQUAL(ctrls->target[1], 1.0); + } + } + } +} + + +BOOST_AUTO_TEST_CASE(Copy) +{ + const int nphases = 2; + const int nwells = 2; + const int nperfs = 2; + + boost::shared_ptr W1(create_wells(nphases, nwells, nperfs), + destroy_wells); + boost::shared_ptr W2; + + if (W1) { + int cells[] = { 0, 9 }; + const double WI = 1.0; + const double ifrac[] = { 1.0, 0.0 }; + + const bool ok0 = add_well(INJECTOR, 0.0, 1, &ifrac[0], &cells[0], + &WI, "INJECTOR", W1.get()); + + const double pfrac[] = { 0.0, 0.0 }; + const bool ok1 = add_well(PRODUCER, 0.0, 1, &pfrac[0], &cells[1], + &WI, "PRODUCER", W1.get()); + + bool ok = ok0 && ok1; + for (int w = 0; ok && (w < W1->number_of_wells); ++w) { + const double distr[] = { 1.0, 0.0 }; + const bool okc1 = append_well_controls(BHP, 1, &distr[0], + w, W1.get()); + const bool okc2 = append_well_controls(SURFACE_RATE, 1, + &distr[0], w, + W1.get()); + + ok = okc1 && okc2; + } + + if (ok) { + W2.reset(clone_wells(W1.get()), destroy_wells); + } + } + + if (W2) { + BOOST_CHECK_EQUAL(W2->number_of_phases, W1->number_of_phases); + BOOST_CHECK_EQUAL(W2->number_of_wells , W1->number_of_wells ); + BOOST_CHECK_EQUAL(W2->well_connpos[0] , W1->well_connpos[0] ); + + for (int w = 0; w < W1->number_of_wells; ++w) { + using std::string; + BOOST_CHECK_EQUAL(string(W2->name[w]), string(W1->name[w])); + BOOST_CHECK_EQUAL( W2->type[w] , W1->type[w] ); + + BOOST_CHECK_EQUAL(W2->well_connpos[w + 1], + W1->well_connpos[w + 1]); + + for (int j = W1->well_connpos[w]; + j < W1->well_connpos[w + 1]; ++j) { + BOOST_CHECK_EQUAL(W2->well_cells[j], W1->well_cells[j]); + BOOST_CHECK_EQUAL(W2->WI [j], W1->WI [j]); + } + + BOOST_CHECK(W1->ctrls[w] != 0); + BOOST_CHECK(W2->ctrls[w] != 0); + + WellControls* c1 = W1->ctrls[w]; + WellControls* c2 = W2->ctrls[w]; + + BOOST_CHECK_EQUAL(c2->num , c1->num ); + BOOST_CHECK_EQUAL(c2->current, c1->current); + + for (int c = 0; c < c1->num; ++c) { + BOOST_CHECK_EQUAL(c2->type [c], c1->type [c]); + BOOST_CHECK_EQUAL(c2->target[c], c1->target[c]); + + for (int p = 0; p < W1->number_of_phases; ++p) { + BOOST_CHECK_EQUAL(c2->distr[c*W1->number_of_phases + p], + c1->distr[c*W1->number_of_phases + p]); + } + } + } + } +}