///////////////////////////////////////////////////////////////////////////////// // // 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 // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RimSummaryCalculation.h" #include "expressionparser/ExpressionParser.h" #include "RiaSummaryCurveDefinition.h" #include "RiaSummaryTools.h" #include "RigTimeHistoryCurveMerger.h" #include "RimProject.h" #include "RimSummaryAddress.h" #include "RimSummaryCalculationCollection.h" #include "RimSummaryCalculationVariable.h" #include "RimSummaryCurve.h" #include "RimSummaryPlot.h" #include "RimSummaryPlotCollection.h" #include "RiuExpressionContextMenuManager.h" #include "cafPdmUiTextEditor.h" #include "cafPdmUiLineEditor.h" #include #include CAF_PDM_SOURCE_INIT(RimSummaryCalculation, "RimSummaryCalculation"); //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryCalculation::RimSummaryCalculation() { CAF_PDM_InitObject("RimSummaryCalculation", ":/octave.png", "Calculation", ""); CAF_PDM_InitFieldNoDefault(&m_description, "Description", "Description", "", "", ""); m_description.uiCapability()->setUiReadOnly(true); CAF_PDM_InitField(&m_expression, "Expression", QString(""), "Expression", "", "", ""); m_expression.uiCapability()->setUiEditorTypeName(caf::PdmUiTextEditor::uiEditorTypeName()); CAF_PDM_InitField(&m_unit, "Unit", QString(""), "Unit", "", "", ""); m_unit.uiCapability()->setUiEditorTypeName(caf::PdmUiLineEditor::uiEditorTypeName()); CAF_PDM_InitFieldNoDefault(&m_variables, "Variables", "Variables", "", "", ""); CAF_PDM_InitFieldNoDefault(&m_calculatedValues, "CalculatedValues", "Calculated Values", "", "", ""); CAF_PDM_InitFieldNoDefault(&m_timesteps, "TimeSteps", "Time Steps", "", "", ""); m_exprContextMenuMgr = std::unique_ptr(new RiuExpressionContextMenuManager()); m_isDirty = false; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::setDescription(const QString& description) { m_description = description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryCalculation::description() const { return m_description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryCalculation::isDirty() const { return m_isDirty; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmChildArrayFieldHandle* RimSummaryCalculation::variables() { return &m_variables; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryCalculationVariable* RimSummaryCalculation::addVariable(const QString& name) { RimSummaryCalculationVariable* v = new RimSummaryCalculationVariable; v->setName(name); m_variables.push_back(v); return v; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::deleteVariable(RimSummaryCalculationVariable* calcVariable) { m_variables.removeChildObject(calcVariable); delete calcVariable; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RimSummaryCalculation::values() const { return m_calculatedValues(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const std::vector& RimSummaryCalculation::timeSteps() const { return m_timesteps(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::setExpression(const QString& expr) { m_expression = expr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryCalculation::expression() const { return m_expression; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryCalculation::unitName() const { return m_unit; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- caf::PdmFieldHandle* RimSummaryCalculation::userDescriptionField() { return &m_description; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryCalculation::parseExpression() { QString leftHandSideVariableName = RimSummaryCalculation::findLeftHandSide(m_expression); if (leftHandSideVariableName.isEmpty()) { QMessageBox::warning(nullptr, "Expression Parser", "Failed to detect left hand side of equation"); return false; } std::vector variableNames = ExpressionParser::detectReferencedVariables(m_expression); if (variableNames.size() < 1) { QMessageBox::warning(nullptr, "Expression Parser", "Failed to detect any variable names"); return false; } // Remove variables not present in expression { std::vector toBeDeleted; for (RimSummaryCalculationVariable* v : m_variables) { if (std::find(variableNames.begin(), variableNames.end(), v->name()) == variableNames.end()) { toBeDeleted.push_back(v); } if (leftHandSideVariableName == v->name()) { toBeDeleted.push_back(v); } } for (RimSummaryCalculationVariable* v : toBeDeleted) { deleteVariable(v); } } for (auto variableName : variableNames) { if (leftHandSideVariableName != variableName) { if (!findByName(variableName)) { this->addVariable(variableName); } } } m_description = buildCalculationName(); return true; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- bool RimSummaryCalculation::calculate() { QString leftHandSideVariableName = RimSummaryCalculation::findLeftHandSide(m_expression); RigTimeHistoryCurveMerger timeHistoryCurveMerger; for (size_t i = 0; i < m_variables.size(); i++) { RimSummaryCalculationVariable* v = m_variables[i]; if (!v->summaryCase()) { QMessageBox::warning(nullptr, "Expression Parser", QString("No summary case defined for variable : %1").arg(v->name())); return false; } if (!v->summaryAddress()) { QMessageBox::warning(nullptr, "Expression Parser", QString("No summary address defined for variable : %1").arg(v->name())); return false; } RimSummaryAddress* sumAdr = v->summaryAddress(); RiaSummaryCurveDefinition curveDef(v->summaryCase(), v->summaryAddress()->address()); std::vector curveValues; RiaSummaryCurveDefinition::resultValues(curveDef, &curveValues); std::vector curveTimeSteps = RiaSummaryCurveDefinition::timeSteps(curveDef); timeHistoryCurveMerger.addCurveData(curveValues, curveTimeSteps); } timeHistoryCurveMerger.computeInterpolatedValues(); ExpressionParser parser; for (size_t i = 0; i < m_variables.size(); i++) { RimSummaryCalculationVariable* v = m_variables[i]; parser.assignVector(v->name(), timeHistoryCurveMerger.interpolatedCurveValuesForAllTimeSteps(i)); } std::vector resultValues; resultValues.resize(timeHistoryCurveMerger.allTimeSteps().size()); parser.assignVector(leftHandSideVariableName, resultValues); QString errorText; bool evaluatedOk = parser.evaluate(m_expression, &errorText); if (evaluatedOk) { m_timesteps.v().clear(); m_calculatedValues.v().clear(); if (timeHistoryCurveMerger.validIntervalsForAllTimeSteps().size() > 0) { size_t firstValidTimeStep = timeHistoryCurveMerger.validIntervalsForAllTimeSteps().front().first; size_t lastValidTimeStep = timeHistoryCurveMerger.validIntervalsForAllTimeSteps().back().second + 1; if (lastValidTimeStep > firstValidTimeStep && lastValidTimeStep <= timeHistoryCurveMerger.allTimeSteps().size()) { std::vector validTimeSteps(timeHistoryCurveMerger.allTimeSteps().begin() + firstValidTimeStep, timeHistoryCurveMerger.allTimeSteps().begin() + lastValidTimeStep); std::vector validValues(resultValues.begin() + firstValidTimeStep, resultValues.begin() + lastValidTimeStep); m_timesteps = validTimeSteps; m_calculatedValues = validValues; } } m_isDirty = false; } else { QString s = "The following error message was received from the parser library : \n\n"; s += errorText; QMessageBox::warning(nullptr, "Expression Parser", s); } return evaluatedOk; } //-------------------------------------------------------------------------------------------------- /// Find the last assignment using := and interpret the text before the := as LHS //-------------------------------------------------------------------------------------------------- QString RimSummaryCalculation::findLeftHandSide(const QString& expresion) { int index = expresion.lastIndexOf(":="); if (index > 0) { QString s = expresion.left(index).simplified(); QStringList words = s.split(" "); if (words.size() > 0) { return words.back(); } } return ""; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::attachToWidget() { for (auto e : m_expression.uiCapability()->connectedEditors()) { caf::PdmUiTextEditor* textEditor = dynamic_cast(e); if (!textEditor) continue; QWidget* containerWidget = textEditor->editorWidget(); if (!containerWidget) continue; for (auto qObj : containerWidget->children()) { QTextEdit* textEdit = dynamic_cast(qObj); if (textEdit) { m_exprContextMenuMgr->attachTextEdit(textEdit); } } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::fieldChangedByUi(const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue) { m_isDirty = true; PdmObject::fieldChangedByUi(changedField, oldValue, newValue); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RimSummaryCalculationVariable* RimSummaryCalculation::findByName(const QString& name) const { for (RimSummaryCalculationVariable* v : m_variables) { if (v->name() == name) { return v; } } return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RimSummaryCalculation::buildCalculationName() const { QString name = "Default Calculation Name"; QString lhs = RimSummaryCalculation::findLeftHandSide(m_expression); if (!lhs.isEmpty()) { name = lhs; name += " ( "; for (RimSummaryCalculationVariable* v : m_variables) { name += v->summaryAddressDisplayString(); if (v != m_variables[m_variables.size() - 1]) { name += ", "; } } name += " )"; } return name; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::defineEditorAttribute(const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute) { if (field == &m_expression) { caf::PdmUiTextEditorAttribute* myAttr = dynamic_cast(attribute); if (myAttr) { myAttr->heightHint = -1; } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RimSummaryCalculation::updateDependentCurvesAndPlots() { RimSummaryCalculationCollection* calcColl = nullptr; this->firstAncestorOrThisOfTypeAsserted(calcColl); calcColl->rebuildCaseMetaData(); RimSummaryPlotCollection* summaryPlotCollection = RiaSummaryTools::summaryPlotCollection(); for (RimSummaryPlot* sumPlot : summaryPlotCollection->summaryPlots()) { bool plotContainsCalculatedCurves = false; for (RimSummaryCurve* sumCurve : sumPlot->summaryCurves()) { if (sumCurve->summaryAddressY().category() == RifEclipseSummaryAddress::SUMMARY_CALCULATED) { sumCurve->updateConnectedEditors(); plotContainsCalculatedCurves = true; } } if (plotContainsCalculatedCurves) { sumPlot->loadDataAndUpdate(); } } }