From 99e4419b3c2ac43e0c4070ae87bb7b568351d208 Mon Sep 17 00:00:00 2001 From: Jose Eduardo Bueno Date: Fri, 25 Sep 2020 16:56:27 -0300 Subject: [PATCH 1/5] Reintroduced multisegment wells --- .../linalg/bda/WellContributions.cpp | 20 +++--- .../linalg/bda/WellContributions.hpp | 22 ++----- .../bda/WellContributionsOCLContainer.cpp | 61 ++++++++++++++++--- .../bda/WellContributionsOCLContainer.hpp | 10 ++- .../linalg/bda/openclSolverBackend.cpp | 5 +- 5 files changed, 75 insertions(+), 43 deletions(-) diff --git a/opm/simulators/linalg/bda/WellContributions.cpp b/opm/simulators/linalg/bda/WellContributions.cpp index f8226232e..fa3ef41cc 100644 --- a/opm/simulators/linalg/bda/WellContributions.cpp +++ b/opm/simulators/linalg/bda/WellContributions.cpp @@ -49,6 +49,15 @@ WellContributions::~WellContributions() } multisegments.clear(); +#if HAVE_OPENCL + h_Cnnzs_ocl.clear(); + h_Dnnzs_ocl.clear(); + h_Bnnzs_ocl.clear(); + h_Ccols_ocl.clear(); + h_Bcols_ocl.clear(); + h_val_pointers_ocl.clear(); +#endif + #if HAVE_CUDA if(cuda_gpu){ freeCudaMemory(); // should come before 'delete[] h_x' @@ -147,15 +156,4 @@ void WellContributions::addMultisegmentWellContribution(unsigned int dim_, unsig ++num_ms_wells; } - -void WellContributions::setReordering(int *toOrder_, bool reorder_) -{ - this->toOrder = toOrder_; - this->reorder = reorder_; - for (auto& ms : multisegments) { - ms->setReordering(toOrder_, reorder_); - } -} - } //namespace Opm - diff --git a/opm/simulators/linalg/bda/WellContributions.hpp b/opm/simulators/linalg/bda/WellContributions.hpp index 60142f014..9f374c7bb 100644 --- a/opm/simulators/linalg/bda/WellContributions.hpp +++ b/opm/simulators/linalg/bda/WellContributions.hpp @@ -65,25 +65,21 @@ public: unsigned int dim; // number of columns in blocks in B and C, equal to StandardWell::numEq unsigned int dim_wells; // number of rows in blocks in B and C, equal to StandardWell::numStaticWellEq + std::vector multisegments; #if HAVE_OPENCL std::vector h_Cnnzs_ocl, h_Dnnzs_ocl, h_Bnnzs_ocl; std::vector h_Ccols_ocl, h_Bcols_ocl; std::vector h_val_pointers_ocl; - std::vector h_x_ocl, h_y_ocl; - - int *toOrder = nullptr; - bool reorder = false; #endif private: - unsigned int num_ms_wells = 0; // number of MultisegmentWells in this object, must equal multisegments.size() - unsigned int N; // number of rows (not blockrows) in vectors x and y - std::vector multisegments; - bool opencl_gpu = false; bool cuda_gpu = false; + unsigned int N; // number of rows (not blockrows) in vectors x and y + unsigned int num_ms_wells = 0; // number of MultisegmentWells in this object, must equal multisegments.size() + #if HAVE_CUDA bool allocated = false; unsigned int num_blocks = 0; // total number of blocks in all wells @@ -120,10 +116,6 @@ private: #endif public: -//#if HAVE_OPENCL -// void applyMSWell(cl::Buffer& d_x, cl::Buffer& d_y); -//#endif - #if HAVE_CUDA /// Set a cudaStream to be used /// \param[in] stream the cudaStream that is used to launch the kernel in @@ -186,12 +178,6 @@ public: unsigned int BnumBlocks, std::vector &Bvalues, std::vector &BcolIndices, std::vector &BrowPointers, unsigned int DnumBlocks, double *Dvalues, int *DcolPointers, int *DrowIndices, std::vector &Cvalues); - - /// If the rows of the matrix are reordered, the columnindices of the matrixdata are incorrect - /// Those indices need to be mapped via toOrder - /// \param[in] toOrder array with mappings - /// \param[in] reorder whether the columnindices need to be reordered or not - void setReordering(int *toOrder, bool reorder); }; } //namespace Opm diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp index ecd6de25b..07952c5ed 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp @@ -24,14 +24,14 @@ #include #include -#include namespace bda { using Opm::OpmLog; using Dune::Timer; - void WellContributionsOCLContainer::init(Opm::WellContributions &wellContribs, int Nb_){ + void WellContributionsOCLContainer::init(Opm::WellContributions &wellContribs, int N_, int Nb_){ + N = N_; Nb = Nb_; dim = wellContribs.dim; dim_wells = wellContribs.dim_wells; @@ -48,9 +48,6 @@ namespace bda s.val_pointers = cl::Buffer(*context, CL_MEM_READ_WRITE, sizeof(unsigned int) * wellContribs.h_val_pointers_ocl.size()); s.toOrder = cl::Buffer(*context, CL_MEM_READ_WRITE, sizeof(int) * Nb); } - else{ - num_std_wells = 0; - } } void WellContributionsOCLContainer::reinit(Opm::WellContributions &wellContribs){ @@ -65,9 +62,9 @@ namespace bda s.val_pointers = cl::Buffer(*context, CL_MEM_READ_WRITE, sizeof(unsigned int) * wellContribs.h_val_pointers_ocl.size()); } - void WellContributionsOCLContainer::copy_to_gpu(Opm::WellContributions &wellContribs){ + void WellContributionsOCLContainer::copy_to_gpu(Opm::WellContributions &wellContribs, int *toOrder_){ if(num_std_wells > 0){ - toOrder.insert(toOrder.end(), wellContribs.toOrder, wellContribs.toOrder + Nb); + toOrder.insert(toOrder.end(), toOrder_, toOrder_ + Nb); cl::Event event; std::vector events(7); @@ -80,6 +77,13 @@ namespace bda queue->enqueueWriteBuffer(s.toOrder, CL_FALSE, 0, sizeof(int) * toOrder.size(), toOrder.data(), nullptr, &events[6]); event.waitForEvents(events); } + + if(!wellContribs.multisegments.empty()){ + multisegments = std::move(wellContribs.multisegments); + num_ms_wells = multisegments.size(); + x_msw.reserve(N); + y_msw.reserve(N); + } } void WellContributionsOCLContainer::update_on_gpu(Opm::WellContributions &wellContribs){ @@ -98,6 +102,10 @@ namespace bda queue->enqueueWriteBuffer(s.val_pointers, CL_FALSE, 0, sizeof(unsigned int) * wellContribs.h_val_pointers_ocl.size(), wellContribs.h_val_pointers_ocl.data(), nullptr, &events[5]); event.waitForEvents(events); } + + if(!wellContribs.multisegments.empty()){ + multisegments = std::move(wellContribs.multisegments); + } } void WellContributionsOCLContainer::setOpenCLContext(cl::Context *context_){ @@ -127,13 +135,50 @@ namespace bda cl::Local(lmem1), cl::Local(lmem2), cl::Local(lmem2)); } + + void WellContributionsOCLContainer::applyMSWells(cl::Buffer& x, cl::Buffer& y) { + cl::Event event; + std::vector events(2); + + // copy vectors x and y from GPU to CPU + queue->enqueueReadBuffer(x, CL_FALSE, 0, sizeof(double) * N, x_msw.data(), nullptr, &events[0]); + queue->enqueueReadBuffer(y, CL_FALSE, 0, sizeof(double) * N, y_msw.data(), nullptr, &events[1]); + event.waitForEvents(events); + + // actually apply MultisegmentWells + for(Opm::MultisegmentWellContribution *well: multisegments){ + well->setReordering(toOrder.data(), true); + well->apply(x_msw.data(), y_msw.data()); + } + + // copy vector y from CPU to GPU + queue->enqueueWriteBuffer(y, CL_FALSE, 0, sizeof(double) * N, y_msw.data(), nullptr, &event); + event.wait(); + } + void WellContributionsOCLContainer::apply(cl::Buffer& x, cl::Buffer& y){ if(num_std_wells > 0){ applyStdWells(x, y); } + + if(num_ms_wells > 0){ + applyMSWells(x, y); + } } WellContributionsOCLContainer::~WellContributionsOCLContainer(){ - toOrder.clear(); + if(num_std_wells > 0){ + toOrder.clear(); + } + + if(num_ms_wells > 0){ + for (auto ms : multisegments) { + delete ms; + } + + multisegments.clear(); + x_msw.clear(); + y_msw.clear(); + } } } // end namespace bda diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp index 8a36b9f74..c14295e1f 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp @@ -22,6 +22,7 @@ #include #include +#include namespace bda { @@ -32,8 +33,10 @@ namespace bda unsigned int num_blocks = 0; unsigned int num_std_wells = 0; unsigned int num_ms_wells = 0; // number of MultisegmentWells in this object, must equal multisegments.size() - int Nb; + int N, Nb; std::vector toOrder; + std::vector x_msw, y_msw; + std::vector multisegments; typedef struct { cl::Buffer Cnnzs, Dnnzs, Bnnzs; @@ -51,14 +54,15 @@ namespace bda void reinit(Opm::WellContributions &wellContribs); void applyStdWells(cl::Buffer& x, cl::Buffer& y); + void applyMSWells(cl::Buffer& x, cl::Buffer& y); public: WellContributionsOCLContainer() {}; ~WellContributionsOCLContainer(); void apply(cl::Buffer& x, cl::Buffer& y); - void init(Opm::WellContributions &wellContribs, int Nb); - void copy_to_gpu(Opm::WellContributions &wellContribs); + void init(Opm::WellContributions &wellContribs, int N, int Nb); + void copy_to_gpu(Opm::WellContributions &wellContribs, int *toOrder_); void update_on_gpu(Opm::WellContributions &wellContribs); void setOpenCLContext(cl::Context *context); void setOpenCLQueue(cl::CommandQueue *queue); diff --git a/opm/simulators/linalg/bda/openclSolverBackend.cpp b/opm/simulators/linalg/bda/openclSolverBackend.cpp index 265cbd0a8..9d9d6e737 100644 --- a/opm/simulators/linalg/bda/openclSolverBackend.cpp +++ b/opm/simulators/linalg/bda/openclSolverBackend.cpp @@ -496,7 +496,7 @@ void openclSolverBackend::initialize(int N_, int nnz_, int dim, doub d_Acols = cl::Buffer(*context, CL_MEM_READ_WRITE, sizeof(int) * nnzb); d_Arows = cl::Buffer(*context, CL_MEM_READ_WRITE, sizeof(int) * (Nb + 1)); - wcontainer->init(wellContribs, Nb); + wcontainer->init(wellContribs, N, Nb); // queue.enqueueNDRangeKernel() is a blocking/synchronous call, at least for NVIDIA // cl::make_kernel<> myKernel(); myKernel(args, arg1, arg2); is also blocking @@ -566,8 +566,7 @@ void openclSolverBackend::copy_system_to_gpu(WellContributions &well queue->enqueueFillBuffer(d_x, 0, 0, sizeof(double) * N, nullptr, &event); event.wait(); - wellContribs.setReordering(toOrder, true); - wcontainer->copy_to_gpu(wellContribs); + wcontainer->copy_to_gpu(wellContribs, toOrder); if (verbosity > 2) { std::ostringstream out; From 3a680f728e4dc67b928b9dc244b4a87e0abf8193 Mon Sep 17 00:00:00 2001 From: Jose Eduardo Bueno Date: Wed, 30 Sep 2020 09:14:20 -0300 Subject: [PATCH 2/5] Removed unnecessary vector clear operations --- .../linalg/bda/WellContributionsOCLContainer.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp index 07952c5ed..a592ae584 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp @@ -167,18 +167,10 @@ namespace bda } WellContributionsOCLContainer::~WellContributionsOCLContainer(){ - if(num_std_wells > 0){ - toOrder.clear(); - } - if(num_ms_wells > 0){ for (auto ms : multisegments) { delete ms; } - - multisegments.clear(); - x_msw.clear(); - y_msw.clear(); } } } // end namespace bda From 7aad035bcf5f72ce30da87f19289e6de06d3bc08 Mon Sep 17 00:00:00 2001 From: Jose Eduardo Bueno Date: Wed, 30 Sep 2020 17:31:35 -0300 Subject: [PATCH 3/5] Added smart pointers --- .../linalg/bda/WellContributions.cpp | 11 +------- .../bda/WellContributionsOCLContainer.cpp | 28 +++++++++++-------- .../bda/WellContributionsOCLContainer.hpp | 6 ++-- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/opm/simulators/linalg/bda/WellContributions.cpp b/opm/simulators/linalg/bda/WellContributions.cpp index fa3ef41cc..3f9f879f1 100644 --- a/opm/simulators/linalg/bda/WellContributions.cpp +++ b/opm/simulators/linalg/bda/WellContributions.cpp @@ -43,22 +43,13 @@ WellContributions::WellContributions(std::string gpu_mode){ WellContributions::~WellContributions() { +#if HAVE_CUDA // delete MultisegmentWellContributions for (auto ms : multisegments) { delete ms; } multisegments.clear(); -#if HAVE_OPENCL - h_Cnnzs_ocl.clear(); - h_Dnnzs_ocl.clear(); - h_Bnnzs_ocl.clear(); - h_Ccols_ocl.clear(); - h_Bcols_ocl.clear(); - h_val_pointers_ocl.clear(); -#endif - -#if HAVE_CUDA if(cuda_gpu){ freeCudaMemory(); // should come before 'delete[] h_x' } diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp index a592ae584..b7e94141b 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp @@ -30,6 +30,18 @@ namespace bda using Opm::OpmLog; using Dune::Timer; + WellContributionsOCLContainer::WellContributionsOCLContainer(){ + multisegments.reset(new mswVecT()); + } + + WellContributionsOCLContainer::~WellContributionsOCLContainer(){ + if(num_ms_wells > 0){ + for (auto ms : *multisegments) { + delete ms; + } + } + } + void WellContributionsOCLContainer::init(Opm::WellContributions &wellContribs, int N_, int Nb_){ N = N_; Nb = Nb_; @@ -79,8 +91,8 @@ namespace bda } if(!wellContribs.multisegments.empty()){ - multisegments = std::move(wellContribs.multisegments); - num_ms_wells = multisegments.size(); + multisegments = std::move(std::make_unique(wellContribs.multisegments)); + num_ms_wells = multisegments->size(); x_msw.reserve(N); y_msw.reserve(N); } @@ -104,7 +116,7 @@ namespace bda } if(!wellContribs.multisegments.empty()){ - multisegments = std::move(wellContribs.multisegments); + multisegments = std::move(std::make_unique(wellContribs.multisegments)); } } @@ -146,7 +158,7 @@ namespace bda event.waitForEvents(events); // actually apply MultisegmentWells - for(Opm::MultisegmentWellContribution *well: multisegments){ + for(auto well: *multisegments){ well->setReordering(toOrder.data(), true); well->apply(x_msw.data(), y_msw.data()); } @@ -165,12 +177,4 @@ namespace bda applyMSWells(x, y); } } - - WellContributionsOCLContainer::~WellContributionsOCLContainer(){ - if(num_ms_wells > 0){ - for (auto ms : multisegments) { - delete ms; - } - } - } } // end namespace bda diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp index c14295e1f..1fbd4b2fa 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp @@ -29,6 +29,8 @@ namespace bda class WellContributionsOCLContainer { private: + typedef std::vector mswVecT; + unsigned int dim, dim_wells; unsigned int num_blocks = 0; unsigned int num_std_wells = 0; @@ -36,7 +38,7 @@ namespace bda int N, Nb; std::vector toOrder; std::vector x_msw, y_msw; - std::vector multisegments; + std::unique_ptr multisegments; typedef struct { cl::Buffer Cnnzs, Dnnzs, Bnnzs; @@ -57,7 +59,7 @@ namespace bda void applyMSWells(cl::Buffer& x, cl::Buffer& y); public: - WellContributionsOCLContainer() {}; + WellContributionsOCLContainer(); ~WellContributionsOCLContainer(); void apply(cl::Buffer& x, cl::Buffer& y); From 89cefd8d953577129d024cd557379bfa7e389f4c Mon Sep 17 00:00:00 2001 From: Jose Eduardo Bueno Date: Wed, 30 Sep 2020 18:34:50 -0300 Subject: [PATCH 4/5] Fixed seg fault when there are only mswells --- .../linalg/bda/WellContributionsOCLContainer.cpp | 7 ++++--- .../linalg/bda/WellContributionsOCLContainer.hpp | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp index b7e94141b..a0082e8f8 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp @@ -75,9 +75,9 @@ namespace bda } void WellContributionsOCLContainer::copy_to_gpu(Opm::WellContributions &wellContribs, int *toOrder_){ - if(num_std_wells > 0){ - toOrder.insert(toOrder.end(), toOrder_, toOrder_ + Nb); + toOrder.insert(toOrder.end(), toOrder_, toOrder_ + Nb); + if(num_std_wells > 0){ cl::Event event; std::vector events(7); queue->enqueueWriteBuffer(s.Cnnzs, CL_FALSE, 0, sizeof(double) * wellContribs.h_Cnnzs_ocl.size(), wellContribs.h_Cnnzs_ocl.data(), nullptr, &events[0]); @@ -159,10 +159,11 @@ namespace bda // actually apply MultisegmentWells for(auto well: *multisegments){ - well->setReordering(toOrder.data(), true); + well->setReordering(toOrder.data(), reorder); well->apply(x_msw.data(), y_msw.data()); } + // copy vector y from CPU to GPU queue->enqueueWriteBuffer(y, CL_FALSE, 0, sizeof(double) * N, y_msw.data(), nullptr, &event); event.wait(); diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp index 1fbd4b2fa..847fd1ff0 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp @@ -31,11 +31,13 @@ namespace bda private: typedef std::vector mswVecT; + int N, Nb; unsigned int dim, dim_wells; unsigned int num_blocks = 0; unsigned int num_std_wells = 0; unsigned int num_ms_wells = 0; // number of MultisegmentWells in this object, must equal multisegments.size() - int N, Nb; + + bool reorder = true; std::vector toOrder; std::vector x_msw, y_msw; std::unique_ptr multisegments; From fbbb21d4828a38a62a3913823b90e916afc06f87 Mon Sep 17 00:00:00 2001 From: Jose Eduardo Bueno Date: Thu, 1 Oct 2020 10:27:34 -0300 Subject: [PATCH 5/5] Removed smart pointers; + delete for copy constr --- .../bda/WellContributionsOCLContainer.cpp | 31 ++++++++----------- .../bda/WellContributionsOCLContainer.hpp | 8 ++--- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp index a0082e8f8..2b13bf79b 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.cpp @@ -30,18 +30,6 @@ namespace bda using Opm::OpmLog; using Dune::Timer; - WellContributionsOCLContainer::WellContributionsOCLContainer(){ - multisegments.reset(new mswVecT()); - } - - WellContributionsOCLContainer::~WellContributionsOCLContainer(){ - if(num_ms_wells > 0){ - for (auto ms : *multisegments) { - delete ms; - } - } - } - void WellContributionsOCLContainer::init(Opm::WellContributions &wellContribs, int N_, int Nb_){ N = N_; Nb = Nb_; @@ -91,8 +79,8 @@ namespace bda } if(!wellContribs.multisegments.empty()){ - multisegments = std::move(std::make_unique(wellContribs.multisegments)); - num_ms_wells = multisegments->size(); + multisegments = std::move(wellContribs.multisegments); + num_ms_wells = multisegments.size(); x_msw.reserve(N); y_msw.reserve(N); } @@ -116,7 +104,7 @@ namespace bda } if(!wellContribs.multisegments.empty()){ - multisegments = std::move(std::make_unique(wellContribs.multisegments)); + multisegments = std::move(wellContribs.multisegments); } } @@ -158,12 +146,11 @@ namespace bda event.waitForEvents(events); // actually apply MultisegmentWells - for(auto well: *multisegments){ - well->setReordering(toOrder.data(), reorder); + for(Opm::MultisegmentWellContribution *well: multisegments){ + well->setReordering(toOrder.data(), true); well->apply(x_msw.data(), y_msw.data()); } - // copy vector y from CPU to GPU queue->enqueueWriteBuffer(y, CL_FALSE, 0, sizeof(double) * N, y_msw.data(), nullptr, &event); event.wait(); @@ -178,4 +165,12 @@ namespace bda applyMSWells(x, y); } } + + WellContributionsOCLContainer::~WellContributionsOCLContainer(){ + if(num_ms_wells > 0){ + for (auto ms : multisegments) { + delete ms; + } + } + } } // end namespace bda diff --git a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp index 847fd1ff0..0120c4881 100644 --- a/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp +++ b/opm/simulators/linalg/bda/WellContributionsOCLContainer.hpp @@ -29,18 +29,15 @@ namespace bda class WellContributionsOCLContainer { private: - typedef std::vector mswVecT; - int N, Nb; unsigned int dim, dim_wells; unsigned int num_blocks = 0; unsigned int num_std_wells = 0; unsigned int num_ms_wells = 0; // number of MultisegmentWells in this object, must equal multisegments.size() - bool reorder = true; std::vector toOrder; std::vector x_msw, y_msw; - std::unique_ptr multisegments; + std::vector multisegments; typedef struct { cl::Buffer Cnnzs, Dnnzs, Bnnzs; @@ -61,8 +58,9 @@ namespace bda void applyMSWells(cl::Buffer& x, cl::Buffer& y); public: - WellContributionsOCLContainer(); + WellContributionsOCLContainer() {}; ~WellContributionsOCLContainer(); + WellContributionsOCLContainer(const WellContributionsOCLContainer&) = delete; void apply(cl::Buffer& x, cl::Buffer& y); void init(Opm::WellContributions &wellContribs, int N, int Nb);