//----------------------------------------------------------------------------- // $Id: QtRun/SyncTimeChangeDlg.cpp 2017/02/16 19:31:20 philw $ //----------------------------------------------------------------------------- #include "SyncTimeChangeDlg.hpp" // this module #include "qt/ui_SyncTimeChangeWidgets.h" #include "QGui.hpp" #include "RwQtUtils.hpp" #include #include #undef slots ///////////////////////// end of qt includes ///////////////////////// #include "AccountMgr.hpp" #include "guiUtils.hpp" #include "SlotGUIUtils.hpp" #include "RootFilter.Slot.hpp" #include "RootSelection.hpp" #include "DataObj.hpp" #include "System.hpp" #include "SimWS.hpp" // // constructors and destructor // SyncTimeChangeDlg::SyncTimeChangeDlg(QWidget* parent) : QDialog(parent), _ui (*(new Ui_SyncTimeChangeWidgets())), _newRunParam(), _origRunParam() { _ui.setupUi (this); initWidgets(); sensitizeWidgets(); // Set Window Icon. QGui::setWindowLogoIcon (this); } // virtual SyncTimeChangeDlg::~SyncTimeChangeDlg() { // Delete ui structure built from Qt4 Designer Ui_SyncTimeChangeWidgets* uiPtr (&_ui); delete uiPtr; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // // initialize widget properties // // private void SyncTimeChangeDlg::initWidgets() { connect (_ui._okButton, SIGNAL (clicked()), SLOT (okButton_clicked())); connect (_ui._cancelButton, SIGNAL (clicked()), SLOT (cancelButton_clicked())); } // // sensitize widgets // // private void SyncTimeChangeDlg::sensitizeWidgets() { bool showAggControls(false); const DeltaTime::TimeUnit origTimeUnit(_origRunParam.getTimestep().units()); const DeltaTime::TimeUnit newTimeUnit(_newRunParam.getTimestep().units()); if (newTimeUnit == DeltaTime::Months) { // Hours or days can be aggregated to month if (origTimeUnit == DeltaTime::Hours || origTimeUnit == DeltaTime::Days) { showAggControls = true; } } if (newTimeUnit == DeltaTime::Years) { // Hours, days, and months can be aggregated to year if (origTimeUnit == DeltaTime::Hours || origTimeUnit == DeltaTime::Days || origTimeUnit == DeltaTime::Months) { showAggControls = true; } } _ui._aggInputGroupBox->setVisible(showAggControls); adjustSize(); } // // apply specifications from dialog // // static; returns error/warning list QStringList SyncTimeChangeDlg::changeRunParamAndSyncSlots( const RunParam& origRunParam, const RunParam& newRunParam, bool doSyncSlots, // Synchronize Objects with New Timestep bool doAggregation, // Aggregate Input Data to the New Timestep bool excludeDiffIntervals, // Exclude Slots with Timesteps Diff than Orig bool failOnNan) // NaNs in Input Source Data Produce Error { static const char* mname ("SyncTimeChangeDlg::changeRunParamAndSyncSlots"); static int callCnt (0); ++callCnt; // Gnats 5800 Diagnostics: Track processing of a particular slot. //-- static const char* testSlotName ( //-- "Reach1 PassAcct1 to Res StoreAcct1.Supply"); //-- Slot* testSlot = rwWorkspace->locateSlot (testSlotName); //-- std::cout << mname << " [#" << callCnt << "] TestSlot " //-- << "\"" << (testSlot ? testSlotName : "NULL") << "\"" //-- << std::endl; QStringList warningStrings; // First apply the new run parameters okstat ok(rwWorkspace->getRunInfo()->setRunParam(newRunParam)); if (!ok) { warningStrings << ok.msg(); return warningStrings; //----->> } QMapslotAndAggMap; DataObj* tempObj(NULL); if (doAggregation) { // Find all slots with inputs QListslotsWithInputs = RootSelection::getAllInputSlots( RootFilter_SeriesHasFlag::filtVal_InputAll()); // Find timesteps that should be aggregated QList aggIntervals; if (excludeDiffIntervals) { // only interval to aggregate is the original run timestep aggIntervals.append(origRunParam.getTimestep()); } else { // include all intervals that can be aggregated to new one aggIntervals.append(DeltaTime(1L, DeltaTime::Hours)); aggIntervals.append(DeltaTime(6L, DeltaTime::Hours)); aggIntervals.append(DeltaTime(12L, DeltaTime::Hours)); aggIntervals.append(DeltaTime(1L, DeltaTime::Days)); if (newRunParam.getTimestep().units() == DeltaTime::Years) { // months will go to year aggIntervals.append(DeltaTime(1L, DeltaTime::Months)); } } // Filter for slots with timesteps that should be aggregated // Also filter out Accounting multislots because values cannot // be set on them directly, but will be taken care of by change // on the linked supply // Also filter out integer indexed slots QListslotsToAggregate; for (int i = 0; i < slotsWithInputs.size(); i++) { SlotColRef slotColRef = slotsWithInputs[i]; if (slotColRef.isAccountingSlot() && slotColRef.multiSlot() != NULL) { // Filter out accounting multislots continue; } if (slotColRef.usersIndexByInt()) { // Filter out integer indexed slots continue; } for (int j = 0; j < aggIntervals.size(); j++) { if (aggIntervals[j] == slotColRef.getStep()) { slotsToAggregate.append(slotColRef); break; } } } // Create a temporary Data Object to hang the agg slots onto tempObj = new DataObj (HypSim::NonSimulation, "TempObjForAgg"); // Create a time agg series slot for each slot for (int i = 0; i < slotsToAggregate.size(); i++) { // Only a series slot can be a source for aggregation slot SeriesSlot* sourceSlot = slotsToAggregate[i].seriesSlot(); if (sourceSlot) { // Check if unit type of source slot has an agg function // specified TimeAggFunc func = SeriesSlot::TimeAggSpec::funcForUnitType( sourceSlot->getUnitType()); if (func == TimeAgg_CNT) { // No function specified QString msg = QString (tr("%1 not aggregated. No default method for %2")) .arg (sourceSlot->getCompleteName()) .arg (sourceSlot->getUnitTypeStr()); warningStrings.append(msg); // Skip slot continue; } // Gnats 5800 Diagnostics //-- const bool isTestSlot (sourceSlot == testSlot); //-- if (isTestSlot) //-- { //-- static int isTestSlotCnt (0); //-- ++isTestSlotCnt; // debug breakpoint //-- } // Create agg slot SeriesSlot* newAggSlot = new SeriesSlot( QString("Temp%1") .arg(i), NOUNITS, NOUNITS, "", Slot::TimeAggSlotBit ); newAggSlot->setSimObj(tempObj); SeriesSlot::TimeAggSpec* spec = newAggSlot->timeAggSpec(true); spec->setTimeAggOfSlot(sourceSlot, false); spec->setTimeAggFunc(func, false); spec->setTimeAggPeriod(newRunParam.getTimestep(), false); spec->setTimeAggFailOnNan(failOnNan, false); spec->disableSlotCallback(); // don't change agg when source changes newAggSlot->updateTimeAggSlotConfig(); Date_Time error; okstat ok = newAggSlot->recomputeTimeAgg(error, true); // input only // We still keep agg slot even if only partially successful // (ok = false), so that data up to the error will show in // the results slotAndAggMap.insert(sourceSlot, newAggSlot); } } } // Synchronize slots if (doSyncSlots) { BlockAllDialogs blockAllDialogs; // Synchronize simulation slots QString error; QList includeTimesteps; if (excludeDiffIntervals) { // Only timestep to be included is the old timestep of the model // because we want only these slots to change to new timestep includeTimesteps.append(origRunParam.getTimestep()); } // else slots with all timesteps changed to new timestep - // this specified by includeTimesteps being empty if (rwWorkspace->syncToInterval(newRunParam.getInitDate(), newRunParam.getEndDate(), false, includeTimesteps, NULL, error)) { QString msg = QString(tr("Error synchronizing simulation slots: %1")) .arg(qPrintable(error)); warningStrings << msg; // Delete the temporary agg slots and temporary data obj QMap::iterator iter; for (iter = slotAndAggMap.begin(); iter != slotAndAggMap.end(); iter++) { delete iter.value(); } slotAndAggMap.clear(); if (tempObj) delete tempObj; return warningStrings; //----->> } // If accounting is enabled must synchronize accounting slots, // to the new run parameters. if (AccountMgr::isInstantiated() && AccountMgr::instance()->isAccountingEnabled()) { try { AccountMgr::instance()->syncWithRunInfo(true, true); } catch (const cwException& except) { QString msg = (tr("Error synchronizing accounting slots: %1")) .arg(except.what()); warningStrings << msg; // Delete the temporary agg slots and temporary data obj QMap::iterator iter; for (iter = slotAndAggMap.begin(); iter != slotAndAggMap.end(); iter++) { delete iter.value(); } slotAndAggMap.clear(); if (tempObj) delete tempObj; return warningStrings; //----->> } // Round the begin accrual date for the new timestep const Date_Time* currentBegAccrual = AccountMgrObj->getBeginAccrualDate(); const Date_Time* earlyDT = Date_Time::getEarliestAllowedDateTime(); Date_Time newBegAccrual = *currentBegAccrual; newBegAccrual.round (earlyDT, &(newRunParam.getTimestep()), Date_Time::Up); AccountMgrObj->setBeginAccrualDate(&newBegAccrual); } } if (doAggregation) { // Copy aggregated input data back to the source slot, which // now has its timestep changed QMap::iterator iter; for (iter = slotAndAggMap.begin(); iter != slotAndAggMap.end(); iter++) { SeriesSlot* srcSlotPtr = iter.key(); SeriesSlot* aggSlot = iter.value(); bool isAccountingSlot( srcSlotPtr->hasAttribute(Slot::AccountingSlotBits)); // Gnats 5800 Diagnostics //-- const bool isTestSlot (srcSlotPtr == testSlot); //-- if (isTestSlot) //-- { //-- static int isTestSlotCnt (0); //-- ++isTestSlotCnt; // debug breakpoint //-- } DeltaTime srcInterval = srcSlotPtr->getStep(); DeltaTime aggInterval = aggSlot->getStep(); if (srcInterval == aggInterval) { Date_Time startDate = *(aggSlot->getStartDate()); Date_Time endDate = *(aggSlot->getEndDate()); const DeltaTime stepSize = aggSlot->getStep(); if (isAccountingSlot) { Date_Time beginAcctDate( *AccountMgrObj->getBeginAccountingDate()); Date_Time endAcctDate( *AccountMgrObj->getEndAccountingDate()); if (startDate < beginAcctDate) startDate = beginAcctDate; if (endDate > endAcctDate) endDate = endAcctDate; } Date_Time curDate (startDate); while (curDate <= endDate) { // Get value and flags from aggregated slot timestep const double val = aggSlot->getValueByDate(&curDate); ValueState state; aggSlot->getStateByDate(&curDate, state); ValueState::FlagList flags; int numFlags = state.getFlags(flags); // Assign value and flag(s) to source slot timestep SetStatus setStat; if (numFlags == 1) { // Set flag and value together if possible setStat = srcSlotPtr->setFlagAndValue (flags.front(), true, val, &curDate, true); } else { srcSlotPtr->setStateByDate(&curDate, state, false); setStat = srcSlotPtr->setValue (val, &curDate); } if ((setStat != SET_OK) && (setStat != SET_NOCHANGE)) { QString msg = QString (tr("Error [%1] setting value in \"%2\" at %3")) .arg ((int) setStat) .arg (srcSlotPtr->getCompleteName()) .arg (curDate.shortAsciiAbbrev (srcSlotPtr->getStep())); warningStrings.append(msg); } curDate += stepSize; } } else { QString msg = QString (tr("Error setting values for %1.")) .arg (srcSlotPtr->getCompleteName()); warningStrings.append(msg); } } // Delete the temporary agg slots and temporary data obj for (iter = slotAndAggMap.begin(); iter != slotAndAggMap.end(); iter++) { delete iter.value(); } slotAndAggMap.clear(); if (tempObj) delete tempObj; } return warningStrings; } // private void SyncTimeChangeDlg::applyChanges() const { static const char* mname ("SyncTimeChangeDlg::applyChanges"); static int callCnt (0); ++callCnt; // Show wait cursor during the rest of this operation. RwQtAutoWaitCursor waitInst (true); // ********************************** // *** Get Operation Parameters *** // ********************************** const bool doSyncSlots = _ui._syncObjectsGroupBox->isChecked(); const bool doAggregation = doSyncSlots && _ui._aggInputGroupBox->isVisible() && _ui._aggInputGroupBox->isChecked(); const bool excludeDiffIntervals = doSyncSlots && _ui._excludeSlotsCheckBox->isChecked(); const bool failOnNan = doSyncSlots && _ui._failNaNRadioButton->isChecked(); // ************************************************ // *** Perform Run Step Size Change Operation *** // ************************************************ QStringList warningStrings = SyncTimeChangeDlg::changeRunParamAndSyncSlots( _origRunParam, _newRunParam, doSyncSlots, // Synchronize Objects with New Timestep doAggregation, // Aggregate Input Data to the New Timestep excludeDiffIntervals, // Exclude Slots with Timesteps Diff than Orig failOnNan); // NaNs in Input Source Data Produce Error // ************************************************* // *** Report Errors/Warnings in Warning Popup *** // ************************************************* // If there are warning strings, report them in a warning message box const int warningCnt = warningStrings.count(); if (warningCnt > 0) { // Keep "only" the first 25 warnings. static const int WarningLimit (25); QStringList limitedWarnings = warningStrings.mid (0, WarningLimit); if (warningCnt > WarningLimit) { const QString Fmt (tr ("Plus %1 more warnings.")); limitedWarnings += Fmt.arg (warningCnt - WarningLimit); } QMessageBox::warning(NULL, QString (tr ("Run Control Notice")), limitedWarnings.join (QChar ('\n'))); } } // // initialize the run parameters // void SyncTimeChangeDlg::initRunParams(const RunParam& newRunParam, const RunParam& origRunParam) { _newRunParam = newRunParam; _origRunParam = origRunParam; // Changing run parameters may change whether agg controls shown sensitizeWidgets(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private slot void SyncTimeChangeDlg::okButton_clicked() { applyChanges(); accept(); // [QDialog] } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // private slot void SyncTimeChangeDlg::cancelButton_clicked() { reject(); // [QDialog] } //--- (end SyncTimeChangeDlg.cpp) ---