2017-10-12 07:30:37 +02:00
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017 Statoil 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.
//
/////////////////////////////////////////////////////////////////////////////////
2017-10-23 14:27:04 +02:00
#include "RimSummaryCalculation.h"
2017-10-12 07:30:37 +02:00
2017-10-13 12:22:35 +02:00
#include "expressionparser/ExpressionParser.h"
2017-10-16 22:04:00 +02:00
#include "RiaSummaryCurveDefinition.h"
2017-10-20 10:20:53 +02:00
#include "RiaSummaryTools.h"
2017-10-16 22:04:00 +02:00
2017-10-20 10:20:53 +02:00
#include "RimProject.h"
2017-10-23 14:42:38 +02:00
#include "RimSummaryCalculationCollection.h"
#include "RimSummaryCalculationVariable.h"
2017-10-16 22:04:00 +02:00
#include "RimSummaryCurve.h"
2017-10-20 10:20:53 +02:00
#include "RimSummaryPlot.h"
#include "RimSummaryPlotCollection.h"
2017-10-12 07:30:37 +02:00
2017-11-02 15:29:39 +01:00
#include "RiuExpressionContextMenuManager.h"
2017-10-13 12:22:35 +02:00
#include "cafPdmUiTextEditor.h"
2017-11-06 13:26:18 +01:00
#include "cafPdmUiLineEditor.h"
2017-10-13 12:22:35 +02:00
2017-10-23 13:31:57 +02:00
#include <QMessageBox>
2017-10-13 14:39:30 +02:00
#include <algorithm>
2017-11-02 15:29:39 +01:00
#include "QMenu"
2017-10-13 12:22:35 +02:00
2017-10-23 14:42:38 +02:00
CAF_PDM_SOURCE_INIT ( RimSummaryCalculation , "RimSummaryCalculation" );
2017-10-12 07:30:37 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
RimSummaryCalculation :: RimSummaryCalculation ()
2017-10-12 07:30:37 +02:00
{
2017-10-23 14:42:38 +02:00
CAF_PDM_InitObject ( "RimSummaryCalculation" , ":/octave.png" , "Calculation" , "" );
2017-10-12 07:30:37 +02:00
2017-11-02 15:29:39 +01:00
CAF_PDM_InitFieldNoDefault ( & m_description , "Description" , "Description" , "" , "" , "" );
2017-10-13 12:22:35 +02:00
m_description . uiCapability () -> setUiReadOnly ( true );
2017-11-03 08:36:51 +01:00
CAF_PDM_InitField ( & m_expression , "Expression" , QString ( "" ), "Expression" , "" , "" , "" );
2017-10-13 12:22:35 +02:00
m_expression . uiCapability () -> setUiEditorTypeName ( caf :: PdmUiTextEditor :: uiEditorTypeName ());
2017-11-06 13:26:18 +01:00
CAF_PDM_InitField ( & m_unit , "Unit" , QString ( "" ), "Unit" , "" , "" , "" );
m_unit . uiCapability () -> setUiEditorTypeName ( caf :: PdmUiLineEditor :: uiEditorTypeName ());
2017-11-02 15:29:39 +01:00
CAF_PDM_InitFieldNoDefault ( & m_variables , "Variables" , "Variables" , "" , "" , "" );
2017-10-12 07:30:37 +02:00
CAF_PDM_InitFieldNoDefault ( & m_calculatedValues , "CalculatedValues" , "Calculated Values" , "" , "" , "" );
2017-10-18 08:20:21 +02:00
2017-11-02 15:29:39 +01:00
CAF_PDM_InitFieldNoDefault ( & m_timesteps , "TimeSteps" , "Time Steps" , "" , "" , "" );
m_exprContextMenuMgr = std :: unique_ptr < RiuExpressionContextMenuManager > ( new RiuExpressionContextMenuManager ());
2017-10-12 07:30:37 +02:00
}
2017-10-13 12:22:35 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
void RimSummaryCalculation :: setDescription ( const QString & description )
2017-10-13 12:22:35 +02:00
{
m_description = description ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
QString RimSummaryCalculation :: description () const
2017-10-13 12:22:35 +02:00
{
return m_description ;
}
2017-10-12 07:30:37 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
caf :: PdmChildArrayFieldHandle * RimSummaryCalculation :: variables ()
2017-10-12 07:30:37 +02:00
{
return & m_variables ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-24 09:57:32 +02:00
RimSummaryCalculationVariable * RimSummaryCalculation :: addVariable ( const QString & name )
2017-10-12 07:30:37 +02:00
{
2017-10-23 14:42:38 +02:00
RimSummaryCalculationVariable * v = new RimSummaryCalculationVariable ;
2017-10-24 09:57:32 +02:00
v -> setName ( name );
2017-10-12 07:30:37 +02:00
m_variables . push_back ( v );
return v ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:42:38 +02:00
void RimSummaryCalculation :: deleteVariable ( RimSummaryCalculationVariable * calcVariable )
2017-10-12 07:30:37 +02:00
{
m_variables . removeChildObject ( calcVariable );
delete calcVariable ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
const std :: vector < double >& RimSummaryCalculation :: values () const
2017-10-12 07:30:37 +02:00
{
return m_calculatedValues ();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
const std :: vector < time_t >& RimSummaryCalculation :: timeSteps () const
2017-10-18 08:20:21 +02:00
{
return m_timesteps ();
}
2017-11-03 08:36:51 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-11-04 01:14:56 +01:00
void RimSummaryCalculation :: setExpression ( const QString & expr )
2017-11-03 08:36:51 +01:00
{
m_expression = expr ;
}
2017-11-04 01:14:56 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimSummaryCalculation :: expression () const
{
return m_expression ;
}
2017-11-06 13:26:18 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RimSummaryCalculation :: unitName () const
{
return m_unit ;
}
2017-10-18 08:20:21 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
caf :: PdmFieldHandle * RimSummaryCalculation :: userDescriptionField ()
2017-10-13 12:22:35 +02:00
{
return & m_description ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
bool RimSummaryCalculation :: parseExpression ()
2017-10-13 12:22:35 +02:00
{
2017-10-23 14:27:04 +02:00
QString leftHandSideVariableName = RimSummaryCalculation :: findLeftHandSide ( m_expression );
2017-10-23 13:31:57 +02:00
if ( leftHandSideVariableName . isEmpty ())
{
QMessageBox :: warning ( nullptr , "Expression Parser" , "Failed to detect left hand side of equation" );
return false ;
}
2017-10-13 12:22:35 +02:00
std :: vector < QString > variableNames = ExpressionParser :: detectReferencedVariables ( m_expression );
2017-10-23 13:45:29 +02:00
if ( variableNames . size () < 1 )
{
QMessageBox :: warning ( nullptr , "Expression Parser" , "Failed to detect any variable names" );
return false ;
}
2017-10-13 12:22:35 +02:00
// Remove variables not present in expression
{
2017-10-23 14:42:38 +02:00
std :: vector < RimSummaryCalculationVariable *> toBeDeleted ;
for ( RimSummaryCalculationVariable * v : m_variables )
2017-10-13 12:22:35 +02:00
{
if ( std :: find ( variableNames . begin (), variableNames . end (), v -> name ()) == variableNames . end ())
{
toBeDeleted . push_back ( v );
}
if ( leftHandSideVariableName == v -> name ())
{
toBeDeleted . push_back ( v );
}
}
2017-10-23 14:42:38 +02:00
for ( RimSummaryCalculationVariable * v : toBeDeleted )
2017-10-13 12:22:35 +02:00
{
deleteVariable ( v );
}
}
for ( auto variableName : variableNames )
{
if ( leftHandSideVariableName != variableName )
{
if ( ! findByName ( variableName ))
{
2017-10-24 09:57:32 +02:00
this -> addVariable ( variableName );
2017-10-13 12:22:35 +02:00
}
}
}
2017-10-20 10:20:53 +02:00
m_description = buildCalculationName ();
2017-10-13 14:33:26 +02:00
2017-10-13 12:22:35 +02:00
return true ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
bool RimSummaryCalculation :: calculate ()
2017-10-12 07:30:37 +02:00
{
2017-10-23 14:27:04 +02:00
QString leftHandSideVariableName = RimSummaryCalculation :: findLeftHandSide ( m_expression );
2017-10-13 12:22:35 +02:00
ExpressionParser parser ;
2017-10-16 22:04:00 +02:00
std :: vector < std :: vector < double >> variableValues ;
variableValues . resize ( m_variables . size ());
2017-10-18 11:18:43 +02:00
std :: vector < time_t > sourceTimeSteps ;
2017-11-02 15:29:39 +01:00
2017-10-13 12:22:35 +02:00
size_t itemCount = 0 ;
2017-10-16 22:04:00 +02:00
for ( size_t i = 0 ; i < m_variables . size (); i ++ )
2017-10-13 12:22:35 +02:00
{
2017-10-23 14:42:38 +02:00
RimSummaryCalculationVariable * v = m_variables [ i ];
2017-10-16 22:04:00 +02:00
if ( ! v -> summaryCase ())
{
2017-10-23 13:45:29 +02:00
QMessageBox :: warning ( nullptr , "Expression Parser" , QString ( "No summary case defined for variable : %1" ). arg ( v -> name ()));
2017-10-16 22:04:00 +02:00
return false ;
}
if ( ! v -> summaryAddress ())
{
2017-10-23 13:45:29 +02:00
QMessageBox :: warning ( nullptr , "Expression Parser" , QString ( "No summary address defined for variable : %1" ). arg ( v -> name ()));
2017-10-16 22:04:00 +02:00
return false ;
}
RimSummaryAddress * sumAdr = v -> summaryAddress ();
RiaSummaryCurveDefinition curveDef ( v -> summaryCase (), v -> summaryAddress () -> address ());
std :: vector < double >& curveValues = variableValues [ i ];
RiaSummaryCurveDefinition :: resultValues ( curveDef , & curveValues );
2017-10-18 11:18:43 +02:00
if ( sourceTimeSteps . size () == 0 )
{
sourceTimeSteps = RiaSummaryCurveDefinition :: timeSteps ( curveDef );
}
2017-10-16 22:04:00 +02:00
if ( itemCount == 0 )
{
itemCount = curveValues . size ();
}
else
{
if ( itemCount != curveValues . size ())
{
2017-10-23 13:45:29 +02:00
QMessageBox :: warning ( nullptr , "Expression Parser" , QString ( "Detected varying number of time steps in input vectors. Only vectors with identical number of time steps is supported." ));
2017-10-16 22:04:00 +02:00
return false ;
}
}
parser . assignVector ( v -> name (), curveValues );
2017-10-13 12:22:35 +02:00
}
if ( itemCount == 0 )
{
2017-10-23 13:45:29 +02:00
QMessageBox :: warning ( nullptr , "Expression Parser" , QString ( "Detected zero time steps in input vectors, which is not supported." ));
2017-10-13 12:22:35 +02:00
return false ;
}
m_calculatedValues . v (). resize ( itemCount );
parser . assignVector ( leftHandSideVariableName , m_calculatedValues . v ());
QString errorText ;
bool evaluatedOk = parser . evaluate ( m_expression , & errorText );
if ( evaluatedOk )
{
2017-10-18 11:18:43 +02:00
// Copy time vector from source
m_timesteps = sourceTimeSteps ;
2017-10-13 12:22:35 +02:00
}
else
{
2017-10-23 13:45:29 +02:00
QString s = "The following error message was received from the parser library : \n\n " ;
s += errorText ;
QMessageBox :: warning ( nullptr , "Expression Parser" , s );
2017-10-13 12:22:35 +02:00
}
return evaluatedOk ;
}
//--------------------------------------------------------------------------------------------------
/// Find the last assignment using := and interpret the text before the := as LHS
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
QString RimSummaryCalculation :: findLeftHandSide ( const QString & expresion )
2017-10-13 12:22:35 +02:00
{
2017-10-23 13:11:38 +02:00
int index = expresion . lastIndexOf ( ":=" );
2017-10-13 12:22:35 +02:00
if ( index > 0 )
{
2017-10-23 13:11:38 +02:00
QString s = expresion . left ( index ). simplified ();
QStringList words = s . split ( " " );
if ( words . size () > 0 )
{
return words . back ();
}
2017-10-13 12:22:35 +02:00
}
return "" ;
}
2017-11-02 15:29:39 +01:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryCalculation :: attachToWidget ()
{
for ( auto e : m_expression . uiCapability () -> connectedEditors ())
{
caf :: PdmUiTextEditor * textEditor = dynamic_cast < caf :: PdmUiTextEditor *> ( e );
if ( ! textEditor ) continue ;
QWidget * containerWidget = textEditor -> editorWidget ();
if ( ! containerWidget ) continue ;
for ( auto qObj : containerWidget -> children ())
{
QTextEdit * textEdit = dynamic_cast < QTextEdit *> ( qObj );
if ( textEdit )
{
m_exprContextMenuMgr -> attachTextEdit ( textEdit );
}
}
}
}
2017-10-13 12:22:35 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:42:38 +02:00
RimSummaryCalculationVariable * RimSummaryCalculation :: findByName ( const QString & name ) const
2017-10-13 12:22:35 +02:00
{
2017-10-23 14:42:38 +02:00
for ( RimSummaryCalculationVariable * v : m_variables )
2017-10-13 12:22:35 +02:00
{
if ( v -> name () == name )
{
return v ;
}
}
return nullptr ;
2017-10-12 07:30:37 +02:00
}
2017-10-13 14:33:26 +02:00
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
QString RimSummaryCalculation :: buildCalculationName () const
2017-10-13 14:33:26 +02:00
{
QString name = "Default Calculation Name" ;
2017-10-23 14:27:04 +02:00
QString lhs = RimSummaryCalculation :: findLeftHandSide ( m_expression );
2017-10-13 14:33:26 +02:00
if ( ! lhs . isEmpty ())
{
name = lhs ;
name += " ( " ;
2017-10-23 14:42:38 +02:00
for ( RimSummaryCalculationVariable * v : m_variables )
2017-10-13 14:33:26 +02:00
{
name += v -> summaryAddressDisplayString ();
if ( v != m_variables [ m_variables . size () - 1 ])
{
name += ", " ;
}
}
name += " )" ;
}
return name ;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
2017-10-23 14:27:04 +02:00
void RimSummaryCalculation :: updateDependentCurvesAndPlots ()
2017-10-13 14:33:26 +02:00
{
2017-10-23 14:42:38 +02:00
RimSummaryCalculationCollection * calcColl = nullptr ;
2017-10-20 10:20:53 +02:00
this -> firstAncestorOrThisOfTypeAsserted ( calcColl );
calcColl -> rebuildCaseMetaData ();
RimSummaryPlotCollection * summaryPlotCollection = RiaSummaryTools :: summaryPlotCollection ();
for ( RimSummaryPlot * sumPlot : summaryPlotCollection -> summaryPlots ())
{
bool plotContainsCalculatedCurves = false ;
for ( RimSummaryCurve * sumCurve : sumPlot -> summaryCurves ())
{
if ( sumCurve -> summaryAddress (). category () == RifEclipseSummaryAddress :: SUMMARY_CALCULATED )
{
sumCurve -> updateConnectedEditors ();
plotContainsCalculatedCurves = true ;
}
}
if ( plotContainsCalculatedCurves )
{
sumPlot -> loadDataAndUpdate ();
}
}
2017-10-13 14:33:26 +02:00
}