mirror of
https://github.com/OPM/ResInsight.git
synced 2026-10-25 22:50:25 -05:00
Add grid data from Sumo using ri-cloud-api Load Eclipse grid data (roff) from Sumo as RimReservoirGridEnsemble, one case per realization, with async fetch of grid properties per timestep. - New: RiaSumoGrid (transport), RifReaderSumoGridProperty (lazy property reader), RimReservoirGridEnsembleSumo, RimRoffCaseSumo, and a "Create Reservoir Grid Ensemble" command. - Shared grid: IJK dimensions compared before download; one RigMainGrid is reused across the ensemble when they match, each realization still parses its own ACTNUM before releasing geometry. - Rename folders: Summary/Sumo -> Summary/Cloud, RimSumoDataSource -> Cloud/ (PDM keywords unchanged, old project files still load). - UI: cloud data sources also shown in the 3D window's Data Sources dock. - Fix: realizations no longer lost on project reload; realizations missing data are now reported instead of silently skipped.
190 lines
11 KiB
C++
190 lines
11 KiB
C++
/////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2024 Equinor ASA
|
|
//
|
|
// ResInsight 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.
|
|
//
|
|
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
|
|
// for more details.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#pragma once
|
|
|
|
#include "RifReaderInterface.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QPointer>
|
|
#include <QString>
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
class RiaSumoConnector;
|
|
|
|
//==================================================================================================
|
|
//
|
|
// Lazily fetches grid cell properties for a single Sumo grid realization. ResInsight's result
|
|
// machinery calls staticResult()/dynamicResult() the first time a property is displayed; this reader
|
|
// then downloads the corresponding roff blob from Sumo and decodes the cell values.
|
|
//
|
|
//==================================================================================================
|
|
class RifReaderSumoGridProperty : public RifReaderInterface
|
|
{
|
|
public:
|
|
// Called whenever what is pending changes, with a user-facing message describing it (empty when nothing
|
|
// is pending). No GUI code lives here: the owner decides where and how to show it.
|
|
using PendingChangedCallback = std::function<void( const QString& )>;
|
|
|
|
// Called once a requested time step is done, successfully or not, so the owner can redraw whatever
|
|
// shows this data.
|
|
using TimeStepArrivedCallback = std::function<void( const QString&, size_t, bool )>;
|
|
|
|
// Called whenever how many of a batch's time steps are done changes: completed, total, and a description
|
|
// of what is currently being transferred (matches pendingDataDescription()). total is 0 once nothing is
|
|
// pending, meaning the batch is over. No GUI code lives here: the owner decides whether and how to show a
|
|
// progress bar from these numbers.
|
|
using BatchProgressChangedCallback = std::function<void( size_t /*completed*/, size_t /*total*/, const QString& /*description*/ )>;
|
|
|
|
RifReaderSumoGridProperty( RiaSumoConnector* connector,
|
|
const QString& caseId,
|
|
const QString& ensembleName,
|
|
const QString& gridName,
|
|
int realization );
|
|
|
|
// Aborts any transfers still in flight for this reader, see RiaSumoConnector::cancelGroup, so they stop
|
|
// consuming connections and logging results for a reader that is no longer there.
|
|
~RifReaderSumoGridProperty() override;
|
|
|
|
void setPendingChangedCallback( PendingChangedCallback callback );
|
|
void setTimeStepArrivedCallback( TimeStepArrivedCallback callback );
|
|
void setBatchProgressChangedCallback( BatchProgressChangedCallback callback );
|
|
|
|
void setStaticProperties( const std::vector<QString>& propertyNames );
|
|
void setDynamicProperties( const std::map<QString, std::vector<QString>>& propertyNameToTimestamps );
|
|
|
|
bool open( const QString& fileName, RigEclipseCaseData* eclipseCase ) override;
|
|
|
|
// Take one time step fetched before this reader existed, see RimRoffCaseSumo::startPropertyFetch.
|
|
void acceptFetchedTimeStep( const QString& propertyName, size_t stepIndex, const QString& isoDateOrInterval, const QByteArray& contents );
|
|
|
|
// Record a transfer started before this reader existed as already on its way. Without this the reader
|
|
// reports nothing in flight, so the user is told nothing while waiting and a second, duplicate transfer
|
|
// is issued the first time the property is read.
|
|
void markTimeStepPending( const QString& propertyName, size_t stepIndex );
|
|
|
|
// What is being transferred right now, for display to the user. Empty when nothing is.
|
|
QString pendingDataDescription() const;
|
|
|
|
bool staticResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, std::vector<double>* values ) override;
|
|
bool dynamicResult( const QString& result, RiaDefines::PorosityModelType matrixOrFracture, size_t stepIndex, std::vector<double>* values ) override;
|
|
|
|
// Fetches and stores exactly the given time steps of a dynamic property - no other time step of it is
|
|
// pulled in. Meant for a caller with no redraw loop of its own that needs a specific set of time steps,
|
|
// e.g. RimStatisticsContourMap: having them already present keeps
|
|
// RigCaseCellResultsData::ensureKnownResultLoaded() from falling back to loading the whole time series.
|
|
// The given steps are requested together as one async batch (so the transfers run in parallel, the same
|
|
// as dynamicResult()'s own look-ahead batch), and this call blocks until every one of them has either
|
|
// arrived or timed out before returning - so by the time it returns, nothing requested by this call is
|
|
// left in flight. Returns false if any requested step never got a value (missing, failed, or timed out).
|
|
bool prefetchDynamicResult( const QString& propertyName, const std::vector<size_t>& stepIndices );
|
|
|
|
private:
|
|
bool fetchAndDecode( const QString& propertyName, const QString& isoDateOrInterval, std::vector<double>* values );
|
|
bool decodeInto( const QByteArray& contents, const QString& propertyName, std::vector<double>* values );
|
|
|
|
// One line per transferred time step, naming the realization and whether it came from a batch or from
|
|
// a single request. This reader is per realization, so without the realization a run of single
|
|
// requests from several readers is indistinguishable from one reader fetching the same steps twice.
|
|
void logTransfer( const QString& propertyName, const QString& isoDateOrInterval, size_t byteCount, bool fromBatch ) const;
|
|
|
|
// The time steps to download together with stepIndex: stepIndex itself, plus every other unloaded step
|
|
// of this property not already pending or loaded, so the whole property can be requested in one batch.
|
|
// Which of these actually run at once is left to QNetworkAccessManager - this reader does not throttle
|
|
// the batch itself.
|
|
std::vector<size_t> timeStepsToFetch( const QString& propertyName, const std::vector<QString>& timestamps, size_t stepIndex );
|
|
|
|
// Where the case keeps the values of one time step of a dynamic property, or null when the property is
|
|
// not registered as a result. Writing here is what makes a prefetched time step available without
|
|
// holding on to the downloaded bytes.
|
|
std::vector<double>* resultValueSlot( const QString& propertyName, size_t stepIndex );
|
|
|
|
// One requested time step of one dynamic property.
|
|
using PendingKey = std::pair<QString, size_t>;
|
|
|
|
// Request the given time steps without waiting, marking them pending. Already pending steps are skipped,
|
|
// so a redraw mid-transfer does not issue a second request. Not capped: every given step not already
|
|
// pending is requested in the same batch, relying on QNetworkAccessManager to bound how many actually
|
|
// run at once and queue the rest.
|
|
void requestTimeStepsAsync( const QString& propertyName, const std::vector<QString>& timestamps, const std::vector<size_t>& steps );
|
|
|
|
// Blocks the calling thread until the given time step is no longer pending or the transfer times out
|
|
// (RiaSumoDefines::gridPropertyTransferTimeoutMillis()). Lets dynamicResult() hand back the real values
|
|
// instead of a placeholder, so callers never see partially-loaded data - the same blocking contract a
|
|
// disk-based reader already has. No GUI code here: whatever progress bar the owner shows via
|
|
// m_onBatchProgressChanged runs independently, driven by requestTimeStepsAsync/onTimeStepArrived. A no-op
|
|
// if the step is not currently pending.
|
|
void waitForTimeStepToArrive( const QString& propertyName, size_t stepIndex );
|
|
|
|
// Called on the connector thread, which is the GUI thread, once per requested time step.
|
|
void onTimeStepArrived( const QString& propertyName, size_t stepIndex, const QString& isoDateOrInterval, const QByteArray& contents );
|
|
|
|
// Fill values with the undefined-cell value, sized to the grid, and report success, so a time step still
|
|
// on its way draws blank instead of logging a load failure on every redraw.
|
|
bool fillWithUndefinedValues( std::vector<double>* values ) const;
|
|
|
|
// Reports the time step is done via m_onTimeStepArrived, successfully or not, so the owner can redraw.
|
|
void notifyTimeStepArrived( const QString& propertyName, size_t stepIndex, bool ok ) const;
|
|
|
|
// Reports what is pending via m_onPendingChanged, built from pendingDataDescription().
|
|
void notifyPendingChanged() const;
|
|
|
|
// Reports how many of a batch's time steps are done via m_onBatchProgressChanged: opens/grows the count
|
|
// the first time something becomes pending (called from requestTimeStepsAsync/markTimeStepPending/
|
|
// acceptFetchedTimeStep) and ticks it as each step arrives (called from onTimeStepArrived), reporting
|
|
// total 0 once m_pending is empty again so the owner knows the batch is over. No GUI code here: the
|
|
// owner decides whether and how to show a progress bar from these numbers.
|
|
void updateBatchProgress();
|
|
|
|
private:
|
|
QPointer<RiaSumoConnector> m_connector;
|
|
QString m_caseId;
|
|
QString m_ensembleName;
|
|
QString m_gridName;
|
|
int m_realization;
|
|
|
|
RigEclipseCaseData* m_caseData; // set in open(); used for cell count and active cell masking
|
|
|
|
std::vector<QString> m_staticProperties;
|
|
std::map<QString, std::vector<QString>> m_dynamicTimestamps; // property name -> sorted iso timestamps
|
|
|
|
// Requested and not yet arrived. Claimed before the request is issued, so it also answers "already on
|
|
// its way?".
|
|
std::set<PendingKey> m_pending;
|
|
|
|
// Dropped when this reader dies. Callbacks hold a weak_ptr and return early once it expires, so a reply
|
|
// outliving the reader cannot write into freed memory.
|
|
std::shared_ptr<bool> m_lifetimeToken;
|
|
|
|
PendingChangedCallback m_onPendingChanged;
|
|
TimeStepArrivedCallback m_onTimeStepArrived;
|
|
BatchProgressChangedCallback m_onBatchProgressChanged;
|
|
|
|
// Bookkeeping only, no GUI: how many time steps have been added to the current batch and how many of
|
|
// those have arrived (successfully or not), reported to the owner via m_onBatchProgressChanged. Both
|
|
// reset to 0 once m_pending empties again.
|
|
size_t m_batchProgressTotal = 0;
|
|
size_t m_batchProgressCompleted = 0;
|
|
};
|