mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #3362 from akva2/date_time_prune
Avoid boost/date_time in simulator objects
This commit is contained in:
64
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.cpp
Normal file
64
opm/simulators/flow/SimulatorFullyImplicitBlackoilEbos.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Copyright 2013, 2015, 2020 SINTEF Digital, Mathematics and Cybernetics.
|
||||
Copyright 2015 Andreas Lauser
|
||||
Copyright 2017 IRIS
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
#include <opm/simulators/timestepping/SimulatorTimer.hpp>
|
||||
|
||||
#include <boost/date_time.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
void outputReportStep(const SimulatorTimer& timer)
|
||||
{
|
||||
std::ostringstream stepMsg;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
|
||||
stepMsg.imbue(std::locale(std::locale::classic(), facet));
|
||||
stepMsg << "\nReport step " << std::setw(2) <<timer.currentStepNum()
|
||||
<< "/" << timer.numSteps()
|
||||
<< " at day " << (double)unit::convert::to(timer.simulationTimeElapsed(), unit::day)
|
||||
<< "/" << (double)unit::convert::to(timer.totalTime(), unit::day)
|
||||
<< ", date = " << timer.currentDateTime();
|
||||
OpmLog::info(stepMsg.str());
|
||||
}
|
||||
|
||||
void outputTimestampFIP(const SimulatorTimer& timer,
|
||||
const std::string& title,
|
||||
const std::string& version)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d %b %Y");
|
||||
ss.imbue(std::locale(std::locale::classic(), facet));
|
||||
ss << "\n **************************************************************************\n"
|
||||
<< " Balance at" << std::setw(10) << (double)unit::convert::to(timer.simulationTimeElapsed(), unit::day) << " Days"
|
||||
<< " *" << std::setw(30) << title << " *\n"
|
||||
<< " Report " << std::setw(4) << timer.reportStepNum() << " " << timer.currentDateTime()
|
||||
<< " * Flow version " << std::setw(11) << version << " *\n"
|
||||
<< " **************************************************************************\n";
|
||||
OpmLog::note(ss.str());
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
@@ -61,6 +61,10 @@ struct EnableTuning<TypeTag, TTag::EclFlowProblem> {
|
||||
|
||||
namespace Opm {
|
||||
|
||||
void outputReportStep(const SimulatorTimer& timer);
|
||||
void outputTimestampFIP(const SimulatorTimer& timer,
|
||||
const std::string& title,
|
||||
const std::string& version);
|
||||
|
||||
/// a simulator for the blackoil model
|
||||
template<class TypeTag>
|
||||
@@ -199,15 +203,7 @@ public:
|
||||
}
|
||||
|
||||
if (terminalOutput_) {
|
||||
std::ostringstream stepMsg;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
|
||||
stepMsg.imbue(std::locale(std::locale::classic(), facet));
|
||||
stepMsg << "\nReport step " << std::setw(2) <<timer.currentStepNum()
|
||||
<< "/" << timer.numSteps()
|
||||
<< " at day " << (double)unit::convert::to(timer.simulationTimeElapsed(), unit::day)
|
||||
<< "/" << (double)unit::convert::to(timer.totalTime(), unit::day)
|
||||
<< ", date = " << timer.currentDateTime();
|
||||
OpmLog::info(stepMsg.str());
|
||||
outputReportStep(timer);
|
||||
}
|
||||
|
||||
// write the inital state at the report stage
|
||||
@@ -289,7 +285,7 @@ public:
|
||||
if (terminalOutput_) {
|
||||
if (!timer.initialStep()) {
|
||||
const std::string version = moduleVersionName();
|
||||
outputTimestampFIP(timer, version);
|
||||
outputTimestampFIP(timer, eclState().getTitle(), version);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,20 +332,6 @@ protected:
|
||||
return std::make_unique<Solver>(solverParam_, std::move(model));
|
||||
}
|
||||
|
||||
void outputTimestampFIP(const SimulatorTimer& timer, const std::string version)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d %b %Y");
|
||||
ss.imbue(std::locale(std::locale::classic(), facet));
|
||||
ss << "\n **************************************************************************\n"
|
||||
<< " Balance at" << std::setw(10) << (double)unit::convert::to(timer.simulationTimeElapsed(), unit::day) << " Days"
|
||||
<< " *" << std::setw(30) << eclState().getTitle() << " *\n"
|
||||
<< " Report " << std::setw(4) << timer.reportStepNum() << " " << timer.currentDateTime()
|
||||
<< " * Flow version " << std::setw(11) << version << " *\n"
|
||||
<< " **************************************************************************\n";
|
||||
OpmLog::note(ss.str());
|
||||
}
|
||||
|
||||
const EclipseState& eclState() const
|
||||
{ return ebosSimulator_.vanguard().eclState(); }
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@
|
||||
|
||||
#include <dune/grid/common/gridview.hh>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include <any>
|
||||
#include <vector>
|
||||
|
||||
@@ -78,71 +76,6 @@ namespace detail {
|
||||
|
||||
double getGravity(const double* g, const int dim);
|
||||
|
||||
|
||||
/// \brief Compute the Euclidian norm of a vector
|
||||
/// \warning In the case that num_components is greater than 1
|
||||
/// an interleaved ordering is assumed. E.g. for each cell
|
||||
/// all phases of that cell are stored consecutively. First
|
||||
/// the ones for cell 0, then the ones for cell 1, ... .
|
||||
/// \param it begin iterator for the given vector
|
||||
/// \param end end iterator for the given vector
|
||||
/// \param num_components number of components (i.e. phases) in the vector
|
||||
/// \param pinfo In a parallel this holds the information about the data distribution.
|
||||
template <class Iterator>
|
||||
inline
|
||||
double euclidianNormSquared( Iterator it, const Iterator end, int num_components, const std::any& pinfo = std::any() )
|
||||
{
|
||||
static_cast<void>(num_components); // Suppress warning in the serial case.
|
||||
static_cast<void>(pinfo); // Suppress warning in non-MPI case.
|
||||
#if HAVE_MPI
|
||||
if ( pinfo.type() == typeid(ParallelISTLInformation) )
|
||||
{
|
||||
const ParallelISTLInformation& info =
|
||||
std::any_cast<const ParallelISTLInformation&>(pinfo);
|
||||
typedef typename Iterator::value_type Scalar;
|
||||
Scalar product = 0.0;
|
||||
int size_per_component = (end - it);
|
||||
size_per_component /= num_components; // two lines to supresse unused warning.
|
||||
assert((end - it) == num_components * size_per_component);
|
||||
|
||||
if( num_components == 1 )
|
||||
{
|
||||
auto component_container =
|
||||
boost::make_iterator_range(it, end);
|
||||
info.computeReduction(component_container,
|
||||
Reduction::makeInnerProductFunctor<double>(),
|
||||
product);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& maskContainer = info.getOwnerMask();
|
||||
auto mask = maskContainer.begin();
|
||||
assert(static_cast<int>(maskContainer.size()) == size_per_component);
|
||||
|
||||
for(int cell = 0; cell < size_per_component; ++cell, ++mask)
|
||||
{
|
||||
Scalar cell_product = (*it) * (*it);
|
||||
++it;
|
||||
for(int component=1; component < num_components;
|
||||
++component, ++it)
|
||||
{
|
||||
cell_product += (*it) * (*it);
|
||||
}
|
||||
product += cell_product * (*mask);
|
||||
}
|
||||
}
|
||||
return info.communicator().sum(product);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
double product = 0.0 ;
|
||||
for( ; it != end; ++it ) {
|
||||
product += ( *it * *it );
|
||||
}
|
||||
return product;
|
||||
}
|
||||
}
|
||||
/// \brief Get the number of local interior cells in a grid.
|
||||
/// \tparam The type of the DUNE grid.
|
||||
/// \param grid The grid which cells we count
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
#include <opm/simulators/timestepping/AdaptiveSimulatorTimer.hpp>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
AdaptiveSimulatorTimer::
|
||||
AdaptiveSimulatorTimer( const SimulatorTimerInterface& timer,
|
||||
const double lastStepTaken,
|
||||
const double maxTimeStep )
|
||||
: start_date_time_( timer.startDateTime() )
|
||||
: start_date_time_( std::make_shared<boost::posix_time::ptime>(timer.startDateTime()) )
|
||||
, start_time_( timer.simulationTimeElapsed() )
|
||||
, total_time_( start_time_ + timer.currentStepLength() )
|
||||
, report_step_( timer.reportStepNum() )
|
||||
@@ -160,7 +162,7 @@ AdaptiveSimulatorTimer& AdaptiveSimulatorTimer::operator++ ()
|
||||
|
||||
boost::posix_time::ptime AdaptiveSimulatorTimer::startDateTime() const
|
||||
{
|
||||
return start_date_time_;
|
||||
return *start_date_time_;
|
||||
}
|
||||
|
||||
/// return copy of object
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
|
||||
#include <opm/simulators/timestepping/SimulatorTimerInterface.hpp>
|
||||
@@ -106,7 +107,7 @@ namespace Opm
|
||||
virtual std::unique_ptr< SimulatorTimerInterface > clone() const;
|
||||
|
||||
protected:
|
||||
const boost::posix_time::ptime start_date_time_;
|
||||
std::shared_ptr<boost::posix_time::ptime> start_date_time_;
|
||||
const double start_time_;
|
||||
const double total_time_;
|
||||
const int report_step_;
|
||||
|
||||
27
opm/simulators/timestepping/AdaptiveTimeSteppingEbos.cpp
Normal file
27
opm/simulators/timestepping/AdaptiveTimeSteppingEbos.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <opm/common/OpmLog/OpmLog.hpp>
|
||||
#include <opm/parser/eclipse/Units/Units.hpp>
|
||||
#include <opm/simulators/timestepping/AdaptiveSimulatorTimer.hpp>
|
||||
#include <boost/date_time.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace Opm {
|
||||
|
||||
void logTimer(const AdaptiveSimulatorTimer& substepTimer)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
|
||||
ss.imbue(std::locale(std::locale::classic(), facet));
|
||||
ss <<"\nStarting time step " << substepTimer.currentStepNum() << ", stepsize "
|
||||
<< unit::convert::to(substepTimer.currentStepLength(), unit::day) << " days,"
|
||||
<< " at day " << (double)unit::convert::to(substepTimer.simulationTimeElapsed(), unit::day)
|
||||
<< "/" << (double)unit::convert::to(substepTimer.totalTime(), unit::day)
|
||||
<< ", date = " << substepTimer.currentDateTime();
|
||||
OpmLog::info(ss.str());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,8 +18,6 @@
|
||||
#include <opm/core/props/phaseUsageFromDeck.hpp>
|
||||
#include <opm/common/Exceptions.hpp>
|
||||
|
||||
#include <boost/date_time.hpp>
|
||||
|
||||
namespace Opm::Properties {
|
||||
|
||||
namespace TTag {
|
||||
@@ -224,6 +222,7 @@ struct MinTimeStepBasedOnNewtonIterations<TypeTag, TTag::FlowTimeSteppingParamet
|
||||
namespace Opm {
|
||||
// AdaptiveTimeStepping
|
||||
//---------------------
|
||||
void logTimer(const AdaptiveSimulatorTimer& substepTimer);
|
||||
|
||||
template<class TypeTag>
|
||||
class AdaptiveTimeSteppingEbos
|
||||
@@ -391,15 +390,7 @@ namespace Opm {
|
||||
// get current delta t
|
||||
const double dt = substepTimer.currentStepLength() ;
|
||||
if (timestepVerbose_) {
|
||||
std::ostringstream ss;
|
||||
boost::posix_time::time_facet* facet = new boost::posix_time::time_facet("%d-%b-%Y");
|
||||
ss.imbue(std::locale(std::locale::classic(), facet));
|
||||
ss <<"\nStarting time step " << substepTimer.currentStepNum() << ", stepsize "
|
||||
<< unit::convert::to(substepTimer.currentStepLength(), unit::day) << " days,"
|
||||
<< " at day " << (double)unit::convert::to(substepTimer.simulationTimeElapsed(), unit::day)
|
||||
<< "/" << (double)unit::convert::to(substepTimer.totalTime(), unit::day)
|
||||
<< ", date = " << substepTimer.currentDateTime();
|
||||
OpmLog::info(ss.str());
|
||||
logTimer(substepTimer);
|
||||
}
|
||||
|
||||
SimulatorReportSingle substepReport;
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <ostream>
|
||||
#include <numeric>
|
||||
|
||||
#include <boost/date_time/gregorian_calendar.hpp>
|
||||
#include <boost/date_time/posix_time/conversion.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
@@ -31,7 +34,7 @@ namespace Opm
|
||||
SimulatorTimer::SimulatorTimer()
|
||||
: current_step_(0),
|
||||
current_time_(0.0),
|
||||
start_date_(2012,1,1) // A really arbitrary default starting value?!
|
||||
start_date_(std::make_shared<boost::gregorian::date>(2012,1,1)) // A really arbitrary default starting value?!
|
||||
{
|
||||
}
|
||||
|
||||
@@ -58,7 +61,7 @@ namespace Opm
|
||||
}
|
||||
|
||||
setCurrentStepNum(report_step);
|
||||
start_date_ = boost::posix_time::from_time_t(schedule.getStartTime()).date();
|
||||
*start_date_ = boost::posix_time::from_time_t(schedule.getStartTime()).date();
|
||||
}
|
||||
|
||||
/// Whether the current step is the first step.
|
||||
@@ -108,7 +111,7 @@ namespace Opm
|
||||
|
||||
boost::posix_time::ptime SimulatorTimer::startDateTime() const
|
||||
{
|
||||
return boost::posix_time::ptime(start_date_);
|
||||
return boost::posix_time::ptime(*start_date_);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
namespace boost { namespace gregorian { class date; } }
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
@@ -50,7 +52,7 @@ namespace Opm
|
||||
void init(const Schedule& schedule, size_t report_step = 0);
|
||||
|
||||
/// Whether the current step is the first step.
|
||||
bool initialStep() const;
|
||||
bool initialStep() const override;
|
||||
|
||||
/// Total number of steps.
|
||||
int numSteps() const;
|
||||
@@ -59,7 +61,7 @@ namespace Opm
|
||||
/// has been completed from the start of the run. The time
|
||||
/// after initialization but before the simulation has started
|
||||
/// is timestep number zero.
|
||||
int currentStepNum() const;
|
||||
int currentStepNum() const override;
|
||||
|
||||
/// Set current step number.
|
||||
void setCurrentStepNum(int step);
|
||||
@@ -68,7 +70,7 @@ namespace Opm
|
||||
/// the simulator will take in the next iteration.
|
||||
///
|
||||
/// @note if done(), it is an error to call currentStepLength().
|
||||
double currentStepLength() const;
|
||||
double currentStepLength() const override;
|
||||
|
||||
/// Previous step length. This is the length of the step that
|
||||
/// was taken to arrive at this time.
|
||||
@@ -76,20 +78,20 @@ namespace Opm
|
||||
/// @note if no increments have been done (i.e. the timer is
|
||||
/// still in its constructed state and currentStepNum() == 0),
|
||||
/// it is an error to call stepLengthTaken().
|
||||
double stepLengthTaken () const;
|
||||
double stepLengthTaken () const override;
|
||||
|
||||
/// Time elapsed since the start of the simulation until the
|
||||
/// beginning of the current time step [s].
|
||||
double simulationTimeElapsed() const;
|
||||
double simulationTimeElapsed() const override;
|
||||
|
||||
/// Total time.
|
||||
double totalTime() const;
|
||||
|
||||
/// Return start date of simulation
|
||||
boost::posix_time::ptime startDateTime() const;
|
||||
boost::posix_time::ptime startDateTime() const override;
|
||||
|
||||
/// Return current date.
|
||||
boost::posix_time::ptime currentDateTime() const;
|
||||
boost::posix_time::ptime currentDateTime() const override;
|
||||
|
||||
/// Set total time.
|
||||
/// This is primarily intended for multi-epoch schedules,
|
||||
@@ -105,24 +107,24 @@ namespace Opm
|
||||
SimulatorTimer& operator++();
|
||||
|
||||
/// advance time by currentStepLength
|
||||
void advance() { this->operator++(); }
|
||||
void advance() override { this->operator++(); }
|
||||
|
||||
/// Return true if op++() has been called numSteps() times.
|
||||
bool done() const;
|
||||
bool done() const override;
|
||||
|
||||
/// Always return false. Timestep failures is handled in the
|
||||
/// substepTimer
|
||||
bool lastStepFailed() const {return false;}
|
||||
bool lastStepFailed() const override { return false; }
|
||||
|
||||
/// return copy of object
|
||||
virtual std::unique_ptr< SimulatorTimerInterface > clone() const;
|
||||
std::unique_ptr< SimulatorTimerInterface > clone() const override;
|
||||
|
||||
private:
|
||||
std::vector<double> timesteps_;
|
||||
int current_step_;
|
||||
double current_time_;
|
||||
double total_time_;
|
||||
boost::gregorian::date start_date_;
|
||||
std::shared_ptr<boost::gregorian::date> start_date_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
40
opm/simulators/timestepping/SimulatorTimerInterface.cpp
Normal file
40
opm/simulators/timestepping/SimulatorTimerInterface.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Copyright (c) 2014 IRIS AS
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <opm/simulators/timestepping/SimulatorTimerInterface.hpp>
|
||||
|
||||
#include <boost/date_time/posix_time/conversion.hpp>
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
|
||||
boost::posix_time::ptime SimulatorTimerInterface::currentDateTime() const
|
||||
{
|
||||
return startDateTime() + boost::posix_time::seconds( (int) simulationTimeElapsed());
|
||||
//boost::posix_time::ptime(startDate()) + boost::posix_time::seconds( (int) simulationTimeElapsed());
|
||||
}
|
||||
|
||||
time_t SimulatorTimerInterface::currentPosixTime() const
|
||||
{
|
||||
tm t = boost::posix_time::to_tm(currentDateTime());
|
||||
return std::mktime(&t);
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
@@ -22,9 +22,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/date_time/gregorian/gregorian.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time_types.hpp>
|
||||
#include <boost/date_time/posix_time/conversion.hpp>
|
||||
namespace boost { namespace posix_time { class ptime; } }
|
||||
|
||||
namespace Opm
|
||||
{
|
||||
@@ -90,19 +88,11 @@ namespace Opm
|
||||
virtual boost::posix_time::ptime startDateTime() const = 0;
|
||||
|
||||
/// Return the current time as a posix time object.
|
||||
virtual boost::posix_time::ptime currentDateTime() const
|
||||
{
|
||||
return startDateTime() + boost::posix_time::seconds( (int) simulationTimeElapsed());
|
||||
//boost::posix_time::ptime(startDate()) + boost::posix_time::seconds( (int) simulationTimeElapsed());
|
||||
}
|
||||
virtual boost::posix_time::ptime currentDateTime() const;
|
||||
|
||||
/// Time elapsed since the start of the POSIX epoch (Jan 1st,
|
||||
/// 1970) until the current time step begins [s].
|
||||
virtual time_t currentPosixTime() const
|
||||
{
|
||||
tm t = boost::posix_time::to_tm(currentDateTime());
|
||||
return std::mktime(&t);
|
||||
}
|
||||
virtual time_t currentPosixTime() const;
|
||||
|
||||
/// Return true if last time step failed
|
||||
virtual bool lastStepFailed() const = 0;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
along with OPM. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <config.h>
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <opm/simulators/timestepping/TimeStepControlInterface.hpp>
|
||||
|
||||
namespace Opm
|
||||
|
||||
Reference in New Issue
Block a user