From e639437cad03aa7df9153e0799dcbdc76ed71a0c Mon Sep 17 00:00:00 2001 From: Thomas Hiller Date: Thu, 17 Feb 2022 16:15:42 +0100 Subject: [PATCH] see CHANGELOG.md --- .gitignore | 1 + CHANGELOG.md | 26 +- NUCLEUSinv/NUCLEUSinv.m | 12 +- NUCLEUSinv/NUCLEUSinv_createGUI.m | 8 +- NUCLEUSinv/NUCLEUSinv_createMenus.m | 23 +- NUCLEUSinv/NUCLEUSinv_createPanelData.m | 6 +- NUCLEUSinv/NUCLEUSinv_createPanelInfo.m | 4 +- .../NUCLEUSinv_createPanelInversionJoint.m | 4 +- .../NUCLEUSinv_createPanelInversionStd.m | 8 +- NUCLEUSinv/NUCLEUSinv_createPanelPetro.m | 99 +- NUCLEUSinv/NUCLEUSinv_createPanelPlots.m | 4 +- NUCLEUSinv/NUCLEUSinv_createPanelProcess.m | 4 +- NUCLEUSinv/NUCLEUSinv_createStatusbar.m | 4 +- NUCLEUSinv/NUCLEUSinv_loadDefaults.m | 13 +- NUCLEUSinv/NUCLEUSinv_processINI.m | 4 +- NUCLEUSinv/NUCLEUSinv_updateInterface.m | 76 +- NUCLEUSmod/NUCLEUSmod.m | 12 +- NUCLEUSmod/NUCLEUSmod_createGUI.m | 4 +- NUCLEUSmod/NUCLEUSmod_createMenus.m | 4 +- NUCLEUSmod/NUCLEUSmod_createPanelCPS.m | 4 +- NUCLEUSmod/NUCLEUSmod_createPanelGeometry.m | 4 +- NUCLEUSmod/NUCLEUSmod_createPanelNMR.m | 88 +- NUCLEUSmod/NUCLEUSmod_createPanelPlots.m | 4 +- NUCLEUSmod/NUCLEUSmod_createStatusbar.m | 4 +- NUCLEUSmod/NUCLEUSmod_loadDefaults.m | 10 +- NUCLEUSmod/NUCLEUSmod_updateInterface.m | 12 +- README.md | 12 +- callbacks/contextmenus/onContextAxisLogLin.m | 4 +- callbacks/contextmenus/onContextAxisT1T2.m | 4 +- callbacks/contextmenus/onContextPlotsPSD.m | 4 +- callbacks/contextmenus/onContextPlotsPSDJ.m | 4 +- callbacks/contextmenus/onContextPlotsRTD.m | 4 +- callbacks/contextmenus/onContextSignalList.m | 4 +- callbacks/contextmenus/onContextTableSelect.m | 4 +- callbacks/edits/onEditCPSTable.m | 4 +- callbacks/edits/onEditValue.m | 22 +- callbacks/listboxes/onListboxData.m | 9 +- callbacks/menus/onMenuExpert.m | 6 +- callbacks/menus/onMenuExportData.m | 4 +- callbacks/menus/onMenuExportGraphics.m | 4 +- callbacks/menus/onMenuExtraColor.m | 4 +- callbacks/menus/onMenuExtraRhoBounds.m | 4 +- callbacks/menus/onMenuHelp.m | 4 +- callbacks/menus/onMenuImport.m | 10 +- callbacks/menus/onMenuJointInversion.m | 4 +- callbacks/menus/onMenuRestartQuit.m | 4 +- callbacks/menus/onMenuSolver.m | 4 +- callbacks/menus/onMenuView.m | 4 +- callbacks/menus/onMenuViewFigures.m | 4 +- callbacks/popup/onPopupGeometryModesN.m | 4 +- callbacks/popup/onPopupGeometryPolyN.m | 4 +- callbacks/popup/onPopupGeometrySinglePSD.m | 4 +- callbacks/popup/onPopupGeometryType.m | 4 +- callbacks/popup/onPopupInvjointGeometryType.m | 4 +- callbacks/popup/onPopupInvjointPolyN.m | 4 +- callbacks/popup/onPopupInvjointType.m | 4 +- callbacks/popup/onPopupInvjointTypeOptional.m | 4 +- callbacks/popup/onPopupInvstdType.m | 16 +- callbacks/popup/onPopupInvstdTypeOptional.m | 8 +- callbacks/popup/onPopupNMRNoiseType.m | 92 + callbacks/popup/onPopupPressureLoglin.m | 4 +- callbacks/popup/onPopupPressureUnits.m | 4 +- callbacks/push/onPushCPSTable.m | 4 +- callbacks/push/onPushRun.m | 4 +- callbacks/push/onPushShowHide.m | 4 +- callbacks/push/onPushStop.m | 4 +- callbacks/radio/onRadioGates.m | 4 +- callbacks/radio/onRadioLorder.m | 4 +- callbacks/radio/onRadioNormalize.m | 4 +- callbacks/radio/onRadioTimescale.m | 6 +- doc/menu.html | 2 +- doc/nucleus/NUCLEUSinv/NUCLEUSinv.html | 16 +- .../NUCLEUSinv/NUCLEUSinv_createGUI.html | 14 +- .../NUCLEUSinv/NUCLEUSinv_createMenus.html | 703 +++--- .../NUCLEUSinv_createPanelData.html | 10 +- .../NUCLEUSinv_createPanelInfo.html | 8 +- .../NUCLEUSinv_createPanelInversionJoint.html | 8 +- .../NUCLEUSinv_createPanelInversionStd.html | 12 +- .../NUCLEUSinv_createPanelPetro.html | 321 +-- .../NUCLEUSinv_createPanelPlots.html | 8 +- .../NUCLEUSinv_createPanelProcess.html | 8 +- .../NUCLEUSinv_createStatusbar.html | 4 +- .../NUCLEUSinv/NUCLEUSinv_loadDefaults.html | 159 +- .../NUCLEUSinv/NUCLEUSinv_processINI.html | 8 +- .../NUCLEUSinv_updateInterface.html | 1682 +++++++------ doc/nucleus/NUCLEUSmod/NUCLEUSmod.html | 16 +- .../NUCLEUSmod/NUCLEUSmod_createGUI.html | 10 +- .../NUCLEUSmod/NUCLEUSmod_createMenus.html | 8 +- .../NUCLEUSmod/NUCLEUSmod_createPanelCPS.html | 8 +- .../NUCLEUSmod_createPanelGeometry.html | 8 +- .../NUCLEUSmod/NUCLEUSmod_createPanelNMR.html | 272 +- .../NUCLEUSmod_createPanelPlots.html | 8 +- .../NUCLEUSmod_createStatusbar.html | 4 +- .../NUCLEUSmod/NUCLEUSmod_loadDefaults.html | 92 +- .../NUCLEUSmod_updateInterface.html | 236 +- .../contextmenus/onContextAxisLogLin.html | 8 +- .../contextmenus/onContextAxisT1T2.html | 8 +- .../contextmenus/onContextPlotsPSD.html | 8 +- .../contextmenus/onContextPlotsPSDJ.html | 8 +- .../contextmenus/onContextPlotsRTD.html | 8 +- .../contextmenus/onContextSignalList.html | 8 +- .../contextmenus/onContextTableSelect.html | 8 +- .../callbacks/edits/onEditCPSTable.html | 8 +- doc/nucleus/callbacks/edits/onEditValue.html | 533 ++-- .../callbacks/listboxes/onListboxData.html | 173 +- doc/nucleus/callbacks/menus/onMenuExpert.html | 10 +- .../callbacks/menus/onMenuExportData.html | 8 +- .../callbacks/menus/onMenuExportGraphics.html | 8 +- .../callbacks/menus/onMenuExtraColor.html | 8 +- .../callbacks/menus/onMenuExtraRhoBounds.html | 8 +- doc/nucleus/callbacks/menus/onMenuHelp.html | 8 +- doc/nucleus/callbacks/menus/onMenuImport.html | 90 +- .../callbacks/menus/onMenuJointInversion.html | 8 +- .../callbacks/menus/onMenuRestartQuit.html | 8 +- doc/nucleus/callbacks/menus/onMenuSolver.html | 8 +- doc/nucleus/callbacks/menus/onMenuView.html | 8 +- .../callbacks/menus/onMenuViewFigures.html | 8 +- doc/nucleus/callbacks/popup/menu.html | 2 +- .../popup/onPopupGeometryModesN.html | 8 +- .../callbacks/popup/onPopupGeometryPolyN.html | 8 +- .../popup/onPopupGeometrySinglePSD.html | 8 +- .../callbacks/popup/onPopupGeometryType.html | 8 +- .../popup/onPopupInvjointGeometryType.html | 8 +- .../callbacks/popup/onPopupInvjointPolyN.html | 8 +- .../callbacks/popup/onPopupInvjointType.html | 8 +- .../popup/onPopupInvjointTypeOptional.html | 8 +- .../callbacks/popup/onPopupInvstdType.html | 140 +- .../popup/onPopupInvstdTypeOptional.html | 76 +- .../callbacks/popup/onPopupNMRNoiseType.html | 166 ++ .../popup/onPopupPressureLoglin.html | 8 +- .../callbacks/popup/onPopupPressureUnits.html | 8 +- .../callbacks/push/onPushCPSTable.html | 8 +- doc/nucleus/callbacks/push/onPushRun.html | 10 +- .../callbacks/push/onPushShowHide.html | 8 +- doc/nucleus/callbacks/push/onPushStop.html | 8 +- doc/nucleus/callbacks/radio/onRadioGates.html | 8 +- .../callbacks/radio/onRadioLorder.html | 8 +- .../callbacks/radio/onRadioNormalize.html | 8 +- .../callbacks/radio/onRadioTimescale.html | 126 +- .../functions/import/LoadNMRData_bamtom.html | 8 +- .../functions/import/LoadNMRData_bgr.html | 8 +- .../functions/import/LoadNMRData_bgr2.html | 2 +- .../functions/import/LoadNMRData_bgrmat.html | 8 +- .../functions/import/LoadNMRData_corelab.html | 8 +- .../functions/import/LoadNMRData_dart.html | 8 +- .../functions/import/LoadNMRData_driver.html | 186 +- .../functions/import/LoadNMRData_field.html | 8 +- .../functions/import/LoadNMRData_helios.html | 195 ++ .../functions/import/LoadNMRData_ibac.html | 8 +- .../functions/import/LoadNMRData_liag.html | 8 +- .../functions/import/LoadNMRData_mouse.html | 10 +- .../import/LoadNMRData_mouselift.html | 232 ++ .../functions/import/LoadNMRData_rwth.html | 10 +- .../functions/import/fixParameterString.html | 10 +- doc/nucleus/functions/import/menu.html | 2 +- .../functions/import/rotateT2phase.html | 10 +- .../functions/interface/ConductView.html | 8 +- .../functions/interface/PhaseView.html | 8 +- .../functions/interface/beautifyAxes.html | 8 +- .../interface/calculateGeometry.html | 8 +- .../calculateGuiOnMonitorPosition.html | 114 +- .../functions/interface/calculateNMR.html | 123 +- .../interface/calibratePorosity.html | 8 +- .../caluclatePressureSaturation.html | 111 +- .../functions/interface/changeColorTheme.html | 10 +- .../interface/checkIfInversionExists.html | 8 +- .../functions/interface/cleanupGUIData.html | 8 +- .../functions/interface/clearAllAxes.html | 8 +- .../functions/interface/clearInversion.html | 8 +- .../functions/interface/clearSingleAxis.html | 8 +- .../interface/displayStatusText.html | 140 +- .../interface/enableGUIelements.html | 10 +- .../functions/interface/exportData.html | 18 +- .../functions/interface/exportGraphics.html | 128 +- .../functions/interface/exportINV.html | 16 +- .../functions/interface/findParentOfType.html | 8 +- doc/nucleus/functions/interface/fixAxes.html | 8 +- .../functions/interface/getColorIndex.html | 8 +- .../functions/interface/getColorTheme.html | 8 +- .../interface/getVersionNoFromString.html | 8 +- .../functions/interface/importASCIIdata.html | 144 +- .../functions/interface/importCPSdata.html | 8 +- .../interface/importCalibrationData.html | 2 +- .../functions/interface/importEXCELdata.html | 8 +- .../functions/interface/importINV2INV.html | 364 +-- .../functions/interface/importMOD2INV.html | 252 +- .../functions/interface/importMOD2MOD.html | 95 +- .../functions/interface/importNMRdata.html | 2216 +++++++++-------- .../functions/interface/makeINIfile.html | 8 +- doc/nucleus/functions/interface/menu.html | 2 +- .../functions/interface/minimizePanel.html | 8 +- .../functions/interface/moveEntryInList.html | 8 +- .../interface/onFigureSizeChange.html | 150 +- .../functions/interface/processNMRData.html | 161 +- .../interface/processNMRDataControl.html | 84 +- .../functions/interface/readINIfile.html | 8 +- .../interface/removeCalculationFields.html | 8 +- .../interface/removeInversionFields.html | 8 +- .../interface/removeSignalFromList.html | 8 +- .../functions/interface/resortDataList.html | 8 +- .../interface/runInversionBatch.html | 320 +-- .../interface/runInversionJoint.html | 1071 ++++---- .../functions/interface/runInversionStd.html | 661 ++--- .../interface/showExtraGraphics.html | 8 +- .../interface/showFitStatistics.html | 8 +- .../interface/showParameterInfo.html | 8 +- .../functions/interface/switchToolTips.html | 8 +- .../interface/uncheckImportMenus.html | 87 +- .../functions/interface/updateCPSTable.html | 8 +- .../interface/updateCPSTableSize.html | 8 +- .../functions/interface/updateInfo.html | 775 +++--- .../functions/interface/updateNMRsignals.html | 185 +- .../functions/interface/updatePlotsCPS.html | 8 +- .../interface/updatePlotsDistribution.html | 726 ++++-- .../updatePlotsDistributionInfo.html | 8 +- .../interface/updatePlotsGeometryType.html | 8 +- .../interface/updatePlotsJointInversion.html | 10 +- .../interface/updatePlotsLcurve.html | 10 +- .../functions/interface/updatePlotsNMR.html | 10 +- .../functions/interface/updatePlotsPSD.html | 8 +- .../interface/updatePlotsSignal.html | 603 ++--- .../interface/updateStatusInformation.html | 8 +- .../functions/interface/updateToolTips.html | 242 +- .../interface/useSignalAsCalibration.html | 10 +- .../inversion/applyGatesToSignal.html | 8 +- .../inversion/applyRegularization.html | 8 +- .../inversion/createKernelMatrix.html | 142 +- .../functions/inversion/estimateJacobian.html | 153 ++ .../inversion/estimateUncertainty.html | 606 +++++ .../inversion/fcn_JointInvfixed.html | 389 +-- .../functions/inversion/fcn_JointInvfree.html | 432 ++-- .../inversion/fcn_JointInvshape.html | 349 +-- .../functions/inversion/fcn_fitFreeT1.html | 8 +- .../inversion/fcn_fitFreeT1_fmin.html | 8 +- .../functions/inversion/fcn_fitFreeT2.html | 8 +- .../inversion/fcn_fitFreeT2_fmin.html | 8 +- .../functions/inversion/fcn_fitFreeT2w.html | 10 +- .../inversion/fcn_fitMultiModal.html | 197 ++ .../functions/inversion/fitDataFree.html | 277 ++- .../functions/inversion/fitDataLSQ.html | 445 ++-- .../functions/inversion/fitDataLUdecomp.html | 365 +-- .../inversion/fitDataMultiModal.html | 415 +++ doc/nucleus/functions/inversion/getChi2.html | 8 +- .../functions/inversion/getConfInterval.html | 10 +- .../functions/inversion/getFitErrors.html | 10 +- .../inversion/getFitFreeJacobian.html | 8 +- .../inversion/getLambdaFromLCurve.html | 8 +- .../functions/inversion/getLambdaFromRMS.html | 8 +- .../functions/inversion/getStudentInvCDF.html | 8 +- .../functions/inversion/getTLogMean.html | 10 +- doc/nucleus/functions/inversion/menu.html | 2 +- .../functions/modeling/addNoiseToSignal.html | 10 +- doc/nucleus/functions/modeling/createPSD.html | 8 +- .../modeling/getAngularityFactor.html | 8 +- .../functions/modeling/getAreaFactor.html | 8 +- .../functions/modeling/getConduct.html | 8 +- .../functions/modeling/getConstants.html | 8 +- .../modeling/getCornerNMRparameter.html | 8 +- .../modeling/getCornerSaturation.html | 8 +- .../modeling/getCriticalPressure.html | 8 +- .../modeling/getGeometryParameter.html | 8 +- .../functions/modeling/getNMRSignal.html | 425 ++-- .../functions/modeling/getNMRTimeVector.html | 8 +- .../modeling/getPartialSaturationMatrix.html | 8 +- .../modeling/getPointCoordinates.html | 8 +- .../modeling/getPressureRangeFromPSD.html | 8 +- .../functions/modeling/getRaRaEps.html | 8 +- .../modeling/getSaturationFromPressure.html | 8 +- .../getSaturationFromPressureBatch.html | 8 +- .../modeling/getSaturationLevelData.html | 8 +- .../functions/modeling/getShapeFactor.html | 8 +- functions/import/LoadNMRData_bamtom.m | 4 +- functions/import/LoadNMRData_bgr.m | 4 +- functions/import/LoadNMRData_bgrmat.m | 4 +- functions/import/LoadNMRData_corelab.m | 4 +- functions/import/LoadNMRData_dart.m | 4 +- functions/import/LoadNMRData_driver.m | 37 +- functions/import/LoadNMRData_field.m | 4 +- functions/import/LoadNMRData_helios.m | 115 + functions/import/LoadNMRData_ibac.m | 4 +- functions/import/LoadNMRData_liag.m | 4 +- functions/import/LoadNMRData_mouse.m | 6 +- functions/import/LoadNMRData_mouselift.m | 151 ++ functions/import/LoadNMRData_rwth.m | 4 +- functions/import/fixParameterString.m | 4 +- functions/import/rotateT2phase.m | 4 +- functions/interface/ConductView.m | 4 +- functions/interface/PhaseView.m | 4 +- functions/interface/beautifyAxes.m | 4 +- functions/interface/calculateGeometry.m | 4 +- .../interface/calculateGuiOnMonitorPosition.m | 36 +- functions/interface/calculateNMR.m | 5 +- functions/interface/calibratePorosity.m | 4 +- .../interface/caluclatePressureSaturation.m | 9 +- functions/interface/changeColorTheme.m | 4 +- functions/interface/checkIfInversionExists.m | 4 +- functions/interface/cleanupGUIData.m | 4 +- functions/interface/clearAllAxes.m | 4 +- functions/interface/clearInversion.m | 4 +- functions/interface/clearSingleAxis.m | 4 +- functions/interface/displayStatusText.m | 15 +- functions/interface/enableGUIelements.m | 6 +- functions/interface/exportData.m | 12 +- functions/interface/exportGraphics.m | 16 +- functions/interface/exportINV.m | 10 +- functions/interface/findParentOfType.m | 4 +- functions/interface/fixAxes.m | 4 +- functions/interface/getColorIndex.m | 4 +- functions/interface/getColorTheme.m | 4 +- functions/interface/getVersionNoFromString.m | 4 +- functions/interface/importASCIIdata.m | 14 +- functions/interface/importCPSdata.m | 4 +- functions/interface/importEXCELdata.m | 4 +- functions/interface/importINV2INV.m | 146 +- functions/interface/importMOD2INV.m | 18 +- functions/interface/importMOD2MOD.m | 7 +- functions/interface/importNMRdata.m | 247 +- functions/interface/makeINIfile.m | 4 +- functions/interface/minimizePanel.m | 4 +- functions/interface/moveEntryInList.m | 4 +- functions/interface/onFigureSizeChange.m | 58 +- functions/interface/processNMRData.m | 7 +- functions/interface/processNMRDataControl.m | 22 +- functions/interface/readINIfile.m | 4 +- functions/interface/removeCalculationFields.m | 4 +- functions/interface/removeInversionFields.m | 4 +- functions/interface/removeSignalFromList.m | 4 +- functions/interface/resortDataList.m | 4 +- functions/interface/runInversionBatch.m | 56 +- functions/interface/runInversionJoint.m | 13 +- functions/interface/runInversionStd.m | 59 +- functions/interface/showExtraGraphics.m | 4 +- functions/interface/showFitStatistics.m | 4 +- functions/interface/showParameterInfo.m | 4 +- functions/interface/switchToolTips.m | 4 +- functions/interface/uncheckImportMenus.m | 9 +- functions/interface/updateCPSTable.m | 4 +- functions/interface/updateCPSTableSize.m | 4 +- functions/interface/updateInfo.m | 139 +- functions/interface/updateNMRsignals.m | 52 +- functions/interface/updatePlotsCPS.m | 4 +- functions/interface/updatePlotsDistribution.m | 282 ++- .../interface/updatePlotsDistributionInfo.m | 4 +- functions/interface/updatePlotsGeometryType.m | 4 +- .../interface/updatePlotsJointInversion.m | 4 +- functions/interface/updatePlotsLcurve.m | 4 +- functions/interface/updatePlotsNMR.m | 4 +- functions/interface/updatePlotsPSD.m | 4 +- functions/interface/updatePlotsSignal.m | 75 +- functions/interface/updateStatusInformation.m | 4 +- functions/interface/updateToolTips.m | 20 +- functions/interface/useSignalAsCalibration.m | 4 +- functions/inversion/applyGatesToSignal.m | 4 +- functions/inversion/applyRegularization.m | 4 +- functions/inversion/createKernelMatrix.m | 13 +- functions/inversion/estimateJacobian.m | 79 + functions/inversion/estimateUncertainty.m | 516 ++++ functions/inversion/fcn_JointInvfixed.m | 20 +- functions/inversion/fcn_JointInvfree.m | 25 +- functions/inversion/fcn_JointInvshape.m | 16 +- functions/inversion/fcn_fitFreeT1.m | 4 +- functions/inversion/fcn_fitFreeT1_fmin.m | 4 +- functions/inversion/fcn_fitFreeT2.m | 4 +- functions/inversion/fcn_fitFreeT2_fmin.m | 4 +- functions/inversion/fcn_fitFreeT2w.m | 4 +- functions/inversion/fcn_fitMultiModal.m | 116 + functions/inversion/fitDataFree.m | 17 +- functions/inversion/fitDataLSQ.m | 50 +- functions/inversion/fitDataLUdecomp.m | 12 +- functions/inversion/fitDataMultiModal.m | 299 +++ functions/inversion/getChi2.m | 4 +- functions/inversion/getConfInterval.m | 4 +- functions/inversion/getFitErrors.m | 4 +- functions/inversion/getFitFreeJacobian.m | 4 +- functions/inversion/getLambdaFromLCurve.m | 4 +- functions/inversion/getLambdaFromRMS.m | 4 +- functions/inversion/getStudentInvCDF.m | 4 +- functions/inversion/getTLogMean.m | 4 +- functions/modeling/addNoiseToSignal.m | 4 +- functions/modeling/createPSD.m | 4 +- functions/modeling/getAngularityFactor.m | 4 +- functions/modeling/getAreaFactor.m | 4 +- functions/modeling/getConduct.m | 4 +- functions/modeling/getConstants.m | 4 +- functions/modeling/getCornerNMRparameter.m | 4 +- functions/modeling/getCornerSaturation.m | 4 +- functions/modeling/getCriticalPressure.m | 4 +- functions/modeling/getGeometryParameter.m | 4 +- functions/modeling/getNMRSignal.m | 30 +- functions/modeling/getNMRTimeVector.m | 4 +- .../modeling/getPartialSaturationMatrix.m | 4 +- functions/modeling/getPointCoordinates.m | 4 +- functions/modeling/getPressureRangeFromPSD.m | 4 +- functions/modeling/getRaRaEps.m | 4 +- .../modeling/getSaturationFromPressure.m | 4 +- .../modeling/getSaturationFromPressureBatch.m | 4 +- functions/modeling/getSaturationLevelData.m | 4 +- functions/modeling/getShapeFactor.m | 4 +- nucleusinv_gui.png | Bin 131322 -> 124974 bytes nucleusmod_gui.png | Bin 123443 -> 129420 bytes startNUCLEUSinv.m | 4 +- startNUCLEUSmod.m | 4 +- 402 files changed, 14321 insertions(+), 9190 deletions(-) create mode 100644 callbacks/popup/onPopupNMRNoiseType.m create mode 100644 doc/nucleus/callbacks/popup/onPopupNMRNoiseType.html create mode 100644 doc/nucleus/functions/import/LoadNMRData_helios.html create mode 100644 doc/nucleus/functions/import/LoadNMRData_mouselift.html create mode 100644 doc/nucleus/functions/inversion/estimateJacobian.html create mode 100644 doc/nucleus/functions/inversion/estimateUncertainty.html create mode 100644 doc/nucleus/functions/inversion/fcn_fitMultiModal.html create mode 100644 doc/nucleus/functions/inversion/fitDataMultiModal.html create mode 100644 functions/import/LoadNMRData_helios.m create mode 100644 functions/import/LoadNMRData_mouselift.m create mode 100644 functions/inversion/estimateJacobian.m create mode 100644 functions/inversion/estimateUncertainty.m create mode 100644 functions/inversion/fcn_fitMultiModal.m create mode 100644 functions/inversion/fitDataMultiModal.m diff --git a/.gitignore b/.gitignore index 43a033e..a3774a7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.log *.toc *.sty +/old_functions diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a1ab0..530ec39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## [0.1.12] - latest + +### Added +- New *SNR*-option in **NUCLEUSmod** to set the noise of the forward modelled NMR data either by noise level or signal-to-noise ratio (SNR) +- New *Multi modal* fitting option in **NUCLEUSinv** (only in Expert mode) with built-in uncertainty estimation for the final RTD +- New import routines to **NUCLEUSinv** for *BGR* devices (*Mouse* and *Helios*) +- Difussion relaxation time *Tdiff* is now considered in **NUCLEUSmod** (*NMR* panel) and **NUCLEUSinv** (*Petro Parameter* panel) +- Added an `AUTHORS.md` file to the repository. + +### Changed +- Changed the default export path and file name for graphics files in **NUCLEUSinv** to the current import path and data file +- Changed the visualization of the imaginary part of the raw data (if available) +- Minor internal changes + +### Fixed +- Fixed an issue regarding the import of T1 data without noise (noise is now estimated on-the-fly via a fit with five free exponents) + ## [0.1.11] - 2021-03-12 ### Added @@ -75,13 +92,13 @@ ## [0.1.6] - 2019-06-24 ### Added -- Added a new *PhaseView* subGUI to view and change the phase between real and imaginary part of a T2 signal. **NUCLEUSinv** +- Added a new *PhaseView* subGUI to view and change the phase between real and imaginary part of a T2 signal. **NUCLEUSinv** - Added a new import filter for the BAM NMR tomograph. **NUCLEUSinv** -- Added an export feature that allows to save T2 raw data into a csv-file (e.g. LIAG format); during save it is possible to overwrite the imaginary part with zeros. **NUCLEUSinv** +- Added an export feature that allows to save T2 raw data into a csv-file (e.g. LIAG format); during save it is possible to overwrite the imaginary part with zeros. **NUCLEUSinv** ### Changed -- If a T2 signal is imported it is automatically rotated to minimize its imaginary part (the fit incorporates real and imag. part of the signal). **NUCLEUSinv** +- If a T2 signal is imported it is automatically rotated to minimize its imaginary part (the fit incorporates real and imag. part of the signal). **NUCLEUSinv** - The *FitStatistics* window layout is now in line with the *PhaseView* layout for consistency reasons. **NUCLEUSinv** ### Fixed @@ -115,7 +132,7 @@ - Due to the new error weights there is no default gating method anymore for the *free exp. inversion*. **NUCLEUSinv** ### Fixed -- T1 inversion recovery factor (1 or 2 depending on inversion or saturation recovery) was missing in *free exp. inversion* and *free joint inversion* (in Jacobian calculation for angular pores). **NUCLEUSinv** +- T1 inversion recovery factor (1 or 2 depending on inversion or saturation recovery) was missing in *free exp. inversion* and *free joint inversion* (in Jacobian calculation for angular pores). **NUCLEUSinv** ## [0.1.3] - 2019-02-22 @@ -154,6 +171,7 @@ Initial Version +[0.1.12]: https://github.com/ThoHiller/nmr-nucleus/compare/v.0.1.11...v.0.1.12 [0.1.11]: https://github.com/ThoHiller/nmr-nucleus/compare/v.0.1.10...v.0.1.11 [0.1.10]: https://github.com/ThoHiller/nmr-nucleus/compare/v.0.1.9...v.0.1.10 [0.1.9]: https://github.com/ThoHiller/nmr-nucleus/compare/v.0.1.8...v.0.1.9 diff --git a/NUCLEUSinv/NUCLEUSinv.m b/NUCLEUSinv/NUCLEUSinv.m index 0fbefb4..b3f772e 100644 --- a/NUCLEUSinv/NUCLEUSinv.m +++ b/NUCLEUSinv/NUCLEUSinv.m @@ -27,8 +27,8 @@ % none % % See also NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -38,10 +38,10 @@ if ~isempty(h0); close(h0); end %% GUI 'header' info and defaults -myui.version = '0.1.11'; -myui.date = '12.03.2021'; -myui.author = {'Thomas Hiller','Stephan Costabel'}; -myui.email = 'thomas.hiller[at]leibniz-liag.de'; +myui.version = '0.1.12'; +myui.date = '17.02.2022'; +myui.author = {'Stephan Costabel','Thomas Hiller'}; +myui.email = 'thomas.hiller[at]bgr.de'; myui.fontsize = 10; myui.inifile = 'NUCLEUSinv.ini'; diff --git a/NUCLEUSinv/NUCLEUSinv_createGUI.m b/NUCLEUSinv/NUCLEUSinv_createGUI.m index 8f1407f..dfd6bca 100644 --- a/NUCLEUSinv/NUCLEUSinv_createGUI.m +++ b/NUCLEUSinv/NUCLEUSinv_createGUI.m @@ -36,8 +36,8 @@ function NUCLEUSinv_createGUI(h,wbon) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -118,7 +118,7 @@ function NUCLEUSinv_createGUI(h,wbon) 'TitleColor',myui.colors.BoxCPS,'ForegroundColor',myui.colors.BoxTitle); % adjust the heights of all left-column-panels -myui.heights = [250 22 22 22 22; -1 109 137 190 299]; +myui.heights = [250 22 22 22 22; -1 109 165 190 299]; % panel header is always 22 high set(gui.panels.main,'Heights',myui.heights(2,:),... 'MinimumHeights',[250 22 22 22 22]); @@ -199,7 +199,7 @@ function NUCLEUSinv_createGUI(h,wbon) % otherwise "fixAxes" throws an error (in NUCLEUSmod); strangely here it % also works without it, but I put it for consistency reasons setappdata(h,'gui',gui); -% changeColorTheme('INV',myui.colors.theme); +changeColorTheme('INV',myui.colors.theme); set(gui.main,'Visible','on'); %% enable all menus diff --git a/NUCLEUSinv/NUCLEUSinv_createMenus.m b/NUCLEUSinv/NUCLEUSinv_createMenus.m index a58b965..a89cb8a 100644 --- a/NUCLEUSinv/NUCLEUSinv_createMenus.m +++ b/NUCLEUSinv/NUCLEUSinv_createMenus.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -61,12 +61,23 @@ % 1.1.1.2.1 BGR std gui.menu.file_import_lab_bgr_std = uimenu(gui.menu.file_import_lab_bgr,... 'Label','BGR std','Tag','Lab','Callback',@onMenuImport); -% 1.1.1.2.2 BGR org -gui.menu.file_import_lab_bgr_org = uimenu(gui.menu.file_import_lab_bgr,... - 'Label','BGR org','Tag','Lab','Callback',@onMenuImport); -% 1.1.1.2.3 BGR mat +% 1.1.1.2.2 BGR mat gui.menu.file_import_lab_bgr_mat = uimenu(gui.menu.file_import_lab_bgr,... 'Label','BGR mat','Tag','Lab','Callback',@onMenuImport); +% 1.1.1.2.3 Mouse CPMG data, single data subfolders from CPMG +gui.menu.file_import_lab_bgr_mouse_cpmg = uimenu(gui.menu.file_import_lab_bgr,... + 'Label','MouseCPMG','Tag','Lab','Callback',@onMenuImport); +% 1.1.1.2.4 Mouse plus Lift, single data subfolder from t1test,... +% ...cpmgfastautotest, or (old Prospa Versions) cpmgfastauto +gui.menu.file_import_lab_bgr_mouse_liftsingle = uimenu(gui.menu.file_import_lab_bgr,... + 'Label','MouseLiftSingle','Tag','Lab','Callback',@onMenuImport); +% 1.1.1.2.5 Mouse plus Lift, all data subfolders from t1test,... +% cpmgfastautotest, or (old Prospa Versions) cpmgfastauto +gui.menu.file_import_lab_bgr_mouse_liftall = uimenu(gui.menu.file_import_lab_bgr,... + 'Label','MouseLiftAll','Tag','Lab','Callback',@onMenuImport); +% 1.1.1.2.6 Helios +gui.menu.file_import_lab_bgr_helios = uimenu(gui.menu.file_import_lab_bgr,... + 'Label','Helios','Tag','Lab','Callback',@onMenuImport); % 1.1.1.3 LIAG gui.menu.file_import_lab_liag = uimenu(gui.menu.file_import_lab,... diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelData.m b/NUCLEUSinv/NUCLEUSinv_createPanelData.m index 0e751e5..d9af8c7 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelData.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelData.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -78,7 +78,7 @@ uimenu(gui.cm_handles.data_list_single,... 'Label','save','Tag','single','Callback',@onContextSignalList); uimenu(gui.cm_handles.data_list_single,... - 'Label','remove','Tag','single','Separator','on',... + 'Label','\remove','Tag','single','Separator','on',... 'Callback',@onContextSignalList); uimenu(gui.cm_handles.data_list_single,... 'Label','use as calib.','Tag','single','Separator','on',... diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelInfo.m b/NUCLEUSinv/NUCLEUSinv_createPanelInfo.m index a1757ce..e6cbf39 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelInfo.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelInfo.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.m b/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.m index 5065213..3587bff 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.m b/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.m index 408db21..e2dd4ef 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -62,10 +62,10 @@ switch data.info.ExpertMode case 'on' tstr = ' '; - istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)','Multi exp. (LU decomp.)'}; + istring = {'Mono exp.','N free exp. (2-5)','Multi exp. (LSQ)','Multi exp. (LU decomp.)','Multi modal'}; case 'off' tstr = ' '; - istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)'}; + istring = {'Mono exp.','N free exp. (2-5)','Multi exp. (LSQ)'}; end gui.popup_handles.invstd_InvType = uicontrol('Parent',gui.panels.invstd.HBox1,... 'Style','popup','String',istring,'FontSize',myui.fontsize,'Enable','off','Value',3,... diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelPetro.m b/NUCLEUSinv/NUCLEUSinv_createPanelPetro.m index b8da803..c52f186 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelPetro.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelPetro.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -36,38 +36,65 @@ gui.panels.petro.VBox = uix.VBox('Parent',gui.panels.petro.main,... 'Spacing',3,'Padding',3); -% Tbulk, surface relaxivity rho and geometry factor a +% Tbulk and Tdiff gui.panels.petro.HBox1 = uix.HBox('Parent',gui.panels.petro.VBox,... 'Spacing',3); -% CBW and BVI +% surface relaxivity rho and geometry factor a gui.panels.petro.HBox2 = uix.HBox('Parent',gui.panels.petro.VBox,... 'Spacing',3); -% calibration switch calib volume +% CBW and BVI gui.panels.petro.HBox3 = uix.HBox('Parent',gui.panels.petro.VBox,... 'Spacing',3); -% sample volume, sample porosity +% calibration switch calib volume gui.panels.petro.HBox4 = uix.HBox('Parent',gui.panels.petro.VBox,... 'Spacing',3); +% sample volume, sample porosity +gui.panels.petro.HBox5 = uix.HBox('Parent',gui.panels.petro.VBox,... + 'Spacing',3); -%% Tbulk, surface relaxivity rho and geometry factor a +%% Tbulk and Tdiff gui.text_handles.petro_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String',['Tbulk [s] | ',char(hex2dec('03C1')),' [µm/s] | geom']); -tstr = 'Set the T bulk [s] value.
'; + 'String','Tbulk [s] | Tdiff [s]'); +tstr = ['Bulk relaxation time in [s].

',... + '1/T = 1/Ts + 1/Tb + 1/Td

',... + 'Default value:
',... + '1e6 (so that 1/Tb is ignored)
']; gui.edit_handles.invstd_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... 'Style','edit','String',num2str(data.invstd.Tbulk),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.Tbulk 1 1]),... 'Tag','invstd_Tbulk','Enable','off','Callback',@onEditValue); -tstr = ['Surface relaxivity rho in [m/s].

',... - '1/T = rho*S/V = rho*a/R.

',... +tstr = ['Diffusion relaxation time in [s].

',... + '1/T = 1/Ts + 1/Tb + 1/Td

',... + 'Td can be caluclated by

',... + 'Td = 12 / (D*(',char(hex2dec('03B3')),'*G*TE)2 )

',... + 'with

',... + 'D = diffusion coefficent of water
',... + char(hex2dec('03B3')),' = gyromagnetic ratio of hydrogen
',... + 'G = magnetic gradient
',... + 'TE = echo time

',... + 'Default value:
',... + '1e6 (so that 1/Td is ignored)
']; +gui.edit_handles.invstd_Tdiff = uicontrol('Parent',gui.panels.petro.HBox1,... + 'Style','edit','String',num2str(data.invstd.Tdiff),'FontSize',myui.fontsize,... + 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.Tdiff 1 1]),... + 'Tag','invstd_Tdiff','Enable','off','Callback',@onEditValue); +set(gui.panels.petro.HBox1,'Widths',[200 -1 -1]); + +%% surface relaxivity rho and geometry factor a +gui.text_handles.petro_rho = uicontrol('Parent',gui.panels.petro.HBox2,... + 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... + 'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); +tstr = ['Surface relaxivity in [µm/s].

',... + '1/Ts = rho*S/V = rho*a/R.

',... 'Default value:
',... - '2.5e-5
']; -gui.edit_handles.param_rho = uicontrol('Parent',gui.panels.petro.HBox1,... + '10
']; +gui.edit_handles.param_rho = uicontrol('Parent',gui.panels.petro.HBox2,... 'Style','edit','String',num2str(data.param.rho),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.rho 1 1]),... 'Tag','param_rho','Enable','off','Callback',@onEditValue); -tstr = ['Give the geometry factor "a" to convert relaxation times to pore radii

',... - '1/T = rho*S/V = rho*a/R.

',... +tstr = ['Geometry factor "a" to convert relaxation times to pore radii.

',... + '1/Ts = rho*S/V = rho*a/R.

',... 'Possible options:
',... '2 capillaries with circular cross section
',... '3 spheres

',... @@ -77,17 +104,17 @@ 'e.g. 2.72 is 90°-45°-45° | 2.87 is 90°-60°-30° | 5.64 is 90°-85°-5°

',... 'Default value:
',... '2
']; -gui.edit_handles.param_geom = uicontrol('Parent',gui.panels.petro.HBox1,... +gui.edit_handles.param_geom = uicontrol('Parent',gui.panels.petro.HBox2,... 'Style','edit','String',num2str(data.param.a),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.a 1 1]),... 'Tag','param_a','Enable','off','Callback',@onEditValue); -set(gui.panels.petro.HBox1,'Widths',[200 -1 -1 -1]); +set(gui.panels.petro.HBox2,'Widths',[200 -1 -1]); %% CBW and BVI -gui.text_handles.petro_CBW = uicontrol('Parent',gui.panels.petro.HBox2,... +gui.text_handles.petro_CBW = uicontrol('Parent',gui.panels.petro.HBox3,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','CBW [ms] | BVI [ms]'); -tstr = ['Give cut-off time between CBW and BVI in [ms].',... + 'String','CBW [ms] | BVI [ms] '); +tstr = ['Cut-off time between CBW and BVI in [ms].',... ' This value depends on lithology.

',... 'Abbreviations:
',... 'CBW clay bound water
',... @@ -96,11 +123,11 @@ 'The value is usually 3 [ms]

',... 'Default value:
',... '3
']; -gui.edit_handles.param_CBW = uicontrol('Parent',gui.panels.petro.HBox2,... +gui.edit_handles.param_CBW = uicontrol('Parent',gui.panels.petro.HBox3,... 'Style','edit','String',num2str(data.param.CBWcutoff),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.CBWcutoff 1 1]),... 'Tag','param_CBWcutoff','Enable','off','Callback',@onEditValue); -tstr = ['Give cut off time between BVI and BVM in [ms].',... +tstr = ['Cut-off time between BVI and BVM in [ms].',... ' This value depends on lithology.

',... 'Abbreviations:
',... 'CBW clay bound water
',... @@ -110,49 +137,51 @@ 'For Carbonates the value is usually 92 [ms]

',... 'Default value:
',... '33
']; -gui.edit_handles.param_BVI = uicontrol('Parent',gui.panels.petro.HBox2,... +gui.edit_handles.param_BVI = uicontrol('Parent',gui.panels.petro.HBox3,... 'Style','edit','String',num2str(data.param.BVIcutoff),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.BVIcutoff 1 1]),... 'Tag','param_BVIcutoff','Enable','off','Callback',@onEditValue); -set(gui.panels.petro.HBox2,'Widths',[200 -1 -1]); +set(gui.panels.petro.HBox3,'Widths',[200 -1 -1]); %% calibration switch, calibration volume -gui.text_handles.petro_calibVol = uicontrol('Parent',gui.panels.petro.HBox3,... +gui.text_handles.petro_calibVol = uicontrol('Parent',gui.panels.petro.HBox4,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','calib. Vol. | calib. Amp.'); + 'String',' calib. Vol. | calib. Amp.'); tstr = ''; -gui.edit_handles.param_calibVol = uicontrol('Parent',gui.panels.petro.HBox3,... +gui.edit_handles.param_calibVol = uicontrol('Parent',gui.panels.petro.HBox4,... 'Style','edit','String',num2str(data.param.calibVol),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibVol 1 1]),... 'Tag','param_calibVol','Enable','off','Callback',@onEditValue); tstr = ''; -gui.edit_handles.param_calibAmp = uicontrol('Parent',gui.panels.petro.HBox3,... +gui.edit_handles.param_calibAmp = uicontrol('Parent',gui.panels.petro.HBox4,... 'Style','edit','String',num2str(data.param.calibAmp),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibAmp 1 1]),... 'Tag','param_calibAmp','Enable','off','Callback',@onEditValue); -set(gui.panels.petro.HBox3,'Widths',[200 -1 -1]); +set(gui.panels.petro.HBox4,'Widths',[200 -1 -1]); %% sample volume, porosity -gui.text_handles.petro_sampVol = uicontrol('Parent',gui.panels.petro.HBox4,... +gui.text_handles.petro_sampVol = uicontrol('Parent',gui.panels.petro.HBox5,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','sample Vol. | porosity'); + 'String','sample Vol. | porosity '); tstr = ' '; -gui.edit_handles.param_sampVol = uicontrol('Parent',gui.panels.petro.HBox4,... +gui.edit_handles.param_sampVol = uicontrol('Parent',gui.panels.petro.HBox5,... 'Style','edit','String',num2str(data.param.sampVol),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.sampVol 1 1]),... 'Tag','param_sampVol','Enable','off','Callback',@onEditValue); -tstr = ['Set porosity (between 0 and 1)

',... +tstr = ['Porosity (between 0 and 1)

',... 'Default value:
',... '1

']; -gui.edit_handles.invstd_porosity = uicontrol('Parent',gui.panels.petro.HBox4,... +gui.edit_handles.invstd_porosity = uicontrol('Parent',gui.panels.petro.HBox5,... 'Style','edit','String',num2str(data.invstd.porosity),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.porosity 1 1]),... 'Tag','invstd_porosity','Enable','off','Callback',@onEditValue); -set(gui.panels.petro.HBox4,'Widths',[200 -1 -1]); +set(gui.panels.petro.HBox5,'Widths',[200 -1 -1]); %% Java Hack to adjust vertical alignment of text fields jh = findjobj(gui.text_handles.petro_Tbulk); jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +jh = findjobj(gui.text_handles.petro_rho); +jh.setVerticalAlignment(javax.swing.JLabel.CENTER); jh = findjobj(gui.text_handles.petro_CBW); jh.setVerticalAlignment(javax.swing.JLabel.CENTER); jh = findjobj(gui.text_handles.petro_calibVol); diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelPlots.m b/NUCLEUSinv/NUCLEUSinv_createPanelPlots.m index fe4cf01..f91d292 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelPlots.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelPlots.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_createPanelProcess.m b/NUCLEUSinv/NUCLEUSinv_createPanelProcess.m index 3d19158..439d8b3 100644 --- a/NUCLEUSinv/NUCLEUSinv_createPanelProcess.m +++ b/NUCLEUSinv/NUCLEUSinv_createPanelProcess.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_createStatusbar.m b/NUCLEUSinv/NUCLEUSinv_createStatusbar.m index b2d0a0a..a4055e4 100644 --- a/NUCLEUSinv/NUCLEUSinv_createStatusbar.m +++ b/NUCLEUSinv/NUCLEUSinv_createStatusbar.m @@ -24,8 +24,8 @@ % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_loadDefaults.m b/NUCLEUSinv/NUCLEUSinv_loadDefaults.m index 7aa925d..5e3985b 100644 --- a/NUCLEUSinv/NUCLEUSinv_loadDefaults.m +++ b/NUCLEUSinv/NUCLEUSinv_loadDefaults.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -127,9 +127,18 @@ out.invstd.noise = 0; % water bulk relaxation time [s] out.invstd.Tbulk = 1e6; +% diffusion relaxation time [s] +out.invstd.Tdiff = 1e6; % porosity value between 0 and 1 [-] out.invstd.porosity = 1; +out.invstd.useUncert = 1; +out.invstd.uncertMethod = 'thresh'; +out.invstd.uncertThresh = 0.05; +out.invstd.uncertChi2 = 0.005; +out.invstd.uncertN = 100; +out.invstd.uncertMax = 1e4; + %% joint inversion panel defaults % joint inversion methods to choose 'free' | 'fixed' | 'shape' out.invjoint.invtype = 'free'; diff --git a/NUCLEUSinv/NUCLEUSinv_processINI.m b/NUCLEUSinv/NUCLEUSinv_processINI.m index a5c939e..937542a 100644 --- a/NUCLEUSinv/NUCLEUSinv_processINI.m +++ b/NUCLEUSinv/NUCLEUSinv_processINI.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSinv/NUCLEUSinv_updateInterface.m b/NUCLEUSinv/NUCLEUSinv_updateInterface.m index 80d65f9..9141613 100644 --- a/NUCLEUSinv/NUCLEUSinv_updateInterface.m +++ b/NUCLEUSinv/NUCLEUSinv_updateInterface.m @@ -32,8 +32,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -69,7 +69,7 @@ %% update standard inversion panel % inversion method popup istring = {'Mono exp.','Several free exp. (2-5)',... - 'Multi exp. (LSQ)','Multi exp. (LU decomp.)'}; + 'Multi exp. (LSQ)','Multi exp. (LU decomp.)','Multi modal'}; set(gui.popup_handles.invstd_InvType,'String',istring); switch data.invstd.invtype case 'mono' @@ -87,9 +87,11 @@ gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); gui = updateInvstdTime(gui,data.invstd.invtype,0,0); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','off',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','off',... + 'String',num2str(data.invstd.Tdiff)); case 'free' % inversion method popup @@ -107,9 +109,11 @@ gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); gui = updateInvstdTime(gui,data.invstd.invtype,0,0); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','off',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','off',... + 'String',num2str(data.invstd.Tdiff)); case 'NNLS' % inversion method popup @@ -145,9 +149,11 @@ gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... data.invstd.Ntime); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','on',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','on',... + 'String',num2str(data.invstd.Tdiff)); case 'LU' % inversion method popup @@ -175,9 +181,39 @@ gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... data.invstd.Ntime); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','on',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','on',... + 'String',num2str(data.invstd.Tdiff)); + + case 'MUMO' + % inversion method popup + set(gui.popup_handles.invstd_InvType,'Value',5,'Enable','on'); + + % additional inversion settings + set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... + 'Value',data.invstd.freeDT,... + 'String',{'1','2','3','4','5'}); + set(gui.text_handles.invstd_InvTypeOpt,... + 'String','No. of modes'); + +% % lambda, smoothness constraint and RTD limits +% gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... +% data.invstd.lambdaR,data.invstd.NlambdaR); +% gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); + gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... + data.invstd.Ntime); + + % lambda, smoothness constraint and RTD limits + gui = updateLambda(gui,data.invstd.regtype,0,0,0); + gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); + + % Tbulk & Tdiff + set(gui.edit_handles.invstd_Tbulk,'Enable','on',... + 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','on',... + 'String',num2str(data.invstd.Tdiff)); end % updates CBW, BVI, rho and a @@ -535,9 +571,11 @@ gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); gui = updateInvstdTime(gui,data.invstd.invtype,0,0); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','off',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','off',... + 'String',num2str(data.invstd.Tdiff)); case 'free' % inversion method popup @@ -555,9 +593,11 @@ gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); gui = updateInvstdTime(gui,data.invstd.invtype,0,0); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','off',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','off',... + 'String',num2str(data.invstd.Tdiff)); case 'NNLS' % inversion method popup @@ -592,9 +632,11 @@ set(gui.edit_handles.invstd_Ntime,'Enable','off',... 'String',num2str(data.invstd.Ntime)); - % Tbulk + % Tbulk & Tdiff set(gui.edit_handles.invstd_Tbulk,'Enable','on',... 'String',num2str(data.invstd.Tbulk)); + set(gui.edit_handles.invstd_Tdiff,'Enable','on',... + 'String',num2str(data.invstd.Tdiff)); end %% update petro parameter panel @@ -681,16 +723,16 @@ set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',0); set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [s] | max [s] | # / dec',... 'FontSize',10); - set(gui.text_handles.petro_Tbulk,'String',... - ['Tbulk [s] | ',char(hex2dec('03C1')),' [µm/s] | geom']); + set(gui.text_handles.petro_Tbulk,'String','Tbulk [s] | Tdiff [s]'); + set(gui.text_handles.petro_rho,'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); case 'ms' set(gui.radio_handles.process_timescale_s,'Enable','on','Value',0); set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',1); set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [ms] | max [ms] | # / dec',... 'FontSize',9); - set(gui.text_handles.petro_Tbulk,'String',... - ['Tbulk [ms] | ',char(hex2dec('03C1')),' [µm/s] | geom']); + set(gui.text_handles.petro_Tbulk,'String','Tbulk [ms] | Tdiff [ms]'); + set(gui.text_handles.petro_rho,'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); end end @@ -704,7 +746,7 @@ set(gui.edit_handles.invstd_time_max,'Enable','off'); set(gui.edit_handles.invstd_Ntime,'Enable','off'); - case {'LU','NNLS'} + case {'LU','NNLS','MUMO'} set(gui.edit_handles.invstd_time_min,'Enable','on','String',num2str(time(1))); set(gui.edit_handles.invstd_time_max,'Enable','on','String',num2str(time(2))); set(gui.edit_handles.invstd_Ntime,'Enable','on','String',num2str(Ntime)); @@ -809,7 +851,7 @@ function gui = updateLorder(gui,invtype,Lorder) switch invtype - case {'mono','free'} + case {'mono','free','MUMO'} set(gui.radio_handles.invstd_Lorder0,'Enable','off'); set(gui.radio_handles.invstd_Lorder1,'Enable','off'); set(gui.radio_handles.invstd_Lorder2,'Enable','off'); @@ -881,7 +923,7 @@ set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); set(gui.edit_handles.param_geom,'Enable','on','String',num2str(p.a)); - case {'LU','NNLS'} + case {'LU','NNLS','MUMO'} set(gui.edit_handles.param_CBW,'Enable','on','String',num2str(p.CBWcutoff)); set(gui.edit_handles.param_BVI,'Enable','on','String',num2str(p.BVIcutoff)); set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); diff --git a/NUCLEUSmod/NUCLEUSmod.m b/NUCLEUSmod/NUCLEUSmod.m index 271b5cf..14bea02 100644 --- a/NUCLEUSmod/NUCLEUSmod.m +++ b/NUCLEUSmod/NUCLEUSmod.m @@ -30,8 +30,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -41,10 +41,10 @@ if ~isempty(h0); close(h0); end %% GUI 'header' info and defaults -myui.version = '0.1.11'; -myui.date = '12.03.2021'; -myui.author = {'Thomas Hiller','Stephan Costabel'}; -myui.email = 'thomas.hiller[at]leibniz-liag.de'; +myui.version = '0.1.12'; +myui.date = '17.02.2022'; +myui.author = {'Stephan Costabel','Thomas Hiller'}; +myui.email = 'thomas.hiller[at]bgr.de'; myui.fontsize = 10; %% Default data settings diff --git a/NUCLEUSmod/NUCLEUSmod_createGUI.m b/NUCLEUSmod/NUCLEUSmod_createGUI.m index 9579889..163c193 100644 --- a/NUCLEUSmod/NUCLEUSmod_createGUI.m +++ b/NUCLEUSmod/NUCLEUSmod_createGUI.m @@ -29,8 +29,8 @@ function NUCLEUSmod_createGUI(h,wbon) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_createMenus.m b/NUCLEUSmod/NUCLEUSmod_createMenus.m index 7b38e1d..462eff1 100644 --- a/NUCLEUSmod/NUCLEUSmod_createMenus.m +++ b/NUCLEUSmod/NUCLEUSmod_createMenus.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_createPanelCPS.m b/NUCLEUSmod/NUCLEUSmod_createPanelCPS.m index ac63e0d..0d1c5b0 100644 --- a/NUCLEUSmod/NUCLEUSmod_createPanelCPS.m +++ b/NUCLEUSmod/NUCLEUSmod_createPanelCPS.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.m b/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.m index 5d5feb1..5485372 100644 --- a/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.m +++ b/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_createPanelNMR.m b/NUCLEUSmod/NUCLEUSmod_createPanelNMR.m index 5755e45..4c707ad 100644 --- a/NUCLEUSmod/NUCLEUSmod_createPanelNMR.m +++ b/NUCLEUSmod/NUCLEUSmod_createPanelNMR.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -34,10 +34,10 @@ gui.panels.nmr.VBox = uix.VBox('Parent',gui.panels.nmr.main,... 'Spacing',3,'Padding',3); -% Tbulk & surface relaxivity rho +% Tbulk & Tdiff gui.panels.nmr.HBox1 = uix.HBox('Parent',gui.panels.nmr.VBox,... 'Spacing',3); -% echo time TE & number of echos +% surface relaxivity rho & echo time TE & number of echos gui.panels.nmr.HBox2 = uix.HBox('Parent',gui.panels.nmr.VBox,... 'Spacing',3); % RUN button @@ -47,51 +47,62 @@ gui.panels.nmr.HBox4 = uix.HBox('Parent',gui.panels.nmr.VBox,... 'Spacing',3); -%% Tbulk & surface relaxivity rho +%% Tbulk & Tdiff gui.text_handles.Tbulk = uicontrol('Parent',gui.panels.nmr.HBox1,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','Tbulk [s]'); -tstr = ['Set relaxation time of bulk water.

',... + 'String','Tbulk [s] | Tdiff [s]'); +tstr = ['Bulk relaxation time in [s].

',... + '1/T = 1/Ts + 1/Tb + 1/Td

',... 'Default value:
',... - '2 s
']; + '2
']; gui.edit_handles.Tbulk = uicontrol('Parent',gui.panels.nmr.HBox1,... 'Style','edit','String',num2str(data.nmr.Tbulk),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.Tbulk 1 1]),... 'Tag','nmr_Tbulk','Enable','on','Callback',@onEditValue); -gui.text_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox1,... +tstr = ['Diffusion relaxation time in [s].

',... + '1/T = 1/Ts + 1/Tb + 1/Td

',... + 'Td can be caluclated by

',... + 'Td = 12 / (D*(',char(hex2dec('03B3')),'*G*TE)2 )

',... + 'with

',... + 'D = diffusion coefficent of water
',... + char(hex2dec('03B3')),' = gyromagnetic ratio of hydrogen
',... + 'G = magnetic gradient
',... + 'TE = echo time

',... + 'Default value:
',... + '1e6 (so that 1/Td is ignored)
']; +gui.edit_handles.Tdiff = uicontrol('Parent',gui.panels.nmr.HBox1,... + 'Style','edit','String',num2str(data.nmr.Tdiff),'FontSize',myui.fontsize,... + 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.Tdiff 1 1]),... + 'Tag','nmr_Tdiff','Enable','on','Callback',@onEditValue); +set(gui.panels.nmr.HBox1,'Widths',[200 -1 -1]); + +%% surface relaxivity rho & echo time TE & number of echoes +gui.text_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox2,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String',[char(hex2dec('03C1')),' [µm/s]']); -tstr = ['Set surface relaxivity &rho .

',... + 'String',[char(hex2dec('03C1')),' [µm/s] | TE [µs] | # echoes']); +tstr = ['Surface relaxivity in [µm/s].

',... + '1/Ts = rho*S/V = rho*a/R.

',... 'Default value:
',... - '10 µm/s
']; -gui.edit_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox1,... + '10
']; +gui.edit_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox2,... 'Style','edit','String',num2str(data.nmr.rho),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.rho 1 1]),... 'Tag','nmr_rho','Enable','on','Callback',@onEditValue); -set(gui.panels.nmr.HBox1,'Widths',[100 -1 100 -1]); - -%% echo time TE & number of echoes -gui.text_handles.TE = uicontrol('Parent',gui.panels.nmr.HBox2,... - 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','echo time [µs]'); -tstr = ['Set echo time.

',... +tstr = ['Echo time TE in [µs].

',... 'Default value:
',... - '1000 µs
']; + '1000
']; gui.edit_handles.TE = uicontrol('Parent',gui.panels.nmr.HBox2,... 'Style','edit','String',num2str(data.nmr.TE),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.TE 1 1]),... 'Tag','nmr_TE','Enable','on','Callback',@onEditValue); -gui.text_handles.echosN = uicontrol('Parent',gui.panels.nmr.HBox2,... - 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','# echoes'); -tstr = ['Set number of echoes.

',... +tstr = ['Number of echoes.

',... 'Default value:
',... '1001
']; gui.edit_handles.echosN = uicontrol('Parent',gui.panels.nmr.HBox2,... 'Style','edit','String',num2str(data.nmr.echosN),'FontSize',myui.fontsize,... 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.echosN 1 1]),... 'Tag','nmr_echosN','Enable','on','Callback',@onEditValue); -set(gui.panels.nmr.HBox2,'Widths',[100 -1 100 -1]); +set(gui.panels.nmr.HBox2,'Widths',[200 -1 -1 -1]); %% RUN button gui.text_handles.nmr_RUN = uicontrol('Parent',gui.panels.nmr.HBox3,... @@ -103,10 +114,19 @@ set(gui.panels.nmr.HBox3,'Widths',[200 -1]); %% noise & porosity -gui.text_handles.noise = uicontrol('Parent',gui.panels.nmr.HBox4,... - 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... - 'String','add noise'); -tstr = ['Set NMR data noise.

',... + +tstr = ['NMR data noise method.

',... + 'A noise level will be used globally for all NMR signals.
',... + 'A signal-to-ratio will be used individually on every single NMR signal.

',... + 'Available options:
',... + 'noise level or SNR

',... + 'Default value:
',... + 'noise level
']; +gui.popup_handles.noisetype = uicontrol('Parent',gui.panels.nmr.HBox4,... + 'Style','popup','String',{'noise level','SNR'},... + 'Value',1,'FontSize',myui.fontsize,'UserData',struct('Tooltipstr',tstr),... + 'Callback',@onPopupNMRNoiseType); +tstr = ['NMR data noise.

',... 'Hint:
',... 'You do not need to press RUN to add noise to the NMR signals.
',... 'The raw NMR signals are stored internally and the noise is
',... @@ -120,7 +140,7 @@ gui.text_handles.porosity = uicontrol('Parent',gui.panels.nmr.HBox4,... 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... 'String','porosity'); -tstr = ['Set porosity value in the range [0, 1].

',... +tstr = ['Porosity value in the range [0, 1].

',... 'Note:
',... 'This value is applied only as a scaling factor to the NMR amplitudes.
',... 'Hence, saturation becomes water content.']; @@ -135,14 +155,8 @@ jh.setVerticalAlignment(javax.swing.JLabel.CENTER); jh = findjobj(gui.text_handles.rho); jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -jh = findjobj(gui.text_handles.TE ); -jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -jh = findjobj(gui.text_handles.echosN ); -jh.setVerticalAlignment(javax.swing.JLabel.CENTER); jh = findjobj(gui.text_handles.nmr_RUN); jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -jh = findjobj(gui.text_handles.noise); -jh.setVerticalAlignment(javax.swing.JLabel.CENTER); jh = findjobj(gui.text_handles.porosity); jh.setVerticalAlignment(javax.swing.JLabel.CENTER); diff --git a/NUCLEUSmod/NUCLEUSmod_createPanelPlots.m b/NUCLEUSmod/NUCLEUSmod_createPanelPlots.m index 9a18aa6..46bf877 100644 --- a/NUCLEUSmod/NUCLEUSmod_createPanelPlots.m +++ b/NUCLEUSmod/NUCLEUSmod_createPanelPlots.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_createStatusbar.m b/NUCLEUSmod/NUCLEUSmod_createStatusbar.m index 66c9b2a..8f2af5d 100644 --- a/NUCLEUSmod/NUCLEUSmod_createStatusbar.m +++ b/NUCLEUSmod/NUCLEUSmod_createStatusbar.m @@ -24,8 +24,8 @@ % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md %------------- BEGIN CODE -------------- diff --git a/NUCLEUSmod/NUCLEUSmod_loadDefaults.m b/NUCLEUSmod/NUCLEUSmod_loadDefaults.m index c382e05..e187ae7 100644 --- a/NUCLEUSmod/NUCLEUSmod_loadDefaults.m +++ b/NUCLEUSmod/NUCLEUSmod_loadDefaults.m @@ -21,8 +21,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -73,10 +73,14 @@ out.nmr.TE = 1000; % number of echoes out.nmr.echosN = 1001; -% noise level [%] +% noise creation type 'level' or 'SNR' +out.nmr.noisetype = 'level'; +% noise level [0:1] or SNR [-] out.nmr.noise = 0; % water bulk relaxation time [s] out.nmr.Tbulk = 2; +% diffusion relaxation time [s] +out.nmr.Tdiff = 1e6; % surface relaxivity [µm/s] out.nmr.rho = 10; % porosity value between 0 and 1 [-] diff --git a/NUCLEUSmod/NUCLEUSmod_updateInterface.m b/NUCLEUSmod/NUCLEUSmod_updateInterface.m index ea8b086..4f0cef9 100644 --- a/NUCLEUSmod/NUCLEUSmod_updateInterface.m +++ b/NUCLEUSmod/NUCLEUSmod_updateInterface.m @@ -22,8 +22,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -125,6 +125,14 @@ end %% update NMR panel +% noise type +switch data.nmr.noisetype + case 'level' % noise level + set(gui.popup_handles.noisetype,'Value',1); + case 'SNR' % signal-to-noise ratio (SNR) + set(gui.popup_handles.noisetype,'Value',2); +end + % all edit fields set(gui.edit_handles.Tbulk,'String',num2str(data.nmr.Tbulk)); set(gui.edit_handles.rho,'String',num2str(data.nmr.rho)); diff --git a/README.md b/README.md index 36192bc..ce8f44b 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ modeling and i**N**version of n**UCL**ear magnetic r**E**sonance data with ang** ### About -**NUCLEUS** is a set of MATLABTM tools, that allow forward and inverse modeling of nuclear magnetic resonance (NMR) relaxometry data. The main front-ends to these tools are two graphical user interfaces, **NUCLEUSmod** and **NUCLEUSinv** for forward and inverse modeling, respectively. For simple NMR relaxometry data inversion, the **NUCLEUSinv** GUI may be a little *feature-rich*. But one of the ideas, when starting to develop this code, was to help students understand the basic concepts of NMR relaxometry data inversion. +**NUCLEUS** is a set of MATLABTM tools, that allow forward and inverse modeling of nuclear magnetic resonance (NMR) relaxometry data (T1 and T2 relaxation). The main front-ends to these tools are two graphical user interfaces, **NUCLEUSmod** and **NUCLEUSinv** for forward and inverse modeling, respectively. For simple NMR relaxometry data inversion, the **NUCLEUSinv** GUI may be a little *feature-rich*. But one of the ideas, when starting to develop this code, was to help students understand the basic concepts of NMR relaxometry data inversion. ###### NUCLEUSmod basic features: @@ -62,7 +62,7 @@ If you do not have the Optimization or Statistics toolboxes then not all feature #### Operating System -I tested it successfully under Windows 7 (64bit) and 10 (64bit) with Matlab R2014b and newer. Always with the latest version of the GUI Layout Toolbox (current version is v2.3.4) +I tested it successfully under Windows 7 (64bit) and 10 (64bit) with Matlab R2014b and newer. Always with the latest version of the GUI Layout Toolbox (current version is v2.3.5) **NOTE:** So far I did not test anything on Linux or a Mac. If you get it to work on either of the two systems (which it basically should I guess) please let me know. @@ -108,13 +108,15 @@ In no particular order and without guarantee that it will ever happen :-) : ### Cite as If you use NUCLEUS for your research, please cite it as: -Thomas Hiller. (2021, March 12). ThoHiller/nmr-nucleus: v0.1.11 (Version v0.1.11). Zenodo. [https://doi.org/10.5281/zenodo.4022195] +Thomas Hiller. (2021, March 12). ThoHiller/nmr-nucleus: v0.1.12 (Version v0.1.12). Zenodo. [https://doi.org/10.5281/zenodo.4022195] Note: Even though the version number might change due to updates, this DOI is permanent (represents all versions) and always links to the latest version. + ### References -1. Hiller, T. and Klitzsch, N., "Joint inversion of nuclear magnetic resonance data from partially saturated rocks using a triangular pore model", *GEOPHYSICS* **83**(4), JM15-JM28, 2018, [DOI](https://doi.org/10.1190/geo2017-0697.1) -2. Costabel, S. and Hiller, T., "Soil hydraulic interpretation of nuclear magnetic resonance measurements based on circular and triangular capillary models", *Vadose Zone Journal*, 2021, e20104, [DOI](https://doi.org/10.1002/vzj2.20104) +1. Hiller, T., Costabel, S., Radic, T., Dlugosch, R. and Müller-Petke, M. "Feasibility study on prepolarized surface nuclear magnetic resonance for soil moisture measurements", *Vadose Zone Journal*, **20**(5), 2021, e20138, [DOI](https://doi.org/10.1002/vzj2.20138) +2. Costabel, S. and Hiller, T., "Soil hydraulic interpretation of nuclear magnetic resonance measurements based on circular and triangular capillary models", *Vadose Zone Journal*, **20**(2), 2021, e20104, [DOI](https://doi.org/10.1002/vzj2.20104) +3. Hiller, T. and Klitzsch, N., "Joint inversion of nuclear magnetic resonance data from partially saturated rocks using a triangular pore model", *GEOPHYSICS*, **83**(4), JM15-JM28, 2018, [DOI](https://doi.org/10.1190/geo2017-0697.1) - - -

MATLAB is a registered trademark of The Mathworks, Inc.

\ No newline at end of file diff --git a/callbacks/contextmenus/onContextAxisLogLin.m b/callbacks/contextmenus/onContextAxisLogLin.m index 47da942..5911082 100644 --- a/callbacks/contextmenus/onContextAxisLogLin.m +++ b/callbacks/contextmenus/onContextAxisLogLin.m @@ -26,8 +26,8 @@ function onContextAxisLogLin(src,~) % none % % See also: NUCLEUSmod, NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextAxisT1T2.m b/callbacks/contextmenus/onContextAxisT1T2.m index cfb049a..e97c777 100644 --- a/callbacks/contextmenus/onContextAxisT1T2.m +++ b/callbacks/contextmenus/onContextAxisT1T2.m @@ -24,8 +24,8 @@ function onContextAxisT1T2(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextPlotsPSD.m b/callbacks/contextmenus/onContextPlotsPSD.m index 1ffb49c..7c28f30 100644 --- a/callbacks/contextmenus/onContextPlotsPSD.m +++ b/callbacks/contextmenus/onContextPlotsPSD.m @@ -27,8 +27,8 @@ function onContextPlotsPSD(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextPlotsPSDJ.m b/callbacks/contextmenus/onContextPlotsPSDJ.m index 38cdbea..3313671 100644 --- a/callbacks/contextmenus/onContextPlotsPSDJ.m +++ b/callbacks/contextmenus/onContextPlotsPSDJ.m @@ -24,8 +24,8 @@ function onContextPlotsPSDJ(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextPlotsRTD.m b/callbacks/contextmenus/onContextPlotsRTD.m index bf64784..91ec38d 100644 --- a/callbacks/contextmenus/onContextPlotsRTD.m +++ b/callbacks/contextmenus/onContextPlotsRTD.m @@ -25,8 +25,8 @@ function onContextPlotsRTD(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextSignalList.m b/callbacks/contextmenus/onContextSignalList.m index 8bbffa6..74f784f 100644 --- a/callbacks/contextmenus/onContextSignalList.m +++ b/callbacks/contextmenus/onContextSignalList.m @@ -31,8 +31,8 @@ function onContextSignalList(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/contextmenus/onContextTableSelect.m b/callbacks/contextmenus/onContextTableSelect.m index 3dd4d82..ca17b45 100644 --- a/callbacks/contextmenus/onContextTableSelect.m +++ b/callbacks/contextmenus/onContextTableSelect.m @@ -25,8 +25,8 @@ function onContextTableSelect(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/edits/onEditCPSTable.m b/callbacks/edits/onEditCPSTable.m index c80bfa4..5570d20 100644 --- a/callbacks/edits/onEditCPSTable.m +++ b/callbacks/edits/onEditCPSTable.m @@ -23,8 +23,8 @@ function onEditCPSTable(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/edits/onEditValue.m b/callbacks/edits/onEditValue.m index 6ef57cf..db2d7a3 100644 --- a/callbacks/edits/onEditValue.m +++ b/callbacks/edits/onEditValue.m @@ -26,7 +26,6 @@ function onEditValue(src,~) % updateInfo % updateNMRsignals % updatePlotsDistribution -% updatePlotsNMR % updatePlotsSignal % % Subfunctions: @@ -36,8 +35,8 @@ function onEditValue(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -146,6 +145,12 @@ function onEditValue(src,~) setappdata(fig,'data',data); set(src,'String',num2str(data.invstd.Tbulk)); end + case 'Tdiff' + if data.invstd.Tdiff <=0 + data.invstd.Tdiff = 1e6; + setappdata(fig,'data',data); + set(src,'String',num2str(data.invstd.Tdiff)); + end case 'porosity' if data.invstd.porosity < 0 || data.invstd.porosity > 1 data.invstd.porosity = 1; @@ -225,9 +230,17 @@ function onEditValue(src,~) case 'nmr' switch out.field case 'noise' + switch data.nmr.noisetype + case 'level' + case 'SNR' + if data.nmr.noise == 0 + data.nmr.noise = Inf; + set(src,'String',num2str(data.nmr.noise)); + end + end + setappdata(fig,'data',data); if isfield(data.results,'NMR') updateNMRsignals; - updatePlotsNMR; end case 'porosity' if data.nmr.porosity <= 0 || data.nmr.porosity > 1 @@ -237,7 +250,6 @@ function onEditValue(src,~) end if isfield(data.results,'NMR') updateNMRsignals; - updatePlotsNMR; end otherwise data = removeCalculationFields(data,'nmr'); diff --git a/callbacks/listboxes/onListboxData.m b/callbacks/listboxes/onListboxData.m index c3d5558..07706f7 100644 --- a/callbacks/listboxes/onListboxData.m +++ b/callbacks/listboxes/onListboxData.m @@ -32,8 +32,8 @@ function onListboxData(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -98,7 +98,7 @@ function onListboxData(src,~) data.process.start = 1; case 'T2' switch data.import.fileformat - case {'dart','field','mouse','NMRMOD','excel'} + case {'dart','excel','field','helios','mouse','NMRMOD'} data.process.gatetype = 'raw'; data.process.start = 1; otherwise @@ -147,7 +147,8 @@ function onListboxData(src,~) end if isfield(data.import,'NMRMOD') data.param.rho = data.import.NMR.para{id}.rho*1e6; - data.invstd.Tbulk = data.import.NMR.para{id}.Tbulk; + data.invstd.Tbulk = data.import.NMR.para{id}.Tbulk; + data.invstd.Tdiff = data.import.NMR.para{id}.Tdiff; data.invstd.porosity = data.import.NMR.para{id}.porosity; end % --- diff --git a/callbacks/menus/onMenuExpert.m b/callbacks/menus/onMenuExpert.m index 02076ab..f566e50 100644 --- a/callbacks/menus/onMenuExpert.m +++ b/callbacks/menus/onMenuExpert.m @@ -31,8 +31,8 @@ function onMenuExpert(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -48,7 +48,7 @@ function onMenuExpert(src,~) % deactivate or activate expert mode switch onoff - case 'on' % it it's on, switch it off + case 'on' % if it's on, switch it off data.info.ExpertMode = 'off'; % menu entry set(gui.menu.extra_expert,'Checked','off'); diff --git a/callbacks/menus/onMenuExportData.m b/callbacks/menus/onMenuExportData.m index bbd2a59..9765c05 100644 --- a/callbacks/menus/onMenuExportData.m +++ b/callbacks/menus/onMenuExportData.m @@ -24,8 +24,8 @@ function onMenuExportData(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuExportGraphics.m b/callbacks/menus/onMenuExportGraphics.m index db838cc..faca7ae 100644 --- a/callbacks/menus/onMenuExportGraphics.m +++ b/callbacks/menus/onMenuExportGraphics.m @@ -26,8 +26,8 @@ function onMenuExportGraphics(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuExtraColor.m b/callbacks/menus/onMenuExtraColor.m index ffc4bf6..2c0860a 100644 --- a/callbacks/menus/onMenuExtraColor.m +++ b/callbacks/menus/onMenuExtraColor.m @@ -23,8 +23,8 @@ function onMenuExtraColor(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuExtraRhoBounds.m b/callbacks/menus/onMenuExtraRhoBounds.m index 42193d5..ffda8e3 100644 --- a/callbacks/menus/onMenuExtraRhoBounds.m +++ b/callbacks/menus/onMenuExtraRhoBounds.m @@ -23,8 +23,8 @@ function onMenuExtraRhoBounds(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuHelp.m b/callbacks/menus/onMenuHelp.m index 3c5b94e..8f97786 100644 --- a/callbacks/menus/onMenuHelp.m +++ b/callbacks/menus/onMenuHelp.m @@ -23,8 +23,8 @@ function onMenuHelp(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuImport.m b/callbacks/menus/onMenuImport.m index 23e07e3..4114d12 100644 --- a/callbacks/menus/onMenuImport.m +++ b/callbacks/menus/onMenuImport.m @@ -29,8 +29,8 @@ function onMenuImport(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -68,13 +68,15 @@ function onMenuImport(src,~) % activate the PhaseView GUI in case real data is imported switch menu_tag - case {'NUCLEUSinv','NUCLEUSmod'} + case 'NUCLEUSmod' set(gui.menu.extra_phaseview,'Enable','off'); otherwise set(gui.menu.extra_phaseview,'Enable','on'); end - % update the "last import" value within the ini-file + % get updated gui data + gui = getappdata(fig,'gui'); + % update the "last import" value within the ini-file gui.myui.inidata.lastimport = [menu_tag,'_',label]; setappdata(fig,'gui',gui); gui = makeINIfile(gui,'update'); diff --git a/callbacks/menus/onMenuJointInversion.m b/callbacks/menus/onMenuJointInversion.m index 2f989d4..4b74e72 100644 --- a/callbacks/menus/onMenuJointInversion.m +++ b/callbacks/menus/onMenuJointInversion.m @@ -30,8 +30,8 @@ function onMenuJointInversion(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuRestartQuit.m b/callbacks/menus/onMenuRestartQuit.m index 6466507..ff947f8 100644 --- a/callbacks/menus/onMenuRestartQuit.m +++ b/callbacks/menus/onMenuRestartQuit.m @@ -24,8 +24,8 @@ function onMenuRestartQuit(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuSolver.m b/callbacks/menus/onMenuSolver.m index fe32035..c668573 100644 --- a/callbacks/menus/onMenuSolver.m +++ b/callbacks/menus/onMenuSolver.m @@ -25,8 +25,8 @@ function onMenuSolver(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuView.m b/callbacks/menus/onMenuView.m index 77dc216..66c699f 100644 --- a/callbacks/menus/onMenuView.m +++ b/callbacks/menus/onMenuView.m @@ -24,8 +24,8 @@ function onMenuView(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/menus/onMenuViewFigures.m b/callbacks/menus/onMenuViewFigures.m index b964396..7a87499 100644 --- a/callbacks/menus/onMenuViewFigures.m +++ b/callbacks/menus/onMenuViewFigures.m @@ -24,8 +24,8 @@ function onMenuViewFigures(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupGeometryModesN.m b/callbacks/popup/onPopupGeometryModesN.m index e2f8ad6..8a12182 100644 --- a/callbacks/popup/onPopupGeometryModesN.m +++ b/callbacks/popup/onPopupGeometryModesN.m @@ -25,8 +25,8 @@ function onPopupGeometryModesN(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupGeometryPolyN.m b/callbacks/popup/onPopupGeometryPolyN.m index 12512ec..89bf707 100644 --- a/callbacks/popup/onPopupGeometryPolyN.m +++ b/callbacks/popup/onPopupGeometryPolyN.m @@ -25,8 +25,8 @@ function onPopupGeometryPolyN(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupGeometrySinglePSD.m b/callbacks/popup/onPopupGeometrySinglePSD.m index de70f90..69457f7 100644 --- a/callbacks/popup/onPopupGeometrySinglePSD.m +++ b/callbacks/popup/onPopupGeometrySinglePSD.m @@ -25,8 +25,8 @@ function onPopupGeometrySinglePSD(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupGeometryType.m b/callbacks/popup/onPopupGeometryType.m index 85a791a..5d30471 100644 --- a/callbacks/popup/onPopupGeometryType.m +++ b/callbacks/popup/onPopupGeometryType.m @@ -25,8 +25,8 @@ function onPopupGeometryType(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupInvjointGeometryType.m b/callbacks/popup/onPopupInvjointGeometryType.m index 904c966..d284620 100644 --- a/callbacks/popup/onPopupInvjointGeometryType.m +++ b/callbacks/popup/onPopupInvjointGeometryType.m @@ -24,8 +24,8 @@ function onPopupInvjointGeometryType(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupInvjointPolyN.m b/callbacks/popup/onPopupInvjointPolyN.m index eda133a..ad02068 100644 --- a/callbacks/popup/onPopupInvjointPolyN.m +++ b/callbacks/popup/onPopupInvjointPolyN.m @@ -24,8 +24,8 @@ function onPopupInvjointPolyN(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupInvjointType.m b/callbacks/popup/onPopupInvjointType.m index e6100e0..ddfaa3c 100644 --- a/callbacks/popup/onPopupInvjointType.m +++ b/callbacks/popup/onPopupInvjointType.m @@ -24,8 +24,8 @@ function onPopupInvjointType(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupInvjointTypeOptional.m b/callbacks/popup/onPopupInvjointTypeOptional.m index 5faede1..444305d 100644 --- a/callbacks/popup/onPopupInvjointTypeOptional.m +++ b/callbacks/popup/onPopupInvjointTypeOptional.m @@ -25,8 +25,8 @@ function onPopupInvjointTypeOptional(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupInvstdType.m b/callbacks/popup/onPopupInvstdType.m index 62edaa9..fe4c6ee 100644 --- a/callbacks/popup/onPopupInvstdType.m +++ b/callbacks/popup/onPopupInvstdType.m @@ -25,8 +25,8 @@ function onPopupInvstdType(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -62,6 +62,11 @@ function onPopupInvstdType(src,~) data.invstd.invtype = 'LU'; data.invstd.regtype = 'auto'; data.invstd.lambda = -1; + + case 5 + data.invstd.invtype = 'MUMO'; + data.invstd.regtype = 'none'; + data.invstd.lambda = 1; end case 'off' @@ -89,12 +94,17 @@ function onPopupInvstdType(src,~) else data.invstd.regtype = 'manual'; data.invstd.lambda = 1e-2; - end + end % update GUI data setappdata(fig,'data',data); % because the gate type could have changed update data onRadioGates(gui.radio_handles.process_gates_log); data = getappdata(fig,'data'); + + case 4 + data.invstd.invtype = 'MUMO'; + data.invstd.regtype = 'none'; + data.invstd.lambda = 1; end end % update GUI data diff --git a/callbacks/popup/onPopupInvstdTypeOptional.m b/callbacks/popup/onPopupInvstdTypeOptional.m index ca3a91d..e9b6278 100644 --- a/callbacks/popup/onPopupInvstdTypeOptional.m +++ b/callbacks/popup/onPopupInvstdTypeOptional.m @@ -27,8 +27,8 @@ function onPopupInvstdTypeOptional(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -135,6 +135,10 @@ function onPopupInvstdTypeOptional(src,~) data.invstd.regtype = 'auto'; data.invstd.lambda = -1; end + + case 'MUMO' + % # free distributions = value (1 to 4) + data.invstd.freeDT = value; end % update GUI data diff --git a/callbacks/popup/onPopupNMRNoiseType.m b/callbacks/popup/onPopupNMRNoiseType.m new file mode 100644 index 0000000..1d381d0 --- /dev/null +++ b/callbacks/popup/onPopupNMRNoiseType.m @@ -0,0 +1,92 @@ +function onPopupNMRNoiseType(src,~) +%onPopupNMRNoiseType selects the noise type to be aplied to the forward +%modelled NMR data +% +% Syntax: +% onPopupNMRNoiseType +% +% Inputs: +% src - handle of the calling object +% +% Outputs: +% none +% +% Example: +% onPopupNMRNoiseType(src,~) +% +% Other m-files required: +% clearSingleAxis.m +% updateCPSTable.m +% +% Subfunctions: +% none +% +% MAT-files required: +% none +% +% See also: NUCLEUSmod +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +%% get GUI handle and data +fig = findobj('Tag','MOD'); +data = getappdata(fig,'data'); +gui = getappdata(fig,'gui'); + +% get the value of the popup menu +value = get(src,'Value'); + +% change settings accordingly +switch value + case 1 % noise level + data.nmr.noisetype = 'level'; + if data.nmr.noise > 0 + data.nmr.noise = 1/data.nmr.noise; + end + case 2 % signal-to-noise ratio (SNR) + data.nmr.noisetype = 'SNR'; + if data.nmr.noise == 0 + data.nmr.noise = inf; + else + data.nmr.noise = 1/data.nmr.noise; + end +end +% update the corresponding edit field +set(gui.edit_handles.noise,'String',num2str(data.nmr.noise)); + +% update GUI data +setappdata(fig,'data',data); +% update NMR data (if available) +if isfield(data.results,'NMR') + updateNMRsignals; +end + +end + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2021 Thomas Hiller +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. \ No newline at end of file diff --git a/callbacks/popup/onPopupPressureLoglin.m b/callbacks/popup/onPopupPressureLoglin.m index 6255b01..d752af6 100644 --- a/callbacks/popup/onPopupPressureLoglin.m +++ b/callbacks/popup/onPopupPressureLoglin.m @@ -25,8 +25,8 @@ function onPopupPressureLoglin(src,~) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/popup/onPopupPressureUnits.m b/callbacks/popup/onPopupPressureUnits.m index 47cfcd3..48b3eb1 100644 --- a/callbacks/popup/onPopupPressureUnits.m +++ b/callbacks/popup/onPopupPressureUnits.m @@ -24,8 +24,8 @@ function onPopupPressureUnits(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/push/onPushCPSTable.m b/callbacks/push/onPushCPSTable.m index 053dacf..b0160e9 100644 --- a/callbacks/push/onPushCPSTable.m +++ b/callbacks/push/onPushCPSTable.m @@ -24,8 +24,8 @@ function onPushCPSTable(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/push/onPushRun.m b/callbacks/push/onPushRun.m index ebc1f02..8ad7eb4 100644 --- a/callbacks/push/onPushRun.m +++ b/callbacks/push/onPushRun.m @@ -27,8 +27,8 @@ function onPushRun(src,~) % calculateNMR % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/push/onPushShowHide.m b/callbacks/push/onPushShowHide.m index 898878e..78eb9cf 100644 --- a/callbacks/push/onPushShowHide.m +++ b/callbacks/push/onPushShowHide.m @@ -23,8 +23,8 @@ function onPushShowHide(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/push/onPushStop.m b/callbacks/push/onPushStop.m index 25fa4ba..123b302 100644 --- a/callbacks/push/onPushStop.m +++ b/callbacks/push/onPushStop.m @@ -26,8 +26,8 @@ function onPushStop(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/radio/onRadioGates.m b/callbacks/radio/onRadioGates.m index 876be07..51404ec 100644 --- a/callbacks/radio/onRadioGates.m +++ b/callbacks/radio/onRadioGates.m @@ -26,8 +26,8 @@ function onRadioGates(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/radio/onRadioLorder.m b/callbacks/radio/onRadioLorder.m index b69dbd8..45a9f56 100644 --- a/callbacks/radio/onRadioLorder.m +++ b/callbacks/radio/onRadioLorder.m @@ -24,8 +24,8 @@ function onRadioLorder(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/radio/onRadioNormalize.m b/callbacks/radio/onRadioNormalize.m index 12ed20b..bcaa50a 100644 --- a/callbacks/radio/onRadioNormalize.m +++ b/callbacks/radio/onRadioNormalize.m @@ -26,8 +26,8 @@ function onRadioNormalize(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/callbacks/radio/onRadioTimescale.m b/callbacks/radio/onRadioTimescale.m index d70df46..e0afa54 100644 --- a/callbacks/radio/onRadioTimescale.m +++ b/callbacks/radio/onRadioTimescale.m @@ -26,8 +26,8 @@ function onRadioTimescale(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -55,6 +55,7 @@ function onRadioTimescale(src,~) data.process.timefac = 1; data.invstd.time = data.invstd.time ./ 1000; data.invstd.Tbulk = data.invstd.Tbulk ./ 1000; + data.invstd.Tdiff = data.invstd.Tdiff ./ 1000; end case 'ms' @@ -63,6 +64,7 @@ function onRadioTimescale(src,~) data.process.timefac = 1000; data.invstd.time = data.invstd.time .* 1000; data.invstd.Tbulk = data.invstd.Tbulk .* 1000; + data.invstd.Tdiff = data.invstd.Tdiff .* 1000; end end diff --git a/doc/menu.html b/doc/menu.html index c55120a..1cd7e85 100644 --- a/doc/menu.html +++ b/doc/menu.html @@ -16,7 +16,7 @@

Matlab Index

Matlab Directories

- +
Generated by m2html © 2005
diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv.html index 67d39e0..c121aed 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv.html @@ -54,8 +54,8 @@

DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also NUCLEUSmod -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- @@ -111,10 +111,10 @@

SOURCE CODE ^if ~isempty(h0); close(h0); end 0039 0040 %% GUI 'header' info and defaults -0041 myui.version = '0.1.11'; -0042 myui.date = '12.03.2021'; -0043 myui.author = {'Thomas Hiller','Stephan Costabel'}; -0044 myui.email = 'thomas.hiller[at]leibniz-liag.de'; +0041 myui.version = '0.1.12'; +0042 myui.date = '17.02.2022'; +0043 myui.author = {'Stephan Costabel','Thomas Hiller'}; +0044 myui.email = 'thomas.hiller[at]bgr.de'; 0045 myui.fontsize = 10; 0046 myui.inifile = 'NUCLEUSinv.ini'; 0047 diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createGUI.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createGUI.html index 7fbeec2..a7f0549 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createGUI.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createGUI.html @@ -63,15 +63,15 @@

DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end)

CROSS-REFERENCE INFORMATION ^

This function calls: +
  • NUCLEUSinv_createMenus creates all GUI menus
  • NUCLEUSinv_createPanelData creates data panel
  • NUCLEUSinv_createPanelInfo creates status bar
  • NUCLEUSinv_createPanelInversionJoint creates joint inversion panel
  • NUCLEUSinv_createPanelInversionStd creates standard inversion panel
  • NUCLEUSinv_createPanelPetro creates Petrophysics panel
  • NUCLEUSinv_createPanelPlots creates graphics panel
  • NUCLEUSinv_createPanelProcess creates process panel
  • NUCLEUSinv_createStatusbar creates status bar
  • NUCLEUSinv_processINI processes the ini-file
  • changeColorTheme changes the color theme of the calling figure
  • displayStatusText shows status information either in the GUI or on the
  • getColorTheme returns the colors of the selected color theme
  • minimizePanel handles the minimization/maximization of all box-panels for
  • switchToolTips switches GUI tool tips either "on" or "off"
  • updateStatusInformation updates all fields inside the bottom status bar
  • updateToolTips updates tool tip entries dependent on the chosen settings
  • This function is called by:
    • NUCLEUSinv is a graphical user interface (GUI) to invert NMR relaxometry
    @@ -118,8 +118,8 @@

    SOURCE CODE ^% none 0037 % 0038 % See also: NUCLEUSinv -0039 % Author: Thomas Hiller -0040 % email: thomas.hiller[at]leibniz-liag.de +0039 % Author: see AUTHORS.md +0040 % email: see AUTHORS.md 0041 % License: MIT License (at end) 0042 0043 %------------- BEGIN CODE -------------- @@ -200,7 +200,7 @@

    SOURCE CODE ^'TitleColor',myui.colors.BoxCPS,'ForegroundColor',myui.colors.BoxTitle); 0119 0120 % adjust the heights of all left-column-panels -0121 myui.heights = [250 22 22 22 22; -1 109 137 190 299]; +0121 myui.heights = [250 22 22 22 22; -1 109 165 190 299]; 0122 % panel header is always 22 high 0123 set(gui.panels.main,'Heights',myui.heights(2,:),... 0124 'MinimumHeights',[250 22 22 22 22]); @@ -281,7 +281,7 @@

    SOURCE CODE ^% otherwise "fixAxes" throws an error (in NUCLEUSmod); strangely here it 0200 % also works without it, but I put it for consistency reasons 0201 setappdata(h,'gui',gui); -0202 % changeColorTheme('INV',myui.colors.theme); +0202 changeColorTheme('INV',myui.colors.theme); 0203 set(gui.main,'Visible','on'); 0204 0205 %% enable all menus diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createMenus.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createMenus.html index 28b991b..acae6e6 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createMenus.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createMenus.html @@ -51,8 +51,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -131,350 +131,361 @@

    SOURCE CODE ^% 1.1.1.2.1 BGR std 0062 gui.menu.file_import_lab_bgr_std = uimenu(gui.menu.file_import_lab_bgr,... 0063 'Label','BGR std','Tag','Lab','Callback',@onMenuImport); -0064 % 1.1.1.2.2 BGR org -0065 gui.menu.file_import_lab_bgr_org = uimenu(gui.menu.file_import_lab_bgr,... -0066 'Label','BGR org','Tag','Lab','Callback',@onMenuImport); -0067 % 1.1.1.2.3 BGR mat -0068 gui.menu.file_import_lab_bgr_mat = uimenu(gui.menu.file_import_lab_bgr,... -0069 'Label','BGR mat','Tag','Lab','Callback',@onMenuImport); -0070 -0071 % 1.1.1.3 LIAG -0072 gui.menu.file_import_lab_liag = uimenu(gui.menu.file_import_lab,... -0073 'Label','LIAG'); -0074 % 1.1.1.3.1 LIAG -0075 gui.menu.file_import_lab_liag_single = uimenu(gui.menu.file_import_lab_liag,... -0076 'Label','LIAG single','Tag','Lab','Callback',@onMenuImport); -0077 % 1.1.1.3.2 LIAG -0078 gui.menu.file_import_lab_liag_project = uimenu(gui.menu.file_import_lab_liag,... -0079 'Label','LIAG from project','Tag','Lab','Callback',@onMenuImport); -0080 -0081 % 1.1.1.4 RWTH -0082 gui.menu.file_import_lab_rwth = uimenu(gui.menu.file_import_lab,... -0083 'Label','RWTH'); -0084 % 1.1.1.4.1 IBAC -0085 gui.menu.file_import_lab_ibac = uimenu(gui.menu.file_import_lab_rwth,... -0086 'Label','IBAC'); -0087 % 1.1.1.4.1.1 IBAC -0088 gui.menu.file_import_lab_ibac_pm5 = uimenu(gui.menu.file_import_lab_ibac,... -0089 'Label','PM5','Tag','Lab','Callback',@onMenuImport); -0090 % 1.1.1.4.1.2 IBAC -0091 gui.menu.file_import_lab_ibac_pm25 = uimenu(gui.menu.file_import_lab_ibac,... -0092 'Label','PM25','Tag','Lab','Callback',@onMenuImport); -0093 -0094 % 1.1.1.4.2 GGE -0095 gui.menu.file_import_lab_gge = uimenu(gui.menu.file_import_lab_rwth,... -0096 'Label','GGE'); -0097 % 1.1.1.4.2.1 GGE ascii -0098 gui.menu.file_import_lab_gge_ascii = uimenu(gui.menu.file_import_lab_gge,... -0099 'Label','GGE ascii','Tag','Lab','Callback',@onMenuImport); -0100 % 1.1.1.4.2.2 GGE field -0101 gui.menu.file_import_lab_gge_field = uimenu(gui.menu.file_import_lab_gge,... -0102 'Label','GGE field','Tag','Lab','Callback',@onMenuImport); -0103 % 1.1.1.4.2.3 GGE Dart -0104 gui.menu.file_import_lab_gge_dart = uimenu(gui.menu.file_import_lab_gge,... -0105 'Label','GGE Dart','Tag','Lab','Callback',@onMenuImport); -0106 -0107 % 1.1.1.5 OTHER -0108 gui.menu.file_import_lab_other = uimenu(gui.menu.file_import_lab,... -0109 'Label','OTHER'); -0110 % 1.1.1.5.1 CoreLab ascii -0111 gui.menu.file_import_lab_corelab = uimenu(gui.menu.file_import_lab_other,... -0112 'Label','CoreLab ascii','Tag','Lab','Callback',@onMenuImport); -0113 % 1.1.1.5.2 MOUSE -0114 gui.menu.file_import_lab_mouse = uimenu(gui.menu.file_import_lab_other,... -0115 'Label','MOUSE','Tag','Lab','Callback',@onMenuImport); -0116 -0117 % 1.1.2 Ascii -0118 gui.menu.file_import_ascii = uimenu(gui.menu.file_import,... -0119 'Label','Ascii'); -0120 % 1.1.2.1 T1 -0121 gui.menu.file_import_lab_ascii_T1 = uimenu(gui.menu.file_import_ascii,... -0122 'Label','T1','Tag','Ascii','Callback',@onMenuImport); -0123 % 1.1.2.2 T2 -0124 gui.menu.file_import_lab_ascii_T2 = uimenu(gui.menu.file_import_ascii,... -0125 'Label','T2','Tag','Ascii','Callback',@onMenuImport); -0126 -0127 % 1.1.3 Excel -0128 gui.menu.file_import_excel = uimenu(gui.menu.file_import,... -0129 'Label','Excel'); -0130 % 1.1.3.1 T1 -0131 gui.menu.file_import_lab_excel_T1 = uimenu(gui.menu.file_import_excel,... -0132 'Label','T1','Tag','Excel','Callback',@onMenuImport); -0133 % 1.1.3.2 T2 -0134 gui.menu.file_import_lab_excel_T2 = uimenu(gui.menu.file_import_excel,... -0135 'Label','T2','Tag','Excel','Callback',@onMenuImport); -0136 -0137 % 1.1.4 NUCLEUSinv -0138 gui.menu.file_import_nmrinv = uimenu(gui.menu.file_import,... -0139 'Label','NUCLEUSinv','Separator','on'); -0140 % 1.1.4.1 NUCLEUSinv session file -0141 gui.menu.file_import_nmrinv_file = uimenu(gui.menu.file_import_nmrinv,... -0142 'Label','Session','Tag','NUCLEUSinv','Callback',@onMenuImport); -0143 -0144 % 1.1.5 NUCLEUSmod -0145 gui.menu.file_import_nmrmod = uimenu(gui.menu.file_import,... -0146 'Label','NUCLEUSmod'); -0147 % 1.1.5.1 NUCLEUSmod from file -0148 gui.menu.file_import_nmrmod_file = uimenu(gui.menu.file_import_nmrmod,... -0149 'Label','File','Tag','NUCLEUSmod','Callback',@onMenuImport); -0150 % 1.1.5.2 NUCLEUSmod from GUI -0151 gui.menu.file_import_nmrmod_gui = uimenu(gui.menu.file_import_nmrmod,.... -0152 'Label','GUI','Tag','NUCLEUSmod','Callback',@onMenuImport); -0153 -0154 % 1.2 Export -0155 gui.menu.file_export = uimenu(gui.menu.file,... -0156 'Label','Export'); -0157 % 1.2.0 Last export -0158 lastexport = gui.myui.inidata.lastexport; -0159 gui.menu.file_export_lastexport = uimenu(gui.menu.file_export,... -0160 'Label',lastexport,'Callback',@onMenuExportData); -0161 % 1.2.1 Data -0162 gui.menu.file_export_data = uimenu(gui.menu.file_export,... -0163 'Label','Data'); -0164 % 1.2.1.1 GUI raw data mat-file -0165 gui.menu.file_export_data_GUImat = uimenu(gui.menu.file_export_data,... -0166 'Label','NUCLEUSinv (raw)','Callback',@onMenuExportData); -0167 % 1.2.1.2 GUI session mat-file -0168 gui.menu.file_export_data_GUImat = uimenu(gui.menu.file_export_data,... -0169 'Label','NUCLEUSinv (session)','Callback',@onMenuExportData); -0170 % 1.2.1.3 Inversion results single xls -0171 gui.menu.file_export_data_invstd_excel = uimenu(gui.menu.file_export_data,... -0172 'Label','EXCEL single (std)','Separator','on','Callback',@onMenuExportData); -0173 % 1.2.1.4 Inversion results single mat-file -0174 gui.menu.file_export_data_invstd_mat_single = uimenu(gui.menu.file_export_data,... -0175 'Label','MAT single (std)','Callback',@onMenuExportData); -0176 % 1.2.1.5 Inversion results all mat-file -0177 gui.menu.file_export_data_invstd_mat_all = uimenu(gui.menu.file_export_data,... -0178 'Label','MAT all (std)','Callback',@onMenuExportData); -0179 % 1.2.1.6 Joint Inversion results xls -0180 gui.menu.file_export_data_invjoint_excel = uimenu(gui.menu.file_export_data,... -0181 'Label','EXCEL all (joint)','Separator','on','Enable','off',... -0182 'Callback',@onMenuExportData); -0183 % 1.2.1.7 Joint Inversion results mat-file -0184 gui.menu.file_export_data_invjoint_mat = uimenu(gui.menu.file_export_data,... -0185 'Label','MAT all (joint)','Enable','off','Callback',@onMenuExportData); -0186 % 1.2.1.8 LIAG archive -0187 gui.menu.file_export_data_liag_archive = uimenu(gui.menu.file_export_data,... -0188 'Label','LIAG archive','Separator','on','Callback',@onMenuExportData); -0189 % 1.2.1.9 LIAG CSV T2 -0190 gui.menu.file_export_data_liag_csvT2 = uimenu(gui.menu.file_export_data,... -0191 'Label','LIAG CSV T2','Callback',@onMenuExportData); -0192 -0193 % 1.2.2 Graphics -0194 gui.menu.file_export_graphics = uimenu(gui.menu.file_export,... -0195 'Label','Graphics'); -0196 % 1.2.2.1 Layout -0197 gui.menu.file_export_graphics_layout = uimenu(gui.menu.file_export_graphics,... -0198 'Label','Layout'); -0199 % 1.2.2.1.1 Layout vertical -0200 gui.menu.file_export_graphics_layout_vert = uimenu(gui.menu.file_export_graphics_layout,... -0201 'Label','vert','Checked','on','Callback',@onMenuExportGraphics); -0202 % 1.2.2.1.2 Layout horizontal -0203 gui.menu.file_export_graphics_layout_horz = uimenu(gui.menu.file_export_graphics_layout,... -0204 'Label','horz','Callback',@onMenuExportGraphics); -0205 % 1.2.2.2 fig -0206 gui.menu.file_export_graphics_fig = uimenu(gui.menu.file_export_graphics,... -0207 'Label','FIG','Callback',@onMenuExportGraphics); -0208 % 1.2.2.3 png -0209 gui.menu.file_export_graphicspng = uimenu(gui.menu.file_export_graphics,... -0210 'Label','PNG','Callback',@onMenuExportGraphics); -0211 % 1.2.2.4 tiff -0212 gui.menu.file_export_graphicstiff = uimenu(gui.menu.file_export_graphics,... -0213 'Label','TIFF','Callback',@onMenuExportGraphics); -0214 % 1.2.2.5 eps -0215 gui.menu.file_export_graphicseps = uimenu(gui.menu.file_export_graphics,... -0216 'Label','EPS','Callback',@onMenuExportGraphics); -0217 -0218 % 1.3 Restart -0219 gui.menu.file_restart = uimenu(gui.menu.file,... -0220 'Label','Restart','Separator','on','Callback',@onMenuRestartQuit); -0221 -0222 % 1.4 Quit -0223 gui.menu.file_quit = uimenu(gui.menu.file,... -0224 'Label','Quit','Callback',@onMenuRestartQuit); -0225 -0226 %% 2. View -0227 gui.menu.view = uimenu(gui.figh,... -0228 'Label','View','Enable','off'); -0229 -0230 % 2.1 tooltips (on/off) -0231 gui.menu.view_tooltips = uimenu(gui.menu.view,... -0232 'Label','Tooltips','Checked','off','Callback',@onMenuView); -0233 switch gui.myui.inidata.tooltips -0234 case 'on' -0235 set(gui.menu.view_tooltips,'Checked','on'); -0236 case 'off' -0237 set(gui.menu.view_tooltips,'Checked','off'); -0238 end -0239 % 2.2 Figure Toolbar -0240 gui.menu.view_toolbar = uimenu(gui.menu.view,... -0241 'Label','Figure Toolbar','Callback',@onMenuView); -0242 -0243 % 2.3 INFO fields in plot panels (on/off) -0244 gui.menu.view_infofields = uimenu(gui.menu.view,... -0245 'Label','INFO fields','Separator','on','Callback',@onMenuView); -0246 -0247 % 2.4 inversion info on command line (on/off) -0248 gui.menu.view_invinfo = uimenu(gui.menu.view,... -0249 'Label','CLI Inv. Info','Callback',@onMenuView); -0250 switch gui.myui.inidata.invinfo -0251 case 'on' -0252 set(gui.menu.view_invinfo,'Checked','on'); -0253 case 'off' -0254 set(gui.menu.view_invinfo,'Checked','off'); -0255 end -0256 -0257 % 2.5 figures -0258 gui.menu.extra_graphics = uimenu(gui.menu.view,... -0259 'Label','Figures','Separator','on'); -0260 % 2.5.1 parameter file info -0261 gui.menu.extra_graphics_parinfo = uimenu(gui.menu.extra_graphics,... -0262 'Label','Parameter Info','Callback',@onMenuViewFigures); -0263 % 2.5.2 fit statistics -0264 gui.menu.extra_graphics_stats = uimenu(gui.menu.extra_graphics,... -0265 'Label','Fit statistics','Callback',@onMenuViewFigures); -0266 switch gui.myui.inidata.expertmode -0267 case 'on' -0268 % 2.5.3 amplitude over time -0269 gui.menu.extra_graphics_amp = uimenu(gui.menu.extra_graphics,... -0270 'Label','AMP-TLGM-SNR','Callback',@onMenuViewFigures); -0271 % 2.5.4 amplitude vs tlgm -0272 gui.menu.extra_graphics_amp2 = uimenu(gui.menu.extra_graphics,... -0273 'Label','AMP vs TLGM','Callback',@onMenuViewFigures); -0274 % 2.5.5 relaxation time distribution over time -0275 gui.menu.extra_graphics_rtd = uimenu(gui.menu.extra_graphics,... -0276 'Label','RTD','Callback',@onMenuViewFigures); -0277 case 'off' -0278 % 2.5.3 amplitude over time -0279 gui.menu.extra_graphics_amp = uimenu(gui.menu.extra_graphics,... -0280 'Label','AMP-TLGM-SNR','Enable','off','Callback',@onMenuViewFigures); -0281 % 2.5.4 amplitude vs tlgm -0282 gui.menu.extra_graphics_amp2 = uimenu(gui.menu.extra_graphics,... -0283 'Label','AMP vs TLGM','Enable','off','Callback',@onMenuViewFigures); -0284 % 2.5.5 relaxation time distribution over time -0285 gui.menu.extra_graphics_rtd = uimenu(gui.menu.extra_graphics,... -0286 'Label','RTD','Enable','off','Callback',@onMenuViewFigures); -0287 end -0288 -0289 % 2.6 PhaseView -0290 gui.menu.extra_phaseview = uimenu(gui.menu.view,... -0291 'Label','PhaseView GUI','Enable','off','Callback',@PhaseView); -0292 % 2.7 ConductVIEW -> hydraulic conductivity -0293 gui.menu.extra_conduct = uimenu(gui.menu.view,... -0294 'Label','ConductView GUI','Enable','off','Callback',@onMenuViewFigures); -0295 -0296 %% 3. Extras -0297 gui.menu.extra = uimenu(gui.figh,... -0298 'Label','Extra','Enable','off'); +0064 % 1.1.1.2.2 BGR mat +0065 gui.menu.file_import_lab_bgr_mat = uimenu(gui.menu.file_import_lab_bgr,... +0066 'Label','BGR mat','Tag','Lab','Callback',@onMenuImport); +0067 % 1.1.1.2.3 Mouse CPMG data, single data subfolders from CPMG +0068 gui.menu.file_import_lab_bgr_mouse_cpmg = uimenu(gui.menu.file_import_lab_bgr,... +0069 'Label','MouseCPMG','Tag','Lab','Callback',@onMenuImport); +0070 % 1.1.1.2.4 Mouse plus Lift, single data subfolder from t1test,... +0071 % ...cpmgfastautotest, or (old Prospa Versions) cpmgfastauto +0072 gui.menu.file_import_lab_bgr_mouse_liftsingle = uimenu(gui.menu.file_import_lab_bgr,... +0073 'Label','MouseLiftSingle','Tag','Lab','Callback',@onMenuImport); +0074 % 1.1.1.2.5 Mouse plus Lift, all data subfolders from t1test,... +0075 % cpmgfastautotest, or (old Prospa Versions) cpmgfastauto +0076 gui.menu.file_import_lab_bgr_mouse_liftall = uimenu(gui.menu.file_import_lab_bgr,... +0077 'Label','MouseLiftAll','Tag','Lab','Callback',@onMenuImport); +0078 % 1.1.1.2.6 Helios +0079 gui.menu.file_import_lab_bgr_helios = uimenu(gui.menu.file_import_lab_bgr,... +0080 'Label','Helios','Tag','Lab','Callback',@onMenuImport); +0081 +0082 % 1.1.1.3 LIAG +0083 gui.menu.file_import_lab_liag = uimenu(gui.menu.file_import_lab,... +0084 'Label','LIAG'); +0085 % 1.1.1.3.1 LIAG +0086 gui.menu.file_import_lab_liag_single = uimenu(gui.menu.file_import_lab_liag,... +0087 'Label','LIAG single','Tag','Lab','Callback',@onMenuImport); +0088 % 1.1.1.3.2 LIAG +0089 gui.menu.file_import_lab_liag_project = uimenu(gui.menu.file_import_lab_liag,... +0090 'Label','LIAG from project','Tag','Lab','Callback',@onMenuImport); +0091 +0092 % 1.1.1.4 RWTH +0093 gui.menu.file_import_lab_rwth = uimenu(gui.menu.file_import_lab,... +0094 'Label','RWTH'); +0095 % 1.1.1.4.1 IBAC +0096 gui.menu.file_import_lab_ibac = uimenu(gui.menu.file_import_lab_rwth,... +0097 'Label','IBAC'); +0098 % 1.1.1.4.1.1 IBAC +0099 gui.menu.file_import_lab_ibac_pm5 = uimenu(gui.menu.file_import_lab_ibac,... +0100 'Label','PM5','Tag','Lab','Callback',@onMenuImport); +0101 % 1.1.1.4.1.2 IBAC +0102 gui.menu.file_import_lab_ibac_pm25 = uimenu(gui.menu.file_import_lab_ibac,... +0103 'Label','PM25','Tag','Lab','Callback',@onMenuImport); +0104 +0105 % 1.1.1.4.2 GGE +0106 gui.menu.file_import_lab_gge = uimenu(gui.menu.file_import_lab_rwth,... +0107 'Label','GGE'); +0108 % 1.1.1.4.2.1 GGE ascii +0109 gui.menu.file_import_lab_gge_ascii = uimenu(gui.menu.file_import_lab_gge,... +0110 'Label','GGE ascii','Tag','Lab','Callback',@onMenuImport); +0111 % 1.1.1.4.2.2 GGE field +0112 gui.menu.file_import_lab_gge_field = uimenu(gui.menu.file_import_lab_gge,... +0113 'Label','GGE field','Tag','Lab','Callback',@onMenuImport); +0114 % 1.1.1.4.2.3 GGE Dart +0115 gui.menu.file_import_lab_gge_dart = uimenu(gui.menu.file_import_lab_gge,... +0116 'Label','GGE Dart','Tag','Lab','Callback',@onMenuImport); +0117 +0118 % 1.1.1.5 OTHER +0119 gui.menu.file_import_lab_other = uimenu(gui.menu.file_import_lab,... +0120 'Label','OTHER'); +0121 % 1.1.1.5.1 CoreLab ascii +0122 gui.menu.file_import_lab_corelab = uimenu(gui.menu.file_import_lab_other,... +0123 'Label','CoreLab ascii','Tag','Lab','Callback',@onMenuImport); +0124 % 1.1.1.5.2 MOUSE +0125 gui.menu.file_import_lab_mouse = uimenu(gui.menu.file_import_lab_other,... +0126 'Label','MOUSE','Tag','Lab','Callback',@onMenuImport); +0127 +0128 % 1.1.2 Ascii +0129 gui.menu.file_import_ascii = uimenu(gui.menu.file_import,... +0130 'Label','Ascii'); +0131 % 1.1.2.1 T1 +0132 gui.menu.file_import_lab_ascii_T1 = uimenu(gui.menu.file_import_ascii,... +0133 'Label','T1','Tag','Ascii','Callback',@onMenuImport); +0134 % 1.1.2.2 T2 +0135 gui.menu.file_import_lab_ascii_T2 = uimenu(gui.menu.file_import_ascii,... +0136 'Label','T2','Tag','Ascii','Callback',@onMenuImport); +0137 +0138 % 1.1.3 Excel +0139 gui.menu.file_import_excel = uimenu(gui.menu.file_import,... +0140 'Label','Excel'); +0141 % 1.1.3.1 T1 +0142 gui.menu.file_import_lab_excel_T1 = uimenu(gui.menu.file_import_excel,... +0143 'Label','T1','Tag','Excel','Callback',@onMenuImport); +0144 % 1.1.3.2 T2 +0145 gui.menu.file_import_lab_excel_T2 = uimenu(gui.menu.file_import_excel,... +0146 'Label','T2','Tag','Excel','Callback',@onMenuImport); +0147 +0148 % 1.1.4 NUCLEUSinv +0149 gui.menu.file_import_nmrinv = uimenu(gui.menu.file_import,... +0150 'Label','NUCLEUSinv','Separator','on'); +0151 % 1.1.4.1 NUCLEUSinv session file +0152 gui.menu.file_import_nmrinv_file = uimenu(gui.menu.file_import_nmrinv,... +0153 'Label','Session','Tag','NUCLEUSinv','Callback',@onMenuImport); +0154 +0155 % 1.1.5 NUCLEUSmod +0156 gui.menu.file_import_nmrmod = uimenu(gui.menu.file_import,... +0157 'Label','NUCLEUSmod'); +0158 % 1.1.5.1 NUCLEUSmod from file +0159 gui.menu.file_import_nmrmod_file = uimenu(gui.menu.file_import_nmrmod,... +0160 'Label','File','Tag','NUCLEUSmod','Callback',@onMenuImport); +0161 % 1.1.5.2 NUCLEUSmod from GUI +0162 gui.menu.file_import_nmrmod_gui = uimenu(gui.menu.file_import_nmrmod,.... +0163 'Label','GUI','Tag','NUCLEUSmod','Callback',@onMenuImport); +0164 +0165 % 1.2 Export +0166 gui.menu.file_export = uimenu(gui.menu.file,... +0167 'Label','Export'); +0168 % 1.2.0 Last export +0169 lastexport = gui.myui.inidata.lastexport; +0170 gui.menu.file_export_lastexport = uimenu(gui.menu.file_export,... +0171 'Label',lastexport,'Callback',@onMenuExportData); +0172 % 1.2.1 Data +0173 gui.menu.file_export_data = uimenu(gui.menu.file_export,... +0174 'Label','Data'); +0175 % 1.2.1.1 GUI raw data mat-file +0176 gui.menu.file_export_data_GUImat = uimenu(gui.menu.file_export_data,... +0177 'Label','NUCLEUSinv (raw)','Callback',@onMenuExportData); +0178 % 1.2.1.2 GUI session mat-file +0179 gui.menu.file_export_data_GUImat = uimenu(gui.menu.file_export_data,... +0180 'Label','NUCLEUSinv (session)','Callback',@onMenuExportData); +0181 % 1.2.1.3 Inversion results single xls +0182 gui.menu.file_export_data_invstd_excel = uimenu(gui.menu.file_export_data,... +0183 'Label','EXCEL single (std)','Separator','on','Callback',@onMenuExportData); +0184 % 1.2.1.4 Inversion results single mat-file +0185 gui.menu.file_export_data_invstd_mat_single = uimenu(gui.menu.file_export_data,... +0186 'Label','MAT single (std)','Callback',@onMenuExportData); +0187 % 1.2.1.5 Inversion results all mat-file +0188 gui.menu.file_export_data_invstd_mat_all = uimenu(gui.menu.file_export_data,... +0189 'Label','MAT all (std)','Callback',@onMenuExportData); +0190 % 1.2.1.6 Joint Inversion results xls +0191 gui.menu.file_export_data_invjoint_excel = uimenu(gui.menu.file_export_data,... +0192 'Label','EXCEL all (joint)','Separator','on','Enable','off',... +0193 'Callback',@onMenuExportData); +0194 % 1.2.1.7 Joint Inversion results mat-file +0195 gui.menu.file_export_data_invjoint_mat = uimenu(gui.menu.file_export_data,... +0196 'Label','MAT all (joint)','Enable','off','Callback',@onMenuExportData); +0197 % 1.2.1.8 LIAG archive +0198 gui.menu.file_export_data_liag_archive = uimenu(gui.menu.file_export_data,... +0199 'Label','LIAG archive','Separator','on','Callback',@onMenuExportData); +0200 % 1.2.1.9 LIAG CSV T2 +0201 gui.menu.file_export_data_liag_csvT2 = uimenu(gui.menu.file_export_data,... +0202 'Label','LIAG CSV T2','Callback',@onMenuExportData); +0203 +0204 % 1.2.2 Graphics +0205 gui.menu.file_export_graphics = uimenu(gui.menu.file_export,... +0206 'Label','Graphics'); +0207 % 1.2.2.1 Layout +0208 gui.menu.file_export_graphics_layout = uimenu(gui.menu.file_export_graphics,... +0209 'Label','Layout'); +0210 % 1.2.2.1.1 Layout vertical +0211 gui.menu.file_export_graphics_layout_vert = uimenu(gui.menu.file_export_graphics_layout,... +0212 'Label','vert','Checked','on','Callback',@onMenuExportGraphics); +0213 % 1.2.2.1.2 Layout horizontal +0214 gui.menu.file_export_graphics_layout_horz = uimenu(gui.menu.file_export_graphics_layout,... +0215 'Label','horz','Callback',@onMenuExportGraphics); +0216 % 1.2.2.2 fig +0217 gui.menu.file_export_graphics_fig = uimenu(gui.menu.file_export_graphics,... +0218 'Label','FIG','Callback',@onMenuExportGraphics); +0219 % 1.2.2.3 png +0220 gui.menu.file_export_graphicspng = uimenu(gui.menu.file_export_graphics,... +0221 'Label','PNG','Callback',@onMenuExportGraphics); +0222 % 1.2.2.4 tiff +0223 gui.menu.file_export_graphicstiff = uimenu(gui.menu.file_export_graphics,... +0224 'Label','TIFF','Callback',@onMenuExportGraphics); +0225 % 1.2.2.5 eps +0226 gui.menu.file_export_graphicseps = uimenu(gui.menu.file_export_graphics,... +0227 'Label','EPS','Callback',@onMenuExportGraphics); +0228 +0229 % 1.3 Restart +0230 gui.menu.file_restart = uimenu(gui.menu.file,... +0231 'Label','Restart','Separator','on','Callback',@onMenuRestartQuit); +0232 +0233 % 1.4 Quit +0234 gui.menu.file_quit = uimenu(gui.menu.file,... +0235 'Label','Quit','Callback',@onMenuRestartQuit); +0236 +0237 %% 2. View +0238 gui.menu.view = uimenu(gui.figh,... +0239 'Label','View','Enable','off'); +0240 +0241 % 2.1 tooltips (on/off) +0242 gui.menu.view_tooltips = uimenu(gui.menu.view,... +0243 'Label','Tooltips','Checked','off','Callback',@onMenuView); +0244 switch gui.myui.inidata.tooltips +0245 case 'on' +0246 set(gui.menu.view_tooltips,'Checked','on'); +0247 case 'off' +0248 set(gui.menu.view_tooltips,'Checked','off'); +0249 end +0250 % 2.2 Figure Toolbar +0251 gui.menu.view_toolbar = uimenu(gui.menu.view,... +0252 'Label','Figure Toolbar','Callback',@onMenuView); +0253 +0254 % 2.3 INFO fields in plot panels (on/off) +0255 gui.menu.view_infofields = uimenu(gui.menu.view,... +0256 'Label','INFO fields','Separator','on','Callback',@onMenuView); +0257 +0258 % 2.4 inversion info on command line (on/off) +0259 gui.menu.view_invinfo = uimenu(gui.menu.view,... +0260 'Label','CLI Inv. Info','Callback',@onMenuView); +0261 switch gui.myui.inidata.invinfo +0262 case 'on' +0263 set(gui.menu.view_invinfo,'Checked','on'); +0264 case 'off' +0265 set(gui.menu.view_invinfo,'Checked','off'); +0266 end +0267 +0268 % 2.5 figures +0269 gui.menu.extra_graphics = uimenu(gui.menu.view,... +0270 'Label','Figures','Separator','on'); +0271 % 2.5.1 parameter file info +0272 gui.menu.extra_graphics_parinfo = uimenu(gui.menu.extra_graphics,... +0273 'Label','Parameter Info','Callback',@onMenuViewFigures); +0274 % 2.5.2 fit statistics +0275 gui.menu.extra_graphics_stats = uimenu(gui.menu.extra_graphics,... +0276 'Label','Fit statistics','Callback',@onMenuViewFigures); +0277 switch gui.myui.inidata.expertmode +0278 case 'on' +0279 % 2.5.3 amplitude over time +0280 gui.menu.extra_graphics_amp = uimenu(gui.menu.extra_graphics,... +0281 'Label','AMP-TLGM-SNR','Callback',@onMenuViewFigures); +0282 % 2.5.4 amplitude vs tlgm +0283 gui.menu.extra_graphics_amp2 = uimenu(gui.menu.extra_graphics,... +0284 'Label','AMP vs TLGM','Callback',@onMenuViewFigures); +0285 % 2.5.5 relaxation time distribution over time +0286 gui.menu.extra_graphics_rtd = uimenu(gui.menu.extra_graphics,... +0287 'Label','RTD','Callback',@onMenuViewFigures); +0288 case 'off' +0289 % 2.5.3 amplitude over time +0290 gui.menu.extra_graphics_amp = uimenu(gui.menu.extra_graphics,... +0291 'Label','AMP-TLGM-SNR','Enable','off','Callback',@onMenuViewFigures); +0292 % 2.5.4 amplitude vs tlgm +0293 gui.menu.extra_graphics_amp2 = uimenu(gui.menu.extra_graphics,... +0294 'Label','AMP vs TLGM','Enable','off','Callback',@onMenuViewFigures); +0295 % 2.5.5 relaxation time distribution over time +0296 gui.menu.extra_graphics_rtd = uimenu(gui.menu.extra_graphics,... +0297 'Label','RTD','Enable','off','Callback',@onMenuViewFigures); +0298 end 0299 -0300 % 3.1 expert mode (on/off) -0301 gui.menu.extra_expert = uimenu(gui.menu.extra,... -0302 'Label','Expert Mode','Callback',@onMenuExpert); -0303 switch gui.myui.inidata.expertmode -0304 case 'on' -0305 set(gui.menu.extra_expert,'Checked','on'); -0306 case 'off' -0307 set(gui.menu.extra_expert,'Checked','of'); -0308 end -0309 -0310 % 3.2 optimization toolbox (on/off) -0311 switch gui.myui.inidata.expertmode -0312 case 'on' -0313 switch data.info.has_optim -0314 case 'on' -0315 gui.menu.extra_solver = uimenu(gui.menu.extra,... -0316 'Label','LSQ Solver','Enable','on'); -0317 case 'off' -0318 gui.menu.extra_solver = uimenu(gui.menu.extra,... -0319 'Label','LSQ Solver','Enable','off'); -0320 end -0321 case 'off' -0322 gui.menu.extra_solver = uimenu(gui.menu.extra,... -0323 'Label','LSQ Solver','Enable','off'); -0324 end -0325 gui.menu.extra_solver_lsqlin = uimenu(gui.menu.extra_solver,... -0326 'Label','LSQLIN (Optim. TB)','Callback',@onMenuSolver); -0327 gui.menu.extra_solver_lsqnonneg = uimenu(gui.menu.extra_solver,... -0328 'Label','LSQNONNEG (default)','Checked','on','Callback',@onMenuSolver); -0329 -0330 % 3.3 joint inversion (on/off) -0331 gui.menu.extra_joint = uimenu(gui.menu.extra,... -0332 'Label','Joint Inversion','Checked','off','Separator','on',... -0333 'Callback',@onMenuJointInversion); -0334 switch gui.myui.inidata.expertmode -0335 case 'on' -0336 set(gui.menu.extra_joint,'Enable','on'); -0337 case 'off' -0338 set(gui.menu.extra_joint,'Enable','off'); -0339 end +0300 % 2.6 PhaseView +0301 gui.menu.extra_phaseview = uimenu(gui.menu.view,... +0302 'Label','PhaseView GUI','Enable','off','Callback',@PhaseView); +0303 % 2.7 ConductVIEW -> hydraulic conductivity +0304 gui.menu.extra_conduct = uimenu(gui.menu.view,... +0305 'Label','ConductView GUI','Enable','off','Callback',@onMenuViewFigures); +0306 +0307 %% 3. Extras +0308 gui.menu.extra = uimenu(gui.figh,... +0309 'Label','Extra','Enable','off'); +0310 +0311 % 3.1 expert mode (on/off) +0312 gui.menu.extra_expert = uimenu(gui.menu.extra,... +0313 'Label','Expert Mode','Callback',@onMenuExpert); +0314 switch gui.myui.inidata.expertmode +0315 case 'on' +0316 set(gui.menu.extra_expert,'Checked','on'); +0317 case 'off' +0318 set(gui.menu.extra_expert,'Checked','of'); +0319 end +0320 +0321 % 3.2 optimization toolbox (on/off) +0322 switch gui.myui.inidata.expertmode +0323 case 'on' +0324 switch data.info.has_optim +0325 case 'on' +0326 gui.menu.extra_solver = uimenu(gui.menu.extra,... +0327 'Label','LSQ Solver','Enable','on'); +0328 case 'off' +0329 gui.menu.extra_solver = uimenu(gui.menu.extra,... +0330 'Label','LSQ Solver','Enable','off'); +0331 end +0332 case 'off' +0333 gui.menu.extra_solver = uimenu(gui.menu.extra,... +0334 'Label','LSQ Solver','Enable','off'); +0335 end +0336 gui.menu.extra_solver_lsqlin = uimenu(gui.menu.extra_solver,... +0337 'Label','LSQLIN (Optim. TB)','Callback',@onMenuSolver); +0338 gui.menu.extra_solver_lsqnonneg = uimenu(gui.menu.extra_solver,... +0339 'Label','LSQNONNEG (default)','Checked','on','Callback',@onMenuSolver); 0340 -0341 % 3.4 set inversion bounds for surface relaxivity rho -0342 gui.menu.extra_joint_rhobounds = uimenu(gui.menu.extra,... -0343 'Label','Surface relaxivity bounds','Enable','off',... -0344 'Callback',@onMenuExtraRhoBounds); -0345 -0346 -0347 %% 4. Color theme -0348 gui.menu.color_theme = uimenu(gui.figh,... -0349 'Label','Color Theme','Enable','off'); -0350 -0351 % 4.1 default color theme -0352 gui.menu.color_theme_standard = uimenu(gui.menu.color_theme,... -0353 'Label','standard','Callback',@onMenuExtraColor); -0354 % 4.2 basic color theme -0355 gui.menu.color_theme_basic = uimenu(gui.menu.color_theme,... -0356 'Label','basic','Callback',@onMenuExtraColor); -0357 % 4.3 dark color theme -0358 gui.menu.color_theme_dark = uimenu(gui.menu.color_theme,... -0359 'Label','dark','Callback',@onMenuExtraColor); -0360 % 4.4 black color theme -0361 gui.menu.color_theme_black = uimenu(gui.menu.color_theme,... -0362 'Label','black','Callback',@onMenuExtraColor); -0363 switch gui.myui.inidata.colortheme -0364 case 'standard' -0365 set(gui.menu.color_theme_standard,'Checked','on'); -0366 case 'basic' -0367 set(gui.menu.color_theme_basic,'Checked','on'); -0368 case 'dark' -0369 set(gui.menu.color_theme_dark,'Checked','on'); -0370 case 'black' -0371 set(gui.menu.color_theme_black,'Checked','on'); -0372 end -0373 -0374 %% 5. Help -0375 gui.menu.help = uimenu(gui.figh,... -0376 'Label','Help','Enable','off'); -0377 -0378 % 5.1 About -0379 gui.menu.help_about = uimenu(gui.menu.help,... -0380 'Label','About','Callback',@onMenuHelp); -0381 -0382 return -0383 -0384 %------------- END OF CODE -------------- -0385 -0386 %% License: -0387 % MIT License -0388 % -0389 % Copyright (c) 2018 Thomas Hiller -0390 % -0391 % Permission is hereby granted, free of charge, to any person obtaining a copy -0392 % of this software and associated documentation files (the "Software"), to deal -0393 % in the Software without restriction, including without limitation the rights -0394 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0395 % copies of the Software, and to permit persons to whom the Software is -0396 % furnished to do so, subject to the following conditions: -0397 % -0398 % The above copyright notice and this permission notice shall be included in all -0399 % copies or substantial portions of the Software. -0400 % -0401 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0402 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0403 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0404 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0405 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0406 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0407 % SOFTWARE. +0341 % 3.3 joint inversion (on/off) +0342 gui.menu.extra_joint = uimenu(gui.menu.extra,... +0343 'Label','Joint Inversion','Checked','off','Separator','on',... +0344 'Callback',@onMenuJointInversion); +0345 switch gui.myui.inidata.expertmode +0346 case 'on' +0347 set(gui.menu.extra_joint,'Enable','on'); +0348 case 'off' +0349 set(gui.menu.extra_joint,'Enable','off'); +0350 end +0351 +0352 % 3.4 set inversion bounds for surface relaxivity rho +0353 gui.menu.extra_joint_rhobounds = uimenu(gui.menu.extra,... +0354 'Label','Surface relaxivity bounds','Enable','off',... +0355 'Callback',@onMenuExtraRhoBounds); +0356 +0357 +0358 %% 4. Color theme +0359 gui.menu.color_theme = uimenu(gui.figh,... +0360 'Label','Color Theme','Enable','off'); +0361 +0362 % 4.1 default color theme +0363 gui.menu.color_theme_standard = uimenu(gui.menu.color_theme,... +0364 'Label','standard','Callback',@onMenuExtraColor); +0365 % 4.2 basic color theme +0366 gui.menu.color_theme_basic = uimenu(gui.menu.color_theme,... +0367 'Label','basic','Callback',@onMenuExtraColor); +0368 % 4.3 dark color theme +0369 gui.menu.color_theme_dark = uimenu(gui.menu.color_theme,... +0370 'Label','dark','Callback',@onMenuExtraColor); +0371 % 4.4 black color theme +0372 gui.menu.color_theme_black = uimenu(gui.menu.color_theme,... +0373 'Label','black','Callback',@onMenuExtraColor); +0374 switch gui.myui.inidata.colortheme +0375 case 'standard' +0376 set(gui.menu.color_theme_standard,'Checked','on'); +0377 case 'basic' +0378 set(gui.menu.color_theme_basic,'Checked','on'); +0379 case 'dark' +0380 set(gui.menu.color_theme_dark,'Checked','on'); +0381 case 'black' +0382 set(gui.menu.color_theme_black,'Checked','on'); +0383 end +0384 +0385 %% 5. Help +0386 gui.menu.help = uimenu(gui.figh,... +0387 'Label','Help','Enable','off'); +0388 +0389 % 5.1 About +0390 gui.menu.help_about = uimenu(gui.menu.help,... +0391 'Label','About','Callback',@onMenuHelp); +0392 +0393 return +0394 +0395 %------------- END OF CODE -------------- +0396 +0397 %% License: +0398 % MIT License +0399 % +0400 % Copyright (c) 2018 Thomas Hiller +0401 % +0402 % Permission is hereby granted, free of charge, to any person obtaining a copy +0403 % of this software and associated documentation files (the "Software"), to deal +0404 % in the Software without restriction, including without limitation the rights +0405 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0406 % copies of the Software, and to permit persons to whom the Software is +0407 % furnished to do so, subject to the following conditions: +0408 % +0409 % The above copyright notice and this permission notice shall be included in all +0410 % copies or substantial portions of the Software. +0411 % +0412 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0413 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0414 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0415 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0416 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0417 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0418 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelData.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelData.html index e762c10..26df343 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelData.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelData.html @@ -52,8 +52,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -96,8 +96,8 @@

    SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- @@ -149,7 +149,7 @@

    SOURCE CODE ^... 0079 'Label','save','Tag','single','Callback',@onContextSignalList); 0080 uimenu(gui.cm_handles.data_list_single,... -0081 'Label','remove','Tag','single','Separator','on',... +0081 'Label','\remove','Tag','single','Separator','on',... 0082 'Callback',@onContextSignalList); 0083 uimenu(gui.cm_handles.data_list_single,... 0084 'Label','use as calib.','Tag','single','Separator','on',... diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInfo.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInfo.html index f2b8b12..f456451 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInfo.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInfo.html @@ -50,8 +50,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.html index 4209fc2..76fb6d7 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionJoint.html @@ -53,8 +53,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.html index 195dd4b..4789d6b 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelInversionStd.html @@ -53,8 +53,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -134,10 +134,10 @@

    SOURCE CODE ^switch data.info.ExpertMode 0063 case 'on' 0064 tstr = ' '; -0065 istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)','Multi exp. (LU decomp.)'}; +0065 istring = {'Mono exp.','N free exp. (2-5)','Multi exp. (LSQ)','Multi exp. (LU decomp.)','Multi modal'}; 0066 case 'off' 0067 tstr = ' '; -0068 istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)'}; +0068 istring = {'Mono exp.','N free exp. (2-5)','Multi exp. (LSQ)'}; 0069 end 0070 gui.popup_handles.invstd_InvType = uicontrol('Parent',gui.panels.invstd.HBox1,... 0071 'Style','popup','String',istring,'FontSize',myui.fontsize,'Enable','off','Value',3,... diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPetro.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPetro.html index 486b24d..4e6181f 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPetro.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPetro.html @@ -53,8 +53,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -108,156 +108,185 @@

    SOURCE CODE ^'Parent',gui.panels.petro.main,... 0037 'Spacing',3,'Padding',3); 0038 -0039 % Tbulk, surface relaxivity rho and geometry factor a +0039 % Tbulk and Tdiff 0040 gui.panels.petro.HBox1 = uix.HBox('Parent',gui.panels.petro.VBox,... 0041 'Spacing',3); -0042 % CBW and BVI +0042 % surface relaxivity rho and geometry factor a 0043 gui.panels.petro.HBox2 = uix.HBox('Parent',gui.panels.petro.VBox,... 0044 'Spacing',3); -0045 % calibration switch calib volume +0045 % CBW and BVI 0046 gui.panels.petro.HBox3 = uix.HBox('Parent',gui.panels.petro.VBox,... 0047 'Spacing',3); -0048 % sample volume, sample porosity +0048 % calibration switch calib volume 0049 gui.panels.petro.HBox4 = uix.HBox('Parent',gui.panels.petro.VBox,... 0050 'Spacing',3); -0051 -0052 %% Tbulk, surface relaxivity rho and geometry factor a -0053 gui.text_handles.petro_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... -0054 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0055 'String',['Tbulk [s] | ',char(hex2dec('03C1')),' [µm/s] | geom']); -0056 tstr = '<HTML>Set the T bulk [s] value.<br>'; -0057 gui.edit_handles.invstd_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... -0058 'Style','edit','String',num2str(data.invstd.Tbulk),'FontSize',myui.fontsize,... -0059 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.Tbulk 1 1]),... -0060 'Tag','invstd_Tbulk','Enable','off','Callback',@onEditValue); -0061 tstr = ['<HTML>Surface relaxivity rho in [m/s].<br><br>',... -0062 '1/T = rho*S/V = rho*a/R.<br><br>',... -0063 '<u>Default value:</u><br>',... -0064 '<b>2.5e-5</b><br>']; -0065 gui.edit_handles.param_rho = uicontrol('Parent',gui.panels.petro.HBox1,... -0066 'Style','edit','String',num2str(data.param.rho),'FontSize',myui.fontsize,... -0067 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.rho 1 1]),... -0068 'Tag','param_rho','Enable','off','Callback',@onEditValue); -0069 tstr = ['<HTML>Give the geometry factor "a" to convert relaxation times to pore radii<br><br>',... -0070 '1/T = rho*S/V = rho*a/R.<br><br>',... -0071 '<u>Possible options:</u><br>',... -0072 '<b>2</b> capillaries with circular cross section<br>',... -0073 '<b>3</b> spheres<br><br>',... -0074 'For <b>right angular</b> and <b>polygonal</b> cross-sections "a" converts to the area-equivalent cylinder radius e.g.:<br>',... -0075 '<b>2.57</b> capillaries with 60°-60°-60° equilateral cross section<br>',... -0076 '<b>2.72-12.18</b> capillaries with 90°-a°-(90-a)° right angular cross section<br>',... -0077 'e.g. <b>2.72</b> is 90°-45°-45° | <b>2.87</b> is 90°-60°-30° | <b>5.64</b> is 90°-85°-5°<br><br>',... -0078 '<u>Default value:</u><br>',... -0079 '<b>2</b><br>']; -0080 gui.edit_handles.param_geom = uicontrol('Parent',gui.panels.petro.HBox1,... -0081 'Style','edit','String',num2str(data.param.a),'FontSize',myui.fontsize,... -0082 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.a 1 1]),... -0083 'Tag','param_a','Enable','off','Callback',@onEditValue); -0084 set(gui.panels.petro.HBox1,'Widths',[200 -1 -1 -1]); -0085 -0086 %% CBW and BVI -0087 gui.text_handles.petro_CBW = uicontrol('Parent',gui.panels.petro.HBox2,... -0088 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0089 'String','CBW [ms] | BVI [ms]'); -0090 tstr = ['<HTML>Give cut-off time between CBW and BVI in [ms].',... -0091 ' This value depends on lithology.<br><br>',... -0092 '<u>Abbreviations:</u><br>',... -0093 '<b>CBW</b> clay bound water<br>',... -0094 '<b>BVI</b> bulk volume irreducible (irreducible water)<br>',... -0095 '<b>BVM</b> bulk volume movable (free water)<br><br>',... -0096 'The value is usually <b>3 [ms]</b><br><br>',... -0097 '<u>Default value:</u><br>',... -0098 '<b>3</b><br>']; -0099 gui.edit_handles.param_CBW = uicontrol('Parent',gui.panels.petro.HBox2,... -0100 'Style','edit','String',num2str(data.param.CBWcutoff),'FontSize',myui.fontsize,... -0101 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.CBWcutoff 1 1]),... -0102 'Tag','param_CBWcutoff','Enable','off','Callback',@onEditValue); -0103 tstr = ['<HTML>Give cut off time between BVI and BVM in [ms].',... -0104 ' This value depends on lithology.<br><br>',... -0105 '<u>Abbreviations:</u><br>',... -0106 '<b>CBW</b> clay bound water<br>',... -0107 '<b>BVI</b> bulk volume irreducible (irreducible water)<br>',... -0108 '<b>BVM</b> bulk volume movable (free water)<br><br>',... -0109 'For Sandstones the value is usually <b>33 [ms]</b><br>',... -0110 'For Carbonates the value is usually <b>92 [ms]</b><br><br>',... -0111 '<u>Default value:</u><br>',... -0112 '<b>33</b><br>']; -0113 gui.edit_handles.param_BVI = uicontrol('Parent',gui.panels.petro.HBox2,... -0114 'Style','edit','String',num2str(data.param.BVIcutoff),'FontSize',myui.fontsize,... -0115 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.BVIcutoff 1 1]),... -0116 'Tag','param_BVIcutoff','Enable','off','Callback',@onEditValue); -0117 set(gui.panels.petro.HBox2,'Widths',[200 -1 -1]); -0118 -0119 %% calibration switch, calibration volume -0120 gui.text_handles.petro_calibVol = uicontrol('Parent',gui.panels.petro.HBox3,... -0121 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0122 'String','calib. Vol. | calib. Amp.'); -0123 tstr = ''; -0124 gui.edit_handles.param_calibVol = uicontrol('Parent',gui.panels.petro.HBox3,... -0125 'Style','edit','String',num2str(data.param.calibVol),'FontSize',myui.fontsize,... -0126 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibVol 1 1]),... -0127 'Tag','param_calibVol','Enable','off','Callback',@onEditValue); -0128 tstr = ''; -0129 gui.edit_handles.param_calibAmp = uicontrol('Parent',gui.panels.petro.HBox3,... -0130 'Style','edit','String',num2str(data.param.calibAmp),'FontSize',myui.fontsize,... -0131 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibAmp 1 1]),... -0132 'Tag','param_calibAmp','Enable','off','Callback',@onEditValue); -0133 set(gui.panels.petro.HBox3,'Widths',[200 -1 -1]); -0134 -0135 %% sample volume, porosity -0136 gui.text_handles.petro_sampVol = uicontrol('Parent',gui.panels.petro.HBox4,... -0137 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0138 'String','sample Vol. | porosity'); -0139 tstr = ' '; -0140 gui.edit_handles.param_sampVol = uicontrol('Parent',gui.panels.petro.HBox4,... -0141 'Style','edit','String',num2str(data.param.sampVol),'FontSize',myui.fontsize,... -0142 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.sampVol 1 1]),... -0143 'Tag','param_sampVol','Enable','off','Callback',@onEditValue); -0144 tstr = ['<HTML>Set porosity (between 0 and 1)<br><br>',... -0145 '<u>Default value:</u><br>',... -0146 '<b>1</b><br><br>']; -0147 gui.edit_handles.invstd_porosity = uicontrol('Parent',gui.panels.petro.HBox4,... -0148 'Style','edit','String',num2str(data.invstd.porosity),'FontSize',myui.fontsize,... -0149 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.porosity 1 1]),... -0150 'Tag','invstd_porosity','Enable','off','Callback',@onEditValue); -0151 set(gui.panels.petro.HBox4,'Widths',[200 -1 -1]); -0152 -0153 %% Java Hack to adjust vertical alignment of text fields -0154 jh = findjobj(gui.text_handles.petro_Tbulk); -0155 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0156 jh = findjobj(gui.text_handles.petro_CBW); -0157 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0158 jh = findjobj(gui.text_handles.petro_calibVol); -0159 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0160 jh = findjobj(gui.text_handles.petro_sampVol); -0161 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0162 -0163 return -0164 -0165 %------------- END OF CODE -------------- -0166 -0167 %% License: -0168 % MIT License -0169 % -0170 % Copyright (c) 2018 Thomas Hiller -0171 % -0172 % Permission is hereby granted, free of charge, to any person obtaining a copy -0173 % of this software and associated documentation files (the "Software"), to deal -0174 % in the Software without restriction, including without limitation the rights -0175 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0176 % copies of the Software, and to permit persons to whom the Software is -0177 % furnished to do so, subject to the following conditions: -0178 % -0179 % The above copyright notice and this permission notice shall be included in all -0180 % copies or substantial portions of the Software. -0181 % -0182 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0183 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0184 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0185 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0186 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0187 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0188 % SOFTWARE. +0051 % sample volume, sample porosity +0052 gui.panels.petro.HBox5 = uix.HBox('Parent',gui.panels.petro.VBox,... +0053 'Spacing',3); +0054 +0055 %% Tbulk and Tdiff +0056 gui.text_handles.petro_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... +0057 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0058 'String','Tbulk [s] | Tdiff [s]'); +0059 tstr = ['<HTML>Bulk relaxation time in [s].<br><br>',... +0060 '1/T = 1/Ts + 1/<b>Tb</b> + 1/Td<br><br>',... +0061 '<u>Default value:</u><br>',... +0062 '<b>1e6</b> (so that 1/<b>Tb</b> is ignored)<br>']; +0063 gui.edit_handles.invstd_Tbulk = uicontrol('Parent',gui.panels.petro.HBox1,... +0064 'Style','edit','String',num2str(data.invstd.Tbulk),'FontSize',myui.fontsize,... +0065 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.Tbulk 1 1]),... +0066 'Tag','invstd_Tbulk','Enable','off','Callback',@onEditValue); +0067 tstr = ['<HTML>Diffusion relaxation time in [s].<br><br>',... +0068 '1/T = 1/Ts + 1/Tb + 1/<b>Td</b><br><br>',... +0069 '<b>Td</b> can be caluclated by<br><br>',... +0070 '<b>Td</b> = 12 / (D*(',char(hex2dec('03B3')),'*G*TE)<sup>2</sup> )<br><br>',... +0071 'with<br><br>',... +0072 'D = diffusion coefficent of water<br>',... +0073 char(hex2dec('03B3')),' = gyromagnetic ratio of hydrogen<br>',... +0074 'G = magnetic gradient<br>',... +0075 'TE = echo time<br><br>',... +0076 '<u>Default value:</u><br>',... +0077 '<b>1e6</b> (so that 1/<b>Td</b> is ignored)<br>']; +0078 gui.edit_handles.invstd_Tdiff = uicontrol('Parent',gui.panels.petro.HBox1,... +0079 'Style','edit','String',num2str(data.invstd.Tdiff),'FontSize',myui.fontsize,... +0080 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.Tdiff 1 1]),... +0081 'Tag','invstd_Tdiff','Enable','off','Callback',@onEditValue); +0082 set(gui.panels.petro.HBox1,'Widths',[200 -1 -1]); +0083 +0084 %% surface relaxivity rho and geometry factor a +0085 gui.text_handles.petro_rho = uicontrol('Parent',gui.panels.petro.HBox2,... +0086 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0087 'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); +0088 tstr = ['<HTML>Surface relaxivity in [µm/s].<br><br>',... +0089 '1/Ts = <b>rho</b>*S/V = <b>rho</b>*a/R.<br><br>',... +0090 '<u>Default value:</u><br>',... +0091 '<b>10</b><br>']; +0092 gui.edit_handles.param_rho = uicontrol('Parent',gui.panels.petro.HBox2,... +0093 'Style','edit','String',num2str(data.param.rho),'FontSize',myui.fontsize,... +0094 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.rho 1 1]),... +0095 'Tag','param_rho','Enable','off','Callback',@onEditValue); +0096 tstr = ['<HTML>Geometry factor "a" to convert relaxation times to pore radii.<br><br>',... +0097 '1/Ts = rho*S/V = rho*<b>a</b>/R.<br><br>',... +0098 '<u>Possible options:</u><br>',... +0099 '<b>2</b> capillaries with circular cross section<br>',... +0100 '<b>3</b> spheres<br><br>',... +0101 'For <b>right angular</b> and <b>polygonal</b> cross-sections "a" converts to the area-equivalent cylinder radius e.g.:<br>',... +0102 '<b>2.57</b> capillaries with 60°-60°-60° equilateral cross section<br>',... +0103 '<b>2.72-12.18</b> capillaries with 90°-a°-(90-a)° right angular cross section<br>',... +0104 'e.g. <b>2.72</b> is 90°-45°-45° | <b>2.87</b> is 90°-60°-30° | <b>5.64</b> is 90°-85°-5°<br><br>',... +0105 '<u>Default value:</u><br>',... +0106 '<b>2</b><br>']; +0107 gui.edit_handles.param_geom = uicontrol('Parent',gui.panels.petro.HBox2,... +0108 'Style','edit','String',num2str(data.param.a),'FontSize',myui.fontsize,... +0109 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.a 1 1]),... +0110 'Tag','param_a','Enable','off','Callback',@onEditValue); +0111 set(gui.panels.petro.HBox2,'Widths',[200 -1 -1]); +0112 +0113 %% CBW and BVI +0114 gui.text_handles.petro_CBW = uicontrol('Parent',gui.panels.petro.HBox3,... +0115 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0116 'String','CBW [ms] | BVI [ms] '); +0117 tstr = ['<HTML>Cut-off time between CBW and BVI in [ms].',... +0118 ' This value depends on lithology.<br><br>',... +0119 '<u>Abbreviations:</u><br>',... +0120 '<b>CBW</b> clay bound water<br>',... +0121 '<b>BVI</b> bulk volume irreducible (irreducible water)<br>',... +0122 '<b>BVM</b> bulk volume movable (free water)<br><br>',... +0123 'The value is usually <b>3 [ms]</b><br><br>',... +0124 '<u>Default value:</u><br>',... +0125 '<b>3</b><br>']; +0126 gui.edit_handles.param_CBW = uicontrol('Parent',gui.panels.petro.HBox3,... +0127 'Style','edit','String',num2str(data.param.CBWcutoff),'FontSize',myui.fontsize,... +0128 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.CBWcutoff 1 1]),... +0129 'Tag','param_CBWcutoff','Enable','off','Callback',@onEditValue); +0130 tstr = ['<HTML>Cut-off time between BVI and BVM in [ms].',... +0131 ' This value depends on lithology.<br><br>',... +0132 '<u>Abbreviations:</u><br>',... +0133 '<b>CBW</b> clay bound water<br>',... +0134 '<b>BVI</b> bulk volume irreducible (irreducible water)<br>',... +0135 '<b>BVM</b> bulk volume movable (free water)<br><br>',... +0136 'For Sandstones the value is usually <b>33 [ms]</b><br>',... +0137 'For Carbonates the value is usually <b>92 [ms]</b><br><br>',... +0138 '<u>Default value:</u><br>',... +0139 '<b>33</b><br>']; +0140 gui.edit_handles.param_BVI = uicontrol('Parent',gui.panels.petro.HBox3,... +0141 'Style','edit','String',num2str(data.param.BVIcutoff),'FontSize',myui.fontsize,... +0142 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.BVIcutoff 1 1]),... +0143 'Tag','param_BVIcutoff','Enable','off','Callback',@onEditValue); +0144 set(gui.panels.petro.HBox3,'Widths',[200 -1 -1]); +0145 +0146 %% calibration switch, calibration volume +0147 gui.text_handles.petro_calibVol = uicontrol('Parent',gui.panels.petro.HBox4,... +0148 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0149 'String',' calib. Vol. | calib. Amp.'); +0150 tstr = ''; +0151 gui.edit_handles.param_calibVol = uicontrol('Parent',gui.panels.petro.HBox4,... +0152 'Style','edit','String',num2str(data.param.calibVol),'FontSize',myui.fontsize,... +0153 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibVol 1 1]),... +0154 'Tag','param_calibVol','Enable','off','Callback',@onEditValue); +0155 tstr = ''; +0156 gui.edit_handles.param_calibAmp = uicontrol('Parent',gui.panels.petro.HBox4,... +0157 'Style','edit','String',num2str(data.param.calibAmp),'FontSize',myui.fontsize,... +0158 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.calibAmp 1 1]),... +0159 'Tag','param_calibAmp','Enable','off','Callback',@onEditValue); +0160 set(gui.panels.petro.HBox4,'Widths',[200 -1 -1]); +0161 +0162 %% sample volume, porosity +0163 gui.text_handles.petro_sampVol = uicontrol('Parent',gui.panels.petro.HBox5,... +0164 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0165 'String','sample Vol. | porosity '); +0166 tstr = ' '; +0167 gui.edit_handles.param_sampVol = uicontrol('Parent',gui.panels.petro.HBox5,... +0168 'Style','edit','String',num2str(data.param.sampVol),'FontSize',myui.fontsize,... +0169 'UserData',struct('Tooltipstr',tstr,'defaults',[data.param.sampVol 1 1]),... +0170 'Tag','param_sampVol','Enable','off','Callback',@onEditValue); +0171 tstr = ['<HTML>Porosity (between 0 and 1)<br><br>',... +0172 '<u>Default value:</u><br>',... +0173 '<b>1</b><br><br>']; +0174 gui.edit_handles.invstd_porosity = uicontrol('Parent',gui.panels.petro.HBox5,... +0175 'Style','edit','String',num2str(data.invstd.porosity),'FontSize',myui.fontsize,... +0176 'UserData',struct('Tooltipstr',tstr,'defaults',[data.invstd.porosity 1 1]),... +0177 'Tag','invstd_porosity','Enable','off','Callback',@onEditValue); +0178 set(gui.panels.petro.HBox5,'Widths',[200 -1 -1]); +0179 +0180 %% Java Hack to adjust vertical alignment of text fields +0181 jh = findjobj(gui.text_handles.petro_Tbulk); +0182 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0183 jh = findjobj(gui.text_handles.petro_rho); +0184 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0185 jh = findjobj(gui.text_handles.petro_CBW); +0186 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0187 jh = findjobj(gui.text_handles.petro_calibVol); +0188 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0189 jh = findjobj(gui.text_handles.petro_sampVol); +0190 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0191 +0192 return +0193 +0194 %------------- END OF CODE -------------- +0195 +0196 %% License: +0197 % MIT License +0198 % +0199 % Copyright (c) 2018 Thomas Hiller +0200 % +0201 % Permission is hereby granted, free of charge, to any person obtaining a copy +0202 % of this software and associated documentation files (the "Software"), to deal +0203 % in the Software without restriction, including without limitation the rights +0204 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0205 % copies of the Software, and to permit persons to whom the Software is +0206 % furnished to do so, subject to the following conditions: +0207 % +0208 % The above copyright notice and this permission notice shall be included in all +0209 % copies or substantial portions of the Software. +0210 % +0211 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0212 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0213 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0214 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0215 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0216 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0217 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPlots.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPlots.html index 8135e9c..0c76271 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPlots.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelPlots.html @@ -53,8 +53,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelProcess.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelProcess.html index 7504b23..5e09ee5 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelProcess.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createPanelProcess.html @@ -53,8 +53,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createStatusbar.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createStatusbar.html index 8a968b5..87557f3 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createStatusbar.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_createStatusbar.html @@ -90,8 +90,8 @@

    SOURCE CODE ^% 0025 % See also: NUCLEUSinv 0026 -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 0030 %------------- BEGIN CODE -------------- 0031 diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_loadDefaults.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_loadDefaults.html index c199bbe..87c71c0 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_loadDefaults.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_loadDefaults.html @@ -50,8 +50,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- @@ -196,78 +196,87 @@

    SOURCE CODE ^% water bulk relaxation time [s] 0129 out.invstd.Tbulk = 1e6; -0130 % porosity value between 0 and 1 [-] -0131 out.invstd.porosity = 1; -0132 -0133 %% joint inversion panel defaults -0134 % joint inversion methods to choose 'free' | 'fixed' | 'shape' -0135 out.invjoint.invtype = 'free'; -0136 % pore size distribution in [m] -0137 out.invjoint.radii = [1e-8 1e-2]; -0138 % No. of steps per decade in pore size distribution -0139 out.invjoint.Nradii = 25; -0140 % regularization options for joint inversion 'free' -0141 out.invjoint.regtype = 'manual'; -0142 % smoothness constraint (order) for joint inversion 'free' -0143 out.invjoint.Lorder = 1; -0144 % regularization parameter for joint inversion 'free' -0145 out.invjoint.lambda = 1; -0146 % L-curve range (lambda) for joint inversion 'free' -0147 out.invjoint.lambdaR = [1e-3 1e2]; -0148 % initial L-curve range (lambda) for joint inversion 'free' -0149 out.invjoint.lambdaRinit = [1e-3 1e2]; -0150 out.invjoint.NlambdaR = 20; -0151 % available pore geometries 'cyl' | 'ang' | 'poly' -0152 out.invjoint.geometry_type = 'cyl'; -0153 % number of polygon sides for geometry 'poly' (3 to 12) -0154 out.invjoint.polyN = 3; -0155 % angle alpha [deg] - fixed to 90° -0156 out.invjoint.alpha = 90; -0157 % angle beta [deg] - changed by user -0158 out.invjoint.beta = 60; -0159 % gamma [deg] - alpha-beta -0160 out.invjoint.gamma = 30; -0161 % start value rho [µm/s] -0162 out.invjoint.rhostart = 20; -0163 % lower and upper boundary for rho [µm/s] -0164 out.invjoint.rhobounds = [0.01 1000]; -0165 % sart value beta [deg] -0166 out.invjoint.anglestart = 25; -0167 -0168 % pressure settings -0169 % CPS table initial values (use,p,S,drain/imb) -0170 out.pressure.table = {true,0,1,'D'}; -0171 % pressure units 'Pa' | 'kPa' | 'MPa' | 'bar' -0172 out.pressure.unit = 'Pa'; -0173 % corresponding scale factors - 1 | 1e-3 | 1e-6 | 1e-5 -0174 out.pressure.unitfac = 1; -0175 -0176 return -0177 -0178 %------------- END OF CODE -------------- -0179 -0180 %% License: -0181 % MIT License -0182 % -0183 % Copyright (c) 2018 Thomas Hiller -0184 % -0185 % Permission is hereby granted, free of charge, to any person obtaining a copy -0186 % of this software and associated documentation files (the "Software"), to deal -0187 % in the Software without restriction, including without limitation the rights -0188 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0189 % copies of the Software, and to permit persons to whom the Software is -0190 % furnished to do so, subject to the following conditions: +0130 % diffusion relaxation time [s] +0131 out.invstd.Tdiff = 1e6; +0132 % porosity value between 0 and 1 [-] +0133 out.invstd.porosity = 1; +0134 +0135 out.invstd.useUncert = 1; +0136 out.invstd.uncertMethod = 'thresh'; +0137 out.invstd.uncertThresh = 0.05; +0138 out.invstd.uncertChi2 = 0.005; +0139 out.invstd.uncertN = 100; +0140 out.invstd.uncertMax = 1e4; +0141 +0142 %% joint inversion panel defaults +0143 % joint inversion methods to choose 'free' | 'fixed' | 'shape' +0144 out.invjoint.invtype = 'free'; +0145 % pore size distribution in [m] +0146 out.invjoint.radii = [1e-8 1e-2]; +0147 % No. of steps per decade in pore size distribution +0148 out.invjoint.Nradii = 25; +0149 % regularization options for joint inversion 'free' +0150 out.invjoint.regtype = 'manual'; +0151 % smoothness constraint (order) for joint inversion 'free' +0152 out.invjoint.Lorder = 1; +0153 % regularization parameter for joint inversion 'free' +0154 out.invjoint.lambda = 1; +0155 % L-curve range (lambda) for joint inversion 'free' +0156 out.invjoint.lambdaR = [1e-3 1e2]; +0157 % initial L-curve range (lambda) for joint inversion 'free' +0158 out.invjoint.lambdaRinit = [1e-3 1e2]; +0159 out.invjoint.NlambdaR = 20; +0160 % available pore geometries 'cyl' | 'ang' | 'poly' +0161 out.invjoint.geometry_type = 'cyl'; +0162 % number of polygon sides for geometry 'poly' (3 to 12) +0163 out.invjoint.polyN = 3; +0164 % angle alpha [deg] - fixed to 90° +0165 out.invjoint.alpha = 90; +0166 % angle beta [deg] - changed by user +0167 out.invjoint.beta = 60; +0168 % gamma [deg] - alpha-beta +0169 out.invjoint.gamma = 30; +0170 % start value rho [µm/s] +0171 out.invjoint.rhostart = 20; +0172 % lower and upper boundary for rho [µm/s] +0173 out.invjoint.rhobounds = [0.01 1000]; +0174 % sart value beta [deg] +0175 out.invjoint.anglestart = 25; +0176 +0177 % pressure settings +0178 % CPS table initial values (use,p,S,drain/imb) +0179 out.pressure.table = {true,0,1,'D'}; +0180 % pressure units 'Pa' | 'kPa' | 'MPa' | 'bar' +0181 out.pressure.unit = 'Pa'; +0182 % corresponding scale factors - 1 | 1e-3 | 1e-6 | 1e-5 +0183 out.pressure.unitfac = 1; +0184 +0185 return +0186 +0187 %------------- END OF CODE -------------- +0188 +0189 %% License: +0190 % MIT License 0191 % -0192 % The above copyright notice and this permission notice shall be included in all -0193 % copies or substantial portions of the Software. -0194 % -0195 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0196 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0197 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0198 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0199 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0200 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0201 % SOFTWARE. +0192 % Copyright (c) 2018 Thomas Hiller +0193 % +0194 % Permission is hereby granted, free of charge, to any person obtaining a copy +0195 % of this software and associated documentation files (the "Software"), to deal +0196 % in the Software without restriction, including without limitation the rights +0197 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0198 % copies of the Software, and to permit persons to whom the Software is +0199 % furnished to do so, subject to the following conditions: +0200 % +0201 % The above copyright notice and this permission notice shall be included in all +0202 % copies or substantial portions of the Software. +0203 % +0204 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0205 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0206 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0207 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0208 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0209 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0210 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_processINI.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_processINI.html index 1918cf2..54cf131 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_processINI.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_processINI.html @@ -50,8 +50,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_updateInterface.html b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_updateInterface.html index 98157ca..e4c1daf 100644 --- a/doc/nucleus/NUCLEUSinv/NUCLEUSinv_updateInterface.html +++ b/doc/nucleus/NUCLEUSinv/NUCLEUSinv_updateInterface.html @@ -59,8 +59,8 @@

    DESCRIPTION ^NUCLEUSinv - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -112,8 +112,8 @@

    SOURCE CODE ^% none 0033 % 0034 % See also: NUCLEUSinv -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- @@ -149,7 +149,7 @@

    SOURCE CODE ^%% update standard inversion panel 0070 % inversion method popup 0071 istring = {'Mono exp.','Several free exp. (2-5)',... -0072 'Multi exp. (LSQ)','Multi exp. (LU decomp.)'}; +0072 'Multi exp. (LSQ)','Multi exp. (LU decomp.)','Multi modal'}; 0073 set(gui.popup_handles.invstd_InvType,'String',istring); 0074 switch data.invstd.invtype 0075 case 'mono' @@ -167,837 +167,879 @@

    SOURCE CODE ^updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); 0088 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); 0089 -0090 % Tbulk +0090 % Tbulk & Tdiff 0091 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... 0092 'String',num2str(data.invstd.Tbulk)); -0093 -0094 case 'free' -0095 % inversion method popup -0096 set(gui.popup_handles.invstd_InvType,'Value',2,'Enable','on'); -0097 -0098 % additional inversion settings -0099 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... -0100 'Value',data.invstd.freeDT,... -0101 'String',{'1','2','3','4','5'}); -0102 set(gui.text_handles.invstd_InvTypeOpt,... -0103 'String','No. of free decay times T'); -0104 -0105 % lambda, smoothness constraint and RTD limits -0106 gui = updateLambda(gui,data.invstd.regtype,0,0,0); -0107 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); -0108 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); -0109 -0110 % Tbulk -0111 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... -0112 'String',num2str(data.invstd.Tbulk)); -0113 -0114 case 'NNLS' -0115 % inversion method popup -0116 set(gui.popup_handles.invstd_InvType,'Value',3,'Enable','on'); +0093 set(gui.edit_handles.invstd_Tdiff,'Enable','off',... +0094 'String',num2str(data.invstd.Tdiff)); +0095 +0096 case 'free' +0097 % inversion method popup +0098 set(gui.popup_handles.invstd_InvType,'Value',2,'Enable','on'); +0099 +0100 % additional inversion settings +0101 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0102 'Value',data.invstd.freeDT,... +0103 'String',{'1','2','3','4','5'}); +0104 set(gui.text_handles.invstd_InvTypeOpt,... +0105 'String','No. of free decay times T'); +0106 +0107 % lambda, smoothness constraint and RTD limits +0108 gui = updateLambda(gui,data.invstd.regtype,0,0,0); +0109 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0110 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); +0111 +0112 % Tbulk & Tdiff +0113 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... +0114 'String',num2str(data.invstd.Tbulk)); +0115 set(gui.edit_handles.invstd_Tdiff,'Enable','off',... +0116 'String',num2str(data.invstd.Tdiff)); 0117 -0118 % additional inversion settings -0119 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... -0120 'String',{'Manual','Tikhonov (SVD)','TSVD (SVD)',... -0121 'DSVD (SVD)','Discrep. (SVD)','L-curve'}); -0122 set(gui.text_handles.invstd_InvTypeOpt,... -0123 'String','regularization options'); -0124 -0125 % regularization options -0126 switch data.invstd.regtype -0127 case 'manual' -0128 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); -0129 case 'gcv_tikh' -0130 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); -0131 case 'gcv_trunc' -0132 set(gui.popup_handles.invstd_InvTypeOpt,'Value',3); -0133 case 'gcv_damp' -0134 set(gui.popup_handles.invstd_InvTypeOpt,'Value',4); -0135 case 'discrep' -0136 set(gui.popup_handles.invstd_InvTypeOpt,'Value',5); -0137 case 'lcurve' -0138 set(gui.popup_handles.invstd_InvTypeOpt,'Value',6); -0139 end -0140 -0141 % lambda, smoothness constraint and RTD limits -0142 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... -0143 data.invstd.lambdaR,data.invstd.NlambdaR); -0144 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); -0145 gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... -0146 data.invstd.Ntime); -0147 -0148 % Tbulk -0149 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... -0150 'String',num2str(data.invstd.Tbulk)); +0118 case 'NNLS' +0119 % inversion method popup +0120 set(gui.popup_handles.invstd_InvType,'Value',3,'Enable','on'); +0121 +0122 % additional inversion settings +0123 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0124 'String',{'Manual','Tikhonov (SVD)','TSVD (SVD)',... +0125 'DSVD (SVD)','Discrep. (SVD)','L-curve'}); +0126 set(gui.text_handles.invstd_InvTypeOpt,... +0127 'String','regularization options'); +0128 +0129 % regularization options +0130 switch data.invstd.regtype +0131 case 'manual' +0132 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); +0133 case 'gcv_tikh' +0134 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); +0135 case 'gcv_trunc' +0136 set(gui.popup_handles.invstd_InvTypeOpt,'Value',3); +0137 case 'gcv_damp' +0138 set(gui.popup_handles.invstd_InvTypeOpt,'Value',4); +0139 case 'discrep' +0140 set(gui.popup_handles.invstd_InvTypeOpt,'Value',5); +0141 case 'lcurve' +0142 set(gui.popup_handles.invstd_InvTypeOpt,'Value',6); +0143 end +0144 +0145 % lambda, smoothness constraint and RTD limits +0146 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... +0147 data.invstd.lambdaR,data.invstd.NlambdaR); +0148 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0149 gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... +0150 data.invstd.Ntime); 0151 -0152 case 'LU' -0153 % inversion method popup -0154 set(gui.popup_handles.invstd_InvType,'Value',4,'Enable','on'); -0155 -0156 % additional inversion settings -0157 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... -0158 'String',{'Manual','Automatic'}); -0159 set(gui.text_handles.invstd_InvTypeOpt,... -0160 'String','regularization options'); +0152 % Tbulk & Tdiff +0153 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... +0154 'String',num2str(data.invstd.Tbulk)); +0155 set(gui.edit_handles.invstd_Tdiff,'Enable','on',... +0156 'String',num2str(data.invstd.Tdiff)); +0157 +0158 case 'LU' +0159 % inversion method popup +0160 set(gui.popup_handles.invstd_InvType,'Value',4,'Enable','on'); 0161 -0162 % regularization options -0163 switch data.invstd.regtype -0164 case 'manual' -0165 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); -0166 case 'auto' -0167 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); -0168 data.invstd.lambda = -1; -0169 end -0170 -0171 % lambda, smoothness constraint and RTD limits -0172 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... -0173 data.invstd.lambdaR,data.invstd.NlambdaR); -0174 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); -0175 gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... -0176 data.invstd.Ntime); -0177 -0178 % Tbulk -0179 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... -0180 'String',num2str(data.invstd.Tbulk)); -0181 end -0182 -0183 % updates CBW, BVI, rho and a -0184 gui = updateParams(gui,data.invstd.invtype,data.param); -0185 -0186 %% update joint inversion panel -0187 % depending if it is activated or not -0188 switch data.info.JointInv -0189 case 'on' -0190 % inversion method dependent -0191 switch data.invjoint.invtype -0192 case 'free' -0193 % inversion method popup -0194 set(gui.popup_handles.invjoint_InvType,'Value',1,'Enable','on'); -0195 -0196 % PSD limits -0197 gui = updateInvjointRadii(gui,data.invjoint.invtype,... -0198 data.invjoint.radii,data.invjoint.Nradii); -0199 -0200 % additional inversion settings -0201 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','on'); -0202 -0203 % regularization options -0204 switch data.invjoint.regtype -0205 case 'manual' -0206 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); -0207 case 'lcurve' -0208 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); -0209 end -0210 -0211 % smoothness constraint and lambda -0212 gui = updateLorderJoint(gui,data.invjoint.invtype,... -0213 data.invjoint.Lorder); -0214 gui = updateLambdaJoint(gui,data.invjoint.regtype,... -0215 data.invjoint.lambda,data.invjoint.lambdaR,... -0216 data.invjoint.NlambdaR,data.invstd.regtype); -0217 -0218 % start values -0219 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); -0220 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); -0221 -0222 % geometry type dependent -0223 switch data.invjoint.geometry_type -0224 case 'cyl' -0225 % geometry popup -0226 set(gui.popup_handles.invjoint_geometry_type,... -0227 'Value',1,'Enable','on'); -0228 -0229 % polygon sides and corresponding angle beta -0230 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0231 'Value',data.invjoint.polyN-2); -0232 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0233 -0234 % angles -0235 set(gui.edit_handles.invjoint_alpha,'String',''); -0236 set(gui.edit_handles.invjoint_beta,'String',''); -0237 set(gui.edit_handles.invjoint_gamma,'String',''); -0238 -0239 case 'ang' -0240 % geometry popup -0241 set(gui.popup_handles.invjoint_geometry_type,... -0242 'Value',2,'Enable','on'); -0243 -0244 % polygon sides and corresponding angle beta -0245 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0246 'Value',data.invjoint.polyN-2); -0247 set(gui.edit_handles.invjoint_beta,'Enable','on'); -0248 -0249 % angles -0250 set(gui.edit_handles.invjoint_alpha,... -0251 'String',num2str(data.invjoint.alpha)); -0252 set(gui.edit_handles.invjoint_beta,... -0253 'String',num2str(data.invjoint.beta)); -0254 set(gui.edit_handles.invjoint_gamma,... -0255 'String',num2str(data.invjoint.gamma)); -0256 -0257 case 'poly' -0258 % geometry popup -0259 set(gui.popup_handles.invjoint_geometry_type,... -0260 'Value',3,'Enable','on'); -0261 -0262 % polygon sides and corresponding angle beta -0263 set(gui.popup_handles.invjoint_polyN,'Enable','on',... -0264 'Value',data.invjoint.polyN-2); -0265 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0266 -0267 % angles -0268 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; -0269 set(gui.edit_handles.invjoint_alpha,... -0270 'String',''); -0271 set(gui.edit_handles.invjoint_beta,... -0272 'String',num2str(polyangle)); -0273 set(gui.edit_handles.invjoint_gamma,... -0274 'String',''); -0275 end -0276 -0277 case 'fixed' -0278 % inversion method popup -0279 set(gui.popup_handles.invjoint_InvType,'Value',2,'Enable','on'); -0280 -0281 % PSD limits -0282 gui = updateInvjointRadii(gui,data.invjoint.invtype,... -0283 data.invjoint.radii,data.invjoint.Nradii); -0284 -0285 % additional inversion settings -0286 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); -0287 -0288 % regularization options -0289 switch data.invjoint.regtype -0290 case 'manual' -0291 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); -0292 case 'lcurve' -0293 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); -0294 end -0295 -0296 % smoothness constraint and lambda -0297 gui = updateLorderJoint(gui,data.invjoint.invtype,... -0298 data.invjoint.Lorder); -0299 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... -0300 data.invjoint.lambdaR,data.invjoint.NlambdaR,... -0301 data.invstd.regtype); -0302 -0303 % start values -0304 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); -0305 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); -0306 -0307 % geometry type dependent -0308 switch data.invjoint.geometry_type -0309 case 'cyl' -0310 % geometry popup -0311 set(gui.popup_handles.invjoint_geometry_type,... -0312 'Value',1,'Enable','on'); -0313 -0314 % polygon sides and corresponding angle beta -0315 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0316 'Value',data.invjoint.polyN-2); -0317 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0318 -0319 % angles -0320 set(gui.edit_handles.invjoint_alpha,'String',''); -0321 set(gui.edit_handles.invjoint_beta,'String',''); -0322 set(gui.edit_handles.invjoint_gamma,'String',''); -0323 -0324 case 'ang' -0325 % geometry popup -0326 set(gui.popup_handles.invjoint_geometry_type,... -0327 'Value',2,'Enable','on'); -0328 -0329 % polygon sides and corresponding angle beta -0330 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0331 'Value',data.invjoint.polyN-2); -0332 set(gui.edit_handles.invjoint_beta,'Enable','on'); -0333 -0334 % angles -0335 set(gui.edit_handles.invjoint_alpha,... -0336 'String',num2str(data.invjoint.alpha)); -0337 set(gui.edit_handles.invjoint_beta,... -0338 'String',num2str(data.invjoint.beta)); -0339 set(gui.edit_handles.invjoint_gamma,... -0340 'String',num2str(data.invjoint.gamma)); -0341 -0342 case 'poly' -0343 % geometry popup -0344 set(gui.popup_handles.invjoint_geometry_type,... -0345 'Value',3,'Enable','on'); -0346 -0347 % polygon sides and corresponding angle beta -0348 set(gui.popup_handles.invjoint_polyN,'Enable','on',... -0349 'Value',data.invjoint.polyN-2); -0350 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0351 -0352 % angles -0353 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; -0354 set(gui.edit_handles.invjoint_alpha,... -0355 'String',''); -0356 set(gui.edit_handles.invjoint_beta,... -0357 'String',num2str(polyangle)); -0358 set(gui.edit_handles.invjoint_gamma,... -0359 'String',''); -0360 end -0361 -0362 case 'shape' -0363 % inversion method popup -0364 set(gui.popup_handles.invjoint_InvType,'Value',3,'Enable','on'); -0365 -0366 % PSD limits -0367 gui = updateInvjointRadii(gui,data.invjoint.invtype,... -0368 data.invjoint.radii,data.invjoint.Nradii); -0369 -0370 % additional inversion settings -0371 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); -0372 -0373 % regularization options -0374 switch data.invjoint.regtype -0375 case 'manual' -0376 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); -0377 case 'lcurve' -0378 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); -0379 end -0380 -0381 % smoothness constraint and lambda -0382 gui = updateLorderJoint(gui,data.invjoint.invtype,... -0383 data.invjoint.Lorder); -0384 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... -0385 data.invjoint.lambdaR,data.invjoint.NlambdaR,data.invstd.regtype); -0386 -0387 % start values -0388 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); -0389 set(gui.edit_handles.invjoint_anglestart,'Enable','on'); -0390 -0391 % geometry type dependent -0392 switch data.invjoint.geometry_type -0393 case 'cyl' -0394 % geometry popup -0395 set(gui.popup_handles.invjoint_geometry_type,... -0396 'Value',1,'Enable','on'); -0397 -0398 % polygon sides and corresponding angle beta -0399 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0400 'Value',data.invjoint.polyN-2); -0401 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0402 -0403 % angles -0404 set(gui.edit_handles.invjoint_alpha,'String',''); -0405 set(gui.edit_handles.invjoint_beta,'String',''); -0406 set(gui.edit_handles.invjoint_gamma,'String',''); -0407 -0408 case 'ang' -0409 % geometry popup -0410 set(gui.popup_handles.invjoint_geometry_type,... -0411 'Value',2,'Enable','on'); -0412 -0413 % polygon sides and corresponding angle beta -0414 set(gui.popup_handles.invjoint_polyN,'Enable','off',... -0415 'Value',data.invjoint.polyN-2); -0416 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0417 -0418 % angles -0419 set(gui.edit_handles.invjoint_alpha,... -0420 'String',num2str(data.invjoint.alpha)); -0421 set(gui.edit_handles.invjoint_beta,... -0422 'String',num2str(data.invjoint.beta)); -0423 set(gui.edit_handles.invjoint_gamma,... -0424 'String',num2str(data.invjoint.gamma)); -0425 -0426 case 'poly' -0427 % geometry popup -0428 set(gui.popup_handles.invjoint_geometry_type,... -0429 'Value',3,'Enable','on'); -0430 -0431 % polygon sides and corresponding angle beta -0432 set(gui.popup_handles.invjoint_polyN,'Enable','on',... -0433 'Value',data.invjoint.polyN-2); -0434 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0435 -0436 % angles -0437 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; -0438 set(gui.edit_handles.invjoint_alpha,... -0439 'String',''); -0440 set(gui.edit_handles.invjoint_beta,... -0441 'String',num2str(polyangle)); -0442 set(gui.edit_handles.invjoint_gamma,... -0443 'String',''); -0444 end -0445 end -0446 -0447 case 'off' -0448 % inversion method popup -0449 set(gui.popup_handles.invjoint_InvType,'Value',1,'Enable','off'); -0450 -0451 % PSD limits -0452 gui = updateInvjointRadii(gui,'off',0,0); -0453 -0454 % additional inversion settings -0455 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); -0456 -0457 % smoothness constraint and lambda -0458 gui = updateLorderJoint(gui,'off',0); -0459 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... -0460 data.invjoint.lambdaR,data.invjoint.NlambdaR,data.invstd.regtype); -0461 -0462 % polygon sides and angles -0463 set(gui.popup_handles.invjoint_polyN,'Enable','off'); -0464 set(gui.edit_handles.invjoint_alpha,'Enable','off'); -0465 set(gui.edit_handles.invjoint_beta,'Enable','off'); -0466 set(gui.edit_handles.invjoint_gamma,'Enable','off'); -0467 -0468 % start values -0469 set(gui.edit_handles.invjoint_rhostart,'Enable','off'); -0470 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); -0471 end -0472 -0473 % this is always updated -0474 switch data.pressure.unit -0475 case 'Pa' -0476 set(gui.popup_handles.invjoint_pressure_units,'Value',1); -0477 case 'kPa' -0478 set(gui.popup_handles.invjoint_pressure_units,'Value',2); -0479 case 'MPa' -0480 set(gui.popup_handles.invjoint_pressure_units,'Value',3); -0481 case 'bar' -0482 set(gui.popup_handles.invjoint_pressure_units,'Value',4); -0483 end -0484 set(gui.table_handles.invjoint_table,... -0485 'ColumnName',{'use',['p [',data.pressure.unit,']'],'S [-]','D / I'}) -0486 -0487 table = data.pressure.table; -0488 for i = 1:size(table,1) -0489 table{i,2} = table{i,2}.*data.pressure.unitfac; -0490 end -0491 set(gui.table_handles.invjoint_table,'Data',table); -0492 -0493 case 'off' -0494 %% update the processing panel -0495 set(gui.edit_handles.process_start,'Enable','on',... -0496 'String',num2str(data.process.start)); -0497 set(gui.edit_handles.process_end,'Enable','on',... -0498 'String',num2str(data.process.end)); -0499 set(gui.edit_handles.process_Nechoes,'Enable','on',... -0500 'String',num2str(data.process.Nechoes)); -0501 -0502 gui = updateGatetype(gui,data.process.gatetype); -0503 set(gui.radio_handles.process_normalize_on,'Enable','off','Value',0); -0504 set(gui.radio_handles.process_normalize_off,'Enable','off','Value',1); -0505 set(gui.radio_handles.process_timescale_s,'Enable','off','Value',1); -0506 set(gui.radio_handles.process_timescale_ms,'Enable','off','Value',0); -0507 -0508 if data.invstd.porosity <= 1 -0509 set(gui.edit_handles.invstd_porosity,'Enable','on',... -0510 'BackgroundColor','w',... -0511 'String',num2str(data.invstd.porosity)); -0512 else -0513 set(gui.edit_handles.invstd_porosity,'Enable','on',... -0514 'BackgroundColor','r',... -0515 'String',num2str(data.invstd.porosity)); -0516 end -0517 -0518 %% update standard inversion panel -0519 % inversion method popup -0520 istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)'}; -0521 set(gui.popup_handles.invstd_InvType,'String',istring); -0522 switch data.invstd.invtype -0523 case 'mono' -0524 % inversion method popup -0525 set(gui.popup_handles.invstd_InvType,'Value',1,'Enable','on'); -0526 -0527 % additional inversion settings -0528 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','off',... -0529 'Value',1,'String','none'); -0530 set(gui.text_handles.invstd_InvTypeOpt,... -0531 'String','No extra options'); -0532 -0533 % lambda, smoothness constraint and RTD limits -0534 gui = updateLambda(gui,data.invstd.regtype,0,0,0); -0535 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); -0536 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); -0537 -0538 % Tbulk -0539 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... -0540 'String',num2str(data.invstd.Tbulk)); -0541 -0542 case 'free' -0543 % inversion method popup -0544 set(gui.popup_handles.invstd_InvType,'Value',2,'Enable','on'); -0545 -0546 % additional inversion settings -0547 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... -0548 'Value',data.invstd.freeDT,... -0549 'String',{'1','2','3','4','5'}); -0550 set(gui.text_handles.invstd_InvTypeOpt,... -0551 'String','No. of free decay times T'); -0552 -0553 % lambda, smoothness constraint and RTD limits -0554 gui = updateLambda(gui,data.invstd.regtype,0,0,0); -0555 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); -0556 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); -0557 -0558 % Tbulk -0559 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... -0560 'String',num2str(data.invstd.Tbulk)); -0561 -0562 case 'NNLS' -0563 % inversion method popup -0564 set(gui.popup_handles.invstd_InvType,'Value',3,'Enable','on'); -0565 -0566 % additional inversion settings -0567 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... -0568 'String',{'Manual','L-curve'}); -0569 set(gui.text_handles.invstd_InvTypeOpt,... -0570 'String','regularization options'); -0571 -0572 % regularization options -0573 switch data.invstd.regtype -0574 case 'manual' -0575 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); -0576 case 'lcurve' -0577 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); -0578 end +0162 % additional inversion settings +0163 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0164 'String',{'Manual','Automatic'}); +0165 set(gui.text_handles.invstd_InvTypeOpt,... +0166 'String','regularization options'); +0167 +0168 % regularization options +0169 switch data.invstd.regtype +0170 case 'manual' +0171 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); +0172 case 'auto' +0173 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); +0174 data.invstd.lambda = -1; +0175 end +0176 +0177 % lambda, smoothness constraint and RTD limits +0178 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... +0179 data.invstd.lambdaR,data.invstd.NlambdaR); +0180 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0181 gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... +0182 data.invstd.Ntime); +0183 +0184 % Tbulk & Tdiff +0185 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... +0186 'String',num2str(data.invstd.Tbulk)); +0187 set(gui.edit_handles.invstd_Tdiff,'Enable','on',... +0188 'String',num2str(data.invstd.Tdiff)); +0189 +0190 case 'MUMO' +0191 % inversion method popup +0192 set(gui.popup_handles.invstd_InvType,'Value',5,'Enable','on'); +0193 +0194 % additional inversion settings +0195 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0196 'Value',data.invstd.freeDT,... +0197 'String',{'1','2','3','4','5'}); +0198 set(gui.text_handles.invstd_InvTypeOpt,... +0199 'String','No. of modes'); +0200 +0201 % % lambda, smoothness constraint and RTD limits +0202 % gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... +0203 % data.invstd.lambdaR,data.invstd.NlambdaR); +0204 % gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0205 gui = updateInvstdTime(gui,data.invstd.invtype,data.invstd.time,... +0206 data.invstd.Ntime); +0207 +0208 % lambda, smoothness constraint and RTD limits +0209 gui = updateLambda(gui,data.invstd.regtype,0,0,0); +0210 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0211 +0212 % Tbulk & Tdiff +0213 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... +0214 'String',num2str(data.invstd.Tbulk)); +0215 set(gui.edit_handles.invstd_Tdiff,'Enable','on',... +0216 'String',num2str(data.invstd.Tdiff)); +0217 end +0218 +0219 % updates CBW, BVI, rho and a +0220 gui = updateParams(gui,data.invstd.invtype,data.param); +0221 +0222 %% update joint inversion panel +0223 % depending if it is activated or not +0224 switch data.info.JointInv +0225 case 'on' +0226 % inversion method dependent +0227 switch data.invjoint.invtype +0228 case 'free' +0229 % inversion method popup +0230 set(gui.popup_handles.invjoint_InvType,'Value',1,'Enable','on'); +0231 +0232 % PSD limits +0233 gui = updateInvjointRadii(gui,data.invjoint.invtype,... +0234 data.invjoint.radii,data.invjoint.Nradii); +0235 +0236 % additional inversion settings +0237 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','on'); +0238 +0239 % regularization options +0240 switch data.invjoint.regtype +0241 case 'manual' +0242 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); +0243 case 'lcurve' +0244 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); +0245 end +0246 +0247 % smoothness constraint and lambda +0248 gui = updateLorderJoint(gui,data.invjoint.invtype,... +0249 data.invjoint.Lorder); +0250 gui = updateLambdaJoint(gui,data.invjoint.regtype,... +0251 data.invjoint.lambda,data.invjoint.lambdaR,... +0252 data.invjoint.NlambdaR,data.invstd.regtype); +0253 +0254 % start values +0255 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); +0256 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); +0257 +0258 % geometry type dependent +0259 switch data.invjoint.geometry_type +0260 case 'cyl' +0261 % geometry popup +0262 set(gui.popup_handles.invjoint_geometry_type,... +0263 'Value',1,'Enable','on'); +0264 +0265 % polygon sides and corresponding angle beta +0266 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0267 'Value',data.invjoint.polyN-2); +0268 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0269 +0270 % angles +0271 set(gui.edit_handles.invjoint_alpha,'String',''); +0272 set(gui.edit_handles.invjoint_beta,'String',''); +0273 set(gui.edit_handles.invjoint_gamma,'String',''); +0274 +0275 case 'ang' +0276 % geometry popup +0277 set(gui.popup_handles.invjoint_geometry_type,... +0278 'Value',2,'Enable','on'); +0279 +0280 % polygon sides and corresponding angle beta +0281 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0282 'Value',data.invjoint.polyN-2); +0283 set(gui.edit_handles.invjoint_beta,'Enable','on'); +0284 +0285 % angles +0286 set(gui.edit_handles.invjoint_alpha,... +0287 'String',num2str(data.invjoint.alpha)); +0288 set(gui.edit_handles.invjoint_beta,... +0289 'String',num2str(data.invjoint.beta)); +0290 set(gui.edit_handles.invjoint_gamma,... +0291 'String',num2str(data.invjoint.gamma)); +0292 +0293 case 'poly' +0294 % geometry popup +0295 set(gui.popup_handles.invjoint_geometry_type,... +0296 'Value',3,'Enable','on'); +0297 +0298 % polygon sides and corresponding angle beta +0299 set(gui.popup_handles.invjoint_polyN,'Enable','on',... +0300 'Value',data.invjoint.polyN-2); +0301 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0302 +0303 % angles +0304 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; +0305 set(gui.edit_handles.invjoint_alpha,... +0306 'String',''); +0307 set(gui.edit_handles.invjoint_beta,... +0308 'String',num2str(polyangle)); +0309 set(gui.edit_handles.invjoint_gamma,... +0310 'String',''); +0311 end +0312 +0313 case 'fixed' +0314 % inversion method popup +0315 set(gui.popup_handles.invjoint_InvType,'Value',2,'Enable','on'); +0316 +0317 % PSD limits +0318 gui = updateInvjointRadii(gui,data.invjoint.invtype,... +0319 data.invjoint.radii,data.invjoint.Nradii); +0320 +0321 % additional inversion settings +0322 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); +0323 +0324 % regularization options +0325 switch data.invjoint.regtype +0326 case 'manual' +0327 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); +0328 case 'lcurve' +0329 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); +0330 end +0331 +0332 % smoothness constraint and lambda +0333 gui = updateLorderJoint(gui,data.invjoint.invtype,... +0334 data.invjoint.Lorder); +0335 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... +0336 data.invjoint.lambdaR,data.invjoint.NlambdaR,... +0337 data.invstd.regtype); +0338 +0339 % start values +0340 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); +0341 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); +0342 +0343 % geometry type dependent +0344 switch data.invjoint.geometry_type +0345 case 'cyl' +0346 % geometry popup +0347 set(gui.popup_handles.invjoint_geometry_type,... +0348 'Value',1,'Enable','on'); +0349 +0350 % polygon sides and corresponding angle beta +0351 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0352 'Value',data.invjoint.polyN-2); +0353 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0354 +0355 % angles +0356 set(gui.edit_handles.invjoint_alpha,'String',''); +0357 set(gui.edit_handles.invjoint_beta,'String',''); +0358 set(gui.edit_handles.invjoint_gamma,'String',''); +0359 +0360 case 'ang' +0361 % geometry popup +0362 set(gui.popup_handles.invjoint_geometry_type,... +0363 'Value',2,'Enable','on'); +0364 +0365 % polygon sides and corresponding angle beta +0366 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0367 'Value',data.invjoint.polyN-2); +0368 set(gui.edit_handles.invjoint_beta,'Enable','on'); +0369 +0370 % angles +0371 set(gui.edit_handles.invjoint_alpha,... +0372 'String',num2str(data.invjoint.alpha)); +0373 set(gui.edit_handles.invjoint_beta,... +0374 'String',num2str(data.invjoint.beta)); +0375 set(gui.edit_handles.invjoint_gamma,... +0376 'String',num2str(data.invjoint.gamma)); +0377 +0378 case 'poly' +0379 % geometry popup +0380 set(gui.popup_handles.invjoint_geometry_type,... +0381 'Value',3,'Enable','on'); +0382 +0383 % polygon sides and corresponding angle beta +0384 set(gui.popup_handles.invjoint_polyN,'Enable','on',... +0385 'Value',data.invjoint.polyN-2); +0386 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0387 +0388 % angles +0389 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; +0390 set(gui.edit_handles.invjoint_alpha,... +0391 'String',''); +0392 set(gui.edit_handles.invjoint_beta,... +0393 'String',num2str(polyangle)); +0394 set(gui.edit_handles.invjoint_gamma,... +0395 'String',''); +0396 end +0397 +0398 case 'shape' +0399 % inversion method popup +0400 set(gui.popup_handles.invjoint_InvType,'Value',3,'Enable','on'); +0401 +0402 % PSD limits +0403 gui = updateInvjointRadii(gui,data.invjoint.invtype,... +0404 data.invjoint.radii,data.invjoint.Nradii); +0405 +0406 % additional inversion settings +0407 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); +0408 +0409 % regularization options +0410 switch data.invjoint.regtype +0411 case 'manual' +0412 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',1); +0413 case 'lcurve' +0414 set(gui.popup_handles.invjoint_InvTypeOpt,'Value',2); +0415 end +0416 +0417 % smoothness constraint and lambda +0418 gui = updateLorderJoint(gui,data.invjoint.invtype,... +0419 data.invjoint.Lorder); +0420 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... +0421 data.invjoint.lambdaR,data.invjoint.NlambdaR,data.invstd.regtype); +0422 +0423 % start values +0424 set(gui.edit_handles.invjoint_rhostart,'Enable','on'); +0425 set(gui.edit_handles.invjoint_anglestart,'Enable','on'); +0426 +0427 % geometry type dependent +0428 switch data.invjoint.geometry_type +0429 case 'cyl' +0430 % geometry popup +0431 set(gui.popup_handles.invjoint_geometry_type,... +0432 'Value',1,'Enable','on'); +0433 +0434 % polygon sides and corresponding angle beta +0435 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0436 'Value',data.invjoint.polyN-2); +0437 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0438 +0439 % angles +0440 set(gui.edit_handles.invjoint_alpha,'String',''); +0441 set(gui.edit_handles.invjoint_beta,'String',''); +0442 set(gui.edit_handles.invjoint_gamma,'String',''); +0443 +0444 case 'ang' +0445 % geometry popup +0446 set(gui.popup_handles.invjoint_geometry_type,... +0447 'Value',2,'Enable','on'); +0448 +0449 % polygon sides and corresponding angle beta +0450 set(gui.popup_handles.invjoint_polyN,'Enable','off',... +0451 'Value',data.invjoint.polyN-2); +0452 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0453 +0454 % angles +0455 set(gui.edit_handles.invjoint_alpha,... +0456 'String',num2str(data.invjoint.alpha)); +0457 set(gui.edit_handles.invjoint_beta,... +0458 'String',num2str(data.invjoint.beta)); +0459 set(gui.edit_handles.invjoint_gamma,... +0460 'String',num2str(data.invjoint.gamma)); +0461 +0462 case 'poly' +0463 % geometry popup +0464 set(gui.popup_handles.invjoint_geometry_type,... +0465 'Value',3,'Enable','on'); +0466 +0467 % polygon sides and corresponding angle beta +0468 set(gui.popup_handles.invjoint_polyN,'Enable','on',... +0469 'Value',data.invjoint.polyN-2); +0470 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0471 +0472 % angles +0473 polyangle = ((data.invjoint.polyN-2)/data.invjoint.polyN)*180; +0474 set(gui.edit_handles.invjoint_alpha,... +0475 'String',''); +0476 set(gui.edit_handles.invjoint_beta,... +0477 'String',num2str(polyangle)); +0478 set(gui.edit_handles.invjoint_gamma,... +0479 'String',''); +0480 end +0481 end +0482 +0483 case 'off' +0484 % inversion method popup +0485 set(gui.popup_handles.invjoint_InvType,'Value',1,'Enable','off'); +0486 +0487 % PSD limits +0488 gui = updateInvjointRadii(gui,'off',0,0); +0489 +0490 % additional inversion settings +0491 set(gui.popup_handles.invjoint_InvTypeOpt,'Enable','off'); +0492 +0493 % smoothness constraint and lambda +0494 gui = updateLorderJoint(gui,'off',0); +0495 gui = updateLambdaJoint(gui,'none',data.invjoint.lambda,... +0496 data.invjoint.lambdaR,data.invjoint.NlambdaR,data.invstd.regtype); +0497 +0498 % polygon sides and angles +0499 set(gui.popup_handles.invjoint_polyN,'Enable','off'); +0500 set(gui.edit_handles.invjoint_alpha,'Enable','off'); +0501 set(gui.edit_handles.invjoint_beta,'Enable','off'); +0502 set(gui.edit_handles.invjoint_gamma,'Enable','off'); +0503 +0504 % start values +0505 set(gui.edit_handles.invjoint_rhostart,'Enable','off'); +0506 set(gui.edit_handles.invjoint_anglestart,'Enable','off'); +0507 end +0508 +0509 % this is always updated +0510 switch data.pressure.unit +0511 case 'Pa' +0512 set(gui.popup_handles.invjoint_pressure_units,'Value',1); +0513 case 'kPa' +0514 set(gui.popup_handles.invjoint_pressure_units,'Value',2); +0515 case 'MPa' +0516 set(gui.popup_handles.invjoint_pressure_units,'Value',3); +0517 case 'bar' +0518 set(gui.popup_handles.invjoint_pressure_units,'Value',4); +0519 end +0520 set(gui.table_handles.invjoint_table,... +0521 'ColumnName',{'use',['p [',data.pressure.unit,']'],'S [-]','D / I'}) +0522 +0523 table = data.pressure.table; +0524 for i = 1:size(table,1) +0525 table{i,2} = table{i,2}.*data.pressure.unitfac; +0526 end +0527 set(gui.table_handles.invjoint_table,'Data',table); +0528 +0529 case 'off' +0530 %% update the processing panel +0531 set(gui.edit_handles.process_start,'Enable','on',... +0532 'String',num2str(data.process.start)); +0533 set(gui.edit_handles.process_end,'Enable','on',... +0534 'String',num2str(data.process.end)); +0535 set(gui.edit_handles.process_Nechoes,'Enable','on',... +0536 'String',num2str(data.process.Nechoes)); +0537 +0538 gui = updateGatetype(gui,data.process.gatetype); +0539 set(gui.radio_handles.process_normalize_on,'Enable','off','Value',0); +0540 set(gui.radio_handles.process_normalize_off,'Enable','off','Value',1); +0541 set(gui.radio_handles.process_timescale_s,'Enable','off','Value',1); +0542 set(gui.radio_handles.process_timescale_ms,'Enable','off','Value',0); +0543 +0544 if data.invstd.porosity <= 1 +0545 set(gui.edit_handles.invstd_porosity,'Enable','on',... +0546 'BackgroundColor','w',... +0547 'String',num2str(data.invstd.porosity)); +0548 else +0549 set(gui.edit_handles.invstd_porosity,'Enable','on',... +0550 'BackgroundColor','r',... +0551 'String',num2str(data.invstd.porosity)); +0552 end +0553 +0554 %% update standard inversion panel +0555 % inversion method popup +0556 istring = {'Mono exp.','Several free exp. (2-5)','Multi exp. (LSQ)'}; +0557 set(gui.popup_handles.invstd_InvType,'String',istring); +0558 switch data.invstd.invtype +0559 case 'mono' +0560 % inversion method popup +0561 set(gui.popup_handles.invstd_InvType,'Value',1,'Enable','on'); +0562 +0563 % additional inversion settings +0564 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','off',... +0565 'Value',1,'String','none'); +0566 set(gui.text_handles.invstd_InvTypeOpt,... +0567 'String','No extra options'); +0568 +0569 % lambda, smoothness constraint and RTD limits +0570 gui = updateLambda(gui,data.invstd.regtype,0,0,0); +0571 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0572 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); +0573 +0574 % Tbulk & Tdiff +0575 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... +0576 'String',num2str(data.invstd.Tbulk)); +0577 set(gui.edit_handles.invstd_Tdiff,'Enable','off',... +0578 'String',num2str(data.invstd.Tdiff)); 0579 -0580 % lambda, smoothness constraint and RTD limits -0581 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... -0582 data.invstd.lambdaR,data.invstd.NlambdaR); -0583 data.invstd.Lorder = 1; -0584 set(gui.radio_handles.invstd_Lorder0,'Value',0,'Enable','off'); -0585 set(gui.radio_handles.invstd_Lorder1,'Value',1,'Enable','off'); -0586 set(gui.radio_handles.invstd_Lorder2,'Value',0,'Enable','off'); -0587 -0588 set(gui.edit_handles.invstd_time_min,'Enable','on',... -0589 'String',num2str(data.invstd.time(1))); -0590 set(gui.edit_handles.invstd_time_max,'Enable','on',... -0591 'String',num2str(data.invstd.time(2))); -0592 set(gui.edit_handles.invstd_Ntime,'Enable','off',... -0593 'String',num2str(data.invstd.Ntime)); -0594 -0595 % Tbulk -0596 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... -0597 'String',num2str(data.invstd.Tbulk)); -0598 end -0599 -0600 %% update petro parameter panel -0601 data.param.CBWcutoff = 3; -0602 data.param.BVIcutoff = 33; -0603 data.param.rho = 10; -0604 data.param.a = 2; -0605 setappdata(fig,'data',data); -0606 set(gui.edit_handles.param_CBW,'Enable','off',... -0607 'String',num2str(data.param.CBWcutoff)); -0608 set(gui.edit_handles.param_BVI,'Enable','off',... -0609 'String',num2str(data.param.BVIcutoff)); -0610 set(gui.edit_handles.param_rho,'Enable','off',... -0611 'String',num2str(data.param.rho)); -0612 set(gui.edit_handles.param_geom,'Enable','off',... -0613 'String',num2str(data.param.a)); -0614 set(gui.edit_handles.param_calibVol,'Enable','on',... -0615 'String',num2str(data.param.calibVol)); -0616 set(gui.edit_handles.param_calibAmp,'Enable','on',... -0617 'String',num2str(data.param.calibAmp)); -0618 set(gui.edit_handles.param_sampVol,'Enable','on',... -0619 'String',num2str(data.param.sampVol)); -0620 end -0621 -0622 % Matlab needs some time -0623 pause(0.001); -0624 -0625 end -0626 -0627 %% sub functions -0628 function gui = updateGatetype(gui,gatetype) -0629 -0630 set(gui.radio_handles.process_gates_log,'Enable','on'); -0631 set(gui.radio_handles.process_gates_lin,'Enable','on'); -0632 set(gui.radio_handles.process_gates_none,'Enable','on'); -0633 -0634 switch gatetype -0635 -0636 case 'log' -0637 -0638 set(gui.radio_handles.process_gates_log,'Value',1); -0639 set(gui.radio_handles.process_gates_lin,'Value',0); -0640 set(gui.radio_handles.process_gates_none,'Value',0); -0641 set(gui.edit_handles.process_Nechoes,'Enable','on'); -0642 -0643 case 'lin' -0644 -0645 set(gui.radio_handles.process_gates_log,'Value',0); -0646 set(gui.radio_handles.process_gates_lin,'Value',1); -0647 set(gui.radio_handles.process_gates_none,'Value',0); -0648 set(gui.edit_handles.process_Nechoes,'Enable','on'); -0649 -0650 case 'raw' -0651 -0652 set(gui.radio_handles.process_gates_log,'Value',0); -0653 set(gui.radio_handles.process_gates_lin,'Value',0); -0654 set(gui.radio_handles.process_gates_none,'Value',1); -0655 set(gui.edit_handles.process_Nechoes,'Enable','off'); -0656 -0657 end -0658 -0659 end -0660 -0661 function gui = updateNormalize(gui,norm) -0662 -0663 switch norm -0664 case 0 -0665 set(gui.radio_handles.process_normalize_on,'Enable','on','Value',0); -0666 set(gui.radio_handles.process_normalize_off,'Enable','on','Value',1); -0667 -0668 case 1 -0669 set(gui.radio_handles.process_normalize_on,'Enable','on','Value',1); -0670 set(gui.radio_handles.process_normalize_off,'Enable','on','Value',0); -0671 end -0672 -0673 end -0674 -0675 function gui = updateTimescale(gui,timescale) -0676 -0677 switch timescale -0678 -0679 case 's' -0680 set(gui.radio_handles.process_timescale_s,'Enable','on','Value',1); -0681 set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',0); -0682 set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [s] | max [s] | # / dec',... -0683 'FontSize',10); -0684 set(gui.text_handles.petro_Tbulk,'String',... -0685 ['Tbulk [s] | ',char(hex2dec('03C1')),' [µm/s] | geom']); +0580 case 'free' +0581 % inversion method popup +0582 set(gui.popup_handles.invstd_InvType,'Value',2,'Enable','on'); +0583 +0584 % additional inversion settings +0585 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0586 'Value',data.invstd.freeDT,... +0587 'String',{'1','2','3','4','5'}); +0588 set(gui.text_handles.invstd_InvTypeOpt,... +0589 'String','No. of free decay times T'); +0590 +0591 % lambda, smoothness constraint and RTD limits +0592 gui = updateLambda(gui,data.invstd.regtype,0,0,0); +0593 gui = updateLorder(gui,data.invstd.invtype,data.invstd.Lorder); +0594 gui = updateInvstdTime(gui,data.invstd.invtype,0,0); +0595 +0596 % Tbulk & Tdiff +0597 set(gui.edit_handles.invstd_Tbulk,'Enable','off',... +0598 'String',num2str(data.invstd.Tbulk)); +0599 set(gui.edit_handles.invstd_Tdiff,'Enable','off',... +0600 'String',num2str(data.invstd.Tdiff)); +0601 +0602 case 'NNLS' +0603 % inversion method popup +0604 set(gui.popup_handles.invstd_InvType,'Value',3,'Enable','on'); +0605 +0606 % additional inversion settings +0607 set(gui.popup_handles.invstd_InvTypeOpt,'Enable','on',... +0608 'String',{'Manual','L-curve'}); +0609 set(gui.text_handles.invstd_InvTypeOpt,... +0610 'String','regularization options'); +0611 +0612 % regularization options +0613 switch data.invstd.regtype +0614 case 'manual' +0615 set(gui.popup_handles.invstd_InvTypeOpt,'Value',1); +0616 case 'lcurve' +0617 set(gui.popup_handles.invstd_InvTypeOpt,'Value',2); +0618 end +0619 +0620 % lambda, smoothness constraint and RTD limits +0621 gui = updateLambda(gui,data.invstd.regtype,data.invstd.lambda,... +0622 data.invstd.lambdaR,data.invstd.NlambdaR); +0623 data.invstd.Lorder = 1; +0624 set(gui.radio_handles.invstd_Lorder0,'Value',0,'Enable','off'); +0625 set(gui.radio_handles.invstd_Lorder1,'Value',1,'Enable','off'); +0626 set(gui.radio_handles.invstd_Lorder2,'Value',0,'Enable','off'); +0627 +0628 set(gui.edit_handles.invstd_time_min,'Enable','on',... +0629 'String',num2str(data.invstd.time(1))); +0630 set(gui.edit_handles.invstd_time_max,'Enable','on',... +0631 'String',num2str(data.invstd.time(2))); +0632 set(gui.edit_handles.invstd_Ntime,'Enable','off',... +0633 'String',num2str(data.invstd.Ntime)); +0634 +0635 % Tbulk & Tdiff +0636 set(gui.edit_handles.invstd_Tbulk,'Enable','on',... +0637 'String',num2str(data.invstd.Tbulk)); +0638 set(gui.edit_handles.invstd_Tdiff,'Enable','on',... +0639 'String',num2str(data.invstd.Tdiff)); +0640 end +0641 +0642 %% update petro parameter panel +0643 data.param.CBWcutoff = 3; +0644 data.param.BVIcutoff = 33; +0645 data.param.rho = 10; +0646 data.param.a = 2; +0647 setappdata(fig,'data',data); +0648 set(gui.edit_handles.param_CBW,'Enable','off',... +0649 'String',num2str(data.param.CBWcutoff)); +0650 set(gui.edit_handles.param_BVI,'Enable','off',... +0651 'String',num2str(data.param.BVIcutoff)); +0652 set(gui.edit_handles.param_rho,'Enable','off',... +0653 'String',num2str(data.param.rho)); +0654 set(gui.edit_handles.param_geom,'Enable','off',... +0655 'String',num2str(data.param.a)); +0656 set(gui.edit_handles.param_calibVol,'Enable','on',... +0657 'String',num2str(data.param.calibVol)); +0658 set(gui.edit_handles.param_calibAmp,'Enable','on',... +0659 'String',num2str(data.param.calibAmp)); +0660 set(gui.edit_handles.param_sampVol,'Enable','on',... +0661 'String',num2str(data.param.sampVol)); +0662 end +0663 +0664 % Matlab needs some time +0665 pause(0.001); +0666 +0667 end +0668 +0669 %% sub functions +0670 function gui = updateGatetype(gui,gatetype) +0671 +0672 set(gui.radio_handles.process_gates_log,'Enable','on'); +0673 set(gui.radio_handles.process_gates_lin,'Enable','on'); +0674 set(gui.radio_handles.process_gates_none,'Enable','on'); +0675 +0676 switch gatetype +0677 +0678 case 'log' +0679 +0680 set(gui.radio_handles.process_gates_log,'Value',1); +0681 set(gui.radio_handles.process_gates_lin,'Value',0); +0682 set(gui.radio_handles.process_gates_none,'Value',0); +0683 set(gui.edit_handles.process_Nechoes,'Enable','on'); +0684 +0685 case 'lin' 0686 -0687 case 'ms' -0688 set(gui.radio_handles.process_timescale_s,'Enable','on','Value',0); -0689 set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',1); -0690 set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [ms] | max [ms] | # / dec',... -0691 'FontSize',9); -0692 set(gui.text_handles.petro_Tbulk,'String',... -0693 ['Tbulk [ms] | ',char(hex2dec('03C1')),' [µm/s] | geom']); -0694 end -0695 -0696 end -0697 -0698 function gui = updateInvstdTime(gui,invtype,time,Ntime) -0699 -0700 switch invtype -0701 -0702 case {'mono','free'} -0703 set(gui.edit_handles.invstd_time_min,'Enable','off'); -0704 set(gui.edit_handles.invstd_time_max,'Enable','off'); -0705 set(gui.edit_handles.invstd_Ntime,'Enable','off'); -0706 -0707 case {'LU','NNLS'} -0708 set(gui.edit_handles.invstd_time_min,'Enable','on','String',num2str(time(1))); -0709 set(gui.edit_handles.invstd_time_max,'Enable','on','String',num2str(time(2))); -0710 set(gui.edit_handles.invstd_Ntime,'Enable','on','String',num2str(Ntime)); -0711 end -0712 +0687 set(gui.radio_handles.process_gates_log,'Value',0); +0688 set(gui.radio_handles.process_gates_lin,'Value',1); +0689 set(gui.radio_handles.process_gates_none,'Value',0); +0690 set(gui.edit_handles.process_Nechoes,'Enable','on'); +0691 +0692 case 'raw' +0693 +0694 set(gui.radio_handles.process_gates_log,'Value',0); +0695 set(gui.radio_handles.process_gates_lin,'Value',0); +0696 set(gui.radio_handles.process_gates_none,'Value',1); +0697 set(gui.edit_handles.process_Nechoes,'Enable','off'); +0698 +0699 end +0700 +0701 end +0702 +0703 function gui = updateNormalize(gui,norm) +0704 +0705 switch norm +0706 case 0 +0707 set(gui.radio_handles.process_normalize_on,'Enable','on','Value',0); +0708 set(gui.radio_handles.process_normalize_off,'Enable','on','Value',1); +0709 +0710 case 1 +0711 set(gui.radio_handles.process_normalize_on,'Enable','on','Value',1); +0712 set(gui.radio_handles.process_normalize_off,'Enable','on','Value',0); 0713 end 0714 -0715 function gui = updateInvjointRadii(gui,invtype,radii,Nradii) +0715 end 0716 -0717 switch invtype -0718 case {'fixed','shape','off'} -0719 set(gui.edit_handles.invjoint_radii_min,'Enable','off'); -0720 set(gui.edit_handles.invjoint_radii_max,'Enable','off'); -0721 set(gui.edit_handles.invjoint_Nradii,'Enable','off'); -0722 -0723 case {'free'} -0724 set(gui.edit_handles.invjoint_radii_min,'Enable','on','String',num2str(radii(1))); -0725 set(gui.edit_handles.invjoint_radii_max,'Enable','on','String',num2str(radii(2))); -0726 set(gui.edit_handles.invjoint_Nradii,'Enable','on','String',num2str(Nradii)); -0727 end -0728 -0729 end -0730 -0731 function gui = updateLambda(gui,regtype,lambda,lambdaR,NlambdaR) -0732 -0733 switch regtype -0734 case 'none' -0735 set(gui.edit_handles.invstd_lambda_min,'Enable','off'); -0736 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); -0737 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); -0738 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0739 -0740 case 'manual' -0741 set(gui.edit_handles.invstd_lambda_min,'Enable','on','String',num2str(lambda)); -0742 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); -0743 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); -0744 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0745 -0746 case 'auto' -0747 set(gui.edit_handles.invstd_lambda_min,'Enable','off','String',num2str(lambda)); -0748 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); -0749 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); -0750 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0751 -0752 case 'lcurve' -0753 set(gui.edit_handles.invstd_lambda_min,'Enable','on','String',num2str(lambdaR(1))); -0754 set(gui.edit_handles.invstd_lambda_max,'Enable','on','String',num2str(lambdaR(2))); -0755 set(gui.edit_handles.invstd_NlambdaR,'Enable','on','String',num2str(NlambdaR)); -0756 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; -0757 -0758 case {'gcv_tikh','gcv_trunc','gcv_damp','discrep'} -0759 set(gui.edit_handles.invstd_lambda_min,'Enable','off','String',num2str(lambda)); -0760 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); -0761 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); -0762 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0763 end -0764 -0765 end -0766 -0767 function gui = updateLambdaJoint(gui,regtype,lambda,lambdaR,NlambdaR,regtypestd) -0768 -0769 switch regtype -0770 case 'none' -0771 switch regtypestd -0772 case 'lcurve' -0773 set(gui.edit_handles.invjoint_lambda_min,'Enable','off'); -0774 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); -0775 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); -0776 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; -0777 -0778 otherwise -0779 set(gui.edit_handles.invjoint_lambda_min,'Enable','off'); -0780 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); -0781 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); -0782 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0783 end -0784 -0785 case 'manual' -0786 switch regtypestd -0787 case 'lcurve' -0788 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambda)); -0789 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); -0790 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); -0791 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; -0792 -0793 otherwise -0794 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambda)); -0795 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); -0796 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); -0797 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; -0798 end +0717 function gui = updateTimescale(gui,timescale) +0718 +0719 switch timescale +0720 +0721 case 's' +0722 set(gui.radio_handles.process_timescale_s,'Enable','on','Value',1); +0723 set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',0); +0724 set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [s] | max [s] | # / dec',... +0725 'FontSize',10); +0726 set(gui.text_handles.petro_Tbulk,'String','Tbulk [s] | Tdiff [s]'); +0727 set(gui.text_handles.petro_rho,'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); +0728 +0729 case 'ms' +0730 set(gui.radio_handles.process_timescale_s,'Enable','on','Value',0); +0731 set(gui.radio_handles.process_timescale_ms,'Enable','on','Value',1); +0732 set(gui.text_handles.invstd_RTDtimes,'String','RTD - min [ms] | max [ms] | # / dec',... +0733 'FontSize',9); +0734 set(gui.text_handles.petro_Tbulk,'String','Tbulk [ms] | Tdiff [ms]'); +0735 set(gui.text_handles.petro_rho,'String',[char(hex2dec('03C1')),' [µm/s] | geom ']); +0736 end +0737 +0738 end +0739 +0740 function gui = updateInvstdTime(gui,invtype,time,Ntime) +0741 +0742 switch invtype +0743 +0744 case {'mono','free'} +0745 set(gui.edit_handles.invstd_time_min,'Enable','off'); +0746 set(gui.edit_handles.invstd_time_max,'Enable','off'); +0747 set(gui.edit_handles.invstd_Ntime,'Enable','off'); +0748 +0749 case {'LU','NNLS','MUMO'} +0750 set(gui.edit_handles.invstd_time_min,'Enable','on','String',num2str(time(1))); +0751 set(gui.edit_handles.invstd_time_max,'Enable','on','String',num2str(time(2))); +0752 set(gui.edit_handles.invstd_Ntime,'Enable','on','String',num2str(Ntime)); +0753 end +0754 +0755 end +0756 +0757 function gui = updateInvjointRadii(gui,invtype,radii,Nradii) +0758 +0759 switch invtype +0760 case {'fixed','shape','off'} +0761 set(gui.edit_handles.invjoint_radii_min,'Enable','off'); +0762 set(gui.edit_handles.invjoint_radii_max,'Enable','off'); +0763 set(gui.edit_handles.invjoint_Nradii,'Enable','off'); +0764 +0765 case {'free'} +0766 set(gui.edit_handles.invjoint_radii_min,'Enable','on','String',num2str(radii(1))); +0767 set(gui.edit_handles.invjoint_radii_max,'Enable','on','String',num2str(radii(2))); +0768 set(gui.edit_handles.invjoint_Nradii,'Enable','on','String',num2str(Nradii)); +0769 end +0770 +0771 end +0772 +0773 function gui = updateLambda(gui,regtype,lambda,lambdaR,NlambdaR) +0774 +0775 switch regtype +0776 case 'none' +0777 set(gui.edit_handles.invstd_lambda_min,'Enable','off'); +0778 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); +0779 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); +0780 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; +0781 +0782 case 'manual' +0783 set(gui.edit_handles.invstd_lambda_min,'Enable','on','String',num2str(lambda)); +0784 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); +0785 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); +0786 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; +0787 +0788 case 'auto' +0789 set(gui.edit_handles.invstd_lambda_min,'Enable','off','String',num2str(lambda)); +0790 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); +0791 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); +0792 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; +0793 +0794 case 'lcurve' +0795 set(gui.edit_handles.invstd_lambda_min,'Enable','on','String',num2str(lambdaR(1))); +0796 set(gui.edit_handles.invstd_lambda_max,'Enable','on','String',num2str(lambdaR(2))); +0797 set(gui.edit_handles.invstd_NlambdaR,'Enable','on','String',num2str(NlambdaR)); +0798 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; 0799 -0800 case 'lcurve' -0801 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambdaR(1))); -0802 set(gui.edit_handles.invjoint_lambda_max,'Enable','on','String',num2str(lambdaR(2))); -0803 set(gui.edit_handles.invjoint_NlambdaR,'Enable','on','String',num2str(NlambdaR)); -0804 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; +0800 case {'gcv_tikh','gcv_trunc','gcv_damp','discrep'} +0801 set(gui.edit_handles.invstd_lambda_min,'Enable','off','String',num2str(lambda)); +0802 set(gui.edit_handles.invstd_lambda_max,'Enable','off'); +0803 set(gui.edit_handles.invstd_NlambdaR,'Enable','off'); +0804 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; 0805 end 0806 0807 end 0808 -0809 function gui = updateLorder(gui,invtype,Lorder) +0809 function gui = updateLambdaJoint(gui,regtype,lambda,lambdaR,NlambdaR,regtypestd) 0810 -0811 switch invtype -0812 case {'mono','free'} -0813 set(gui.radio_handles.invstd_Lorder0,'Enable','off'); -0814 set(gui.radio_handles.invstd_Lorder1,'Enable','off'); -0815 set(gui.radio_handles.invstd_Lorder2,'Enable','off'); -0816 -0817 case {'LU','NNLS'} -0818 set(gui.radio_handles.invstd_Lorder0,'Enable','on'); -0819 set(gui.radio_handles.invstd_Lorder1,'Enable','on'); -0820 set(gui.radio_handles.invstd_Lorder2,'Enable','on'); -0821 -0822 switch Lorder -0823 case 0 -0824 set(gui.radio_handles.invstd_Lorder0,'Value',1); -0825 set(gui.radio_handles.invstd_Lorder1,'Value',0); -0826 set(gui.radio_handles.invstd_Lorder2,'Value',0); -0827 -0828 case 1 -0829 set(gui.radio_handles.invstd_Lorder0,'Value',0); -0830 set(gui.radio_handles.invstd_Lorder1,'Value',1); -0831 set(gui.radio_handles.invstd_Lorder2,'Value',0); -0832 -0833 case 2 -0834 set(gui.radio_handles.invstd_Lorder0,'Value',0); -0835 set(gui.radio_handles.invstd_Lorder1,'Value',0); -0836 set(gui.radio_handles.invstd_Lorder2,'Value',1); -0837 end -0838 end -0839 -0840 end -0841 -0842 function gui = updateLorderJoint(gui,invtype,Lorder) -0843 -0844 switch invtype -0845 case {'fixed','shape','off'} -0846 set(gui.radio_handles.invjoint_Lorder0,'Enable','off'); -0847 set(gui.radio_handles.invjoint_Lorder1,'Enable','off'); -0848 set(gui.radio_handles.invjoint_Lorder2,'Enable','off'); -0849 -0850 case 'free' -0851 set(gui.radio_handles.invjoint_Lorder0,'Enable','on'); -0852 set(gui.radio_handles.invjoint_Lorder1,'Enable','on'); -0853 set(gui.radio_handles.invjoint_Lorder2,'Enable','on'); -0854 -0855 switch Lorder -0856 case 0 -0857 set(gui.radio_handles.invjoint_Lorder0,'Value',1); -0858 set(gui.radio_handles.invjoint_Lorder1,'Value',0); -0859 set(gui.radio_handles.invjoint_Lorder2,'Value',0); -0860 -0861 case 1 -0862 set(gui.radio_handles.invjoint_Lorder0,'Value',0); -0863 set(gui.radio_handles.invjoint_Lorder1,'Value',1); -0864 set(gui.radio_handles.invjoint_Lorder2,'Value',0); -0865 -0866 case 2 -0867 set(gui.radio_handles.invjoint_Lorder0,'Value',0); -0868 set(gui.radio_handles.invjoint_Lorder1,'Value',0); -0869 set(gui.radio_handles.invjoint_Lorder2,'Value',1); -0870 end -0871 end -0872 -0873 end -0874 -0875 function gui = updateParams(gui,invtype,p) -0876 -0877 switch invtype -0878 case {'mono','free'} -0879 set(gui.edit_handles.param_CBW,'Enable','off','String',num2str(p.CBWcutoff)); -0880 set(gui.edit_handles.param_BVI,'Enable','off','String',num2str(p.BVIcutoff)); -0881 set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); -0882 set(gui.edit_handles.param_geom,'Enable','on','String',num2str(p.a)); -0883 -0884 case {'LU','NNLS'} -0885 set(gui.edit_handles.param_CBW,'Enable','on','String',num2str(p.CBWcutoff)); -0886 set(gui.edit_handles.param_BVI,'Enable','on','String',num2str(p.BVIcutoff)); -0887 set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); -0888 set(gui.edit_handles.param_geom,'Enable','on','String',num2str(p.a)); -0889 -0890 end -0891 set(gui.edit_handles.param_calibVol,'Enable','on','String',num2str(p.calibVol)); -0892 set(gui.edit_handles.param_calibAmp,'Enable','on','String',num2str(p.calibAmp)); -0893 set(gui.edit_handles.param_sampVol,'Enable','on','String',num2str(p.sampVol)); -0894 -0895 end -0896 -0897 %------------- END OF CODE -------------- -0898 -0899 %% License: -0900 % MIT License -0901 % -0902 % Copyright (c) 2018 Thomas Hiller -0903 % -0904 % Permission is hereby granted, free of charge, to any person obtaining a copy -0905 % of this software and associated documentation files (the "Software"), to deal -0906 % in the Software without restriction, including without limitation the rights -0907 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0908 % copies of the Software, and to permit persons to whom the Software is -0909 % furnished to do so, subject to the following conditions: -0910 % -0911 % The above copyright notice and this permission notice shall be included in all -0912 % copies or substantial portions of the Software. -0913 % -0914 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0915 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0916 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0917 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0918 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0919 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0920 % SOFTWARE. +0811 switch regtype +0812 case 'none' +0813 switch regtypestd +0814 case 'lcurve' +0815 set(gui.edit_handles.invjoint_lambda_min,'Enable','off'); +0816 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); +0817 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); +0818 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; +0819 +0820 otherwise +0821 set(gui.edit_handles.invjoint_lambda_min,'Enable','off'); +0822 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); +0823 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); +0824 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; +0825 end +0826 +0827 case 'manual' +0828 switch regtypestd +0829 case 'lcurve' +0830 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambda)); +0831 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); +0832 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); +0833 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; +0834 +0835 otherwise +0836 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambda)); +0837 set(gui.edit_handles.invjoint_lambda_max,'Enable','off'); +0838 set(gui.edit_handles.invjoint_NlambdaR,'Enable','off'); +0839 gui.plots.DistPanel.TabTitles = {'RTD','PSD','PSD (joint)'}; +0840 end +0841 +0842 case 'lcurve' +0843 set(gui.edit_handles.invjoint_lambda_min,'Enable','on','String',num2str(lambdaR(1))); +0844 set(gui.edit_handles.invjoint_lambda_max,'Enable','on','String',num2str(lambdaR(2))); +0845 set(gui.edit_handles.invjoint_NlambdaR,'Enable','on','String',num2str(NlambdaR)); +0846 gui.plots.DistPanel.TabTitles = {'L-CURVE','RMS','PSD (joint)'}; +0847 end +0848 +0849 end +0850 +0851 function gui = updateLorder(gui,invtype,Lorder) +0852 +0853 switch invtype +0854 case {'mono','free','MUMO'} +0855 set(gui.radio_handles.invstd_Lorder0,'Enable','off'); +0856 set(gui.radio_handles.invstd_Lorder1,'Enable','off'); +0857 set(gui.radio_handles.invstd_Lorder2,'Enable','off'); +0858 +0859 case {'LU','NNLS'} +0860 set(gui.radio_handles.invstd_Lorder0,'Enable','on'); +0861 set(gui.radio_handles.invstd_Lorder1,'Enable','on'); +0862 set(gui.radio_handles.invstd_Lorder2,'Enable','on'); +0863 +0864 switch Lorder +0865 case 0 +0866 set(gui.radio_handles.invstd_Lorder0,'Value',1); +0867 set(gui.radio_handles.invstd_Lorder1,'Value',0); +0868 set(gui.radio_handles.invstd_Lorder2,'Value',0); +0869 +0870 case 1 +0871 set(gui.radio_handles.invstd_Lorder0,'Value',0); +0872 set(gui.radio_handles.invstd_Lorder1,'Value',1); +0873 set(gui.radio_handles.invstd_Lorder2,'Value',0); +0874 +0875 case 2 +0876 set(gui.radio_handles.invstd_Lorder0,'Value',0); +0877 set(gui.radio_handles.invstd_Lorder1,'Value',0); +0878 set(gui.radio_handles.invstd_Lorder2,'Value',1); +0879 end +0880 end +0881 +0882 end +0883 +0884 function gui = updateLorderJoint(gui,invtype,Lorder) +0885 +0886 switch invtype +0887 case {'fixed','shape','off'} +0888 set(gui.radio_handles.invjoint_Lorder0,'Enable','off'); +0889 set(gui.radio_handles.invjoint_Lorder1,'Enable','off'); +0890 set(gui.radio_handles.invjoint_Lorder2,'Enable','off'); +0891 +0892 case 'free' +0893 set(gui.radio_handles.invjoint_Lorder0,'Enable','on'); +0894 set(gui.radio_handles.invjoint_Lorder1,'Enable','on'); +0895 set(gui.radio_handles.invjoint_Lorder2,'Enable','on'); +0896 +0897 switch Lorder +0898 case 0 +0899 set(gui.radio_handles.invjoint_Lorder0,'Value',1); +0900 set(gui.radio_handles.invjoint_Lorder1,'Value',0); +0901 set(gui.radio_handles.invjoint_Lorder2,'Value',0); +0902 +0903 case 1 +0904 set(gui.radio_handles.invjoint_Lorder0,'Value',0); +0905 set(gui.radio_handles.invjoint_Lorder1,'Value',1); +0906 set(gui.radio_handles.invjoint_Lorder2,'Value',0); +0907 +0908 case 2 +0909 set(gui.radio_handles.invjoint_Lorder0,'Value',0); +0910 set(gui.radio_handles.invjoint_Lorder1,'Value',0); +0911 set(gui.radio_handles.invjoint_Lorder2,'Value',1); +0912 end +0913 end +0914 +0915 end +0916 +0917 function gui = updateParams(gui,invtype,p) +0918 +0919 switch invtype +0920 case {'mono','free'} +0921 set(gui.edit_handles.param_CBW,'Enable','off','String',num2str(p.CBWcutoff)); +0922 set(gui.edit_handles.param_BVI,'Enable','off','String',num2str(p.BVIcutoff)); +0923 set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); +0924 set(gui.edit_handles.param_geom,'Enable','on','String',num2str(p.a)); +0925 +0926 case {'LU','NNLS','MUMO'} +0927 set(gui.edit_handles.param_CBW,'Enable','on','String',num2str(p.CBWcutoff)); +0928 set(gui.edit_handles.param_BVI,'Enable','on','String',num2str(p.BVIcutoff)); +0929 set(gui.edit_handles.param_rho,'Enable','on','String',num2str(p.rho)); +0930 set(gui.edit_handles.param_geom,'Enable','on','String',num2str(p.a)); +0931 +0932 end +0933 set(gui.edit_handles.param_calibVol,'Enable','on','String',num2str(p.calibVol)); +0934 set(gui.edit_handles.param_calibAmp,'Enable','on','String',num2str(p.calibAmp)); +0935 set(gui.edit_handles.param_sampVol,'Enable','on','String',num2str(p.sampVol)); +0936 +0937 end +0938 +0939 %------------- END OF CODE -------------- +0940 +0941 %% License: +0942 % MIT License +0943 % +0944 % Copyright (c) 2018 Thomas Hiller +0945 % +0946 % Permission is hereby granted, free of charge, to any person obtaining a copy +0947 % of this software and associated documentation files (the "Software"), to deal +0948 % in the Software without restriction, including without limitation the rights +0949 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0950 % copies of the Software, and to permit persons to whom the Software is +0951 % furnished to do so, subject to the following conditions: +0952 % +0953 % The above copyright notice and this permission notice shall be included in all +0954 % copies or substantial portions of the Software. +0955 % +0956 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0957 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0958 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0959 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0960 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0961 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0962 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod.html index c90580d..5df6cac 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod.html @@ -57,8 +57,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0031 % 0032 % See also: NUCLEUSinv -0033 % Author: Thomas Hiller -0034 % email: thomas.hiller[at]leibniz-liag.de +0033 % Author: see AUTHORS.md +0034 % email: see AUTHORS.md 0035 % License: MIT License (at end) 0036 0037 %------------- BEGIN CODE -------------- @@ -117,10 +117,10 @@

    SOURCE CODE ^if ~isempty(h0); close(h0); end 0042 0043 %% GUI 'header' info and defaults -0044 myui.version = '0.1.11'; -0045 myui.date = '12.03.2021'; -0046 myui.author = {'Thomas Hiller','Stephan Costabel'}; -0047 myui.email = 'thomas.hiller[at]leibniz-liag.de'; +0044 myui.version = '0.1.12'; +0045 myui.date = '17.02.2022'; +0046 myui.author = {'Stephan Costabel','Thomas Hiller'}; +0047 myui.email = 'thomas.hiller[at]bgr.de'; 0048 myui.fontsize = 10; 0049 0050 %% Default data settings diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createGUI.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createGUI.html index 98300ce..5e7becf 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createGUI.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createGUI.html @@ -56,15 +56,15 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end)

    CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSmod_createMenus creates all GUI menus
  • NUCLEUSmod_createPanelCPS creates pressure panel
  • NUCLEUSmod_createPanelGeometry creates geometry panel
  • NUCLEUSmod_createPanelNMR creates NMR panel
  • NUCLEUSmod_createPanelPlots creates graphics panel
  • NUCLEUSmod_createStatusbar creates status bar
  • displayStatusText shows status information either in the GUI or on the
  • fixAxes fixes an ugly Matlab bug when resizing a box-panel which holds an
  • minimizePanel handles the minimization/maximization of all box-panels for
  • updateStatusInformation updates all fields inside the bottom status bar
  • This function is called by:
    • NUCLEUSmod is a graphical user interface (GUI) to forward model NMR
    @@ -104,8 +104,8 @@

    SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSmod -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createMenus.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createMenus.html index d05ba64..5f91bb1 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createMenus.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createMenus.html @@ -50,8 +50,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelCPS.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelCPS.html index ac3d7c7..7ee5e7a 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelCPS.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelCPS.html @@ -51,8 +51,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.html index 7932573..cafe391 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelGeometry.html @@ -51,8 +51,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelNMR.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelNMR.html index 264bb6a..856520a 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelNMR.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelNMR.html @@ -51,15 +51,15 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end)

    CROSS-REFERENCE INFORMATION ^

    This function calls:
      -
    • onEditValue updates all edit field values, checks for wrong inputs and
    • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
    +
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onPopupNMRNoiseType selects the noise type to be aplied to the forward
  • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
  • This function is called by: @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -104,10 +104,10 @@

    SOURCE CODE ^'Parent',gui.panels.nmr.main,... 0035 'Spacing',3,'Padding',3); 0036 -0037 % Tbulk & surface relaxivity rho +0037 % Tbulk & Tdiff 0038 gui.panels.nmr.HBox1 = uix.HBox('Parent',gui.panels.nmr.VBox,... 0039 'Spacing',3); -0040 % echo time TE & number of echos +0040 % surface relaxivity rho & echo time TE & number of echos 0041 gui.panels.nmr.HBox2 = uix.HBox('Parent',gui.panels.nmr.VBox,... 0042 'Spacing',3); 0043 % RUN button @@ -117,131 +117,145 @@

    SOURCE CODE ^'Parent',gui.panels.nmr.VBox,... 0048 'Spacing',3); 0049 -0050 %% Tbulk & surface relaxivity rho +0050 %% Tbulk & Tdiff 0051 gui.text_handles.Tbulk = uicontrol('Parent',gui.panels.nmr.HBox1,... 0052 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0053 'String','Tbulk [s]'); -0054 tstr = ['<HTML>Set relaxation time of bulk water.<br><br>',... -0055 '<u>Default value:</u><br>',... -0056 '<b>2 s</b><br>']; -0057 gui.edit_handles.Tbulk = uicontrol('Parent',gui.panels.nmr.HBox1,... -0058 'Style','edit','String',num2str(data.nmr.Tbulk),'FontSize',myui.fontsize,... -0059 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.Tbulk 1 1]),... -0060 'Tag','nmr_Tbulk','Enable','on','Callback',@onEditValue); -0061 gui.text_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox1,... -0062 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0063 'String',[char(hex2dec('03C1')),' [µm/s]']); -0064 tstr = ['<HTML>Set surface relaxivity &rho .<br><br>',... -0065 '<u>Default value:</u><br>',... -0066 '<b>10 µm/s</b><br>']; -0067 gui.edit_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox1,... -0068 'Style','edit','String',num2str(data.nmr.rho),'FontSize',myui.fontsize,... -0069 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.rho 1 1]),... -0070 'Tag','nmr_rho','Enable','on','Callback',@onEditValue); -0071 set(gui.panels.nmr.HBox1,'Widths',[100 -1 100 -1]); -0072 -0073 %% echo time TE & number of echoes -0074 gui.text_handles.TE = uicontrol('Parent',gui.panels.nmr.HBox2,... -0075 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0076 'String','echo time [µs]'); -0077 tstr = ['<HTML>Set echo time.<br><br>',... -0078 '<u>Default value:</u><br>',... -0079 '<b>1000 µs</b><br>']; -0080 gui.edit_handles.TE = uicontrol('Parent',gui.panels.nmr.HBox2,... -0081 'Style','edit','String',num2str(data.nmr.TE),'FontSize',myui.fontsize,... -0082 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.TE 1 1]),... -0083 'Tag','nmr_TE','Enable','on','Callback',@onEditValue); -0084 gui.text_handles.echosN = uicontrol('Parent',gui.panels.nmr.HBox2,... -0085 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0086 'String','# echoes'); -0087 tstr = ['<HTML>Set number of echoes.<br><br>',... -0088 '<u>Default value:</u><br>',... -0089 '<b>1001</b><br>']; -0090 gui.edit_handles.echosN = uicontrol('Parent',gui.panels.nmr.HBox2,... -0091 'Style','edit','String',num2str(data.nmr.echosN),'FontSize',myui.fontsize,... -0092 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.echosN 1 1]),... -0093 'Tag','nmr_echosN','Enable','on','Callback',@onEditValue); -0094 set(gui.panels.nmr.HBox2,'Widths',[100 -1 100 -1]); -0095 -0096 %% RUN button -0097 gui.text_handles.nmr_RUN = uicontrol('Parent',gui.panels.nmr.HBox3,... -0098 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0099 'String','calculate NMR'); -0100 gui.push_handles.nmr_RUN = uicontrol('Parent',gui.panels.nmr.HBox3,... -0101 'Style','pushbutton','String','RUN','FontSize',myui.fontsize,'Tag','nmr',... -0102 'BackgroundColor','g','Enable','on','Callback',@onPushRun); -0103 set(gui.panels.nmr.HBox3,'Widths',[200 -1]); -0104 -0105 %% noise & porosity -0106 gui.text_handles.noise = uicontrol('Parent',gui.panels.nmr.HBox4,... -0107 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0108 'String','add noise'); -0109 tstr = ['<HTML>Set NMR data noise.<br><br>',... -0110 '<u>Hint:</u><br>',... -0111 'You do not need to press RUN to add noise to the NMR signals.<br>',... -0112 'The raw NMR signals are stored internally and the noise is<br>',... -0113 'applied instantaneously.<br><br>',... -0114 '<u>Default value:</u><br>',... -0115 '<b>0</b><br>']; -0116 gui.edit_handles.noise = uicontrol('Parent',gui.panels.nmr.HBox4,... -0117 'Style','edit','String',num2str(data.nmr.noise),'FontSize',myui.fontsize,... -0118 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.noise 1 1]),... -0119 'Tag','nmr_noise','Enable','on','Callback',@onEditValue); -0120 gui.text_handles.porosity = uicontrol('Parent',gui.panels.nmr.HBox4,... -0121 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... -0122 'String','porosity'); -0123 tstr = ['<HTML>Set porosity value in the range [0, 1].<br><br>',... -0124 '<u>Note:</u><br>',... -0125 'This value is applied only as a scaling factor to the NMR amplitudes.<br>',... -0126 'Hence, saturation becomes water content.']; -0127 gui.edit_handles.porosity = uicontrol('Parent',gui.panels.nmr.HBox4,... -0128 'Style','edit','String',num2str(data.nmr.porosity),'FontSize',myui.fontsize,... -0129 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.porosity 1 1]),... -0130 'Tag','nmr_porosity','Enable','on','Callback',@onEditValue); -0131 set(gui.panels.nmr.HBox4,'Widths',[100 -1 100 -1]); -0132 -0133 %% Java Hack to adjust vertical alignment of text fields -0134 jh = findjobj(gui.text_handles.Tbulk); -0135 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0136 jh = findjobj(gui.text_handles.rho); -0137 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0138 jh = findjobj(gui.text_handles.TE ); -0139 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0140 jh = findjobj(gui.text_handles.echosN ); -0141 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0142 jh = findjobj(gui.text_handles.nmr_RUN); -0143 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0144 jh = findjobj(gui.text_handles.noise); -0145 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0146 jh = findjobj(gui.text_handles.porosity); -0147 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); -0148 -0149 return -0150 -0151 %------------- END OF CODE -------------- +0053 'String','Tbulk [s] | Tdiff [s]'); +0054 tstr = ['<HTML>Bulk relaxation time in [s].<br><br>',... +0055 '1/T = 1/Ts + 1/<b>Tb</b> + 1/Td<br><br>',... +0056 '<u>Default value:</u><br>',... +0057 '<b>2</b><br>']; +0058 gui.edit_handles.Tbulk = uicontrol('Parent',gui.panels.nmr.HBox1,... +0059 'Style','edit','String',num2str(data.nmr.Tbulk),'FontSize',myui.fontsize,... +0060 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.Tbulk 1 1]),... +0061 'Tag','nmr_Tbulk','Enable','on','Callback',@onEditValue); +0062 tstr = ['<HTML>Diffusion relaxation time in [s].<br><br>',... +0063 '1/T = 1/Ts + 1/Tb + 1/<b>Td</b><br><br>',... +0064 '<b>Td</b> can be caluclated by<br><br>',... +0065 '<b>Td</b> = 12 / (D*(',char(hex2dec('03B3')),'*G*TE)<sup>2</sup> )<br><br>',... +0066 'with<br><br>',... +0067 'D = diffusion coefficent of water<br>',... +0068 char(hex2dec('03B3')),' = gyromagnetic ratio of hydrogen<br>',... +0069 'G = magnetic gradient<br>',... +0070 'TE = echo time<br><br>',... +0071 '<u>Default value:</u><br>',... +0072 '<b>1e6</b> (so that 1/<b>Td</b> is ignored)<br>']; +0073 gui.edit_handles.Tdiff = uicontrol('Parent',gui.panels.nmr.HBox1,... +0074 'Style','edit','String',num2str(data.nmr.Tdiff),'FontSize',myui.fontsize,... +0075 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.Tdiff 1 1]),... +0076 'Tag','nmr_Tdiff','Enable','on','Callback',@onEditValue); +0077 set(gui.panels.nmr.HBox1,'Widths',[200 -1 -1]); +0078 +0079 %% surface relaxivity rho & echo time TE & number of echoes +0080 gui.text_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox2,... +0081 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0082 'String',[char(hex2dec('03C1')),' [µm/s] | TE [µs] | # echoes']); +0083 tstr = ['<HTML>Surface relaxivity in [µm/s].<br><br>',... +0084 '1/Ts = <b>rho</b>*S/V = <b>rho</b>*a/R.<br><br>',... +0085 '<u>Default value:</u><br>',... +0086 '<b>10</b><br>']; +0087 gui.edit_handles.rho = uicontrol('Parent',gui.panels.nmr.HBox2,... +0088 'Style','edit','String',num2str(data.nmr.rho),'FontSize',myui.fontsize,... +0089 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.rho 1 1]),... +0090 'Tag','nmr_rho','Enable','on','Callback',@onEditValue); +0091 tstr = ['<HTML>Echo time TE in [µs].<br><br>',... +0092 '<u>Default value:</u><br>',... +0093 '<b>1000</b><br>']; +0094 gui.edit_handles.TE = uicontrol('Parent',gui.panels.nmr.HBox2,... +0095 'Style','edit','String',num2str(data.nmr.TE),'FontSize',myui.fontsize,... +0096 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.TE 1 1]),... +0097 'Tag','nmr_TE','Enable','on','Callback',@onEditValue); +0098 tstr = ['<HTML>Number of echoes.<br><br>',... +0099 '<u>Default value:</u><br>',... +0100 '<b>1001</b><br>']; +0101 gui.edit_handles.echosN = uicontrol('Parent',gui.panels.nmr.HBox2,... +0102 'Style','edit','String',num2str(data.nmr.echosN),'FontSize',myui.fontsize,... +0103 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.echosN 1 1]),... +0104 'Tag','nmr_echosN','Enable','on','Callback',@onEditValue); +0105 set(gui.panels.nmr.HBox2,'Widths',[200 -1 -1 -1]); +0106 +0107 %% RUN button +0108 gui.text_handles.nmr_RUN = uicontrol('Parent',gui.panels.nmr.HBox3,... +0109 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0110 'String','calculate NMR'); +0111 gui.push_handles.nmr_RUN = uicontrol('Parent',gui.panels.nmr.HBox3,... +0112 'Style','pushbutton','String','RUN','FontSize',myui.fontsize,'Tag','nmr',... +0113 'BackgroundColor','g','Enable','on','Callback',@onPushRun); +0114 set(gui.panels.nmr.HBox3,'Widths',[200 -1]); +0115 +0116 %% noise & porosity +0117 +0118 tstr = ['<HTML>NMR data noise method.<br><br>',... +0119 'A noise level will be used globally for all NMR signals.<br>',... +0120 'A signal-to-ratio will be used individually on every single NMR signal.<br><br>',... +0121 '<u>Available options:</u><br>',... +0122 '<b>noise level</b> or <b>SNR</b> <br><br>',... +0123 '<u>Default value:</u><br>',... +0124 '<b>noise level</b> <br>']; +0125 gui.popup_handles.noisetype = uicontrol('Parent',gui.panels.nmr.HBox4,... +0126 'Style','popup','String',{'noise level','SNR'},... +0127 'Value',1,'FontSize',myui.fontsize,'UserData',struct('Tooltipstr',tstr),... +0128 'Callback',@onPopupNMRNoiseType); +0129 tstr = ['<HTML>NMR data noise.<br><br>',... +0130 '<u>Hint:</u><br>',... +0131 'You do not need to press RUN to add noise to the NMR signals.<br>',... +0132 'The raw NMR signals are stored internally and the noise is<br>',... +0133 'applied instantaneously.<br><br>',... +0134 '<u>Default value:</u><br>',... +0135 '<b>0</b><br>']; +0136 gui.edit_handles.noise = uicontrol('Parent',gui.panels.nmr.HBox4,... +0137 'Style','edit','String',num2str(data.nmr.noise),'FontSize',myui.fontsize,... +0138 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.noise 1 1]),... +0139 'Tag','nmr_noise','Enable','on','Callback',@onEditValue); +0140 gui.text_handles.porosity = uicontrol('Parent',gui.panels.nmr.HBox4,... +0141 'Style','text','FontSize',myui.fontsize,'HorizontalAlignment','center',... +0142 'String','porosity'); +0143 tstr = ['<HTML>Porosity value in the range [0, 1].<br><br>',... +0144 '<u>Note:</u><br>',... +0145 'This value is applied only as a scaling factor to the NMR amplitudes.<br>',... +0146 'Hence, saturation becomes water content.']; +0147 gui.edit_handles.porosity = uicontrol('Parent',gui.panels.nmr.HBox4,... +0148 'Style','edit','String',num2str(data.nmr.porosity),'FontSize',myui.fontsize,... +0149 'UserData',struct('Tooltipstr',tstr,'defaults',[data.nmr.porosity 1 1]),... +0150 'Tag','nmr_porosity','Enable','on','Callback',@onEditValue); +0151 set(gui.panels.nmr.HBox4,'Widths',[100 -1 100 -1]); 0152 -0153 %% License: -0154 % MIT License -0155 % -0156 % Copyright (c) 2018 Thomas Hiller -0157 % -0158 % Permission is hereby granted, free of charge, to any person obtaining a copy -0159 % of this software and associated documentation files (the "Software"), to deal -0160 % in the Software without restriction, including without limitation the rights -0161 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0162 % copies of the Software, and to permit persons to whom the Software is -0163 % furnished to do so, subject to the following conditions: -0164 % -0165 % The above copyright notice and this permission notice shall be included in all -0166 % copies or substantial portions of the Software. -0167 % -0168 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0169 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0170 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0171 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0172 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0173 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0174 % SOFTWARE. +0153 %% Java Hack to adjust vertical alignment of text fields +0154 jh = findjobj(gui.text_handles.Tbulk); +0155 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0156 jh = findjobj(gui.text_handles.rho); +0157 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0158 jh = findjobj(gui.text_handles.nmr_RUN); +0159 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0160 jh = findjobj(gui.text_handles.porosity); +0161 jh.setVerticalAlignment(javax.swing.JLabel.CENTER); +0162 +0163 return +0164 +0165 %------------- END OF CODE -------------- +0166 +0167 %% License: +0168 % MIT License +0169 % +0170 % Copyright (c) 2018 Thomas Hiller +0171 % +0172 % Permission is hereby granted, free of charge, to any person obtaining a copy +0173 % of this software and associated documentation files (the "Software"), to deal +0174 % in the Software without restriction, including without limitation the rights +0175 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0176 % copies of the Software, and to permit persons to whom the Software is +0177 % furnished to do so, subject to the following conditions: +0178 % +0179 % The above copyright notice and this permission notice shall be included in all +0180 % copies or substantial portions of the Software. +0181 % +0182 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0183 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0184 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0185 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0186 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0187 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0188 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelPlots.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelPlots.html index e4bf449..92a0435 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelPlots.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createPanelPlots.html @@ -51,8 +51,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createStatusbar.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createStatusbar.html index 8d34348..c94ce91 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createStatusbar.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_createStatusbar.html @@ -90,8 +90,8 @@

    SOURCE CODE ^% 0025 % See also: NUCLEUSmod 0026 -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 0030 %------------- BEGIN CODE -------------- 0031 diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_loadDefaults.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_loadDefaults.html index 167b3d3..fd4e9d6 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_loadDefaults.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_loadDefaults.html @@ -48,8 +48,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -88,8 +88,8 @@

    SOURCE CODE ^% none 0022 % 0023 % See also: NUCLEUSmod -0024 % Author: Thomas Hiller -0025 % email: thomas.hiller[at]leibniz-liag.de +0024 % Author: see AUTHORS.md +0025 % email: see AUTHORS.md 0026 % License: MIT License (at end) 0027 0028 %------------- BEGIN CODE -------------- @@ -140,47 +140,51 @@

    SOURCE CODE ^% number of echoes 0075 out.nmr.echosN = 1001; -0076 % noise level [%] -0077 out.nmr.noise = 0; -0078 % water bulk relaxation time [s] -0079 out.nmr.Tbulk = 2; -0080 % surface relaxivity [µm/s] -0081 out.nmr.rho = 10; -0082 % porosity value between 0 and 1 [-] -0083 out.nmr.porosity = 1; -0084 % plot T2 data as default -0085 out.nmr.toplot = 'T2'; -0086 % use linear x-axes as default (log=1, lin=2) -0087 out.nmr.loglinx = 2; -0088 % use linear y-axes as default (log=1, lin=2) -0089 out.nmr.logliny = 2; -0090 -0091 return -0092 -0093 %------------- END OF CODE -------------- +0076 % noise creation type 'level' or 'SNR' +0077 out.nmr.noisetype = 'level'; +0078 % noise level [0:1] or SNR [-] +0079 out.nmr.noise = 0; +0080 % water bulk relaxation time [s] +0081 out.nmr.Tbulk = 2; +0082 % diffusion relaxation time [s] +0083 out.nmr.Tdiff = 1e6; +0084 % surface relaxivity [µm/s] +0085 out.nmr.rho = 10; +0086 % porosity value between 0 and 1 [-] +0087 out.nmr.porosity = 1; +0088 % plot T2 data as default +0089 out.nmr.toplot = 'T2'; +0090 % use linear x-axes as default (log=1, lin=2) +0091 out.nmr.loglinx = 2; +0092 % use linear y-axes as default (log=1, lin=2) +0093 out.nmr.logliny = 2; 0094 -0095 %% License: -0096 % MIT License -0097 % -0098 % Copyright (c) 2018 Thomas Hiller -0099 % -0100 % Permission is hereby granted, free of charge, to any person obtaining a copy -0101 % of this software and associated documentation files (the "Software"), to deal -0102 % in the Software without restriction, including without limitation the rights -0103 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0104 % copies of the Software, and to permit persons to whom the Software is -0105 % furnished to do so, subject to the following conditions: -0106 % -0107 % The above copyright notice and this permission notice shall be included in all -0108 % copies or substantial portions of the Software. -0109 % -0110 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0111 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0112 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0113 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0114 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0115 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0116 % SOFTWARE. +0095 return +0096 +0097 %------------- END OF CODE -------------- +0098 +0099 %% License: +0100 % MIT License +0101 % +0102 % Copyright (c) 2018 Thomas Hiller +0103 % +0104 % Permission is hereby granted, free of charge, to any person obtaining a copy +0105 % of this software and associated documentation files (the "Software"), to deal +0106 % in the Software without restriction, including without limitation the rights +0107 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0108 % copies of the Software, and to permit persons to whom the Software is +0109 % furnished to do so, subject to the following conditions: +0110 % +0111 % The above copyright notice and this permission notice shall be included in all +0112 % copies or substantial portions of the Software. +0113 % +0114 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0115 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0116 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0117 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0118 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0119 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0120 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_updateInterface.html b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_updateInterface.html index e3a4f0f..13fd8a7 100644 --- a/doc/nucleus/NUCLEUSmod/NUCLEUSmod_updateInterface.html +++ b/doc/nucleus/NUCLEUSmod/NUCLEUSmod_updateInterface.html @@ -49,8 +49,8 @@

    DESCRIPTION ^NUCLEUSmod - Author: Thomas Hiller - email: thomas.hiller[at]leibniz-liag.de + Author: see AUTHORS.md + email: see AUTHORS.md License: MIT License (at end) @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0023 % 0024 % See also: NUCLEUSmod -0025 % Author: Thomas Hiller -0026 % email: thomas.hiller[at]leibniz-liag.de +0025 % Author: see AUTHORS.md +0026 % email: see AUTHORS.md 0027 % License: MIT License (at end) 0028 0029 %------------- BEGIN CODE -------------- @@ -195,117 +195,125 @@

    SOURCE CODE ^end 0126 0127 %% update NMR panel -0128 % all edit fields -0129 set(gui.edit_handles.Tbulk,'String',num2str(data.nmr.Tbulk)); -0130 set(gui.edit_handles.rho,'String',num2str(data.nmr.rho)); -0131 set(gui.edit_handles.TE,'String',num2str(data.nmr.TE)); -0132 set(gui.edit_handles.echosN,'String',num2str(data.nmr.echosN)); -0133 set(gui.edit_handles.noise,'String',num2str(data.nmr.noise)); -0134 set(gui.edit_handles.porosity,'String',num2str(data.nmr.porosity)); +0128 % noise type +0129 switch data.nmr.noisetype +0130 case 'level' % noise level +0131 set(gui.popup_handles.noisetype,'Value',1); +0132 case 'SNR' % signal-to-noise ratio (SNR) +0133 set(gui.popup_handles.noisetype,'Value',2); +0134 end 0135 -0136 end -0137 -0138 %% sub functions -0139 function gui = updateGeometryRange(gui,ispsd,modesN) -0140 -0141 switch ispsd -0142 case 0 % single pore -0143 set(gui.popup_handles.singlepsd,'Value',1); -0144 set(gui.edit_handles.range_min,'Enable','off'); -0145 set(gui.edit_handles.range_max,'Enable','off'); -0146 set(gui.edit_handles.rangeN,'Enable','off'); -0147 set(gui.popup_handles.modesN,'Enable','off','Value',modesN); -0148 case 1 % PSD -0149 set(gui.popup_handles.singlepsd,'Value',2); -0150 set(gui.edit_handles.range_min,'Enable','on'); -0151 set(gui.edit_handles.range_max,'Enable','on'); -0152 set(gui.edit_handles.rangeN,'Enable','on'); -0153 set(gui.popup_handles.modesN,'Enable','on','Value',modesN); -0154 end -0155 end -0156 -0157 function gui = updateGeometryModesN(gui,ispsd,modesN,modes) -0158 -0159 % set the values -0160 set(gui.edit_handles.mode1,'String',num2str(modes(1,1))); -0161 set(gui.edit_handles.sig1,'String',num2str(modes(1,2))); -0162 set(gui.edit_handles.amp1,'String',num2str(modes(1,3))); -0163 set(gui.edit_handles.mode2,'String',num2str(modes(2,1))); -0164 set(gui.edit_handles.sig2,'String',num2str(modes(2,2))); -0165 set(gui.edit_handles.amp2,'String',num2str(modes(2,3))); -0166 set(gui.edit_handles.mode3,'String',num2str(modes(3,1))); -0167 set(gui.edit_handles.sig3,'String',num2str(modes(3,2))); -0168 set(gui.edit_handles.amp3,'String',num2str(modes(3,3))); -0169 -0170 % enable / disable -0171 switch modesN -0172 case 1 -0173 switch ispsd -0174 case 0 % single pore -0175 set(gui.edit_handles.sig1,'Enable','off'); -0176 set(gui.edit_handles.amp1,'Enable','off'); -0177 set(gui.edit_handles.mode2,'Enable','off'); -0178 set(gui.edit_handles.sig2,'Enable','off'); -0179 set(gui.edit_handles.amp2,'Enable','off'); -0180 set(gui.edit_handles.mode3,'Enable','off'); -0181 set(gui.edit_handles.sig3,'Enable','off'); -0182 set(gui.edit_handles.amp3,'Enable','off'); -0183 case 1 % PSD with 1 mode -0184 set(gui.edit_handles.sig1,'Enable','on'); -0185 set(gui.edit_handles.amp1,'Enable','on'); -0186 set(gui.edit_handles.mode2,'Enable','off'); -0187 set(gui.edit_handles.sig2,'Enable','off'); -0188 set(gui.edit_handles.amp2,'Enable','off'); -0189 set(gui.edit_handles.mode3,'Enable','off'); -0190 set(gui.edit_handles.sig3,'Enable','off'); -0191 set(gui.edit_handles.amp3,'Enable','off'); -0192 end -0193 case 2 % PSD with 2 modes -0194 set(gui.edit_handles.sig1,'Enable','on'); -0195 set(gui.edit_handles.amp1,'Enable','on'); -0196 set(gui.edit_handles.mode2,'Enable','on'); -0197 set(gui.edit_handles.sig2,'Enable','on'); -0198 set(gui.edit_handles.amp2,'Enable','on'); -0199 set(gui.edit_handles.mode3,'Enable','off'); -0200 set(gui.edit_handles.sig3,'Enable','off'); -0201 set(gui.edit_handles.amp3,'Enable','off'); -0202 case 3 % PSD with 3 modes -0203 set(gui.edit_handles.sig1,'Enable','on'); -0204 set(gui.edit_handles.amp1,'Enable','on'); -0205 set(gui.edit_handles.mode2,'Enable','on'); -0206 set(gui.edit_handles.sig2,'Enable','on'); -0207 set(gui.edit_handles.amp2,'Enable','on'); -0208 set(gui.edit_handles.mode3,'Enable','on'); -0209 set(gui.edit_handles.sig3,'Enable','on'); -0210 set(gui.edit_handles.amp3,'Enable','on'); -0211 end -0212 -0213 end -0214 -0215 %------------- END OF CODE -------------- -0216 -0217 %% License: -0218 % MIT License -0219 % -0220 % Copyright (c) 2018 Thomas Hiller -0221 % -0222 % Permission is hereby granted, free of charge, to any person obtaining a copy -0223 % of this software and associated documentation files (the "Software"), to deal -0224 % in the Software without restriction, including without limitation the rights -0225 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0226 % copies of the Software, and to permit persons to whom the Software is -0227 % furnished to do so, subject to the following conditions: -0228 % -0229 % The above copyright notice and this permission notice shall be included in all -0230 % copies or substantial portions of the Software. -0231 % -0232 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0233 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0234 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0235 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0236 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0237 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0238 % SOFTWARE. +0136 % all edit fields +0137 set(gui.edit_handles.Tbulk,'String',num2str(data.nmr.Tbulk)); +0138 set(gui.edit_handles.rho,'String',num2str(data.nmr.rho)); +0139 set(gui.edit_handles.TE,'String',num2str(data.nmr.TE)); +0140 set(gui.edit_handles.echosN,'String',num2str(data.nmr.echosN)); +0141 set(gui.edit_handles.noise,'String',num2str(data.nmr.noise)); +0142 set(gui.edit_handles.porosity,'String',num2str(data.nmr.porosity)); +0143 +0144 end +0145 +0146 %% sub functions +0147 function gui = updateGeometryRange(gui,ispsd,modesN) +0148 +0149 switch ispsd +0150 case 0 % single pore +0151 set(gui.popup_handles.singlepsd,'Value',1); +0152 set(gui.edit_handles.range_min,'Enable','off'); +0153 set(gui.edit_handles.range_max,'Enable','off'); +0154 set(gui.edit_handles.rangeN,'Enable','off'); +0155 set(gui.popup_handles.modesN,'Enable','off','Value',modesN); +0156 case 1 % PSD +0157 set(gui.popup_handles.singlepsd,'Value',2); +0158 set(gui.edit_handles.range_min,'Enable','on'); +0159 set(gui.edit_handles.range_max,'Enable','on'); +0160 set(gui.edit_handles.rangeN,'Enable','on'); +0161 set(gui.popup_handles.modesN,'Enable','on','Value',modesN); +0162 end +0163 end +0164 +0165 function gui = updateGeometryModesN(gui,ispsd,modesN,modes) +0166 +0167 % set the values +0168 set(gui.edit_handles.mode1,'String',num2str(modes(1,1))); +0169 set(gui.edit_handles.sig1,'String',num2str(modes(1,2))); +0170 set(gui.edit_handles.amp1,'String',num2str(modes(1,3))); +0171 set(gui.edit_handles.mode2,'String',num2str(modes(2,1))); +0172 set(gui.edit_handles.sig2,'String',num2str(modes(2,2))); +0173 set(gui.edit_handles.amp2,'String',num2str(modes(2,3))); +0174 set(gui.edit_handles.mode3,'String',num2str(modes(3,1))); +0175 set(gui.edit_handles.sig3,'String',num2str(modes(3,2))); +0176 set(gui.edit_handles.amp3,'String',num2str(modes(3,3))); +0177 +0178 % enable / disable +0179 switch modesN +0180 case 1 +0181 switch ispsd +0182 case 0 % single pore +0183 set(gui.edit_handles.sig1,'Enable','off'); +0184 set(gui.edit_handles.amp1,'Enable','off'); +0185 set(gui.edit_handles.mode2,'Enable','off'); +0186 set(gui.edit_handles.sig2,'Enable','off'); +0187 set(gui.edit_handles.amp2,'Enable','off'); +0188 set(gui.edit_handles.mode3,'Enable','off'); +0189 set(gui.edit_handles.sig3,'Enable','off'); +0190 set(gui.edit_handles.amp3,'Enable','off'); +0191 case 1 % PSD with 1 mode +0192 set(gui.edit_handles.sig1,'Enable','on'); +0193 set(gui.edit_handles.amp1,'Enable','on'); +0194 set(gui.edit_handles.mode2,'Enable','off'); +0195 set(gui.edit_handles.sig2,'Enable','off'); +0196 set(gui.edit_handles.amp2,'Enable','off'); +0197 set(gui.edit_handles.mode3,'Enable','off'); +0198 set(gui.edit_handles.sig3,'Enable','off'); +0199 set(gui.edit_handles.amp3,'Enable','off'); +0200 end +0201 case 2 % PSD with 2 modes +0202 set(gui.edit_handles.sig1,'Enable','on'); +0203 set(gui.edit_handles.amp1,'Enable','on'); +0204 set(gui.edit_handles.mode2,'Enable','on'); +0205 set(gui.edit_handles.sig2,'Enable','on'); +0206 set(gui.edit_handles.amp2,'Enable','on'); +0207 set(gui.edit_handles.mode3,'Enable','off'); +0208 set(gui.edit_handles.sig3,'Enable','off'); +0209 set(gui.edit_handles.amp3,'Enable','off'); +0210 case 3 % PSD with 3 modes +0211 set(gui.edit_handles.sig1,'Enable','on'); +0212 set(gui.edit_handles.amp1,'Enable','on'); +0213 set(gui.edit_handles.mode2,'Enable','on'); +0214 set(gui.edit_handles.sig2,'Enable','on'); +0215 set(gui.edit_handles.amp2,'Enable','on'); +0216 set(gui.edit_handles.mode3,'Enable','on'); +0217 set(gui.edit_handles.sig3,'Enable','on'); +0218 set(gui.edit_handles.amp3,'Enable','on'); +0219 end +0220 +0221 end +0222 +0223 %------------- END OF CODE -------------- +0224 +0225 %% License: +0226 % MIT License +0227 % +0228 % Copyright (c) 2018 Thomas Hiller +0229 % +0230 % Permission is hereby granted, free of charge, to any person obtaining a copy +0231 % of this software and associated documentation files (the "Software"), to deal +0232 % in the Software without restriction, including without limitation the rights +0233 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0234 % copies of the Software, and to permit persons to whom the Software is +0235 % furnished to do so, subject to the following conditions: +0236 % +0237 % The above copyright notice and this permission notice shall be included in all +0238 % copies or substantial portions of the Software. +0239 % +0240 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0241 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0242 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0243 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0244 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0245 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0246 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/contextmenus/onContextAxisLogLin.html b/doc/nucleus/callbacks/contextmenus/onContextAxisLogLin.html index af5e2c2..22e1853 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextAxisLogLin.html +++ b/doc/nucleus/callbacks/contextmenus/onContextAxisLogLin.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSmod, NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextAxisT1T2.html b/doc/nucleus/callbacks/contextmenus/onContextAxisT1T2.html index 3812389..f892bd7 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextAxisT1T2.html +++ b/doc/nucleus/callbacks/contextmenus/onContextAxisT1T2.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextPlotsPSD.html b/doc/nucleus/callbacks/contextmenus/onContextPlotsPSD.html index 68a1a53..903db67 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextPlotsPSD.html +++ b/doc/nucleus/callbacks/contextmenus/onContextPlotsPSD.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSmod -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextPlotsPSDJ.html b/doc/nucleus/callbacks/contextmenus/onContextPlotsPSDJ.html index 26953c4..8bb1399 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextPlotsPSDJ.html +++ b/doc/nucleus/callbacks/contextmenus/onContextPlotsPSDJ.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextPlotsRTD.html b/doc/nucleus/callbacks/contextmenus/onContextPlotsRTD.html index 8ba280e..453e9a7 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextPlotsRTD.html +++ b/doc/nucleus/callbacks/contextmenus/onContextPlotsRTD.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextSignalList.html b/doc/nucleus/callbacks/contextmenus/onContextSignalList.html index 1ad11b1..9303d99 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextSignalList.html +++ b/doc/nucleus/callbacks/contextmenus/onContextSignalList.html @@ -58,8 +58,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0032 % 0033 % See also: NUCLEUSinv -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/contextmenus/onContextTableSelect.html b/doc/nucleus/callbacks/contextmenus/onContextTableSelect.html index 5e85b9d..feb50c1 100644 --- a/doc/nucleus/callbacks/contextmenus/onContextTableSelect.html +++ b/doc/nucleus/callbacks/contextmenus/onContextTableSelect.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/edits/onEditCPSTable.html b/doc/nucleus/callbacks/edits/onEditCPSTable.html index 15d6f81..ac9ce15 100644 --- a/doc/nucleus/callbacks/edits/onEditCPSTable.html +++ b/doc/nucleus/callbacks/edits/onEditCPSTable.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/edits/onEditValue.html b/doc/nucleus/callbacks/edits/onEditValue.html index 43592c7..d31956c 100644 --- a/doc/nucleus/callbacks/edits/onEditValue.html +++ b/doc/nucleus/callbacks/edits/onEditValue.html @@ -53,7 +53,6 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • NUCLEUSmod_updateInterface updates all GUI elements
  • calculateGeometry calculates the shape dependent geometry parameters
  • calibratePorosity determines a sample's porosity from a calibration
  • clearSingleAxis clears an individual axis
  • processNMRDataControl prepares simple NMR raw data processing
  • removeCalculationFields deletes corresponding fields from NUCLEUSmod
  • removeInversionFields deletes all inversion result fields from NUCLEUSinv
  • updateInfo updates the information shown in all information list boxes
  • updateNMRsignals adds noise to the forward NMR signals and scales the
  • updatePlotsDistribution plots the RTD and PSD curves into NUCLEUSinv
  • updatePlotsSignal plots the raw and processed NMR signals in NUCLEUSinv
  • This function is called by: @@ -110,265 +109,277 @@

    SOURCE CODE ^% updateInfo 0027 % updateNMRsignals 0028 % updatePlotsDistribution -0029 % updatePlotsNMR -0030 % updatePlotsSignal -0031 % -0032 % Subfunctions: -0033 % createDataString -0034 % -0035 % MAT-files required: -0036 % none -0037 % -0038 % See also: NUCLEUSinv -0039 % Author: Thomas Hiller -0040 % email: thomas.hiller[at]leibniz-liag.de -0041 % License: MIT License (at end) -0042 -0043 %------------- BEGIN CODE -------------- -0044 -0045 %% get GUI handle and data -0046 fig = ancestor(src,'figure','toplevel'); -0047 fig_tag = get(fig,'Tag'); -0048 gui = getappdata(fig,'gui'); -0049 data = getappdata(fig,'data'); -0050 INVdata = getappdata(fig,'INVdata'); -0051 -0052 %% the generic part works for both GUIS -0053 % get the value of the field -0054 value = str2double(get(src,'String')); -0055 % get the user data of the field -0056 userdata = get(src,'UserData'); -0057 -0058 % check if the value is numeric -0059 % if not reset to defaults stored in user data -0060 defaults = userdata.defaults; -0061 if isnan(value) -0062 set(src,'String',num2str(defaults(1))); -0063 value = str2double(get(src,'String')); -0064 end -0065 -0066 % get the tag -0067 tag = get(src,'Tag'); -0068 out = createDataString(tag); -0069 -0070 % update the corresponding data field -0071 updstr = [out.updstr,'(',num2str(defaults(2)),',',... -0072 num2str(defaults(3)),')','=value;']; -0073 eval(updstr); -0074 % update the data inside the GUI -0075 setappdata(fig,'data',data); -0076 -0077 % switch depending on what figure called -0078 switch fig_tag -0079 case 'INV' -0080 % id of the chosen NMR signal -0081 id = get(gui.listbox_handles.signal,'Value'); -0082 -0083 % switch depending on the parent panel -0084 switch out.panel -0085 case 'process' -0086 % remove temporary data fields -0087 data = removeInversionFields(data); -0088 setappdata(fig,'data',data); -0089 % process the current selected signal -0090 id = get(gui.listbox_handles.signal,'Value'); -0091 switch out.field -0092 case {'Nechoes'} -0093 if value == 0 -0094 data.process.Nechoes = 1; -0095 set(src,'String',num2str(data.process.Nechoes)); -0096 setappdata(fig,'data',data); -0097 end -0098 case 'end' -0099 % check if the first sample is really smaller than -0100 % the last one; if not reset everything -0101 first = str2double(get(gui.edit_handles.process_start,'String')); -0102 last = value; -0103 maxL = length(data.import.NMR.data{id}.signal); -0104 if last == 0 || last <= first || last > maxL -0105 data.process.end = maxL; -0106 set(src,'String',num2str(data.process.end)); -0107 setappdata(fig,'data',data); -0108 end -0109 end -0110 % process the current selected signal -0111 processNMRDataControl(fig,id); -0112 updatePlotsSignal; -0113 updateInfo(src); -0114 % clear axes -0115 clearSingleAxis(gui.axes_handles.rtd); -0116 clearSingleAxis(gui.axes_handles.psd); -0117 % set focus on data -0118 set(gui.plots.SignalPanel,'Selection',1); -0119 case 'param' -0120 switch out.field -0121 case {'rho','a','CBWcutoff','BVIcutoff'} -0122 if isstruct(INVdata{id}) -0123 eval(['INVdata{',num2str(id),'}.',... -0124 out.panel,'.',out.field,'=value;']); -0125 setappdata(fig,'INVdata',INVdata); -0126 end -0127 updatePlotsDistribution; -0128 updateInfo(gui.plots.SignalPanel); -0129 case 'calibAmp' -0130 data.calib.amp = data.param.calibAmp; -0131 setappdata(fig,'data',data); -0132 case 'calibVol' -0133 data.calib.vol = data.param.calibVol; -0134 setappdata(fig,'data',data); -0135 case 'sampVol' -0136 calibratePorosity; -0137 end -0138 case 'invstd' -0139 switch out.field -0140 case 'lambdaR' -0141 data.invstd.lambda = data.invstd.lambdaR(1); -0142 setappdata(fig,'data',data); -0143 case 'Tbulk' -0144 if data.invstd.Tbulk <=0 -0145 data.invstd.Tbulk = 2; -0146 setappdata(fig,'data',data); -0147 set(src,'String',num2str(data.invstd.Tbulk)); -0148 end -0149 case 'porosity' -0150 if data.invstd.porosity < 0 || data.invstd.porosity > 1 -0151 data.invstd.porosity = 1; -0152 setappdata(fig,'data',data); -0153 set(src,'String',num2str(data.invstd.porosity)); -0154 end -0155 updatePlotsDistribution; -0156 end -0157 case 'invjoint' -0158 switch out.field -0159 case 'lambdaR' -0160 data.invjoint.lambda = data.invjoint.lambdaR(1); -0161 setappdata(fig,'data',data); -0162 case 'beta' -0163 if value < 0.1 -0164 value = 1; -0165 elseif value > 89.9 -0166 value = 89; -0167 end -0168 eval(updstr); -0169 data.invjoint.gamma = data.invjoint.alpha - data.invjoint.beta; -0170 setappdata(fig,'data',data); -0171 NUCLEUSinv_updateInterface; -0172 case 'rhostart' -0173 case 'anglestart' -0174 if value < 0.1 -0175 value = 1; -0176 elseif value > 89.9 -0177 value = 89; -0178 end -0179 eval(updstr); -0180 data.invjoint.beta = value; -0181 data.invjoint.gamma = data.invjoint.alpha - data.invjoint.beta; -0182 setappdata(fig,'data',data); -0183 NUCLEUSinv_updateInterface; -0184 end -0185 end % switch out.panel -0186 setappdata(fig,'gui',gui); -0187 case 'MOD' -0188 % switch depending on the parent panel -0189 switch out.panel -0190 case 'geometry' -0191 switch out.field -0192 case 'beta' -0193 if value < 0.5 -0194 value = 1; -0195 elseif value > 89.5 -0196 value = 89; -0197 end -0198 eval(updstr); -0199 setappdata(fig,'data',data); -0200 otherwise -0201 % nothing to do -0202 end -0203 data = removeCalculationFields(data,'cps'); -0204 data = removeCalculationFields(data,'nmr'); -0205 clearSingleAxis(gui.axes_handles.cps); -0206 clearSingleAxis(gui.axes_handles.nmr); -0207 setappdata(fig,'data',data); -0208 calculateGeometry; -0209 -0210 case 'pressure' -0211 switch out.field -0212 case 'range' -0213 updstr = [out.updstr,'(',num2str(defaults(2)),... -0214 ',',num2str(defaults(3)),')',... -0215 '=value/data.pressure.unitfac;']; -0216 eval(updstr); -0217 end -0218 data = removeCalculationFields(data,'cps'); -0219 data = removeCalculationFields(data,'nmr'); -0220 clearSingleAxis(gui.axes_handles.cps); -0221 clearSingleAxis(gui.axes_handles.nmr); -0222 setappdata(fig,'data',data); -0223 NUCLEUSmod_updateInterface; -0224 -0225 case 'nmr' -0226 switch out.field -0227 case 'noise' -0228 if isfield(data.results,'NMR') -0229 updateNMRsignals; -0230 updatePlotsNMR; -0231 end -0232 case 'porosity' -0233 if data.nmr.porosity <= 0 || data.nmr.porosity > 1 -0234 data.nmr.porosity = 1; -0235 set(src,'String',num2str(data.nmr.porosity)); -0236 setappdata(fig,'data',data); -0237 end -0238 if isfield(data.results,'NMR') -0239 updateNMRsignals; -0240 updatePlotsNMR; -0241 end -0242 otherwise -0243 data = removeCalculationFields(data,'nmr'); -0244 clearSingleAxis(gui.axes_handles.nmr); -0245 setappdata(fig,'data',data); -0246 NUCLEUSmod_updateInterface; -0247 end -0248 end -0249 otherwise -0250 helpdlg({'function: onEditValue','Something is utterly wrong.'},... -0251 'wrong fig tag'); -0252 end -0253 -0254 end -0255 -0256 function out = createDataString(tag) -0257 ind = strfind(tag,'_'); -0258 out.panel = tag(1:ind(1)-1); -0259 out.field = tag(ind(1)+1:end); -0260 tag(ind) = '.'; -0261 out.updstr = ['data.',tag]; -0262 end -0263 -0264 %------------- END OF CODE -------------- +0029 % updatePlotsSignal +0030 % +0031 % Subfunctions: +0032 % createDataString +0033 % +0034 % MAT-files required: +0035 % none +0036 % +0037 % See also: NUCLEUSinv +0038 % Author: see AUTHORS.md +0039 % email: see AUTHORS.md +0040 % License: MIT License (at end) +0041 +0042 %------------- BEGIN CODE -------------- +0043 +0044 %% get GUI handle and data +0045 fig = ancestor(src,'figure','toplevel'); +0046 fig_tag = get(fig,'Tag'); +0047 gui = getappdata(fig,'gui'); +0048 data = getappdata(fig,'data'); +0049 INVdata = getappdata(fig,'INVdata'); +0050 +0051 %% the generic part works for both GUIS +0052 % get the value of the field +0053 value = str2double(get(src,'String')); +0054 % get the user data of the field +0055 userdata = get(src,'UserData'); +0056 +0057 % check if the value is numeric +0058 % if not reset to defaults stored in user data +0059 defaults = userdata.defaults; +0060 if isnan(value) +0061 set(src,'String',num2str(defaults(1))); +0062 value = str2double(get(src,'String')); +0063 end +0064 +0065 % get the tag +0066 tag = get(src,'Tag'); +0067 out = createDataString(tag); +0068 +0069 % update the corresponding data field +0070 updstr = [out.updstr,'(',num2str(defaults(2)),',',... +0071 num2str(defaults(3)),')','=value;']; +0072 eval(updstr); +0073 % update the data inside the GUI +0074 setappdata(fig,'data',data); +0075 +0076 % switch depending on what figure called +0077 switch fig_tag +0078 case 'INV' +0079 % id of the chosen NMR signal +0080 id = get(gui.listbox_handles.signal,'Value'); +0081 +0082 % switch depending on the parent panel +0083 switch out.panel +0084 case 'process' +0085 % remove temporary data fields +0086 data = removeInversionFields(data); +0087 setappdata(fig,'data',data); +0088 % process the current selected signal +0089 id = get(gui.listbox_handles.signal,'Value'); +0090 switch out.field +0091 case {'Nechoes'} +0092 if value == 0 +0093 data.process.Nechoes = 1; +0094 set(src,'String',num2str(data.process.Nechoes)); +0095 setappdata(fig,'data',data); +0096 end +0097 case 'end' +0098 % check if the first sample is really smaller than +0099 % the last one; if not reset everything +0100 first = str2double(get(gui.edit_handles.process_start,'String')); +0101 last = value; +0102 maxL = length(data.import.NMR.data{id}.signal); +0103 if last == 0 || last <= first || last > maxL +0104 data.process.end = maxL; +0105 set(src,'String',num2str(data.process.end)); +0106 setappdata(fig,'data',data); +0107 end +0108 end +0109 % process the current selected signal +0110 processNMRDataControl(fig,id); +0111 updatePlotsSignal; +0112 updateInfo(src); +0113 % clear axes +0114 clearSingleAxis(gui.axes_handles.rtd); +0115 clearSingleAxis(gui.axes_handles.psd); +0116 % set focus on data +0117 set(gui.plots.SignalPanel,'Selection',1); +0118 case 'param' +0119 switch out.field +0120 case {'rho','a','CBWcutoff','BVIcutoff'} +0121 if isstruct(INVdata{id}) +0122 eval(['INVdata{',num2str(id),'}.',... +0123 out.panel,'.',out.field,'=value;']); +0124 setappdata(fig,'INVdata',INVdata); +0125 end +0126 updatePlotsDistribution; +0127 updateInfo(gui.plots.SignalPanel); +0128 case 'calibAmp' +0129 data.calib.amp = data.param.calibAmp; +0130 setappdata(fig,'data',data); +0131 case 'calibVol' +0132 data.calib.vol = data.param.calibVol; +0133 setappdata(fig,'data',data); +0134 case 'sampVol' +0135 calibratePorosity; +0136 end +0137 case 'invstd' +0138 switch out.field +0139 case 'lambdaR' +0140 data.invstd.lambda = data.invstd.lambdaR(1); +0141 setappdata(fig,'data',data); +0142 case 'Tbulk' +0143 if data.invstd.Tbulk <=0 +0144 data.invstd.Tbulk = 2; +0145 setappdata(fig,'data',data); +0146 set(src,'String',num2str(data.invstd.Tbulk)); +0147 end +0148 case 'Tdiff' +0149 if data.invstd.Tdiff <=0 +0150 data.invstd.Tdiff = 1e6; +0151 setappdata(fig,'data',data); +0152 set(src,'String',num2str(data.invstd.Tdiff)); +0153 end +0154 case 'porosity' +0155 if data.invstd.porosity < 0 || data.invstd.porosity > 1 +0156 data.invstd.porosity = 1; +0157 setappdata(fig,'data',data); +0158 set(src,'String',num2str(data.invstd.porosity)); +0159 end +0160 updatePlotsDistribution; +0161 end +0162 case 'invjoint' +0163 switch out.field +0164 case 'lambdaR' +0165 data.invjoint.lambda = data.invjoint.lambdaR(1); +0166 setappdata(fig,'data',data); +0167 case 'beta' +0168 if value < 0.1 +0169 value = 1; +0170 elseif value > 89.9 +0171 value = 89; +0172 end +0173 eval(updstr); +0174 data.invjoint.gamma = data.invjoint.alpha - data.invjoint.beta; +0175 setappdata(fig,'data',data); +0176 NUCLEUSinv_updateInterface; +0177 case 'rhostart' +0178 case 'anglestart' +0179 if value < 0.1 +0180 value = 1; +0181 elseif value > 89.9 +0182 value = 89; +0183 end +0184 eval(updstr); +0185 data.invjoint.beta = value; +0186 data.invjoint.gamma = data.invjoint.alpha - data.invjoint.beta; +0187 setappdata(fig,'data',data); +0188 NUCLEUSinv_updateInterface; +0189 end +0190 end % switch out.panel +0191 setappdata(fig,'gui',gui); +0192 case 'MOD' +0193 % switch depending on the parent panel +0194 switch out.panel +0195 case 'geometry' +0196 switch out.field +0197 case 'beta' +0198 if value < 0.5 +0199 value = 1; +0200 elseif value > 89.5 +0201 value = 89; +0202 end +0203 eval(updstr); +0204 setappdata(fig,'data',data); +0205 otherwise +0206 % nothing to do +0207 end +0208 data = removeCalculationFields(data,'cps'); +0209 data = removeCalculationFields(data,'nmr'); +0210 clearSingleAxis(gui.axes_handles.cps); +0211 clearSingleAxis(gui.axes_handles.nmr); +0212 setappdata(fig,'data',data); +0213 calculateGeometry; +0214 +0215 case 'pressure' +0216 switch out.field +0217 case 'range' +0218 updstr = [out.updstr,'(',num2str(defaults(2)),... +0219 ',',num2str(defaults(3)),')',... +0220 '=value/data.pressure.unitfac;']; +0221 eval(updstr); +0222 end +0223 data = removeCalculationFields(data,'cps'); +0224 data = removeCalculationFields(data,'nmr'); +0225 clearSingleAxis(gui.axes_handles.cps); +0226 clearSingleAxis(gui.axes_handles.nmr); +0227 setappdata(fig,'data',data); +0228 NUCLEUSmod_updateInterface; +0229 +0230 case 'nmr' +0231 switch out.field +0232 case 'noise' +0233 switch data.nmr.noisetype +0234 case 'level' +0235 case 'SNR' +0236 if data.nmr.noise == 0 +0237 data.nmr.noise = Inf; +0238 set(src,'String',num2str(data.nmr.noise)); +0239 end +0240 end +0241 setappdata(fig,'data',data); +0242 if isfield(data.results,'NMR') +0243 updateNMRsignals; +0244 end +0245 case 'porosity' +0246 if data.nmr.porosity <= 0 || data.nmr.porosity > 1 +0247 data.nmr.porosity = 1; +0248 set(src,'String',num2str(data.nmr.porosity)); +0249 setappdata(fig,'data',data); +0250 end +0251 if isfield(data.results,'NMR') +0252 updateNMRsignals; +0253 end +0254 otherwise +0255 data = removeCalculationFields(data,'nmr'); +0256 clearSingleAxis(gui.axes_handles.nmr); +0257 setappdata(fig,'data',data); +0258 NUCLEUSmod_updateInterface; +0259 end +0260 end +0261 otherwise +0262 helpdlg({'function: onEditValue','Something is utterly wrong.'},... +0263 'wrong fig tag'); +0264 end 0265 -0266 %% License: -0267 % MIT License -0268 % -0269 % Copyright (c) 2018 Thomas Hiller -0270 % -0271 % Permission is hereby granted, free of charge, to any person obtaining a copy -0272 % of this software and associated documentation files (the "Software"), to deal -0273 % in the Software without restriction, including without limitation the rights -0274 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0275 % copies of the Software, and to permit persons to whom the Software is -0276 % furnished to do so, subject to the following conditions: -0277 % -0278 % The above copyright notice and this permission notice shall be included in all -0279 % copies or substantial portions of the Software. +0266 end +0267 +0268 function out = createDataString(tag) +0269 ind = strfind(tag,'_'); +0270 out.panel = tag(1:ind(1)-1); +0271 out.field = tag(ind(1)+1:end); +0272 tag(ind) = '.'; +0273 out.updstr = ['data.',tag]; +0274 end +0275 +0276 %------------- END OF CODE -------------- +0277 +0278 %% License: +0279 % MIT License 0280 % -0281 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0282 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0283 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0284 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0285 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0286 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0287 % SOFTWARE. +0281 % Copyright (c) 2018 Thomas Hiller +0282 % +0283 % Permission is hereby granted, free of charge, to any person obtaining a copy +0284 % of this software and associated documentation files (the "Software"), to deal +0285 % in the Software without restriction, including without limitation the rights +0286 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0287 % copies of the Software, and to permit persons to whom the Software is +0288 % furnished to do so, subject to the following conditions: +0289 % +0290 % The above copyright notice and this permission notice shall be included in all +0291 % copies or substantial portions of the Software. +0292 % +0293 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0294 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0295 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0296 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0297 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0298 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0299 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/listboxes/onListboxData.html b/doc/nucleus/callbacks/listboxes/onListboxData.html index c158387..35d90c8 100644 --- a/doc/nucleus/callbacks/listboxes/onListboxData.html +++ b/doc/nucleus/callbacks/listboxes/onListboxData.html @@ -59,8 +59,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=NUCLEUSinv_updateInterface updates all GUI elements
  • onPopupInvstdType selects the inversion method for the standard inversion
  • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
  • PhaseView is an extra subGUI to visualize the phase of a T2 signal
  • clearSingleAxis clears an individual axis
  • processNMRDataControl prepares simple NMR raw data processing
  • removeInversionFields deletes all inversion result fields from NUCLEUSinv
  • showFitStatistics shows an extra fit statistics figure (for T2 data)
  • updateInfo updates the information shown in all information list boxes
  • updatePlotsDistribution plots the RTD and PSD curves into NUCLEUSinv
  • updatePlotsJointInversion plots the joint-inversion results in NUCLEUSinv
  • updatePlotsLcurve plots the results of the L-curve calculation
  • updatePlotsSignal plots the raw and processed NMR signals in NUCLEUSinv
  • This function is called by: +
  • NUCLEUSinv_createPanelData creates data panel
  • importINV2INV imports a previously saved NUCLEUSinv session
  • runInversionBatch batch processes the inversion using for all NMR signals
  • @@ -110,8 +110,8 @@

    SOURCE CODE ^% none 0033 % 0034 % See also: NUCLEUSinv -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- @@ -176,7 +176,7 @@

    SOURCE CODE ^case 'T2' 0100 switch data.import.fileformat -0101 case {'dart','field','mouse','NMRMOD','excel'} +0101 case {'dart','excel','field','helios','mouse','NMRMOD'} 0102 data.process.gatetype = 'raw'; 0103 data.process.start = 1; 0104 otherwise @@ -225,86 +225,87 @@

    SOURCE CODE ^end 0148 if isfield(data.import,'NMRMOD') 0149 data.param.rho = data.import.NMR.para{id}.rho*1e6; -0150 data.invstd.Tbulk = data.import.NMR.para{id}.Tbulk; -0151 data.invstd.porosity = data.import.NMR.para{id}.porosity; -0152 end -0153 % --- -0154 -0155 % update the figure data -0156 setappdata(fig,'data',data); -0157 -0158 % process the NMR data -0159 processNMRDataControl(fig,id); -0160 % update interface -0161 NUCLEUSinv_updateInterface; -0162 -0163 % update the data plot axes -0164 updatePlotsSignal; -0165 % update the info fields -0166 updateInfo(gui.plots.SignalPanel); -0167 gui = getappdata(fig,'gui'); -0168 -0169 % clear inversion axes -0170 clearSingleAxis(gui.axes_handles.rtd); -0171 clearSingleAxis(gui.axes_handles.psd); -0172 end -0173 -0174 % set focus on data -0175 % set(gui.plots.SignalPanel,'Selection',1); -0176 % set(gui.plots.DistPanel,'Selection',1); -0177 -0178 % reset all RUN buttons -0179 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... -0180 'BackgroundColor','g','Enable','on','Callback',@onPushRun); -0181 set(gui.push_handles.invjoint_run,'String','<HTML><u>R</u>UN',... -0182 'BackgroundColor','g','Enable','on','Callback',@onPushRun); -0183 -0184 % if the Fit Statistics window is open update it -0185 if ~isempty(findobj('Tag','FITSTATS')) -0186 showFitStatistics; -0187 end -0188 % if the PhaseView window is open update it -0189 if ~isempty(findobj('Tag','PHASEVIEW')) -0190 PhaseView(gui.menu.extra_phaseview); -0191 end -0192 else -0193 helpdlg({'onListboxData:','Only choose one data set at a time.'},... -0194 'too many data'); -0195 end -0196 else -0197 helpdlg('Nothing to do because there is no data loaded!',... -0198 'onListboxData: Load NMR data first.'); -0199 end -0200 -0201 % update the figure data -0202 setappdata(fig,'gui',gui); -0203 -0204 end -0205 -0206 %------------- END OF CODE -------------- -0207 -0208 %% License: -0209 % MIT License -0210 % -0211 % Copyright (c) 2018 Thomas Hiller -0212 % -0213 % Permission is hereby granted, free of charge, to any person obtaining a copy -0214 % of this software and associated documentation files (the "Software"), to deal -0215 % in the Software without restriction, including without limitation the rights -0216 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0217 % copies of the Software, and to permit persons to whom the Software is -0218 % furnished to do so, subject to the following conditions: -0219 % -0220 % The above copyright notice and this permission notice shall be included in all -0221 % copies or substantial portions of the Software. -0222 % -0223 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0224 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0225 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0226 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0227 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0228 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0229 % SOFTWARE. +0150 data.invstd.Tbulk = data.import.NMR.para{id}.Tbulk; +0151 data.invstd.Tdiff = data.import.NMR.para{id}.Tdiff; +0152 data.invstd.porosity = data.import.NMR.para{id}.porosity; +0153 end +0154 % --- +0155 +0156 % update the figure data +0157 setappdata(fig,'data',data); +0158 +0159 % process the NMR data +0160 processNMRDataControl(fig,id); +0161 % update interface +0162 NUCLEUSinv_updateInterface; +0163 +0164 % update the data plot axes +0165 updatePlotsSignal; +0166 % update the info fields +0167 updateInfo(gui.plots.SignalPanel); +0168 gui = getappdata(fig,'gui'); +0169 +0170 % clear inversion axes +0171 clearSingleAxis(gui.axes_handles.rtd); +0172 clearSingleAxis(gui.axes_handles.psd); +0173 end +0174 +0175 % set focus on data +0176 % set(gui.plots.SignalPanel,'Selection',1); +0177 % set(gui.plots.DistPanel,'Selection',1); +0178 +0179 % reset all RUN buttons +0180 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... +0181 'BackgroundColor','g','Enable','on','Callback',@onPushRun); +0182 set(gui.push_handles.invjoint_run,'String','<HTML><u>R</u>UN',... +0183 'BackgroundColor','g','Enable','on','Callback',@onPushRun); +0184 +0185 % if the Fit Statistics window is open update it +0186 if ~isempty(findobj('Tag','FITSTATS')) +0187 showFitStatistics; +0188 end +0189 % if the PhaseView window is open update it +0190 if ~isempty(findobj('Tag','PHASEVIEW')) +0191 PhaseView(gui.menu.extra_phaseview); +0192 end +0193 else +0194 helpdlg({'onListboxData:','Only choose one data set at a time.'},... +0195 'too many data'); +0196 end +0197 else +0198 helpdlg('Nothing to do because there is no data loaded!',... +0199 'onListboxData: Load NMR data first.'); +0200 end +0201 +0202 % update the figure data +0203 setappdata(fig,'gui',gui); +0204 +0205 end +0206 +0207 %------------- END OF CODE -------------- +0208 +0209 %% License: +0210 % MIT License +0211 % +0212 % Copyright (c) 2018 Thomas Hiller +0213 % +0214 % Permission is hereby granted, free of charge, to any person obtaining a copy +0215 % of this software and associated documentation files (the "Software"), to deal +0216 % in the Software without restriction, including without limitation the rights +0217 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0218 % copies of the Software, and to permit persons to whom the Software is +0219 % furnished to do so, subject to the following conditions: +0220 % +0221 % The above copyright notice and this permission notice shall be included in all +0222 % copies or substantial portions of the Software. +0223 % +0224 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0225 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0226 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0227 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0228 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0229 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0230 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/menus/onMenuExpert.html b/doc/nucleus/callbacks/menus/onMenuExpert.html index 9adb255..982a24b 100644 --- a/doc/nucleus/callbacks/menus/onMenuExpert.html +++ b/doc/nucleus/callbacks/menus/onMenuExpert.html @@ -58,8 +58,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0032 % 0033 % See also: NUCLEUSinv -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- @@ -125,7 +125,7 @@

    SOURCE CODE ^% deactivate or activate expert mode 0050 switch onoff -0051 case 'on' % it it's on, switch it off +0051 case 'on' % if it's on, switch it off 0052 data.info.ExpertMode = 'off'; 0053 % menu entry 0054 set(gui.menu.extra_expert,'Checked','off'); diff --git a/doc/nucleus/callbacks/menus/onMenuExportData.html b/doc/nucleus/callbacks/menus/onMenuExportData.html index 96b9095..c1fb58a 100644 --- a/doc/nucleus/callbacks/menus/onMenuExportData.html +++ b/doc/nucleus/callbacks/menus/onMenuExportData.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuExportGraphics.html b/doc/nucleus/callbacks/menus/onMenuExportGraphics.html index 7892e5d..4fe1cd8 100644 --- a/doc/nucleus/callbacks/menus/onMenuExportGraphics.html +++ b/doc/nucleus/callbacks/menus/onMenuExportGraphics.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv, NUCLEUSmod -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuExtraColor.html b/doc/nucleus/callbacks/menus/onMenuExtraColor.html index 4158187..bdad2b5 100644 --- a/doc/nucleus/callbacks/menus/onMenuExtraColor.html +++ b/doc/nucleus/callbacks/menus/onMenuExtraColor.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuExtraRhoBounds.html b/doc/nucleus/callbacks/menus/onMenuExtraRhoBounds.html index a4c71a3..3864b30 100644 --- a/doc/nucleus/callbacks/menus/onMenuExtraRhoBounds.html +++ b/doc/nucleus/callbacks/menus/onMenuExtraRhoBounds.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuHelp.html b/doc/nucleus/callbacks/menus/onMenuHelp.html index 6bfa900..c4411f1 100644 --- a/doc/nucleus/callbacks/menus/onMenuHelp.html +++ b/doc/nucleus/callbacks/menus/onMenuHelp.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuImport.html b/doc/nucleus/callbacks/menus/onMenuImport.html index 5ef1d59..0c8c71e 100644 --- a/doc/nucleus/callbacks/menus/onMenuImport.html +++ b/doc/nucleus/callbacks/menus/onMenuImport.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv, NUCLEUSmod -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- @@ -143,55 +143,57 @@

    SOURCE CODE ^% activate the PhaseView GUI in case real data is imported 0070 switch menu_tag -0071 case {'NUCLEUSinv','NUCLEUSmod'} +0071 case 'NUCLEUSmod' 0072 set(gui.menu.extra_phaseview,'Enable','off'); 0073 otherwise 0074 set(gui.menu.extra_phaseview,'Enable','on'); 0075 end 0076 -0077 % update the "last import" value within the ini-file -0078 gui.myui.inidata.lastimport = [menu_tag,'_',label]; -0079 setappdata(fig,'gui',gui); -0080 gui = makeINIfile(gui,'update'); -0081 % and the menu entry itself -0082 switch label -0083 case 'LIAG from project' -0084 label = 'LIAG last project'; -0085 set(gui.menu.file_import_lastimport,'Label',label,... -0086 'Tag',menu_tag,'Callback',@onMenuImport) -0087 otherwise -0088 set(gui.menu.file_import_lastimport,'Label',label,... -0089 'Tag',menu_tag,'Callback',@onMenuImport); -0090 end -0091 setappdata(fig,'gui',gui); -0092 end -0093 +0077 % get updated gui data +0078 gui = getappdata(fig,'gui'); +0079 % update the "last import" value within the ini-file +0080 gui.myui.inidata.lastimport = [menu_tag,'_',label]; +0081 setappdata(fig,'gui',gui); +0082 gui = makeINIfile(gui,'update'); +0083 % and the menu entry itself +0084 switch label +0085 case 'LIAG from project' +0086 label = 'LIAG last project'; +0087 set(gui.menu.file_import_lastimport,'Label',label,... +0088 'Tag',menu_tag,'Callback',@onMenuImport) +0089 otherwise +0090 set(gui.menu.file_import_lastimport,'Label',label,... +0091 'Tag',menu_tag,'Callback',@onMenuImport); +0092 end +0093 setappdata(fig,'gui',gui); 0094 end 0095 -0096 %------------- END OF CODE -------------- +0096 end 0097 -0098 %% License: -0099 % MIT License -0100 % -0101 % Copyright (c) 2018 Thomas Hiller +0098 %------------- END OF CODE -------------- +0099 +0100 %% License: +0101 % MIT License 0102 % -0103 % Permission is hereby granted, free of charge, to any person obtaining a copy -0104 % of this software and associated documentation files (the "Software"), to deal -0105 % in the Software without restriction, including without limitation the rights -0106 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0107 % copies of the Software, and to permit persons to whom the Software is -0108 % furnished to do so, subject to the following conditions: -0109 % -0110 % The above copyright notice and this permission notice shall be included in all -0111 % copies or substantial portions of the Software. -0112 % -0113 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0114 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0115 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0116 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0117 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0118 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0119 % SOFTWARE. +0103 % Copyright (c) 2018 Thomas Hiller +0104 % +0105 % Permission is hereby granted, free of charge, to any person obtaining a copy +0106 % of this software and associated documentation files (the "Software"), to deal +0107 % in the Software without restriction, including without limitation the rights +0108 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0109 % copies of the Software, and to permit persons to whom the Software is +0110 % furnished to do so, subject to the following conditions: +0111 % +0112 % The above copyright notice and this permission notice shall be included in all +0113 % copies or substantial portions of the Software. +0114 % +0115 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0116 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0117 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0118 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0119 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0120 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0121 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/menus/onMenuJointInversion.html b/doc/nucleus/callbacks/menus/onMenuJointInversion.html index 08bc671..0de3dc8 100644 --- a/doc/nucleus/callbacks/menus/onMenuJointInversion.html +++ b/doc/nucleus/callbacks/menus/onMenuJointInversion.html @@ -57,8 +57,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0031 % 0032 % See also: NUCLEUSinv -0033 % Author: Thomas Hiller -0034 % email: thomas.hiller[at]leibniz-liag.de +0033 % Author: see AUTHORS.md +0034 % email: see AUTHORS.md 0035 % License: MIT License (at end) 0036 0037 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuRestartQuit.html b/doc/nucleus/callbacks/menus/onMenuRestartQuit.html index 9d0eb42..61cfa82 100644 --- a/doc/nucleus/callbacks/menus/onMenuRestartQuit.html +++ b/doc/nucleus/callbacks/menus/onMenuRestartQuit.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuSolver.html b/doc/nucleus/callbacks/menus/onMenuSolver.html index b5add69..c497fb5 100644 --- a/doc/nucleus/callbacks/menus/onMenuSolver.html +++ b/doc/nucleus/callbacks/menus/onMenuSolver.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuView.html b/doc/nucleus/callbacks/menus/onMenuView.html index 450947a..82dcdec 100644 --- a/doc/nucleus/callbacks/menus/onMenuView.html +++ b/doc/nucleus/callbacks/menus/onMenuView.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/menus/onMenuViewFigures.html b/doc/nucleus/callbacks/menus/onMenuViewFigures.html index ba60aaa..7591d1a 100644 --- a/doc/nucleus/callbacks/menus/onMenuViewFigures.html +++ b/doc/nucleus/callbacks/menus/onMenuViewFigures.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/menu.html b/doc/nucleus/callbacks/popup/menu.html index 90864a8..505f02b 100644 --- a/doc/nucleus/callbacks/popup/menu.html +++ b/doc/nucleus/callbacks/popup/menu.html @@ -18,7 +18,7 @@

    Index for nucleus\callbacks\popup

    Matlab files in this directory:

    +
  • onPopupGeometryModesN
  • onPopupGeometryPolyN
  • onPopupGeometrySinglePSD
  • onPopupGeometryType
  • onPopupInvjointGeometryType
  • onPopupInvjointPolyN
  • onPopupInvjointType
  • onPopupInvjointTypeOptional
  • onPopupInvstdType
  • onPopupInvstdTypeOptional
  • onPopupNMRNoiseType
  • onPopupPressureLoglin
  • onPopupPressureUnits
  • diff --git a/doc/nucleus/callbacks/popup/onPopupGeometryModesN.html b/doc/nucleus/callbacks/popup/onPopupGeometryModesN.html index 25565c8..9c0cb44 100644 --- a/doc/nucleus/callbacks/popup/onPopupGeometryModesN.html +++ b/doc/nucleus/callbacks/popup/onPopupGeometryModesN.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupGeometryPolyN.html b/doc/nucleus/callbacks/popup/onPopupGeometryPolyN.html index a2c16d1..405e7ce 100644 --- a/doc/nucleus/callbacks/popup/onPopupGeometryPolyN.html +++ b/doc/nucleus/callbacks/popup/onPopupGeometryPolyN.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupGeometrySinglePSD.html b/doc/nucleus/callbacks/popup/onPopupGeometrySinglePSD.html index cbd3eab..e328cdf 100644 --- a/doc/nucleus/callbacks/popup/onPopupGeometrySinglePSD.html +++ b/doc/nucleus/callbacks/popup/onPopupGeometrySinglePSD.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupGeometryType.html b/doc/nucleus/callbacks/popup/onPopupGeometryType.html index 52d9171..b2e116d 100644 --- a/doc/nucleus/callbacks/popup/onPopupGeometryType.html +++ b/doc/nucleus/callbacks/popup/onPopupGeometryType.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupInvjointGeometryType.html b/doc/nucleus/callbacks/popup/onPopupInvjointGeometryType.html index 3c47ec4..86cd5bf 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvjointGeometryType.html +++ b/doc/nucleus/callbacks/popup/onPopupInvjointGeometryType.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupInvjointPolyN.html b/doc/nucleus/callbacks/popup/onPopupInvjointPolyN.html index dad1dfa..8ccbb57 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvjointPolyN.html +++ b/doc/nucleus/callbacks/popup/onPopupInvjointPolyN.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupInvjointType.html b/doc/nucleus/callbacks/popup/onPopupInvjointType.html index 5570608..d1384aa 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvjointType.html +++ b/doc/nucleus/callbacks/popup/onPopupInvjointType.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupInvjointTypeOptional.html b/doc/nucleus/callbacks/popup/onPopupInvjointTypeOptional.html index 6205b64..0ce1742 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvjointTypeOptional.html +++ b/doc/nucleus/callbacks/popup/onPopupInvjointTypeOptional.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupInvstdType.html b/doc/nucleus/callbacks/popup/onPopupInvstdType.html index 85a592b..37a51e5 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvstdType.html +++ b/doc/nucleus/callbacks/popup/onPopupInvstdType.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- @@ -133,72 +133,82 @@

    SOURCE CODE ^'LU'; 0063 data.invstd.regtype = 'auto'; 0064 data.invstd.lambda = -1; -0065 end -0066 -0067 case 'off' -0068 switch value -0069 case 1 -0070 data.invstd.invtype = 'mono'; -0071 data.invstd.regtype = 'none'; -0072 data.invstd.lambda = 1; -0073 -0074 case 2 -0075 data.invstd.invtype = 'free'; +0065 +0066 case 5 +0067 data.invstd.invtype = 'MUMO'; +0068 data.invstd.regtype = 'none'; +0069 data.invstd.lambda = 1; +0070 end +0071 +0072 case 'off' +0073 switch value +0074 case 1 +0075 data.invstd.invtype = 'mono'; 0076 data.invstd.regtype = 'none'; 0077 data.invstd.lambda = 1; 0078 -0079 case 3 -0080 data.invstd.invtype = 'NNLS'; -0081 % for multi-exponential inversion log-gating is default -0082 data.process.gatetype = 'log'; +0079 case 2 +0080 data.invstd.invtype = 'free'; +0081 data.invstd.regtype = 'none'; +0082 data.invstd.lambda = 1; 0083 -0084 % set LIAG defaults -0085 if isfield(data.import,'LIAG') -0086 data.invstd.regtype = 'lcurve'; -0087 data.invstd.lambda = 1; -0088 data.invstd.lambdaR = [1e-5 1]; -0089 else -0090 data.invstd.regtype = 'manual'; -0091 data.invstd.lambda = 1e-2; -0092 end -0093 % update GUI data -0094 setappdata(fig,'data',data); -0095 % because the gate type could have changed update data -0096 onRadioGates(gui.radio_handles.process_gates_log); -0097 data = getappdata(fig,'data'); -0098 end -0099 end -0100 % update GUI data -0101 setappdata(fig,'data',data); -0102 % update interface -0103 NUCLEUSinv_updateInterface; -0104 -0105 end -0106 -0107 %------------- END OF CODE -------------- -0108 -0109 %% License: -0110 % MIT License -0111 % -0112 % Copyright (c) 2018 Thomas Hiller -0113 % -0114 % Permission is hereby granted, free of charge, to any person obtaining a copy -0115 % of this software and associated documentation files (the "Software"), to deal -0116 % in the Software without restriction, including without limitation the rights -0117 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0118 % copies of the Software, and to permit persons to whom the Software is -0119 % furnished to do so, subject to the following conditions: -0120 % -0121 % The above copyright notice and this permission notice shall be included in all -0122 % copies or substantial portions of the Software. +0084 case 3 +0085 data.invstd.invtype = 'NNLS'; +0086 % for multi-exponential inversion log-gating is default +0087 data.process.gatetype = 'log'; +0088 +0089 % set LIAG defaults +0090 if isfield(data.import,'LIAG') +0091 data.invstd.regtype = 'lcurve'; +0092 data.invstd.lambda = 1; +0093 data.invstd.lambdaR = [1e-5 1]; +0094 else +0095 data.invstd.regtype = 'manual'; +0096 data.invstd.lambda = 1e-2; +0097 end +0098 % update GUI data +0099 setappdata(fig,'data',data); +0100 % because the gate type could have changed update data +0101 onRadioGates(gui.radio_handles.process_gates_log); +0102 data = getappdata(fig,'data'); +0103 +0104 case 4 +0105 data.invstd.invtype = 'MUMO'; +0106 data.invstd.regtype = 'none'; +0107 data.invstd.lambda = 1; +0108 end +0109 end +0110 % update GUI data +0111 setappdata(fig,'data',data); +0112 % update interface +0113 NUCLEUSinv_updateInterface; +0114 +0115 end +0116 +0117 %------------- END OF CODE -------------- +0118 +0119 %% License: +0120 % MIT License +0121 % +0122 % Copyright (c) 2018 Thomas Hiller 0123 % -0124 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0125 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0126 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0127 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0128 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0129 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0130 % SOFTWARE. +0124 % Permission is hereby granted, free of charge, to any person obtaining a copy +0125 % of this software and associated documentation files (the "Software"), to deal +0126 % in the Software without restriction, including without limitation the rights +0127 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0128 % copies of the Software, and to permit persons to whom the Software is +0129 % furnished to do so, subject to the following conditions: +0130 % +0131 % The above copyright notice and this permission notice shall be included in all +0132 % copies or substantial portions of the Software. +0133 % +0134 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0135 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0136 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0137 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0138 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0139 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0140 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/popup/onPopupInvstdTypeOptional.html b/doc/nucleus/callbacks/popup/onPopupInvstdTypeOptional.html index f895843..b4741b1 100644 --- a/doc/nucleus/callbacks/popup/onPopupInvstdTypeOptional.html +++ b/doc/nucleus/callbacks/popup/onPopupInvstdTypeOptional.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSinv -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- @@ -208,39 +208,43 @@

    SOURCE CODE ^'auto'; 0136 data.invstd.lambda = -1; 0137 end -0138 end -0139 -0140 % update GUI data -0141 setappdata(fig,'data',data); -0142 % update interface -0143 NUCLEUSinv_updateInterface; -0144 -0145 end -0146 -0147 %------------- END OF CODE -------------- +0138 +0139 case 'MUMO' +0140 % # free distributions = value (1 to 4) +0141 data.invstd.freeDT = value; +0142 end +0143 +0144 % update GUI data +0145 setappdata(fig,'data',data); +0146 % update interface +0147 NUCLEUSinv_updateInterface; 0148 -0149 %% License: -0150 % MIT License -0151 % -0152 % Copyright (c) 2018 Thomas Hiller -0153 % -0154 % Permission is hereby granted, free of charge, to any person obtaining a copy -0155 % of this software and associated documentation files (the "Software"), to deal -0156 % in the Software without restriction, including without limitation the rights -0157 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0158 % copies of the Software, and to permit persons to whom the Software is -0159 % furnished to do so, subject to the following conditions: -0160 % -0161 % The above copyright notice and this permission notice shall be included in all -0162 % copies or substantial portions of the Software. -0163 % -0164 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0165 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0166 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0167 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0168 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0169 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0170 % SOFTWARE. +0149 end +0150 +0151 %------------- END OF CODE -------------- +0152 +0153 %% License: +0154 % MIT License +0155 % +0156 % Copyright (c) 2018 Thomas Hiller +0157 % +0158 % Permission is hereby granted, free of charge, to any person obtaining a copy +0159 % of this software and associated documentation files (the "Software"), to deal +0160 % in the Software without restriction, including without limitation the rights +0161 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0162 % copies of the Software, and to permit persons to whom the Software is +0163 % furnished to do so, subject to the following conditions: +0164 % +0165 % The above copyright notice and this permission notice shall be included in all +0166 % copies or substantial portions of the Software. +0167 % +0168 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0169 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0170 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0171 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0172 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0173 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0174 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/callbacks/popup/onPopupNMRNoiseType.html b/doc/nucleus/callbacks/popup/onPopupNMRNoiseType.html new file mode 100644 index 0000000..903caf3 --- /dev/null +++ b/doc/nucleus/callbacks/popup/onPopupNMRNoiseType.html @@ -0,0 +1,166 @@ + + + + Description of onPopupNMRNoiseType + + + + + + + + + + + +

    onPopupNMRNoiseType +

    + +

    PURPOSE ^

    +
    selects the noise type to be aplied to the forward
    + +

    SYNOPSIS ^

    +
    function onPopupNMRNoiseType(src,~)
    + +

    DESCRIPTION ^

    +
    onPopupNMRNoiseType selects the noise type to be aplied to the forward
    +modelled NMR data
    +
    + Syntax:
    +       onPopupNMRNoiseType
    +
    + Inputs:
    +       src - handle of the calling object
    +
    + Outputs:
    +       none
    +
    + Example:
    +       onPopupNMRNoiseType(src,~)
    +
    + Other m-files required:
    +       clearSingleAxis.m
    +       updateCPSTable.m
    +
    + Subfunctions:
    +       none
    +
    + MAT-files required:
    +       none
    +
    + See also: NUCLEUSmod
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function onPopupNMRNoiseType(src,~)
    +0002 %onPopupNMRNoiseType selects the noise type to be aplied to the forward
    +0003 %modelled NMR data
    +0004 %
    +0005 % Syntax:
    +0006 %       onPopupNMRNoiseType
    +0007 %
    +0008 % Inputs:
    +0009 %       src - handle of the calling object
    +0010 %
    +0011 % Outputs:
    +0012 %       none
    +0013 %
    +0014 % Example:
    +0015 %       onPopupNMRNoiseType(src,~)
    +0016 %
    +0017 % Other m-files required:
    +0018 %       clearSingleAxis.m
    +0019 %       updateCPSTable.m
    +0020 %
    +0021 % Subfunctions:
    +0022 %       none
    +0023 %
    +0024 % MAT-files required:
    +0025 %       none
    +0026 %
    +0027 % See also: NUCLEUSmod
    +0028 % Author: see AUTHORS.md
    +0029 % email: see AUTHORS.md
    +0030 % License: MIT License (at end)
    +0031 
    +0032 %------------- BEGIN CODE --------------
    +0033 
    +0034 %% get GUI handle and data
    +0035 fig = findobj('Tag','MOD');
    +0036 data = getappdata(fig,'data');
    +0037 gui = getappdata(fig,'gui');
    +0038 
    +0039 % get the value of the popup menu
    +0040 value = get(src,'Value');
    +0041 
    +0042 % change settings accordingly
    +0043 switch value
    +0044     case 1 % noise level
    +0045         data.nmr.noisetype = 'level';
    +0046         if data.nmr.noise > 0
    +0047             data.nmr.noise = 1/data.nmr.noise;
    +0048         end        
    +0049     case 2 % signal-to-noise ratio (SNR)
    +0050         data.nmr.noisetype = 'SNR';
    +0051         if data.nmr.noise == 0
    +0052             data.nmr.noise = inf;
    +0053         else
    +0054             data.nmr.noise = 1/data.nmr.noise;
    +0055         end        
    +0056 end
    +0057 % update the corresponding edit field
    +0058 set(gui.edit_handles.noise,'String',num2str(data.nmr.noise));
    +0059 
    +0060 % update GUI data
    +0061 setappdata(fig,'data',data);
    +0062 % update NMR data (if available)
    +0063 if isfield(data.results,'NMR')
    +0064     updateNMRsignals;
    +0065 end
    +0066 
    +0067 end
    +0068 
    +0069 %------------- END OF CODE --------------
    +0070 
    +0071 %% License:
    +0072 % MIT License
    +0073 %
    +0074 % Copyright (c) 2021 Thomas Hiller
    +0075 %
    +0076 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0077 % of this software and associated documentation files (the "Software"), to deal
    +0078 % in the Software without restriction, including without limitation the rights
    +0079 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0080 % copies of the Software, and to permit persons to whom the Software is
    +0081 % furnished to do so, subject to the following conditions:
    +0082 %
    +0083 % The above copyright notice and this permission notice shall be included in all
    +0084 % copies or substantial portions of the Software.
    +0085 %
    +0086 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0087 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0088 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0089 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0090 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0091 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0092 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/callbacks/popup/onPopupPressureLoglin.html b/doc/nucleus/callbacks/popup/onPopupPressureLoglin.html index d2dd444..87ff18f 100644 --- a/doc/nucleus/callbacks/popup/onPopupPressureLoglin.html +++ b/doc/nucleus/callbacks/popup/onPopupPressureLoglin.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/popup/onPopupPressureUnits.html b/doc/nucleus/callbacks/popup/onPopupPressureUnits.html index 05bc112..9aed5a1 100644 --- a/doc/nucleus/callbacks/popup/onPopupPressureUnits.html +++ b/doc/nucleus/callbacks/popup/onPopupPressureUnits.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/push/onPushCPSTable.html b/doc/nucleus/callbacks/push/onPushCPSTable.html index 7798c0f..fe029c6 100644 --- a/doc/nucleus/callbacks/push/onPushCPSTable.html +++ b/doc/nucleus/callbacks/push/onPushCPSTable.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/push/onPushRun.html b/doc/nucleus/callbacks/push/onPushRun.html index dbb9b2d..9efaa6b 100644 --- a/doc/nucleus/callbacks/push/onPushRun.html +++ b/doc/nucleus/callbacks/push/onPushRun.html @@ -54,8 +54,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=calculateNMR calculates the NMR signals for the full and partially saturated
  • caluclatePressureSaturation calculates the geometry dependent pressure
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • This function is called by: +
  • NUCLEUSinv_createPanelInversionJoint creates joint inversion panel
  • NUCLEUSinv_createPanelInversionStd creates standard inversion panel
  • NUCLEUSmod_createPanelCPS creates pressure panel
  • NUCLEUSmod_createPanelNMR creates NMR panel
  • onContextSignalList handles the calls from the context menu of the data
  • onListboxData handles the calls from the context menu of the data
  • onPushStop recognizes that a STOP push button was pressed and resets the
  • caluclatePressureSaturation calculates the geometry dependent pressure
  • runInversionBatch batch processes the inversion using for all NMR signals
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • @@ -100,8 +100,8 @@

    SOURCE CODE ^% calculateNMR 0028 % 0029 % See also: NUCLEUSinv -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/push/onPushShowHide.html b/doc/nucleus/callbacks/push/onPushShowHide.html index 1390482..a90cfa7 100644 --- a/doc/nucleus/callbacks/push/onPushShowHide.html +++ b/doc/nucleus/callbacks/push/onPushShowHide.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/push/onPushStop.html b/doc/nucleus/callbacks/push/onPushStop.html index 950fb37..5c6d797 100644 --- a/doc/nucleus/callbacks/push/onPushStop.html +++ b/doc/nucleus/callbacks/push/onPushStop.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/radio/onRadioGates.html b/doc/nucleus/callbacks/radio/onRadioGates.html index 193e5cc..6b576d9 100644 --- a/doc/nucleus/callbacks/radio/onRadioGates.html +++ b/doc/nucleus/callbacks/radio/onRadioGates.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/radio/onRadioLorder.html b/doc/nucleus/callbacks/radio/onRadioLorder.html index 35ac910..bc97afe 100644 --- a/doc/nucleus/callbacks/radio/onRadioLorder.html +++ b/doc/nucleus/callbacks/radio/onRadioLorder.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/radio/onRadioNormalize.html b/doc/nucleus/callbacks/radio/onRadioNormalize.html index 454d428..1ef34e5 100644 --- a/doc/nucleus/callbacks/radio/onRadioNormalize.html +++ b/doc/nucleus/callbacks/radio/onRadioNormalize.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/callbacks/radio/onRadioTimescale.html b/doc/nucleus/callbacks/radio/onRadioTimescale.html index c7ad599..5176a82 100644 --- a/doc/nucleus/callbacks/radio/onRadioTimescale.html +++ b/doc/nucleus/callbacks/radio/onRadioTimescale.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -127,68 +127,70 @@

    SOURCE CODE ^end -0059 -0060 case 'ms' -0061 data.process.timescale = 'ms'; -0062 if data.process.timefac == 1 -0063 data.process.timefac = 1000; -0064 data.invstd.time = data.invstd.time .* 1000; -0065 data.invstd.Tbulk = data.invstd.Tbulk .* 1000; -0066 end -0067 end -0068 -0069 % update GUI data -0070 setappdata(fig,'data',data); -0071 % update interface -0072 NUCLEUSinv_updateInterface; -0073 % process NMR signal -0074 processNMRDataControl(fig,id); -0075 % update plots -0076 updatePlotsSignal; -0077 % clear axes -0078 clearSingleAxis(gui.axes_handles.rtd); -0079 clearSingleAxis(gui.axes_handles.psd); -0080 % set focus on data -0081 set(gui.plots.SignalPanel,'Selection',1); -0082 -0083 else -0084 % reset to defaults -0085 data.process.timescale = 's'; -0086 % update GUI data -0087 setappdata(fig,'data',data); -0088 % update interface -0089 NUCLEUSinv_updateInterface; -0090 helpdlg('Nothing to do because no data set is selected!',... -0091 'onRadioTimescale: Select NMR data first.'); -0092 end -0093 +0058 data.invstd.Tdiff = data.invstd.Tdiff ./ 1000; +0059 end +0060 +0061 case 'ms' +0062 data.process.timescale = 'ms'; +0063 if data.process.timefac == 1 +0064 data.process.timefac = 1000; +0065 data.invstd.time = data.invstd.time .* 1000; +0066 data.invstd.Tbulk = data.invstd.Tbulk .* 1000; +0067 data.invstd.Tdiff = data.invstd.Tdiff .* 1000; +0068 end +0069 end +0070 +0071 % update GUI data +0072 setappdata(fig,'data',data); +0073 % update interface +0074 NUCLEUSinv_updateInterface; +0075 % process NMR signal +0076 processNMRDataControl(fig,id); +0077 % update plots +0078 updatePlotsSignal; +0079 % clear axes +0080 clearSingleAxis(gui.axes_handles.rtd); +0081 clearSingleAxis(gui.axes_handles.psd); +0082 % set focus on data +0083 set(gui.plots.SignalPanel,'Selection',1); +0084 +0085 else +0086 % reset to defaults +0087 data.process.timescale = 's'; +0088 % update GUI data +0089 setappdata(fig,'data',data); +0090 % update interface +0091 NUCLEUSinv_updateInterface; +0092 helpdlg('Nothing to do because no data set is selected!',... +0093 'onRadioTimescale: Select NMR data first.'); 0094 end 0095 -0096 %------------- END OF CODE -------------- +0096 end 0097 -0098 %% License: -0099 % MIT License -0100 % -0101 % Copyright (c) 2018 Thomas Hiller +0098 %------------- END OF CODE -------------- +0099 +0100 %% License: +0101 % MIT License 0102 % -0103 % Permission is hereby granted, free of charge, to any person obtaining a copy -0104 % of this software and associated documentation files (the "Software"), to deal -0105 % in the Software without restriction, including without limitation the rights -0106 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0107 % copies of the Software, and to permit persons to whom the Software is -0108 % furnished to do so, subject to the following conditions: -0109 % -0110 % The above copyright notice and this permission notice shall be included in all -0111 % copies or substantial portions of the Software. -0112 % -0113 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0114 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0115 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0116 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0117 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0118 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0119 % SOFTWARE. +0103 % Copyright (c) 2018 Thomas Hiller +0104 % +0105 % Permission is hereby granted, free of charge, to any person obtaining a copy +0106 % of this software and associated documentation files (the "Software"), to deal +0107 % in the Software without restriction, including without limitation the rights +0108 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0109 % copies of the Software, and to permit persons to whom the Software is +0110 % furnished to do so, subject to the following conditions: +0111 % +0112 % The above copyright notice and this permission notice shall be included in all +0113 % copies or substantial portions of the Software. +0114 % +0115 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0116 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0117 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0118 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0119 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0120 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0121 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/import/LoadNMRData_bamtom.html b/doc/nucleus/functions/import/LoadNMRData_bamtom.html index fb61596..6f3150d 100644 --- a/doc/nucleus/functions/import/LoadNMRData_bamtom.html +++ b/doc/nucleus/functions/import/LoadNMRData_bamtom.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_bgr.html b/doc/nucleus/functions/import/LoadNMRData_bgr.html index ae440da..9286697 100644 --- a/doc/nucleus/functions/import/LoadNMRData_bgr.html +++ b/doc/nucleus/functions/import/LoadNMRData_bgr.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_bgr2.html b/doc/nucleus/functions/import/LoadNMRData_bgr2.html index 0f4917a..fd41330 100644 --- a/doc/nucleus/functions/import/LoadNMRData_bgr2.html +++ b/doc/nucleus/functions/import/LoadNMRData_bgr2.html @@ -69,7 +69,7 @@

    CROSS-REFERENCE INFORMATION ^
 <li><a href=fixParameterString cleans parameter file lines when the properties have a
  • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
  • This function is called by: +

    SUBFUNCTIONS ^

    diff --git a/doc/nucleus/functions/import/LoadNMRData_bgrmat.html b/doc/nucleus/functions/import/LoadNMRData_bgrmat.html index c3b4d89..e8bb426 100644 --- a/doc/nucleus/functions/import/LoadNMRData_bgrmat.html +++ b/doc/nucleus/functions/import/LoadNMRData_bgrmat.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_corelab.html b/doc/nucleus/functions/import/LoadNMRData_corelab.html index 95a3ab0..3696a9a 100644 --- a/doc/nucleus/functions/import/LoadNMRData_corelab.html +++ b/doc/nucleus/functions/import/LoadNMRData_corelab.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_dart.html b/doc/nucleus/functions/import/LoadNMRData_dart.html index 673451c..2c987f7 100644 --- a/doc/nucleus/functions/import/LoadNMRData_dart.html +++ b/doc/nucleus/functions/import/LoadNMRData_dart.html @@ -58,8 +58,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0032 % 0033 % See also: NUCLEUSinv -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_driver.html b/doc/nucleus/functions/import/LoadNMRData_driver.html index 8344351..d264e57 100644 --- a/doc/nucleus/functions/import/LoadNMRData_driver.html +++ b/doc/nucleus/functions/import/LoadNMRData_driver.html @@ -54,8 +54,9 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • LoadNMRData_bamtom loads BAM TOM data
  • LoadNMRData_bgr loads standard BGR NMR data
  • LoadNMRData_bgrmat loads already preprocessed BGR NMR data; check the
  • LoadNMRData_corelab loads NMR data as provided by Corelab
  • LoadNMRData_dart loads RWTH NMR data (recorded with the Dart device); the
  • LoadNMRData_field loads RWTH field NMR data (Blümich group bore-hole tool)
  • LoadNMRData_helios loads BGR NMR data from a typical folder structure
  • LoadNMRData_ibac imports NMR data from the PM5 and PM25
  • LoadNMRData_liag loads LIAG NMR data
  • LoadNMRData_mouse loads NMR data saved by the MOUSE
  • LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
  • This function is called by: @@ -111,93 +112,100 @@

    SOURCE CODE ^% LoadNMRData_mouse 0028 % LoadNMRData_liag 0029 % LoadNMRData_bgr -0030 % LoadNMRData_bgr2 +0030 % LoadNMRData_mousecpmg 0031 % LoadNMRData_bgrmat -0032 % LoadNMRData_bamtom -0033 % -0034 % Subfunctions: -0035 % none -0036 % -0037 % MAT-files required: -0038 % none -0039 % -0040 % See also: NUCLEUSinv -0041 % Author: Thomas Hiller -0042 % email: thomas.hiller[at]leibniz-liag.de -0043 % License: MIT License (at end) -0044 -0045 %------------- BEGIN CODE -------------- -0046 -0047 %% select the routine depending on the file format -0048 switch in.fileformat -0049 case 'bamtom' -0050 out = LoadNMRData_bamtom(in); -0051 case 'bgr' -0052 out = LoadNMRData_bgr(in); -0053 case 'bgr2' -0054 out = LoadNMRData_bgr2(in); -0055 case 'bgrmat' -0056 out = LoadNMRData_bgrmat(in); -0057 case 'corelab' -0058 out = LoadNMRData_corelab(in); -0059 case 'dart' -0060 in.version = 2; % use the updated mat-file format -0061 out = LoadNMRData_dart(in); -0062 case 'field' -0063 out = LoadNMRData_field(in); -0064 case 'liag' -0065 out = LoadNMRData_liag(in); -0066 case 'mouse' -0067 out = LoadNMRData_mouse(in); -0068 case 'rwth' -0069 out = LoadNMRData_rwth(in); -0070 case 'pm5' -0071 out = LoadNMRData_ibac(in); -0072 case 'pm25' -0073 out = LoadNMRData_ibac(in); -0074 end -0075 -0076 % if an imported T2 signal has no imaginary part, the noise is estimated -0077 % from an exponential fit -0078 for i = 1:numel(out.nmrData) -0079 if strcmp(out.nmrData{i}.flag,'T2') && isreal(out.nmrData{i}.signal) -0080 disp('NUCLUESinv import: Estimating noise from exponential fit ...'); -0081 param.T1IRfac = out.nmrData{i}.T1IRfac; -0082 param.noise = 0; -0083 param.optim = 'off'; -0084 invstd = fitDataFree(out.nmrData{i}.time,out.nmrData{i}.signal,... -0085 'T2',param,5); -0086 out.nmrData{i}.noise = invstd.rms; -0087 disp('NUCLUESinv import: done.') -0088 end -0089 end -0090 -0091 return -0092 -0093 %------------- END OF CODE -------------- -0094 -0095 %% License: -0096 % MIT License -0097 % -0098 % Copyright (c) 2018 Thomas Hiller -0099 % -0100 % Permission is hereby granted, free of charge, to any person obtaining a copy -0101 % of this software and associated documentation files (the "Software"), to deal -0102 % in the Software without restriction, including without limitation the rights -0103 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0104 % copies of the Software, and to permit persons to whom the Software is -0105 % furnished to do so, subject to the following conditions: +0032 % LoadNMRData_helios +0033 % LoadNMRData_bamtom +0034 % +0035 % Subfunctions: +0036 % none +0037 % +0038 % MAT-files required: +0039 % none +0040 % +0041 % See also: NUCLEUSinv +0042 % Author: see AUTHORS.md +0043 % email: see AUTHORS.md +0044 % License: MIT License (at end) +0045 +0046 %------------- BEGIN CODE -------------- +0047 +0048 %% select the routine depending on the file format +0049 switch in.fileformat +0050 case 'bamtom' +0051 out = LoadNMRData_bamtom(in); +0052 case 'bgr' +0053 out = LoadNMRData_bgr(in); +0054 case 'MouseCPMG' +0055 out = LoadNMRData_mousecpmg(in); +0056 case 'bgrmat' +0057 out = LoadNMRData_bgrmat(in); +0058 case {'MouseLiftSingle','MouseLiftAll'} +0059 out = LoadNMRData_mouselift(in); +0060 case 'helios' +0061 out = LoadNMRData_helios(in); +0062 case 'corelab' +0063 out = LoadNMRData_corelab(in); +0064 case 'dart' +0065 in.version = 2; % use the updated mat-file format +0066 out = LoadNMRData_dart(in); +0067 case 'field' +0068 out = LoadNMRData_field(in); +0069 case 'liag' +0070 out = LoadNMRData_liag(in); +0071 case 'mouse' +0072 out = LoadNMRData_mouse(in); +0073 case 'rwth' +0074 out = LoadNMRData_rwth(in); +0075 case 'pm5' +0076 out = LoadNMRData_ibac(in); +0077 case 'pm25' +0078 out = LoadNMRData_ibac(in); +0079 end +0080 +0081 % if an imported T2 signal has no imaginary part, the noise is estimated +0082 % from an exponential fit +0083 if ~strcmp(in.fileformat,'helios') +0084 for i = 1:numel(out.nmrData) +0085 if isreal(out.nmrData{i}.signal) +0086 disp('NUCLUESinv import: Estimating noise from exponential fit ...'); +0087 param.T1IRfac = out.nmrData{i}.T1IRfac; +0088 param.noise = 0; +0089 param.optim = 'off'; +0090 invstd = fitDataFree(out.nmrData{i}.time,out.nmrData{i}.signal,... +0091 out.nmrData{i}.flag,param,5); +0092 out.nmrData{i}.noise = invstd.rms; +0093 disp('NUCLUESinv import: done.') +0094 end +0095 end +0096 end +0097 +0098 return +0099 +0100 %------------- END OF CODE -------------- +0101 +0102 %% License: +0103 % MIT License +0104 % +0105 % Copyright (c) 2018 Thomas Hiller 0106 % -0107 % The above copyright notice and this permission notice shall be included in all -0108 % copies or substantial portions of the Software. -0109 % -0110 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0111 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0112 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0113 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0114 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0115 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0116 % SOFTWARE. +0107 % Permission is hereby granted, free of charge, to any person obtaining a copy +0108 % of this software and associated documentation files (the "Software"), to deal +0109 % in the Software without restriction, including without limitation the rights +0110 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0111 % copies of the Software, and to permit persons to whom the Software is +0112 % furnished to do so, subject to the following conditions: +0113 % +0114 % The above copyright notice and this permission notice shall be included in all +0115 % copies or substantial portions of the Software. +0116 % +0117 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0118 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0119 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0120 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0121 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0122 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0123 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/import/LoadNMRData_field.html b/doc/nucleus/functions/import/LoadNMRData_field.html index c5df338..fffb32a 100644 --- a/doc/nucleus/functions/import/LoadNMRData_field.html +++ b/doc/nucleus/functions/import/LoadNMRData_field.html @@ -57,8 +57,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0031 % 0032 % See also: NUCLEUSinv -0033 % Author: Thomas Hiller -0034 % email: thomas.hiller[at]leibniz-liag.de +0033 % Author: see AUTHORS.md +0034 % email: see AUTHORS.md 0035 % License: MIT License (at end) 0036 0037 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_helios.html b/doc/nucleus/functions/import/LoadNMRData_helios.html new file mode 100644 index 0000000..42ebcea --- /dev/null +++ b/doc/nucleus/functions/import/LoadNMRData_helios.html @@ -0,0 +1,195 @@ + + + + Description of LoadNMRData_helios + + + + + + + + + + + +

    LoadNMRData_helios +

    + +

    PURPOSE ^

    +
    loads BGR NMR data from a typical folder structure
    + +

    SYNOPSIS ^

    +
    function out = LoadNMRData_helios(in)
    + +

    DESCRIPTION ^

    +
    LoadNMRData_helios loads BGR NMR data from a typical folder structure 
    +produced by the Helios NMR Scanner 
    +
    + Syntax:
    +       out = LoadNMRData_helios(in)
    +
    + Inputs:
    +       in - input structure
    +       in.path - data path
    +       in.T1T2 - T1 / T2 flag
    +       in.fileformat - 'helios'
    +
    + Outputs:
    +       out - output structure
    +       out.parData - parameter file data
    +       out.nmrData - NMR data
    +
    + Example:
    +       out = LoadNMRData_helios(in)
    +
    + Other m-files required:
    +       fixParameterString
    +
    + Subfunctions:
    +       LoadDataFile
    +
    + MAT-files required:
    +       none
    +
    + See also: NUCLEUSinv
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
    +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function out = LoadNMRData_helios(in)
    +0002 %LoadNMRData_helios loads BGR NMR data from a typical folder structure
    +0003 %produced by the Helios NMR Scanner
    +0004 %
    +0005 % Syntax:
    +0006 %       out = LoadNMRData_helios(in)
    +0007 %
    +0008 % Inputs:
    +0009 %       in - input structure
    +0010 %       in.path - data path
    +0011 %       in.T1T2 - T1 / T2 flag
    +0012 %       in.fileformat - 'helios'
    +0013 %
    +0014 % Outputs:
    +0015 %       out - output structure
    +0016 %       out.parData - parameter file data
    +0017 %       out.nmrData - NMR data
    +0018 %
    +0019 % Example:
    +0020 %       out = LoadNMRData_helios(in)
    +0021 %
    +0022 % Other m-files required:
    +0023 %       fixParameterString
    +0024 %
    +0025 % Subfunctions:
    +0026 %       LoadDataFile
    +0027 %
    +0028 % MAT-files required:
    +0029 %       none
    +0030 %
    +0031 % See also: NUCLEUSinv
    +0032 % Author: see AUTHORS.md
    +0033 % email: see AUTHORS.md
    +0034 % License: MIT License (at end)
    +0035 
    +0036 %------------- BEGIN CODE --------------
    +0037 
    +0038 %% start processing the files
    +0039 
    +0040 % read the data file
    +0041 datafile = dir(fullfile(in.path,in.name));
    +0042 [data,parData] = LoadDataFile(in.path,in.name,in.T1T2);
    +0043 
    +0044 % get file statistics
    +0045 nmrData.datfile = datafile.name;
    +0046 nmrData.date = datafile.date;
    +0047 nmrData.datenum = datafile.datenum;
    +0048 nmrData.bytes = datafile.bytes;
    +0049 
    +0050 % save the NMR data
    +0051 nmrData.flag = data.flag;
    +0052 nmrData.T1IRfac = 1;
    +0053 nmrData.time = data.time;
    +0054 nmrData.signal = data.signal;
    +0055 nmrData.raw = data.raw;
    +0056 if strcmp(in.T1T2,'T2')
    +0057     nmrData.phase = data.phase;
    +0058 end
    +0059 
    +0060 % save data to output struct
    +0061 out.parData = parData;
    +0062 out.nmrData = nmrData;
    +0063 
    +0064 end
    +0065 
    +0066 %% load NMR data file
    +0067 function [data,pardata] = LoadDataFile(datapath,fname,flag)
    +0068 
    +0069 A = importdata(fullfile(datapath,fname),'\t');
    +0070 t_echo = A(1,2);
    +0071 n_echo = A(1,3);
    +0072 
    +0073 time = t_echo:t_echo:t_echo*n_echo;
    +0074 time = time(:);
    +0075 re = -A(2,1:length(time));
    +0076 im = -A(3,1:length(time));
    +0077 re = re(:);
    +0078 im = im(:);
    +0079 
    +0080 data.flag = flag;
    +0081 data.time = time;
    +0082 data.signal = complex(re,im);
    +0083 [data.signal,data.phase] = rotateT2phase(data.signal);
    +0084 
    +0085 data.raw.time = data.time;
    +0086 data.raw.signal = data.signal;
    +0087 
    +0088 pardata.t_echo = t_echo; 
    +0089 pardata.n_echo = n_echo; 
    +0090 
    +0091 end
    +0092 %------------- END OF CODE --------------
    +0093 
    +0094 %% License:
    +0095 % MIT License
    +0096 %
    +0097 % Copyright (c) 2021 Stephan Costabel
    +0098 %
    +0099 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0100 % of this software and associated documentation files (the "Software"), to deal
    +0101 % in the Software without restriction, including without limitation the rights
    +0102 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0103 % copies of the Software, and to permit persons to whom the Software is
    +0104 % furnished to do so, subject to the following conditions:
    +0105 %
    +0106 % The above copyright notice and this permission notice shall be included in all
    +0107 % copies or substantial portions of the Software.
    +0108 %
    +0109 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0110 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0111 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0112 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0113 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0114 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0115 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/import/LoadNMRData_ibac.html b/doc/nucleus/functions/import/LoadNMRData_ibac.html index ff41bbd..3a0a2bc 100644 --- a/doc/nucleus/functions/import/LoadNMRData_ibac.html +++ b/doc/nucleus/functions/import/LoadNMRData_ibac.html @@ -59,8 +59,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0033 % 0034 % See also: NUCLEUSinv -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_liag.html b/doc/nucleus/functions/import/LoadNMRData_liag.html index 9c9a4eb..6b3f19d 100644 --- a/doc/nucleus/functions/import/LoadNMRData_liag.html +++ b/doc/nucleus/functions/import/LoadNMRData_liag.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/LoadNMRData_mouse.html b/doc/nucleus/functions/import/LoadNMRData_mouse.html index cce8ec3..7000acd 100644 --- a/doc/nucleus/functions/import/LoadNMRData_mouse.html +++ b/doc/nucleus/functions/import/LoadNMRData_mouse.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- @@ -210,7 +210,7 @@

    SOURCE CODE ^%% License: 0135 % MIT License 0136 % -0137 % Copyright (c) 2018 Thomas Hiller +0137 % Copyright (c) 2021 Stephan Costabel 0138 % 0139 % Permission is hereby granted, free of charge, to any person obtaining a copy 0140 % of this software and associated documentation files (the "Software"), to deal diff --git a/doc/nucleus/functions/import/LoadNMRData_mouselift.html b/doc/nucleus/functions/import/LoadNMRData_mouselift.html new file mode 100644 index 0000000..5fe5694 --- /dev/null +++ b/doc/nucleus/functions/import/LoadNMRData_mouselift.html @@ -0,0 +1,232 @@ + + + + Description of LoadNMRData_mouselift + + + + + + + + + + + +

    LoadNMRData_mouselift +

    + +

    PURPOSE ^

    +
    loads NMR Mouse data from an original folder structure as
    + +

    SYNOPSIS ^

    +
    function out = LoadNMRData_mouselift(in)
    + +

    DESCRIPTION ^

    +
    LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as
    +generated by the NMR MOUSE when using it wogether with the lift
    +
    + Syntax:
    +       out = LoadNMRData_mouselift(in)
    +
    + Inputs:
    +       in - input structure
    +       in.path - data path
    +       in.T1T2 - T1 / T2 flag
    +       in.fileformat - 'mouse'
    +
    + Outputs:
    +       out - output structure
    +       out.parData - parameter file data
    +       out.nmrData - NMR data
    +
    + Example:
    +       out = LoadNMRData_mouselift(in)
    +
    + Other m-files required:
    +       fixParameterString
    +
    + Subfunctions:
    +       LoadDataFile
    +       LoadParameterFile
    +
    + MAT-files required:
    +       none
    +
    + See also: NUCLEUSinv
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    • fixParameterString cleans parameter file lines when the properties have a
    • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
    +This function is called by: + + + +

    SUBFUNCTIONS ^

    + + +

    SOURCE CODE ^

    +
    0001 function out = LoadNMRData_mouselift(in)
    +0002 %LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as
    +0003 %generated by the NMR MOUSE when using it wogether with the lift
    +0004 %
    +0005 % Syntax:
    +0006 %       out = LoadNMRData_mouselift(in)
    +0007 %
    +0008 % Inputs:
    +0009 %       in - input structure
    +0010 %       in.path - data path
    +0011 %       in.T1T2 - T1 / T2 flag
    +0012 %       in.fileformat - 'mouse'
    +0013 %
    +0014 % Outputs:
    +0015 %       out - output structure
    +0016 %       out.parData - parameter file data
    +0017 %       out.nmrData - NMR data
    +0018 %
    +0019 % Example:
    +0020 %       out = LoadNMRData_mouselift(in)
    +0021 %
    +0022 % Other m-files required:
    +0023 %       fixParameterString
    +0024 %
    +0025 % Subfunctions:
    +0026 %       LoadDataFile
    +0027 %       LoadParameterFile
    +0028 %
    +0029 % MAT-files required:
    +0030 %       none
    +0031 %
    +0032 % See also: NUCLEUSinv
    +0033 % Author: see AUTHORS.md
    +0034 % email: see AUTHORS.md
    +0035 % License: MIT License (at end)
    +0036 
    +0037 %------------- BEGIN CODE --------------
    +0038 
    +0039 %% start processing the files
    +0040 % load Parameter file
    +0041 [parData] = LoadParameterFile(in.path,'acq.par');
    +0042 
    +0043 % find all data files
    +0044 files = dir(fullfile(in.path,'*.dat'));
    +0045 
    +0046 % remove not needed dat-files
    +0047 switch in.T1T2
    +0048     case 'T1'
    +0049         files = files(~ismember({files.name},...
    +0050             {'data2D.dat','T1Axis.dat'}));
    +0051     case 'T2'
    +0052         files = files(~ismember({files.name},...
    +0053             {'data2D.dat','T2Axis.dat'}));
    +0054 end
    +0055 
    +0056 nmrData = cell(1,size(files,1));
    +0057 for i = 1:size(files,1)
    +0058     % read the data file
    +0059     data  = LoadDataFile(in.path,files(i).name,in.T1T2);
    +0060     
    +0061     % get file statistics
    +0062     nmrData{i}.datfile = files(i).name;
    +0063     nmrData{i}.date = files(i).date;
    +0064     nmrData{i}.datenum = files(i).datenum;
    +0065     nmrData{i}.bytes = files(i).bytes;
    +0066     
    +0067     % save the NMR data
    +0068     nmrData{i}.flag = data.flag;
    +0069     nmrData{i}.T1IRfac = 1;
    +0070     nmrData{i}.time = data.time;
    +0071     nmrData{i}.signal = data.signal;
    +0072     nmrData{i}.raw = data.raw;
    +0073     if strcmp(in.T1T2,'T2')
    +0074         nmrData{i}.phase = data.phase;
    +0075     end
    +0076     clear data
    +0077 end
    +0078 
    +0079 % save data to output struct
    +0080 out.parData = parData;
    +0081 out.nmrData = nmrData;
    +0082 
    +0083 end
    +0084 
    +0085 %% load NMR data file
    +0086 function [data] = LoadDataFile(datapath,fname,flag)
    +0087 
    +0088 d = load(fullfile(datapath,fname));
    +0089 
    +0090 if size(d,2) == 3
    +0091     data.flag = flag;
    +0092     data.time = d(:,1);
    +0093     switch flag
    +0094         case 'T1'
    +0095             data.signal = d(:,2);
    +0096         case 'T2'
    +0097             data.signal = complex(d(:,2),d(:,3));
    +0098             [data.signal,data.phase] = rotateT2phase(data.signal);
    +0099     end
    +0100 else
    +0101     data.flag = '0';
    +0102     data.time = 0;
    +0103     data.signal = 0;
    +0104     data.phase = 0;
    +0105 end
    +0106 
    +0107 data.raw.time = data.time;
    +0108 data.raw.signal = data.signal;
    +0109 
    +0110 end
    +0111 
    +0112 %% load parameter file
    +0113 function [data] = LoadParameterFile(datapath,fname)
    +0114 
    +0115 fid = fopen(fullfile(datapath,fname));
    +0116 d = textscan(fid,'%s','Delimiter','\n');
    +0117 fclose(fid);
    +0118 
    +0119 for i = 1:size(d{1},1)
    +0120     str = char(d{1}(i));
    +0121     str = fixParameterString(str);
    +0122     eval(['data.',str,';']);
    +0123 end
    +0124 data.all = d;
    +0125 
    +0126 end
    +0127 
    +0128 %------------- END OF CODE --------------
    +0129 
    +0130 %% License:
    +0131 % MIT License
    +0132 %
    +0133 % Copyright (c) 2021 Stephan Costabel
    +0134 %
    +0135 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0136 % of this software and associated documentation files (the "Software"), to deal
    +0137 % in the Software without restriction, including without limitation the rights
    +0138 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0139 % copies of the Software, and to permit persons to whom the Software is
    +0140 % furnished to do so, subject to the following conditions:
    +0141 %
    +0142 % The above copyright notice and this permission notice shall be included in all
    +0143 % copies or substantial portions of the Software.
    +0144 %
    +0145 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0146 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0147 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0148 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0149 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0150 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0151 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/import/LoadNMRData_rwth.html b/doc/nucleus/functions/import/LoadNMRData_rwth.html index 551e87b..4c0fcd9 100644 --- a/doc/nucleus/functions/import/LoadNMRData_rwth.html +++ b/doc/nucleus/functions/import/LoadNMRData_rwth.html @@ -57,15 +57,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • fixParameterString cleans parameter file lines when the properties have a
  • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
  • displayStatusText shows status information either in the GUI or on the
  • This function is called by: @@ -108,8 +108,8 @@

    SOURCE CODE ^% none 0031 % 0032 % See also: NUCLEUSinv -0033 % Author: Thomas Hiller -0034 % email: thomas.hiller[at]leibniz-liag.de +0033 % Author: see AUTHORS.md +0034 % email: see AUTHORS.md 0035 % License: MIT License (at end) 0036 0037 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/fixParameterString.html b/doc/nucleus/functions/import/fixParameterString.html index c5a5080..94844f9 100644 --- a/doc/nucleus/functions/import/fixParameterString.html +++ b/doc/nucleus/functions/import/fixParameterString.html @@ -51,8 +51,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • LoadNMRData_bamtom loads BAM TOM data
  • LoadNMRData_bgr loads standard BGR NMR data
  • LoadNMRData_bgr2 loads BGR NMR data from an original folder structure as
  • LoadNMRData_field loads RWTH field NMR data (Blümich group bore-hole tool)
  • LoadNMRData_ibac imports NMR data from the PM5 and PM25
  • LoadNMRData_liag loads LIAG NMR data
  • LoadNMRData_mouse loads NMR data saved by the MOUSE
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • importNMRdata is the general import routine for NMR data
  • +
  • LoadNMRData_bamtom loads BAM TOM data
  • LoadNMRData_bgr loads standard BGR NMR data
  • LoadNMRData_bgr2 loads BGR NMR data from an original folder structure as
  • LoadNMRData_field loads RWTH field NMR data (Blümich group bore-hole tool)
  • LoadNMRData_ibac imports NMR data from the PM5 and PM25
  • LoadNMRData_liag loads LIAG NMR data
  • LoadNMRData_mouse loads NMR data saved by the MOUSE
  • LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • importNMRdata is the general import routine for NMR data
  • @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/import/menu.html b/doc/nucleus/functions/import/menu.html index 4f497f3..af10a67 100644 --- a/doc/nucleus/functions/import/menu.html +++ b/doc/nucleus/functions/import/menu.html @@ -18,7 +18,7 @@

    Index for nucleus\functions\import

    Matlab files in this directory:

    +
  • LoadNMRData_bamtom
  • LoadNMRData_bgr
  • LoadNMRData_bgr2
  • LoadNMRData_bgrmat
  • LoadNMRData_corelab
  • LoadNMRData_dart
  • LoadNMRData_driver
  • LoadNMRData_field
  • LoadNMRData_helios
  • LoadNMRData_ibac
  • LoadNMRData_liag
  • LoadNMRData_mouse
  • LoadNMRData_mouselift
  • LoadNMRData_rwth
  • fixParameterString
  • rotateT2phase
  • diff --git a/doc/nucleus/functions/import/rotateT2phase.html b/doc/nucleus/functions/import/rotateT2phase.html index f31c946..2a5c8a1 100644 --- a/doc/nucleus/functions/import/rotateT2phase.html +++ b/doc/nucleus/functions/import/rotateT2phase.html @@ -52,8 +52,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • LoadNMRData_bamtom loads BAM TOM data
  • LoadNMRData_bgr loads standard BGR NMR data
  • LoadNMRData_bgr2 loads BGR NMR data from an original folder structure as
  • LoadNMRData_bgrmat loads already preprocessed BGR NMR data; check the
  • LoadNMRData_corelab loads NMR data as provided by Corelab
  • LoadNMRData_field loads RWTH field NMR data (Blümich group bore-hole tool)
  • LoadNMRData_ibac imports NMR data from the PM5 and PM25
  • LoadNMRData_liag loads LIAG NMR data
  • LoadNMRData_mouse loads NMR data saved by the MOUSE
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • importASCIIdata imports NMR data from ASCII files
  • importEXCELdata imports NMR data from Excel files
  • importNMRdata is the general import routine for NMR data
  • +
  • LoadNMRData_bamtom loads BAM TOM data
  • LoadNMRData_bgr loads standard BGR NMR data
  • LoadNMRData_bgr2 loads BGR NMR data from an original folder structure as
  • LoadNMRData_bgrmat loads already preprocessed BGR NMR data; check the
  • LoadNMRData_corelab loads NMR data as provided by Corelab
  • LoadNMRData_field loads RWTH field NMR data (Blümich group bore-hole tool)
  • LoadNMRData_helios loads BGR NMR data from a typical folder structure
  • LoadNMRData_ibac imports NMR data from the PM5 and PM25
  • LoadNMRData_liag loads LIAG NMR data
  • LoadNMRData_mouse loads NMR data saved by the MOUSE
  • LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • importASCIIdata imports NMR data from ASCII files
  • importEXCELdata imports NMR data from Excel files
  • importNMRdata is the general import routine for NMR data
  • SUBFUNCTIONS ^

    @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/ConductView.html b/doc/nucleus/functions/interface/ConductView.html index b347b5d..e38b508 100644 --- a/doc/nucleus/functions/interface/ConductView.html +++ b/doc/nucleus/functions/interface/ConductView.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSmod, NUCLEUSinv -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/PhaseView.html b/doc/nucleus/functions/interface/PhaseView.html index d6d1c13..68bd035 100644 --- a/doc/nucleus/functions/interface/PhaseView.html +++ b/doc/nucleus/functions/interface/PhaseView.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/beautifyAxes.html b/doc/nucleus/functions/interface/beautifyAxes.html index abfedfc..86d5663 100644 --- a/doc/nucleus/functions/interface/beautifyAxes.html +++ b/doc/nucleus/functions/interface/beautifyAxes.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/calculateGeometry.html b/doc/nucleus/functions/interface/calculateGeometry.html index 3c03d1f..28ce2e2 100644 --- a/doc/nucleus/functions/interface/calculateGeometry.html +++ b/doc/nucleus/functions/interface/calculateGeometry.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSmod -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/calculateGuiOnMonitorPosition.html b/doc/nucleus/functions/interface/calculateGuiOnMonitorPosition.html index 38f644a..a030a50 100644 --- a/doc/nucleus/functions/interface/calculateGuiOnMonitorPosition.html +++ b/doc/nucleus/functions/interface/calculateGuiOnMonitorPosition.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -103,66 +103,52 @@

    SOURCE CODE ^%% get the monitor layout 0034 scr = get(0,'MonitorPosition'); 0035 if size(scr,1) > 1 -0036 ind = find(scr(:,1)==1 & scr(:,2)==1); -0037 sw = scr(ind,3); % width -0038 sh = scr(ind,4); % height -0039 else -0040 sw = scr(3); % width -0041 sh = scr(4); % height -0042 end -0043 -0044 %% positioning of the GUI -0045 if numel(scr) > 4 -0046 % dual screen mode -0047 % GUI on second screen -0048 gh = 730; % reference height -0049 gw = ceil(gh*aspect_ratio); % reference width (1152) -0050 if any(scr(:,1)<0) -0051 pos = [-sw+(sw-gw)/2 (sh-gh)/3 gw gh]; -0052 else -0053 pos = [sw+(sw-gw)/2 (sh-gh)/3 gw gh]; -0054 end -0055 % if any screen is smaller than 800 -0056 if any(scr(:,4)<800) -0057 pos = [(sw-gw)/2 (sh-gh)/3 gw gh]; -0058 end -0059 else -0060 % single screen mode -0061 if any(scr(:,4)<800) -0062 gh = 600; % reference height for small screens -0063 else -0064 gh = 730; % reference height -0065 end -0066 gw = ceil(gh*aspect_ratio); % reference width (960) -0067 pos = [(sw-gw)/2 (sh-gh)/2 gw gh]; -0068 end -0069 -0070 return -0071 -0072 %------------- END OF CODE -------------- -0073 -0074 % License: -0075 % MIT License -0076 % -0077 % Copyright (c) 2018 Thomas Hiller -0078 % -0079 % Permission is hereby granted, free of charge, to any person obtaining a copy -0080 % of this software and associated documentation files (the "Software"), to deal -0081 % in the Software without restriction, including without limitation the rights -0082 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0083 % copies of the Software, and to permit persons to whom the Software is -0084 % furnished to do so, subject to the following conditions: -0085 % -0086 % The above copyright notice and this permission notice shall be included in all -0087 % copies or substantial portions of the Software. -0088 % -0089 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0090 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0091 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0092 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0093 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0094 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0095 % SOFTWARE. +0036 % find main screen +0037 ind = find(scr(:,1)==1 & scr(:,2)==1); +0038 sw = scr(ind,3); % width +0039 sh = scr(ind,4); % height +0040 else +0041 sw = scr(3); % width +0042 sh = scr(4); % height +0043 end +0044 +0045 %% GUI positioning +0046 if any(sh<800) +0047 gh = 600; % reference height for small screens +0048 else +0049 gh = 730; % reference height +0050 end +0051 % reference width +0052 gw = ceil(gh*aspect_ratio); % 960 or 1152 +0053 % GUI position +0054 pos = round([(sw-gw)/2 (sh-gh)/3 gw gh]); +0055 +0056 return +0057 +0058 %------------- END OF CODE -------------- +0059 +0060 % License: +0061 % MIT License +0062 % +0063 % Copyright (c) 2018 Thomas Hiller +0064 % +0065 % Permission is hereby granted, free of charge, to any person obtaining a copy +0066 % of this software and associated documentation files (the "Software"), to deal +0067 % in the Software without restriction, including without limitation the rights +0068 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0069 % copies of the Software, and to permit persons to whom the Software is +0070 % furnished to do so, subject to the following conditions: +0071 % +0072 % The above copyright notice and this permission notice shall be included in all +0073 % copies or substantial portions of the Software. +0074 % +0075 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0076 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0077 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0078 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0079 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0080 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0081 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/calculateNMR.html b/doc/nucleus/functions/interface/calculateNMR.html index f9abbd4..0e20915 100644 --- a/doc/nucleus/functions/interface/calculateNMR.html +++ b/doc/nucleus/functions/interface/calculateNMR.html @@ -55,15 +55,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • displayStatusText shows status information either in the GUI or on the
  • updateNMRsignals adds noise to the forward NMR signals and scales the
  • updatePlotsNMR plots the forward modeled NMR data in NUCLEUSmod
  • getNMRSignal calculates the NMR signal of a fully or partially saturated
  • getNMRTimeVector Creates a NMR time vector depending on the given echo
  • This function is called by:
    • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
    @@ -102,8 +102,8 @@

    SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSmod -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- @@ -119,62 +119,63 @@

    SOURCE CODE ^% generate a time vector from the echo time 'TE' and number of echoes 'echosN' 0046 nmr.t = getNMRTimeVector(data.nmr.TE,'µs','N',data.nmr.echosN); 0047 nmr.Tb = data.nmr.Tbulk; -0048 nmr.rho = data.nmr.rho/1e6; % µm/s to m/s -0049 -0050 % wait-bar option -0051 wbopts.show = true; -0052 wbopts.tag = 'MOD'; -0053 -0054 % disable the RUN button to indicate a running calculation -0055 set(gui.push_handles.nmr_RUN,'String','RUNNING ...','Enable','inactive'); -0056 -0057 % calculate the NMR signals for ALL pressure / saturation steps -0058 displayStatusText(gui,'Calculating NMR signals ... '); -0059 NMR = getNMRSignal(nmr,data.geometry.type,data.results.SAT,data.results.psddata,wbopts); -0060 % always keep a copy of the raw data (without noise) -0061 NMR.raw = NMR; -0062 -0063 % update the global GUI data -0064 data.results.NMR = NMR; -0065 setappdata(fig,'data',data); -0066 -0067 % add noise to the NMR signals -0068 updateNMRsignals; -0069 displayStatusText(gui,'Calculating NMR signals ... done'); -0070 updatePlotsNMR; -0071 % enable the RUN button again -0072 set(gui.push_handles.nmr_RUN,'String','RUN','Enable','on'); -0073 -0074 else -0075 helpdlg({'calculateNMR:','Create CPS data first.'}); -0076 end -0077 -0078 end -0079 -0080 %------------- END OF CODE -------------- -0081 -0082 %% License: -0083 % MIT License -0084 % -0085 % Copyright (c) 2018 Thomas Hiller -0086 % -0087 % Permission is hereby granted, free of charge, to any person obtaining a copy -0088 % of this software and associated documentation files (the "Software"), to deal -0089 % in the Software without restriction, including without limitation the rights -0090 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0091 % copies of the Software, and to permit persons to whom the Software is -0092 % furnished to do so, subject to the following conditions: -0093 % -0094 % The above copyright notice and this permission notice shall be included in all -0095 % copies or substantial portions of the Software. -0096 % -0097 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0098 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0099 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0100 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0101 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0102 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0103 % SOFTWARE. +0048 nmr.Td = data.nmr.Tdiff; +0049 nmr.rho = data.nmr.rho/1e6; % µm/s to m/s +0050 +0051 % wait-bar option +0052 wbopts.show = true; +0053 wbopts.tag = 'MOD'; +0054 +0055 % disable the RUN button to indicate a running calculation +0056 set(gui.push_handles.nmr_RUN,'String','RUNNING ...','Enable','inactive'); +0057 +0058 % calculate the NMR signals for ALL pressure / saturation steps +0059 displayStatusText(gui,'Calculating NMR signals ... '); +0060 NMR = getNMRSignal(nmr,data.geometry.type,data.results.SAT,data.results.psddata,wbopts); +0061 % always keep a copy of the raw data (without noise) +0062 NMR.raw = NMR; +0063 +0064 % update the global GUI data +0065 data.results.NMR = NMR; +0066 setappdata(fig,'data',data); +0067 +0068 % add noise to the NMR signals +0069 updateNMRsignals; +0070 displayStatusText(gui,'Calculating NMR signals ... done'); +0071 updatePlotsNMR; +0072 % enable the RUN button again +0073 set(gui.push_handles.nmr_RUN,'String','RUN','Enable','on'); +0074 +0075 else +0076 helpdlg({'calculateNMR:','Create CPS data first.'}); +0077 end +0078 +0079 end +0080 +0081 %------------- END OF CODE -------------- +0082 +0083 %% License: +0084 % MIT License +0085 % +0086 % Copyright (c) 2018 Thomas Hiller +0087 % +0088 % Permission is hereby granted, free of charge, to any person obtaining a copy +0089 % of this software and associated documentation files (the "Software"), to deal +0090 % in the Software without restriction, including without limitation the rights +0091 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0092 % copies of the Software, and to permit persons to whom the Software is +0093 % furnished to do so, subject to the following conditions: +0094 % +0095 % The above copyright notice and this permission notice shall be included in all +0096 % copies or substantial portions of the Software. +0097 % +0098 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0099 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0100 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0101 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0102 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0103 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0104 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/calibratePorosity.html b/doc/nucleus/functions/interface/calibratePorosity.html index eaec0ab..61353a6 100644 --- a/doc/nucleus/functions/interface/calibratePorosity.html +++ b/doc/nucleus/functions/interface/calibratePorosity.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/caluclatePressureSaturation.html b/doc/nucleus/functions/interface/caluclatePressureSaturation.html index ff71cb6..028a089 100644 --- a/doc/nucleus/functions/interface/caluclatePressureSaturation.html +++ b/doc/nucleus/functions/interface/caluclatePressureSaturation.html @@ -56,15 +56,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
  • clearSingleAxis clears an individual axis
  • displayStatusText shows status information either in the GUI or on the
  • updateCPSTable updates the CPS table in NUCLEUSmod
  • updatePlotsCPS plots the CPS curve into the corresponding NUCLEUSmod axis
  • getConstants provides some physical constants to the forward calculation
  • getSaturationFromPressureBatch
  • This function is called by:
    • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
    @@ -106,8 +106,8 @@

    SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSmod -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- @@ -164,57 +164,60 @@

    SOURCE CODE ^%% reset NMR plots 0088 clearSingleAxis(gui.axes_handles.nmr); 0089 displayStatusText(gui,'Calculating saturation ... done'); -0090 % enable the RUN button again +0090 % enable the pressure RUN button again 0091 set(gui.push_handles.press_RUN,'String','RUN','Enable','on'); -0092 % enable hydraulic conductivity GUI menu only for PSD data -0093 if data.geometry.ispsd -0094 set(gui.menu.view_conduct,'Enable','on'); -0095 else -0096 set(gui.menu.view_conduct,'Enable','off'); -0097 end -0098 -0099 end -0100 -0101 function [indd,indi] = getInitialSatLevels(SAT,N) -0102 % initially there are 5 saturation levels on each branch -0103 SatLevels = linspace(1/N,1,N); -0104 indd = zeros(size(SatLevels)); -0105 indi = zeros(size(SatLevels)); -0106 for i = 1:5 -0107 indd(i) = find(abs(SAT.Sdfull-SatLevels(i))==min(abs(SAT.Sdfull-SatLevels(i))),1,'last'); -0108 indi(i) = find(abs(SAT.Sifull-SatLevels(i))==min(abs(SAT.Sifull-SatLevels(i))),1,'last'); -0109 end -0110 indd = unique(indd); -0111 indi = unique(indi); -0112 % the full saturation one is always the first one -0113 indd(1) = 1; -0114 indi(1) = 1; -0115 end -0116 -0117 %------------- END OF CODE -------------- -0118 -0119 %% License: -0120 % MIT License -0121 % -0122 % Copyright (c) 2018 Thomas Hiller -0123 % -0124 % Permission is hereby granted, free of charge, to any person obtaining a copy -0125 % of this software and associated documentation files (the "Software"), to deal -0126 % in the Software without restriction, including without limitation the rights -0127 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0128 % copies of the Software, and to permit persons to whom the Software is -0129 % furnished to do so, subject to the following conditions: -0130 % -0131 % The above copyright notice and this permission notice shall be included in all -0132 % copies or substantial portions of the Software. +0092 % reset the NMR RUN button +0093 set(gui.push_handles.nmr_RUN,'String','RUN','Enable','on',... +0094 'BackgroundColor','g','Callback',@onPushRun); +0095 % enable hydraulic conductivity GUI menu only for PSD data +0096 if data.geometry.ispsd +0097 set(gui.menu.view_conduct,'Enable','on'); +0098 else +0099 set(gui.menu.view_conduct,'Enable','off'); +0100 end +0101 +0102 end +0103 +0104 function [indd,indi] = getInitialSatLevels(SAT,N) +0105 % initially there are 5 saturation levels on each branch +0106 SatLevels = linspace(1/N,1,N); +0107 indd = zeros(size(SatLevels)); +0108 indi = zeros(size(SatLevels)); +0109 for i = 1:5 +0110 indd(i) = find(abs(SAT.Sdfull-SatLevels(i))==min(abs(SAT.Sdfull-SatLevels(i))),1,'last'); +0111 indi(i) = find(abs(SAT.Sifull-SatLevels(i))==min(abs(SAT.Sifull-SatLevels(i))),1,'last'); +0112 end +0113 indd = unique(indd); +0114 indi = unique(indi); +0115 % the full saturation one is always the first one +0116 indd(1) = 1; +0117 indi(1) = 1; +0118 end +0119 +0120 %------------- END OF CODE -------------- +0121 +0122 %% License: +0123 % MIT License +0124 % +0125 % Copyright (c) 2018 Thomas Hiller +0126 % +0127 % Permission is hereby granted, free of charge, to any person obtaining a copy +0128 % of this software and associated documentation files (the "Software"), to deal +0129 % in the Software without restriction, including without limitation the rights +0130 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0131 % copies of the Software, and to permit persons to whom the Software is +0132 % furnished to do so, subject to the following conditions: 0133 % -0134 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0135 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0136 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0137 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0138 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0139 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0140 % SOFTWARE. +0134 % The above copyright notice and this permission notice shall be included in all +0135 % copies or substantial portions of the Software. +0136 % +0137 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0138 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0139 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0140 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0141 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0142 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0143 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/changeColorTheme.html b/doc/nucleus/functions/interface/changeColorTheme.html index 558c3cb..e25643e 100644 --- a/doc/nucleus/functions/interface/changeColorTheme.html +++ b/doc/nucleus/functions/interface/changeColorTheme.html @@ -58,8 +58,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=getColorTheme returns the colors of the selected color theme
  • makeINIfile creates or updates the ini-File
  • updatePlotsCPS plots the CPS curve into the corresponding NUCLEUSmod axis
  • updatePlotsDistribution plots the RTD and PSD curves into NUCLEUSinv
  • updatePlotsJointInversion plots the joint-inversion results in NUCLEUSinv
  • updatePlotsNMR plots the forward modeled NMR data in NUCLEUSmod
  • updatePlotsPSD plots the pore size distribution in NUCLEUSmod
  • updatePlotsSignal plots the raw and processed NMR signals in NUCLEUSinv
  • This function is called by: +
  • NUCLEUSinv_createGUI controls the creation of all GUI elements
  • onMenuExtraColor handles the color theme menu of both GUIs
  • @@ -108,8 +108,8 @@

    SOURCE CODE ^% none 0032 % 0033 % See also: NUCLEUSinv, NUCLEUSmod -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/checkIfInversionExists.html b/doc/nucleus/functions/interface/checkIfInversionExists.html index ee01a5f..50bec8a 100644 --- a/doc/nucleus/functions/interface/checkIfInversionExists.html +++ b/doc/nucleus/functions/interface/checkIfInversionExists.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/cleanupGUIData.html b/doc/nucleus/functions/interface/cleanupGUIData.html index ff131f5..cd3e468 100644 --- a/doc/nucleus/functions/interface/cleanupGUIData.html +++ b/doc/nucleus/functions/interface/cleanupGUIData.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/clearAllAxes.html b/doc/nucleus/functions/interface/clearAllAxes.html index eded74d..72ebb9f 100644 --- a/doc/nucleus/functions/interface/clearAllAxes.html +++ b/doc/nucleus/functions/interface/clearAllAxes.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/clearInversion.html b/doc/nucleus/functions/interface/clearInversion.html index 0b74d42..7921d54 100644 --- a/doc/nucleus/functions/interface/clearInversion.html +++ b/doc/nucleus/functions/interface/clearInversion.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/clearSingleAxis.html b/doc/nucleus/functions/interface/clearSingleAxis.html index 8c67355..97ff03f 100644 --- a/doc/nucleus/functions/interface/clearSingleAxis.html +++ b/doc/nucleus/functions/interface/clearSingleAxis.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/displayStatusText.html b/doc/nucleus/functions/interface/displayStatusText.html index e7faf54..a76d9bd 100644 --- a/doc/nucleus/functions/interface/displayStatusText.html +++ b/doc/nucleus/functions/interface/displayStatusText.html @@ -4,7 +4,7 @@ Description of displayStatusText - + @@ -20,13 +20,14 @@

    displayStatusText

    PURPOSE ^

    -
    clears all axes of a given figure
    +
    shows status information either in the GUI or on the

    SYNOPSIS ^

    function displayStatusText(gui,string)

    DESCRIPTION ^

    -
    displayStatusText clears all axes of a given figure
    +
    displayStatusText shows status information either in the GUI or on the
    +commandline
     
      Syntax:
            displayStatusText(gui,string)
    @@ -51,8 +52,8 @@ 

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • NUCLEUSinv_createGUI controls the creation of all GUI elements
  • NUCLEUSmod_createGUI controls the creation of all GUI elements
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • calculateNMR calculates the NMR signals for the full and partially saturated
  • caluclatePressureSaturation calculates the geometry dependent pressure
  • exportData exports data from both GUIs into various formats
  • exportGraphics exports graphics from both GUIs into various formats
  • exportINV exports NUCLEUSinv GUI data to a mat-file
  • importCalibrationData
  • importNMRdata is the general import routine for NMR data
  • runInversionBatch batch processes the inversion using for all NMR signals
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • useSignalAsCalibration uses E0 as porosity calibration factor.
  • +
  • NUCLEUSinv_createGUI controls the creation of all GUI elements
  • NUCLEUSmod_createGUI controls the creation of all GUI elements
  • LoadNMRData_rwth loads RWTH NMR data (saved by the old Prospa version)
  • calculateNMR calculates the NMR signals for the full and partially saturated
  • caluclatePressureSaturation calculates the geometry dependent pressure
  • exportData exports data from both GUIs into various formats
  • exportGraphics exports graphics from both GUIs into various formats
  • exportINV exports NUCLEUSinv GUI data to a mat-file
  • importCalibrationData
  • importINV2INV imports a previously saved NUCLEUSinv session
  • importNMRdata is the general import routine for NMR data
  • runInversionBatch batch processes the inversion using for all NMR signals
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • useSignalAsCalibration uses E0 as porosity calibration factor.
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • SOURCE CODE ^

    0001 function displayStatusText(gui,string)
    -0002 %displayStatusText clears all axes of a given figure
    -0003 %
    -0004 % Syntax:
    -0005 %       displayStatusText(gui,string)
    -0006 %
    -0007 % Inputs:
    -0008 %       gui - figure gui elements structure
    -0009 %       string - string to show in status bar
    -0010 %
    -0011 % Outputs:
    -0012 %       none
    -0013 %
    -0014 % Example:
    -0015 %       displayStatusText(gui,string)
    -0016 %
    -0017 % Other m-files required:
    -0018 %       none
    -0019 %
    -0020 % Subfunctions:
    -0021 %       none
    -0022 %
    -0023 % MAT-files required:
    -0024 %       none
    -0025 %
    -0026 % See also: NUCLEUSinv, NUCLEUSmod
    -0027 % Author: Thomas Hiller
    -0028 % email: thomas.hiller[at]leibniz-liag.de
    -0029 % License: MIT License (at end)
    -0030 
    -0031 %------------- BEGIN CODE --------------
    -0032 
    -0033 %% display status / info text
    -0034 set(gui.textStatus,'String',string);
    -0035 pause(0.001);
    -0036 
    -0037 end
    -0038 
    -0039 %------------- END OF CODE --------------
    -0040 
    -0041 %% License:
    -0042 % MIT License
    -0043 %
    -0044 % Copyright (c) 2018 Thomas Hiller
    -0045 %
    -0046 % Permission is hereby granted, free of charge, to any person obtaining a copy
    -0047 % of this software and associated documentation files (the "Software"), to deal
    -0048 % in the Software without restriction, including without limitation the rights
    -0049 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    -0050 % copies of the Software, and to permit persons to whom the Software is
    -0051 % furnished to do so, subject to the following conditions:
    -0052 %
    -0053 % The above copyright notice and this permission notice shall be included in all
    -0054 % copies or substantial portions of the Software.
    -0055 %
    -0056 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -0057 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -0058 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    -0059 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    -0060 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    -0061 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -0062 % SOFTWARE.
    +0002 %displayStatusText shows status information either in the GUI or on the +0003 %commandline +0004 % +0005 % Syntax: +0006 % displayStatusText(gui,string) +0007 % +0008 % Inputs: +0009 % gui - figure gui elements structure +0010 % string - string to show in status bar +0011 % +0012 % Outputs: +0013 % none +0014 % +0015 % Example: +0016 % displayStatusText(gui,string) +0017 % +0018 % Other m-files required: +0019 % none +0020 % +0021 % Subfunctions: +0022 % none +0023 % +0024 % MAT-files required: +0025 % none +0026 % +0027 % See also: NUCLEUSinv, NUCLEUSmod +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md +0030 % License: MIT License (at end) +0031 +0032 %------------- BEGIN CODE -------------- +0033 +0034 %% display status / info text +0035 if isstruct(gui) +0036 set(gui.textStatus,'String',string); +0037 pause(0.001); +0038 else +0039 disp(string); +0040 end +0041 +0042 end +0043 +0044 %------------- END OF CODE -------------- +0045 +0046 %% License: +0047 % MIT License +0048 % +0049 % Copyright (c) 2018 Thomas Hiller +0050 % +0051 % Permission is hereby granted, free of charge, to any person obtaining a copy +0052 % of this software and associated documentation files (the "Software"), to deal +0053 % in the Software without restriction, including without limitation the rights +0054 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0055 % copies of the Software, and to permit persons to whom the Software is +0056 % furnished to do so, subject to the following conditions: +0057 % +0058 % The above copyright notice and this permission notice shall be included in all +0059 % copies or substantial portions of the Software. +0060 % +0061 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0062 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0063 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0064 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0065 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0066 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0067 % SOFTWARE.

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/enableGUIelements.html b/doc/nucleus/functions/interface/enableGUIelements.html index 7bdc60b..0583b02 100644 --- a/doc/nucleus/functions/interface/enableGUIelements.html +++ b/doc/nucleus/functions/interface/enableGUIelements.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -124,7 +124,7 @@

    SOURCE CODE ^% process panel 0055 data.process.end = 0; 0056 switch data.import.fileformat -0057 case {'field','dart'} +0057 case {'dart','field','helios'} 0058 data.process.gatetype = 'raw'; 0059 otherwise 0060 data.process.gatetype = 'log'; diff --git a/doc/nucleus/functions/interface/exportData.html b/doc/nucleus/functions/interface/exportData.html index 7883cd9..96e5aae 100644 --- a/doc/nucleus/functions/interface/exportData.html +++ b/doc/nucleus/functions/interface/exportData.html @@ -56,15 +56,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • displayStatusText shows status information either in the GUI or on the
  • exportGraphics exports graphics from both GUIs into various formats
  • exportINV exports NUCLEUSinv GUI data to a mat-file
  • This function is called by: @@ -106,8 +106,8 @@

    SOURCE CODE ^% none 0030 % 0031 % See also: NUCLEUSinv, NUCLEUSmod -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- @@ -226,7 +226,7 @@

    SOURCE CODE ^'results'); 0150 0151 % save to file -0152 save(fullfile(spath,sfile),'idata'); +0152 save(fullfile(spath,sfile),'idata','-v7.3'); 0153 0154 % display info text 0155 displayStatusText(gui,... @@ -260,7 +260,7 @@

    SOURCE CODE ^'results'); 0184 0185 % save to file -0186 save(fullfile(spath,sfile),'idata'); +0186 save(fullfile(spath,sfile),'idata','-v7.3'); 0187 0188 % display info text 0189 displayStatusText(gui,... @@ -407,7 +407,7 @@

    SOURCE CODE ^if ~isequal(FileName,0) || ~isequal(PathName,0) 0331 clear data 0332 data = out; %#ok<NASGU> -0333 save(fullfile(PathName,FileName),'data'); +0333 save(fullfile(PathName,FileName),'data','-v7.3'); 0334 displayStatusText(gui,'Saving to MAT-file ... done.'); 0335 else 0336 displayStatusText(gui,'Saving to MAT-file ... canceled.'); @@ -501,7 +501,7 @@

    SOURCE CODE ^'T2 [',unit,']'],'amplitude [a.u.]'}; 0425 end 0426 -0427 case {'LU','NNLS'} +0427 case {'LU','NNLS','MUMO'} 0428 tmp4 = [INVdata{id}.results.invstd.T1T2me(:)... 0429 INVdata{id}.results.invstd.T1T2f(:)]; 0430 header4 = {['relaxation times [',unit,']'],'amplitudes [a.u.]'}; diff --git a/doc/nucleus/functions/interface/exportGraphics.html b/doc/nucleus/functions/interface/exportGraphics.html index a34ee25..c16a77e 100644 --- a/doc/nucleus/functions/interface/exportGraphics.html +++ b/doc/nucleus/functions/interface/exportGraphics.html @@ -51,15 +51,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • displayStatusText shows status information either in the GUI or on the
  • This function is called by: @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -373,61 +373,71 @@

    SOURCE CODE ^switch fig_tag 0305 case 'INV' -0306 [FileName,PathName,~] = uiputfile({putext,put1},... -0307 ['NUCLEUSinv: Save ',statstr,' Graphics'],... -0308 fullfile(pwd,'NUCLEUSinv_inversion')); -0309 case 'MOD' -0310 [FileName,PathName,~] = uiputfile({putext,put1},... -0311 ['NUCLEUSmod: Save ',statstr,' Graphics'],... -0312 fullfile(pwd,'NUCLEUSmod_forward')); -0313 end -0314 if ~isequal(FileName,0) || ~isequal(PathName,0) -0315 print(expfig,fullfile(PathName,FileName),'-r300',driver); -0316 close(expfig); -0317 displayStatusText(gui,['Saving ',statstr,'-file ... done.']); -0318 else -0319 displayStatusText(gui,['Saving ',statstr,'-file ... canceled.']); -0320 figure(expfig); -0321 end -0322 otherwise -0323 % nothing to do ... fig-files are not saved automatically -0324 end -0325 -0326 if nargout > 0 -0327 varargout{1} = expfig; -0328 if isjoint -0329 varargout{2} = [ax1 ax2 ax3]; -0330 else -0331 varargout{2} = [ax1 ax2]; -0332 end -0333 end -0334 -0335 end -0336 -0337 %------------- END OF CODE -------------- -0338 -0339 %% License: -0340 % MIT License -0341 % -0342 % Copyright (c) 2018 Thomas Hiller -0343 % -0344 % Permission is hereby granted, free of charge, to any person obtaining a copy -0345 % of this software and associated documentation files (the "Software"), to deal -0346 % in the Software without restriction, including without limitation the rights -0347 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0348 % copies of the Software, and to permit persons to whom the Software is -0349 % furnished to do so, subject to the following conditions: -0350 % -0351 % The above copyright notice and this permission notice shall be included in all -0352 % copies or substantial portions of the Software. +0306 % which NMR signal +0307 id = get(gui.listbox_handles.signal,'Value'); +0308 % get the new file name +0309 sfilename = data.import.NMR.filesShort{id}; +0310 ind1 = strfind(sfilename,'.'); +0311 if isempty(ind1) +0312 sfilename = [sfilename,'_INV']; +0313 else +0314 sfilename = [sfilename(1:ind1-1),'_INV']; +0315 end +0316 [FileName,PathName,~] = uiputfile({putext,put1},... +0317 ['NUCLEUSinv: Save ',statstr,' Graphics'],... +0318 fullfile(data.import.path,sfilename)); +0319 case 'MOD' +0320 [FileName,PathName,~] = uiputfile({putext,put1},... +0321 ['NUCLEUSmod: Save ',statstr,' Graphics'],... +0322 fullfile(pwd,'NUCLEUSmod_forward')); +0323 end +0324 if ~isequal(FileName,0) || ~isequal(PathName,0) +0325 print(expfig,fullfile(PathName,FileName),'-r300',driver); +0326 close(expfig); +0327 displayStatusText(gui,['Saving ',statstr,'-file ... done.']); +0328 else +0329 displayStatusText(gui,['Saving ',statstr,'-file ... canceled.']); +0330 figure(expfig); +0331 end +0332 otherwise +0333 % nothing to do ... fig-files are not saved automatically +0334 end +0335 +0336 if nargout > 0 +0337 varargout{1} = expfig; +0338 if isjoint +0339 varargout{2} = [ax1 ax2 ax3]; +0340 else +0341 varargout{2} = [ax1 ax2]; +0342 end +0343 end +0344 +0345 end +0346 +0347 %------------- END OF CODE -------------- +0348 +0349 %% License: +0350 % MIT License +0351 % +0352 % Copyright (c) 2018 Thomas Hiller 0353 % -0354 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0355 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0356 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0357 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0358 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0359 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0360 % SOFTWARE.

    +0354 % Permission is hereby granted, free of charge, to any person obtaining a copy +0355 % of this software and associated documentation files (the "Software"), to deal +0356 % in the Software without restriction, including without limitation the rights +0357 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0358 % copies of the Software, and to permit persons to whom the Software is +0359 % furnished to do so, subject to the following conditions: +0360 % +0361 % The above copyright notice and this permission notice shall be included in all +0362 % copies or substantial portions of the Software. +0363 % +0364 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0365 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0366 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0367 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0368 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0369 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0370 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/exportINV.html b/doc/nucleus/functions/interface/exportINV.html index f8c7d40..65b06ab 100644 --- a/doc/nucleus/functions/interface/exportINV.html +++ b/doc/nucleus/functions/interface/exportINV.html @@ -57,15 +57,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • displayStatusText shows status information either in the GUI or on the
  • This function is called by: @@ -106,8 +106,8 @@

    SOURCE CODE ^% none 0031 % 0032 % See also: NUCLEUSinv -0033 % Author: Thomas Hiller -0034 % email: thomas.hiller[at]leibniz-liag.de +0033 % Author: see AUTHORS.md +0034 % email: see AUTHORS.md 0035 % License: MIT License (at end) 0036 0037 %------------- BEGIN CODE -------------- @@ -139,7 +139,7 @@

    SOURCE CODE ^% save to default file at local data path -0066 save(fullfile(data.import.path,'NUCLEUSinv_raw.mat'),'savedata'); +0066 save(fullfile(data.import.path,'NUCLEUSinv_raw.mat'),'savedata','-v7.3'); 0067 clear savedata; 0068 0069 % display info text @@ -166,7 +166,7 @@

    SOURCE CODE ^if dosilent -0093 save(fullfile(sfile),'savedata'); +0093 save(fullfile(sfile),'savedata','-v7.3'); 0094 clear savedata; 0095 else 0096 % session file name @@ -185,7 +185,7 @@

    SOURCE CODE ^% if user didn't cancel save session 0111 if sum([sfile spath]) > 0 -0112 save(fullfile(spath,sfile),'savedata'); +0112 save(fullfile(spath,sfile),'savedata','-v7.3'); 0113 clear savedata; 0114 0115 % display info text diff --git a/doc/nucleus/functions/interface/findParentOfType.html b/doc/nucleus/functions/interface/findParentOfType.html index cd7f0f7..ca8c4b2 100644 --- a/doc/nucleus/functions/interface/findParentOfType.html +++ b/doc/nucleus/functions/interface/findParentOfType.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: NUCLEUSinv, NUCLEUSmod -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/fixAxes.html b/doc/nucleus/functions/interface/fixAxes.html index 69c3551..2eecf92 100644 --- a/doc/nucleus/functions/interface/fixAxes.html +++ b/doc/nucleus/functions/interface/fixAxes.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSinv, NUCLEUSmod -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/getColorIndex.html b/doc/nucleus/functions/interface/getColorIndex.html index 65f9e0c..c4b048b 100644 --- a/doc/nucleus/functions/interface/getColorIndex.html +++ b/doc/nucleus/functions/interface/getColorIndex.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv, NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/getColorTheme.html b/doc/nucleus/functions/interface/getColorTheme.html index 3ce09f5..d230002 100644 --- a/doc/nucleus/functions/interface/getColorTheme.html +++ b/doc/nucleus/functions/interface/getColorTheme.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/getVersionNoFromString.html b/doc/nucleus/functions/interface/getVersionNoFromString.html index f0331f0..0b599b5 100644 --- a/doc/nucleus/functions/interface/getVersionNoFromString.html +++ b/doc/nucleus/functions/interface/getVersionNoFromString.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/importASCIIdata.html b/doc/nucleus/functions/interface/importASCIIdata.html index cc28e0d..f7b750c 100644 --- a/doc/nucleus/functions/interface/importASCIIdata.html +++ b/doc/nucleus/functions/interface/importASCIIdata.html @@ -53,15 +53,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
  • cleanupGUIData removes unnecessary fields from the global "data" struct
  • clearAllAxes clears all axes of a given figure
  • enableGUIelements activates all necessary GUI elements after successful
  • makeINIfile creates or updates the ini-File
  • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
  • This function is called by: @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -201,71 +201,79 @@

    SOURCE CODE ^else 0131 data.import.NMR.data{c}.signal = tmp_data(:,2); -0132 end -0133 data.import.NMR.data{c}.T1IRfac = 1; -0134 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; -0135 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; -0136 -0137 % dummy parameter data -0138 data.import.NMR.para{c} = 0; -0139 end -0140 end -0141 -0142 % save file names -0143 data.import.NMR.files = fnames; -0144 data.import.NMR.filesShort = shownames; -0145 -0146 % update the ini-file -0147 gui.myui.inidata.importpath = data.import.path; -0148 gui = makeINIfile(gui,'update'); +0132 data.import.NMR.data{c}.phase = 0; +0133 +0134 param.T1IRfac = 1; +0135 param.noise = 0; +0136 param.optim = 'off'; +0137 invstd = fitDataFree(data.import.NMR.data{c}.time,data.import.NMR.data{c}.signal,... +0138 'T2',param,5); +0139 data.import.NMR.data{c}.noise = invstd.rms; +0140 end +0141 data.import.NMR.data{c}.T1IRfac = 1; +0142 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; +0143 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; +0144 +0145 % dummy parameter data +0146 data.import.NMR.para{c} = 0; +0147 end +0148 end 0149 -0150 % update the list of file names -0151 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); -0152 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); +0150 % save file names +0151 data.import.NMR.files = fnames; +0152 data.import.NMR.filesShort = shownames; 0153 -0154 % create a global INVdata struct for every file in the list -0155 if isstruct(getappdata(fig,'INVdata')) -0156 setappdata(fig,'INVdata',[]); -0157 end -0158 INVdata = cell(length(data.import.NMR.filesShort),1); -0159 setappdata(fig,'INVdata',INVdata); -0160 -0161 % clear all axes -0162 clearAllAxes(gui.figh); -0163 -0164 % update GUI data and interface -0165 setappdata(fig,'data',data); -0166 setappdata(fig,'gui',gui); -0167 enableGUIelements('ASCII'); -0168 NUCLEUSinv_updateInterface; -0169 end -0170 -0171 end -0172 -0173 %------------- END OF CODE -------------- -0174 -0175 %% License: -0176 % MIT License -0177 % -0178 % Copyright (c) 2018 Thomas Hiller -0179 % -0180 % Permission is hereby granted, free of charge, to any person obtaining a copy -0181 % of this software and associated documentation files (the "Software"), to deal -0182 % in the Software without restriction, including without limitation the rights -0183 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0184 % copies of the Software, and to permit persons to whom the Software is -0185 % furnished to do so, subject to the following conditions: -0186 % -0187 % The above copyright notice and this permission notice shall be included in all -0188 % copies or substantial portions of the Software. -0189 % -0190 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0191 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0192 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0193 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0194 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0195 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0196 % SOFTWARE. +0154 % update the ini-file +0155 gui.myui.inidata.importpath = data.import.path; +0156 gui = makeINIfile(gui,'update'); +0157 +0158 % update the list of file names +0159 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); +0160 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); +0161 +0162 % create a global INVdata struct for every file in the list +0163 if isstruct(getappdata(fig,'INVdata')) +0164 setappdata(fig,'INVdata',[]); +0165 end +0166 INVdata = cell(length(data.import.NMR.filesShort),1); +0167 setappdata(fig,'INVdata',INVdata); +0168 +0169 % clear all axes +0170 clearAllAxes(gui.figh); +0171 +0172 % update GUI data and interface +0173 setappdata(fig,'data',data); +0174 setappdata(fig,'gui',gui); +0175 enableGUIelements('ASCII'); +0176 NUCLEUSinv_updateInterface; +0177 end +0178 +0179 end +0180 +0181 %------------- END OF CODE -------------- +0182 +0183 %% License: +0184 % MIT License +0185 % +0186 % Copyright (c) 2018 Thomas Hiller +0187 % +0188 % Permission is hereby granted, free of charge, to any person obtaining a copy +0189 % of this software and associated documentation files (the "Software"), to deal +0190 % in the Software without restriction, including without limitation the rights +0191 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0192 % copies of the Software, and to permit persons to whom the Software is +0193 % furnished to do so, subject to the following conditions: +0194 % +0195 % The above copyright notice and this permission notice shall be included in all +0196 % copies or substantial portions of the Software. +0197 % +0198 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0199 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0200 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0201 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0202 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0203 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0204 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/importCPSdata.html b/doc/nucleus/functions/interface/importCPSdata.html index 9b5b08c..8623c42 100644 --- a/doc/nucleus/functions/interface/importCPSdata.html +++ b/doc/nucleus/functions/interface/importCPSdata.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/importCalibrationData.html b/doc/nucleus/functions/interface/importCalibrationData.html index f4abf20..f67a1b2 100644 --- a/doc/nucleus/functions/interface/importCalibrationData.html +++ b/doc/nucleus/functions/interface/importCalibrationData.html @@ -60,7 +60,7 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • displayStatusText shows status information either in the GUI or on the
  • This function is called by:
    diff --git a/doc/nucleus/functions/interface/importEXCELdata.html b/doc/nucleus/functions/interface/importEXCELdata.html index a0fbe55..0f4e5d0 100644 --- a/doc/nucleus/functions/interface/importEXCELdata.html +++ b/doc/nucleus/functions/interface/importEXCELdata.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSinv -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/importINV2INV.html b/doc/nucleus/functions/interface/importINV2INV.html index cbbaf78..734b1de 100644 --- a/doc/nucleus/functions/interface/importINV2INV.html +++ b/doc/nucleus/functions/interface/importINV2INV.html @@ -54,15 +54,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • onListboxData handles the calls from the context menu of the data
  • onMenuExpert handles the call from the menu that activates / deactivates
  • onMenuJointInversion handles the call from the menu that activates / deactivates
  • onMenuView handles the view menu entries
  • displayStatusText shows status information either in the GUI or on the
  • enableGUIelements activates all necessary GUI elements after successful
  • getVersionNoFromString converts the version string to a numeric value
  • makeINIfile creates or updates the ini-File
  • This function is called by: @@ -100,8 +100,8 @@

    SOURCE CODE ^% none 0028 % 0029 % See also: NUCLEUSinv -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- @@ -128,177 +128,191 @@

    SOURCE CODE ^end 0056 0057 % only continue if user didn't cancel -0058 if sum(Sessionpath) > 0 -0059 % check if it is a valid session file -0060 tmp = load(fullfile(Sessionpath,Sessionfile),'savedata'); -0061 if isfield(tmp,'savedata') && isfield(tmp.savedata,'data') && ... -0062 isfield(tmp.savedata,'id') && isfield(tmp.savedata,'INVdata') -0063 savedata = tmp.savedata; -0064 -0065 % check import uimenu -0066 set(src,'Checked','on'); +0058 if sum(Sessionpath) > 0 +0059 % display info text +0060 displayStatusText(gui,'Importing GUI session from mat-file ...'); +0061 +0062 % check if it is a valid session file +0063 tmp = load(fullfile(Sessionpath,Sessionfile),'savedata'); +0064 if isfield(tmp,'savedata') && isfield(tmp.savedata,'data') && ... +0065 isfield(tmp.savedata,'id') && isfield(tmp.savedata,'INVdata') +0066 savedata = tmp.savedata; 0067 -0068 % adjust menu entry for expert mode -0069 switch savedata.data.info.ExpertMode -0070 case 'on' -0071 set(gui.menu.extra_expert,'Checked','off'); -0072 case 'off' -0073 set(gui.menu.extra_expert,'Checked','on'); -0074 end -0075 onMenuExpert(gui.menu.extra_expert); -0076 -0077 % adjust menu entry for joint inversion -0078 switch savedata.data.info.JointInv -0079 case 'on' -0080 set(gui.menu.extra_joint,'Checked','off'); -0081 case 'off' -0082 set(gui.menu.extra_joint,'Checked','on'); -0083 end -0084 onMenuJointInversion(gui.menu.extra_joint); -0085 enableGUIelements('NMR'); -0086 -0087 % adjust menu entry for comand line inversion info -0088 switch savedata.data.info.InvInfo -0089 case 'on' -0090 set(gui.menu.view_invinfo,'Checked','off'); -0091 case 'off' -0092 set(gui.menu.view_invinfo,'Checked','on'); -0093 end -0094 onMenuView(gui.menu.view_invinfo); -0095 -0096 % adjust menu entry for tool tips -0097 switch savedata.data.info.ToolTips -0098 case 'on' -0099 set(gui.menu.view_tooltips,'Checked','off'); -0100 case 'off' -0101 set(gui.menu.view_tooltips,'Checked','on'); -0102 end -0103 onMenuView(gui.menu.view_tooltips); -0104 -0105 % update GUI data from session mat-file -0106 data = savedata.data; -0107 INVdata = savedata.INVdata; -0108 -0109 % backward compatibility with older versions -0110 % current GUI version -0111 version = getVersionNoFromString(gui.myui.version); -0112 % import GUI version -0113 version_in = getVersionNoFromString(savedata.myui.version); -0114 if version_in < version -0115 if version_in < 19 % changes introduced with v.0.1.9 -0116 if strcmp(savedata.data.invstd.invtype,'ILA') -0117 data.invstd.invtype = 'LU'; -0118 end -0119 for i = 1:numel(savedata.INVdata) -0120 if strcmp(savedata.INVdata{i}.invstd.invtype,'ILA') -0121 INVdata{i}.invstd.invtype = 'LU'; -0122 end -0123 end -0124 end -0125 end -0126 -0127 % check if the import path exists on this machine -0128 isdir_import = dir(data.import.path); -0129 % if not replace it with the path the session-file was loaded from -0130 if isempty(isdir_import) -0131 data.import.path = Sessionpath; -0132 end -0133 % update the path info field with "path" ("file") -0134 if data.import.file > 0 -0135 tmpstr = fullfile(data.import.path,data.import.file); -0136 else -0137 tmpstr = data.import.path; -0138 end -0139 % update GUI data -0140 setappdata(fig,'data',data); -0141 setappdata(fig,'gui',gui); -0142 setappdata(fig,'INVdata',INVdata); -0143 -0144 % update the ini-file -0145 gui.myui.inidata.importpath = data.import.path; -0146 gui = makeINIfile(gui,'update'); -0147 setappdata(fig,'gui',gui); -0148 -0149 if length(tmpstr)>50 -0150 set(gui.text_handles.data_path,'String',['...',tmpstr(end-50:end)],... -0151 'HorizontalAlignment','left'); -0152 else -0153 set(gui.text_handles.data_path,'String',tmpstr,... -0154 'HorizontalAlignment','left'); -0155 end -0156 set(gui.text_handles.data_path,'TooltipString',tmpstr); -0157 -0158 % update the list of file names -0159 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); -0160 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); -0161 -0162 % update GUI interface -0163 NUCLEUSinv_updateInterface; -0164 -0165 % color the file names where there is an inversion set -0166 for id = 1:size(INVdata,1) -0167 if isstruct(INVdata{id}) -0168 strL = get(gui.listbox_handles.signal,'String'); -0169 str1 = strL{id}; -0170 str2 = ['<HTML><BODY bgcolor="rgb(',... -0171 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',... -0172 str1,'</BODY></HTML>']; -0173 strL{id} = str2; -0174 set(gui.listbox_handles.signal,'String',strL); -0175 end -0176 end -0177 -0178 % set focus on last file used in previous session -0179 set(gui.listbox_handles.signal,'Value',savedata.id); -0180 -0181 % show corresponding file data -0182 updatePlotsSignal; -0183 if isstruct(INVdata{savedata.id}) -0184 if isfield(data,'results') -0185 if isfield(data.results,'invstd') -0186 updatePlotsDistribution; -0187 updateInfo(gui.plots.SignalPanel); -0188 end -0189 if isfield(data.results,'invjoint') -0190 updatePlotsJointInversion; -0191 end -0192 if isfield(data.results,'lcurve') -0193 updatePlotsLcurve; -0194 end -0195 end -0196 end -0197 else -0198 helpdlg({'importINV2INV:';... -0199 'This seems to be not a valid NUCLEUSinv session file'},... -0200 'No session data found'); -0201 end -0202 -0203 end -0204 -0205 %------------- END OF CODE -------------- -0206 -0207 %% License: -0208 % MIT License -0209 % -0210 % Copyright (c) 2018 Thomas Hiller -0211 % -0212 % Permission is hereby granted, free of charge, to any person obtaining a copy -0213 % of this software and associated documentation files (the "Software"), to deal -0214 % in the Software without restriction, including without limitation the rights -0215 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0216 % copies of the Software, and to permit persons to whom the Software is -0217 % furnished to do so, subject to the following conditions: -0218 % -0219 % The above copyright notice and this permission notice shall be included in all -0220 % copies or substantial portions of the Software. -0221 % -0222 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0223 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0224 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0225 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0226 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0227 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0228 % SOFTWARE. +0068 % check import uimenu +0069 set(src,'Checked','on'); +0070 +0071 % backward compatibility with older versions +0072 % display info text +0073 displayStatusText(gui,'Importing GUI session from mat-file ... backward compatibility.'); +0074 % current GUI version +0075 version = getVersionNoFromString(gui.myui.version); +0076 % import GUI version +0077 version_in = getVersionNoFromString(savedata.myui.version); +0078 if version_in < version +0079 if version_in < 19 % changes introduced with v.0.1.9 +0080 % rename 'ILA' to 'LU' in savedata +0081 if strcmp(savedata.data.invstd.invtype,'ILA') +0082 savedata.data.invstd.invtype = 'LU'; +0083 end +0084 for i = 1:numel(savedata.INVdata) +0085 if isstruct(savedata.INVdata{i}) && ... +0086 strcmp(savedata.INVdata{i}.invstd.invtype,'ILA') +0087 savedata.INVdata{i}.invstd.invtype = 'LU'; +0088 end +0089 end +0090 end +0091 if version_in < 112 % changes introduced with v.0.1.12 +0092 % add 'Tdiff' field to savedata +0093 savedata.data.invstd.Tdiff = data.invstd.Tdiff; +0094 for i = 1:numel(savedata.INVdata) +0095 if isstruct(savedata.INVdata{i}) +0096 savedata.INVdata{i}.invstd.Tdiff = data.invstd.Tdiff; +0097 end +0098 end +0099 end +0100 end +0101 +0102 % update GUI data from session mat-file +0103 data = savedata.data; +0104 INVdata = savedata.INVdata; +0105 +0106 % display info text +0107 displayStatusText(gui,'Importing GUI session from mat-file ... update GUI data.'); +0108 % check if the import path exists on this machine +0109 isdir_import = dir(data.import.path); +0110 % if not replace it with the path the session-file was loaded from +0111 if isempty(isdir_import) +0112 data.import.path = Sessionpath; +0113 end +0114 % update the path info field with "path" ("file") +0115 if data.import.file > 0 +0116 tmpstr = fullfile(data.import.path,data.import.file); +0117 else +0118 tmpstr = data.import.path; +0119 end +0120 % update GUI data +0121 setappdata(fig,'data',data); +0122 setappdata(fig,'gui',gui); +0123 setappdata(fig,'INVdata',INVdata); +0124 +0125 % update the ini-file +0126 gui.myui.inidata.importpath = data.import.path; +0127 gui = makeINIfile(gui,'update'); +0128 setappdata(fig,'gui',gui); +0129 +0130 if length(tmpstr)>50 +0131 set(gui.text_handles.data_path,'String',['...',tmpstr(end-50:end)],... +0132 'HorizontalAlignment','left'); +0133 else +0134 set(gui.text_handles.data_path,'String',tmpstr,... +0135 'HorizontalAlignment','left'); +0136 end +0137 set(gui.text_handles.data_path,'TooltipString',tmpstr); +0138 +0139 % update the list of file names +0140 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); +0141 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); +0142 +0143 % display info text +0144 displayStatusText(gui,'Importing GUI session from mat-file ... update GUI interface.'); +0145 % update GUI interface +0146 NUCLEUSinv_updateInterface; +0147 +0148 % color the file names where there is an inversion set +0149 for id = 1:size(INVdata,1) +0150 if isstruct(INVdata{id}) +0151 strL = get(gui.listbox_handles.signal,'String'); +0152 str1 = strL{id}; +0153 str2 = ['<HTML><BODY bgcolor="rgb(',... +0154 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',... +0155 str1,'</BODY></HTML>']; +0156 strL{id} = str2; +0157 set(gui.listbox_handles.signal,'String',strL); +0158 end +0159 end +0160 +0161 % display info text +0162 displayStatusText(gui,'Importing GUI session from mat-file ... update GUI menus.'); +0163 % adjust menu entry for expert mode +0164 switch savedata.data.info.ExpertMode +0165 case 'on' +0166 set(gui.menu.extra_expert,'Checked','off'); +0167 case 'off' +0168 set(gui.menu.extra_expert,'Checked','on'); +0169 end +0170 onMenuExpert(gui.menu.extra_expert); +0171 +0172 % adjust menu entry for joint inversion +0173 switch savedata.data.info.JointInv +0174 case 'on' +0175 set(gui.menu.extra_joint,'Checked','off'); +0176 case 'off' +0177 set(gui.menu.extra_joint,'Checked','on'); +0178 end +0179 onMenuJointInversion(gui.menu.extra_joint); +0180 enableGUIelements('NMR'); +0181 +0182 % adjust menu entry for comand line inversion info +0183 switch savedata.data.info.InvInfo +0184 case 'on' +0185 set(gui.menu.view_invinfo,'Checked','off'); +0186 case 'off' +0187 set(gui.menu.view_invinfo,'Checked','on'); +0188 end +0189 onMenuView(gui.menu.view_invinfo); +0190 +0191 % adjust menu entry for tool tips +0192 switch savedata.data.info.ToolTips +0193 case 'on' +0194 set(gui.menu.view_tooltips,'Checked','off'); +0195 case 'off' +0196 set(gui.menu.view_tooltips,'Checked','on'); +0197 end +0198 onMenuView(gui.menu.view_tooltips); +0199 +0200 % display info text +0201 displayStatusText(gui,'Importing GUI session from mat-file ... show last file.'); +0202 % set focus on last file used in previous session +0203 set(gui.listbox_handles.signal,'Value',savedata.id); +0204 % and simulate click to update all relevant GUI elements +0205 onListboxData(gui.listbox_handles.signal); +0206 +0207 % display info text +0208 displayStatusText(gui,'Importing GUI session from mat-file ... done.'); +0209 else +0210 % display info text +0211 displayStatusText(gui,'Importing GUI session from mat-file ... cancelled.'); +0212 +0213 helpdlg({'importINV2INV:';... +0214 'This seems to be not a valid NUCLEUSinv session file'},... +0215 'No session data found'); +0216 end +0217 end +0218 +0219 %------------- END OF CODE -------------- +0220 +0221 %% License: +0222 % MIT License +0223 % +0224 % Copyright (c) 2018 Thomas Hiller +0225 % +0226 % Permission is hereby granted, free of charge, to any person obtaining a copy +0227 % of this software and associated documentation files (the "Software"), to deal +0228 % in the Software without restriction, including without limitation the rights +0229 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0230 % copies of the Software, and to permit persons to whom the Software is +0231 % furnished to do so, subject to the following conditions: +0232 % +0233 % The above copyright notice and this permission notice shall be included in all +0234 % copies or substantial portions of the Software. +0235 % +0236 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0237 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0238 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0239 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0240 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0241 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0242 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/importMOD2INV.html b/doc/nucleus/functions/interface/importMOD2INV.html index 955985b..8b7b82f 100644 --- a/doc/nucleus/functions/interface/importMOD2INV.html +++ b/doc/nucleus/functions/interface/importMOD2INV.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSinv -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -245,122 +245,136 @@

    SOURCE CODE ^switch T1T2 0174 case 'T1' 0175 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EdT1(dL(i),:)'; -0176 case 'T2' -0177 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EdT2(dL(i),:)'; -0178 end -0179 data.import.NMR.data{c}.phase = 0; -0180 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; -0181 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; -0182 -0183 data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; -0184 data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; -0185 data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; -0186 data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; -0187 -0188 table{c,1} = true; -0189 table{c,2} = data.import.NMRMOD.p(dL(i)); -0190 table{c,3} = data.import.NMRMOD.Sd(dL(i)); -0191 table{c,4} = 'D'; -0192 end -0193 -0194 % now we import imbibition NMR data -0195 for i = 1:numel(iL) -0196 % the individual file names -0197 c = c + 1; -0198 fnames(c).parfile = ''; -0199 fnames(c).datafile = data.import.file; -0200 fnames(c).T2specfile = ''; -0201 -0202 shownames{c} = ['NUCLEUSmod_',T1T2,'_imb_',num2str(i)]; -0203 -0204 % the 'header' data -0205 data.import.NMR.data{c}.datfile = fileID.name; -0206 data.import.NMR.data{c}.date = fileID.date; -0207 data.import.NMR.data{c}.datenum = fileID.datenum; -0208 data.import.NMR.data{c}.bytes = fileID.bytes; -0209 % the NMR data -0210 data.import.NMR.data{c}.flag = T1T2; -0211 data.import.NMR.data{c}.T1IRfac = T1IRfac; -0212 data.import.NMR.data{c}.time = data.import.NMRMOD.nmr.t(:); -0213 switch T1T2 -0214 case 'T1' -0215 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT1(iL(i),:)'; -0216 case 'T2' -0217 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT2(iL(i),:)'; -0218 end -0219 data.import.NMR.data{c}.phase = 0; -0220 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; -0221 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; -0222 -0223 data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; -0224 data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; -0225 data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; -0226 data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; -0227 -0228 table{c,1} = true; -0229 table{c,2} = data.import.NMRMOD.p(iL(i)); -0230 table{c,3} = data.import.NMRMOD.Si(iL(i)); -0231 table{c,4} = 'I'; -0232 end -0233 -0234 data.import.NMR.files = fnames; -0235 data.import.NMR.filesShort = shownames; -0236 -0237 % import pressure / saturation data -0238 data.pressure.unit = data.import.NMRMOD.gui_pressure.unit; -0239 data.pressure.unitfac = data.import.NMRMOD.gui_pressure.unitfac; -0240 data.pressure.table = table; -0241 -0242 % update the list of file names -0243 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); -0244 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); -0245 -0246 % create a global INVdata struct for every file in the list -0247 if isstruct(getappdata(fig,'INVdata')) -0248 setappdata(fig,'INVdata',[]); -0249 end -0250 INVdata = cell(length(data.import.NMR.filesShort),1); -0251 setappdata(fig,'INVdata',INVdata); -0252 -0253 % clear all axes -0254 clearAllAxes(gui.figh); +0176 data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(dL(i),2); +0177 case 'T2' +0178 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EdT2(dL(i),:)'; +0179 data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(dL(i),4); +0180 end +0181 data.import.NMR.data{c}.phase = 0; +0182 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; +0183 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; +0184 +0185 data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; +0186 data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; +0187 if isfield(data.import.NMRMOD.nmr,'Td') +0188 data.import.NMR.para{c}.Tdiff = data.import.NMRMOD.nmr.Td; +0189 else +0190 data.import.NMR.para{c}.Tdiff = 1e6; +0191 end +0192 data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; +0193 data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; +0194 +0195 table{c,1} = true; +0196 table{c,2} = data.import.NMRMOD.p(dL(i)); +0197 table{c,3} = data.import.NMRMOD.Sd(dL(i)); +0198 table{c,4} = 'D'; +0199 end +0200 +0201 % now we import imbibition NMR data +0202 for i = 1:numel(iL) +0203 % the individual file names +0204 c = c + 1; +0205 fnames(c).parfile = ''; +0206 fnames(c).datafile = data.import.file; +0207 fnames(c).T2specfile = ''; +0208 +0209 shownames{c} = ['NUCLEUSmod_',T1T2,'_imb_',num2str(i)]; +0210 +0211 % the 'header' data +0212 data.import.NMR.data{c}.datfile = fileID.name; +0213 data.import.NMR.data{c}.date = fileID.date; +0214 data.import.NMR.data{c}.datenum = fileID.datenum; +0215 data.import.NMR.data{c}.bytes = fileID.bytes; +0216 % the NMR data +0217 data.import.NMR.data{c}.flag = T1T2; +0218 data.import.NMR.data{c}.T1IRfac = T1IRfac; +0219 data.import.NMR.data{c}.time = data.import.NMRMOD.nmr.t(:); +0220 switch T1T2 +0221 case 'T1' +0222 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT1(iL(i),:)'; +0223 data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(iL(i),1); +0224 case 'T2' +0225 data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT2(iL(i),:)'; +0226 data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(iL(i),3); +0227 end +0228 data.import.NMR.data{c}.phase = 0; +0229 data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; +0230 data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; +0231 +0232 data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; +0233 data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; +0234 if isfield(data.import.NMRMOD.nmr,'Td') +0235 data.import.NMR.para{c}.Tdiff = data.import.NMRMOD.nmr.Td; +0236 else +0237 data.import.NMR.para{c}.Tdiff = 1e6; +0238 end +0239 data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; +0240 data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; +0241 +0242 table{c,1} = true; +0243 table{c,2} = data.import.NMRMOD.p(iL(i)); +0244 table{c,3} = data.import.NMRMOD.Si(iL(i)); +0245 table{c,4} = 'I'; +0246 end +0247 +0248 data.import.NMR.files = fnames; +0249 data.import.NMR.filesShort = shownames; +0250 +0251 % import pressure / saturation data +0252 data.pressure.unit = data.import.NMRMOD.gui_pressure.unit; +0253 data.pressure.unitfac = data.import.NMRMOD.gui_pressure.unitfac; +0254 data.pressure.table = table; 0255 -0256 % enable GUI data and interface -0257 setappdata(fig,'data',data); -0258 setappdata(fig,'gui',gui); -0259 enableGUIelements('MOD'); -0260 NUCLEUSinv_updateInterface; -0261 else -0262 helpdlg({'importNUCLEUSmod:',... -0263 'NUCLEUSmod data import unsuccessful.'},'import error'); -0264 end -0265 -0266 end -0267 -0268 %------------- END OF CODE -------------- -0269 -0270 %% License: -0271 % MIT License -0272 % -0273 % Copyright (c) 2018 Thomas Hiller -0274 % -0275 % Permission is hereby granted, free of charge, to any person obtaining a copy -0276 % of this software and associated documentation files (the "Software"), to deal -0277 % in the Software without restriction, including without limitation the rights -0278 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0279 % copies of the Software, and to permit persons to whom the Software is -0280 % furnished to do so, subject to the following conditions: -0281 % -0282 % The above copyright notice and this permission notice shall be included in all -0283 % copies or substantial portions of the Software. -0284 % -0285 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0286 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0287 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0288 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0289 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0290 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0291 % SOFTWARE. +0256 % update the list of file names +0257 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); +0258 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); +0259 +0260 % create a global INVdata struct for every file in the list +0261 if isstruct(getappdata(fig,'INVdata')) +0262 setappdata(fig,'INVdata',[]); +0263 end +0264 INVdata = cell(length(data.import.NMR.filesShort),1); +0265 setappdata(fig,'INVdata',INVdata); +0266 +0267 % clear all axes +0268 clearAllAxes(gui.figh); +0269 +0270 % enable GUI data and interface +0271 setappdata(fig,'data',data); +0272 setappdata(fig,'gui',gui); +0273 enableGUIelements('MOD'); +0274 NUCLEUSinv_updateInterface; +0275 else +0276 helpdlg({'importNUCLEUSmod:',... +0277 'NUCLEUSmod data import unsuccessful.'},'import error'); +0278 end +0279 +0280 end +0281 +0282 %------------- END OF CODE -------------- +0283 +0284 %% License: +0285 % MIT License +0286 % +0287 % Copyright (c) 2018 Thomas Hiller +0288 % +0289 % Permission is hereby granted, free of charge, to any person obtaining a copy +0290 % of this software and associated documentation files (the "Software"), to deal +0291 % in the Software without restriction, including without limitation the rights +0292 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0293 % copies of the Software, and to permit persons to whom the Software is +0294 % furnished to do so, subject to the following conditions: +0295 % +0296 % The above copyright notice and this permission notice shall be included in all +0297 % copies or substantial portions of the Software. +0298 % +0299 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0300 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0301 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0302 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0303 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0304 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0305 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/importMOD2MOD.html b/doc/nucleus/functions/interface/importMOD2MOD.html index ce46b92..ce034be 100644 --- a/doc/nucleus/functions/interface/importMOD2MOD.html +++ b/doc/nucleus/functions/interface/importMOD2MOD.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: NUCLEUSmod -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- @@ -123,49 +123,52 @@

    SOURCE CODE ^% update GUI data -0061 setappdata(fig,'data',data); -0062 % update the interface -0063 NUCLEUSmod_updateInterface; -0064 % update the axes -0065 updatePlotsPSD; -0066 % update the axes -0067 updatePlotsCPS; -0068 updatePlotsNMR; -0069 end -0070 -0071 end -0072 -0073 %------------- END OF CODE -------------- -0074 -0075 %% License: -0076 % MIT License -0077 % -0078 % Copyright (c) 2018 Thomas Hiller -0079 % -0080 % Permission is hereby granted, free of charge, to any person obtaining a copy -0081 % of this software and associated documentation files (the "Software"), to deal -0082 % in the Software without restriction, including without limitation the rights -0083 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0084 % copies of the Software, and to permit persons to whom the Software is -0085 % furnished to do so, subject to the following conditions: -0086 % -0087 % The above copyright notice and this permission notice shall be included in all -0088 % copies or substantial portions of the Software. +0054 if ~isfield(guidata.NMRMOD_GUI.nmr,'Td') +0055 data.nmr.Td = 1e6; +0056 end +0057 data.results.constants = guidata.constants; +0058 data.results.GEOM = guidata.GEOM; +0059 data.results.NMR = guidata.NMR; +0060 data.results.psddata = guidata.psddata; +0061 data.results.SAT = guidata.SAT; +0062 +0063 % update GUI data +0064 setappdata(fig,'data',data); +0065 % update the interface +0066 NUCLEUSmod_updateInterface; +0067 % update the axes +0068 updatePlotsPSD; +0069 % update the axes +0070 updatePlotsCPS; +0071 updatePlotsNMR; +0072 end +0073 +0074 end +0075 +0076 %------------- END OF CODE -------------- +0077 +0078 %% License: +0079 % MIT License +0080 % +0081 % Copyright (c) 2018 Thomas Hiller +0082 % +0083 % Permission is hereby granted, free of charge, to any person obtaining a copy +0084 % of this software and associated documentation files (the "Software"), to deal +0085 % in the Software without restriction, including without limitation the rights +0086 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0087 % copies of the Software, and to permit persons to whom the Software is +0088 % furnished to do so, subject to the following conditions: 0089 % -0090 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0091 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0092 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0093 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0094 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0095 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0096 % SOFTWARE. +0090 % The above copyright notice and this permission notice shall be included in all +0091 % copies or substantial portions of the Software. +0092 % +0093 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0094 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0095 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0096 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0097 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0098 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0099 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/importNMRdata.html b/doc/nucleus/functions/interface/importNMRdata.html index 02e9764..72327b9 100644 --- a/doc/nucleus/functions/interface/importNMRdata.html +++ b/doc/nucleus/functions/interface/importNMRdata.html @@ -57,6 +57,7 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • LoadNMRData_driver loads NMR raw data from different file formats
  • fixParameterString cleans parameter file lines when the properties have a
  • rotateT2phase rotateT2phase rotates the complex NMR T2 signal so that the imaginary
  • cleanupGUIData removes unnecessary fields from the global "data" struct
  • clearAllAxes clears all axes of a given figure
  • displayStatusText shows status information either in the GUI or on the
  • enableGUIelements activates all necessary GUI elements after successful
  • makeINIfile creates or updates the ini-File
  • This function is called by: @@ -80,7 +81,7 @@

    CROSS-REFERENCE INFORMATION ^
 
 <h2><a name=SUBFUNCTIONS ^

    +
  • function [NMRpath,NMRfile] = getNMRPathFile(label,import)
  • function [data,gui] = importDataMouseCPMG(data,gui)
  • function [data,gui] = importDataBGRmat(data,gui)
  • function [data,gui] = importDataBGRliftSingle(data,gui)
  • function [data,gui] = importDataBGRliftAll(data,gui)
  • function [data,gui] = importDataHelios(data,gui)
  • function [data,gui] = importDataDart(data,gui)
  • function [data,gui] = importDataGeneral(data,gui)
  • function [data,gui] = importDataLIAG(data,gui)
  • function [data,gui] = importDataLIAGproject(data,gui)
  • function [data,gui] = importDataMouse(data,gui)
  • function [data,gui] = importDataIBAC(data,gui)
  • function data = loadGUIParameters(datapath,fname)
  • function data = loadGUIrawdata(data,NMRpath,NMRfile)
  • SOURCE CODE ^

    0001 function importNMRdata(src)
    @@ -115,1048 +116,1199 @@ 

    SOURCE CODE ^% importDataLIAG 0031 % importDataLIAGproject 0032 % importDataMouse -0033 % loadGUIParameters -0034 % loadGUIrawdata -0035 % -0036 % MAT-files required: -0037 % none -0038 % -0039 % See also: NUCLEUSinv, NUCLEUSmod -0040 % Author: Thomas Hiller -0041 % email: thomas.hiller[at]leibniz-liag.de -0042 % License: MIT License (at end) -0043 -0044 %------------- BEGIN CODE -------------- -0045 -0046 %% get GUI handle and data -0047 fig = findobj('Tag','INV'); -0048 gui = getappdata(fig,'gui'); -0049 data = getappdata(fig,'data'); -0050 -0051 % try is used to catch any import error -0052 % often the wrong input file format is the case -0053 try -0054 % check what import format is chosen -0055 label = get(src,'Label'); -0056 -0057 % set file format for later use -0058 if strcmp(label,'GGE ascii') -0059 data.import.fileformat = 'rwth'; -0060 elseif strcmp(label,'GGE field') -0061 data.import.fileformat = 'field'; -0062 elseif strcmp(label,'GGE Dart') -0063 data.import.fileformat = 'dart'; -0064 elseif strcmp(label,'CoreLab ascii') -0065 data.import.fileformat = 'corelab'; -0066 elseif strcmp(label,'MOUSE') -0067 data.import.fileformat = 'mouse'; -0068 elseif strcmp(label,'LIAG single') -0069 data.import.fileformat = 'liag'; -0070 elseif strcmp(label,'LIAG from project') -0071 data.import.fileformat = 'liag'; -0072 elseif strcmp(label,'LIAG last project') -0073 data.import.fileformat = 'liag'; -0074 elseif strcmp(label,'BGR std') -0075 data.import.fileformat = 'bgr'; -0076 elseif strcmp(label,'BGR org') -0077 data.import.fileformat = 'bgr2'; -0078 elseif strcmp(label,'BGR mat') -0079 data.import.fileformat = 'bgrmat'; -0080 elseif strcmp(label,'BAM TOM') -0081 data.import.fileformat = 'bamtom'; -0082 elseif strcmp(label,'PM5') -0083 data.import.fileformat = 'pm5'; -0084 elseif strcmp(label,'PM25') -0085 data.import.fileformat = 'pm25'; -0086 else -0087 helpdlg('Something is utterly wrong.','onMenuImport: Choose again.'); -0088 end -0089 -0090 % remove info field if any -0091 ih = findobj('Tag','inv_info'); -0092 if ~isempty(ih); delete(ih); end -0093 -0094 % depending on the import format get the corresponding path / file -0095 [NMRpath,NMRfile] = getNMRPathFile(label,data.import); +0033 % importDataHelios +0034 % loadGUIParameters +0035 % loadGUIrawdata +0036 % +0037 % MAT-files required: +0038 % none +0039 % +0040 % See also: NUCLEUSinv, NUCLEUSmod +0041 % Author: see AUTHORS.md +0042 % email: see AUTHORS.md +0043 % License: MIT License (at end) +0044 +0045 %------------- BEGIN CODE -------------- +0046 +0047 %% get GUI handle and data +0048 fig = findobj('Tag','INV'); +0049 gui = getappdata(fig,'gui'); +0050 data = getappdata(fig,'data'); +0051 +0052 % try is used to catch any import error +0053 % often the wrong input file format is the case +0054 try +0055 % check what import format is chosen +0056 label = get(src,'Label'); +0057 +0058 % set file format for later use +0059 if strcmp(label,'GGE ascii') +0060 data.import.fileformat = 'rwth'; +0061 elseif strcmp(label,'GGE field') +0062 data.import.fileformat = 'field'; +0063 elseif strcmp(label,'GGE Dart') +0064 data.import.fileformat = 'dart'; +0065 elseif strcmp(label,'CoreLab ascii') +0066 data.import.fileformat = 'corelab'; +0067 elseif strcmp(label,'MOUSE') +0068 data.import.fileformat = 'mouse'; +0069 elseif strcmp(label,'LIAG single') +0070 data.import.fileformat = 'liag'; +0071 elseif strcmp(label,'LIAG from project') +0072 data.import.fileformat = 'liag'; +0073 elseif strcmp(label,'LIAG last project') +0074 data.import.fileformat = 'liag'; +0075 elseif strcmp(label,'BGR std') +0076 data.import.fileformat = 'bgr'; +0077 elseif strcmp(label,'MouseCPMG') +0078 data.import.fileformat = 'MouseCPMG'; +0079 elseif strcmp(label,'BGR mat') +0080 data.import.fileformat = 'bgrmat'; +0081 elseif strcmp(label,'MouseLiftSingle') +0082 data.import.fileformat = 'MouseLiftSingle'; +0083 elseif strcmp(label,'MouseLiftAll') +0084 data.import.fileformat = 'MouseLiftAll'; +0085 elseif strcmp(label,'Helios') +0086 data.import.fileformat = 'helios'; +0087 elseif strcmp(label,'BAM TOM') +0088 data.import.fileformat = 'bamtom'; +0089 elseif strcmp(label,'PM5') +0090 data.import.fileformat = 'pm5'; +0091 elseif strcmp(label,'PM25') +0092 data.import.fileformat = 'pm25'; +0093 else +0094 helpdlg('Something is utterly wrong.','onMenuImport: Choose again.'); +0095 end 0096 -0097 % only continue if user didn't cancel -0098 if sum([NMRpath NMRfile]) > 0 -0099 % remove old fields and data -0100 data = cleanupGUIData(data); -0101 -0102 % check for mat-file with GUI rawdata and import data -0103 isfile = dir(fullfile(NMRpath,'NUCLEUSinv_raw.mat')); -0104 -0105 % if there is nor raw-file import from folder/file -0106 if isempty(isfile) -0107 % import data -0108 data.import.path = NMRpath; -0109 data.import.file = -1; -0110 displayStatusText(gui,'Reading NMR Data ...'); -0111 % call the corresponding subroutines -0112 switch label -0113 case {'GGE ascii','GGE field','CoreLab ascii','BGR std',... -0114 'BAM TOM'} -0115 [data,gui] = importDataGeneral(data,gui); -0116 case 'Dart' -0117 data.import.file = NMRfile; -0118 [data,gui] = importDataDart(data,gui); -0119 case 'MOUSE' -0120 [data,gui] = importDataMouse(data,gui); -0121 case 'LIAG single' -0122 [data,gui] = importDataLIAG(data,gui); -0123 case {'LIAG from project','LIAG last project'} -0124 [data,gui] = importDataLIAGproject(data,gui); -0125 % make the Petro Panel visible as default -0126 tmp_h = gui.myui.heights(2,:); -0127 tmp_h(3) = gui.myui.heights(2,3); -0128 % but the CPS panel stays minimized -0129 tmp_h(5) = gui.myui.heights(1,3); -0130 set(gui.panels.main,'Heights',tmp_h); -0131 set(gui.panels.petro.main,'Minimized',false); -0132 case 'BGR org' -0133 [data,gui] = importDataBGR(data,gui); -0134 case 'BGR mat' -0135 data.import.file = NMRfile; -0136 [data,gui] = importDataBGRmat(data,gui); -0137 case {'PM5','PM25'} -0138 data.import.file = NMRfile; -0139 [data,gui] = importDataIBAC(data,gui); -0140 end -0141 displayStatusText(gui,'Reading NMR Data ... done'); -0142 else -0143 % import from mat-file -0144 displayStatusText(gui,'Importing NMR raw data from mat-file ...'); -0145 data = loadGUIrawdata(data,NMRpath,NMRfile); -0146 displayStatusText(gui,'Importing NMR raw data from mat-file ... done'); -0147 end -0148 -0149 % update the path info field with "NMRpath" ("NMRfile") -0150 if NMRfile > 0 -0151 tmpstr = fullfile(NMRpath,NMRfile); -0152 else -0153 tmpstr = NMRpath; -0154 end -0155 if length(tmpstr)>50 -0156 set(gui.text_handles.data_path,'String',['...',tmpstr(end-50:end)],... -0157 'HorizontalAlignment','left'); -0158 else -0159 set(gui.text_handles.data_path,'String',tmpstr,... -0160 'HorizontalAlignment','left'); +0097 % remove info field if any +0098 ih = findobj('Tag','inv_info'); +0099 if ~isempty(ih); delete(ih); end +0100 +0101 % depending on the import format get the corresponding path / file +0102 [NMRpath,NMRfile] = getNMRPathFile(label,data.import); +0103 +0104 % only continue if user didn't cancel +0105 if sum([NMRpath NMRfile]) > 0 +0106 % remove old fields and data +0107 data = cleanupGUIData(data); +0108 +0109 % check for mat-file with GUI rawdata and import data +0110 isfile = dir(fullfile(NMRpath,'NUCLEUSinv_raw.mat')); +0111 +0112 % if there is no raw-file import from folder/file +0113 if isempty(isfile) +0114 % import data +0115 data.import.path = NMRpath; +0116 data.import.file = -1; +0117 displayStatusText(gui,'Reading NMR Data ...'); +0118 % call the corresponding subroutines +0119 switch label +0120 case {'GGE ascii','GGE field','CoreLab ascii','BGR std',... +0121 'BAM TOM'} +0122 [data,gui] = importDataGeneral(data,gui); +0123 case 'Dart' +0124 data.import.file = NMRfile; +0125 [data,gui] = importDataDart(data,gui); +0126 case 'MOUSE' +0127 [data,gui] = importDataMouse(data,gui); +0128 case 'LIAG single' +0129 [data,gui] = importDataLIAG(data,gui); +0130 case {'LIAG from project','LIAG last project'} +0131 [data,gui] = importDataLIAGproject(data,gui); +0132 % make the Petro Panel visible as default +0133 tmp_h = gui.myui.heights(2,:); +0134 tmp_h(3) = gui.myui.heights(2,3); +0135 % but the CPS panel stays minimized +0136 tmp_h(5) = gui.myui.heights(1,3); +0137 set(gui.panels.main,'Heights',tmp_h); +0138 set(gui.panels.petro.main,'Minimized',false); +0139 case 'BGR mat' +0140 data.import.file = NMRfile; +0141 [data,gui] = importDataBGRmat(data,gui); +0142 case 'MouseCPMG' +0143 [data,gui] = importDataMouseCPMG(data,gui); +0144 case 'MouseLiftSingle' +0145 [data,gui] = importDataBGRliftSingle(data,gui); +0146 case 'MouseLiftAll' +0147 [data,gui] = importDataBGRliftAll(data,gui); +0148 case 'Helios' +0149 data.import.file = NMRfile; +0150 [data,gui] = importDataHelios(data,gui); +0151 case {'PM5','PM25'} +0152 data.import.file = NMRfile; +0153 [data,gui] = importDataIBAC(data,gui); +0154 end +0155 displayStatusText(gui,'Reading NMR Data ... done'); +0156 else +0157 % import from mat-file +0158 displayStatusText(gui,'Importing NMR raw data from mat-file ...'); +0159 data = loadGUIrawdata(data,NMRpath,NMRfile); +0160 displayStatusText(gui,'Importing NMR raw data from mat-file ... done'); 0161 end -0162 set(gui.text_handles.data_path,'TooltipString',tmpstr); -0163 -0164 % update the ini-file -0165 gui.myui.inidata.importpath = data.import.path; -0166 gui = makeINIfile(gui,'update'); -0167 -0168 % update the list of file names -0169 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); -0170 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); -0171 -0172 % create a global INVdata struct for every file in the list -0173 if isstruct(getappdata(fig,'INVdata')) -0174 setappdata(fig,'INVdata',[]); +0162 +0163 % update the path info field with "NMRpath" ("NMRfile") +0164 if NMRfile > 0 +0165 tmpstr = fullfile(NMRpath,NMRfile); +0166 else +0167 tmpstr = NMRpath; +0168 end +0169 if length(tmpstr)>50 +0170 set(gui.text_handles.data_path,'String',['...',tmpstr(end-50:end)],... +0171 'HorizontalAlignment','left'); +0172 else +0173 set(gui.text_handles.data_path,'String',tmpstr,... +0174 'HorizontalAlignment','left'); 0175 end -0176 INVdata = cell(length(data.import.NMR.filesShort),1); -0177 setappdata(fig,'INVdata',INVdata); -0178 -0179 % clear all axes -0180 clearAllAxes(gui.figh); +0176 set(gui.text_handles.data_path,'TooltipString',tmpstr); +0177 +0178 % update the ini-file +0179 gui.myui.inidata.importpath = data.import.path; +0180 gui = makeINIfile(gui,'update'); 0181 -0182 % update GUI data and interface -0183 setappdata(fig,'data',data); -0184 setappdata(fig,'gui',gui); -0185 enableGUIelements('NMR'); -0186 -0187 % special treatment of LIAG projects -0188 switch label -0189 case {'LIAG from project','LIAG last project'} -0190 cpath = data.import.LIAG.calibrationpath; -0191 % check if this calibration file was already used -0192 isfile = dir(fullfile(cpath,'NUCLEUS_calibData.mat')); -0193 if ~isempty(isfile) -0194 id = 2; -0195 % if data is there reuse it -0196 calib = load(fullfile(cpath,isfile.name),'calib'); -0197 INVdata{id} = calib.calib; -0198 data.calib = INVdata{id}.calib; -0199 data.import.LIAG.Tbulk = INVdata{id}.results.invstd.T2; -0200 setappdata(fig,'INVdata',INVdata); -0201 setappdata(fig,'data',data); -0202 % color the list entry -0203 strL = get(gui.listbox_handles.signal,'String'); -0204 str1 = strL{id}; -0205 str2 = ['<HTML><BODY bgcolor="rgb(',... -0206 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',... -0207 str1,'</BODY></HTML>']; -0208 strL{id} = str2; -0209 set(gui.listbox_handles.signal,'String',strL); -0210 end -0211 otherwise -0212 end -0213 % update GUI interface -0214 NUCLEUSinv_updateInterface; -0215 end -0216 -0217 catch ME -0218 % show error message in case import fails -0219 errmsg = {ME.message;[ME.stack(1).name,' Line: ',num2str(ME.stack(1).line)];... -0220 'Check File Format settings.'}; -0221 errordlg(errmsg,'importNMRdata: Error!'); -0222 -0223 end -0224 -0225 end -0226 -0227 %% -0228 function [NMRpath,NMRfile] = getNMRPathFile(label,import) -0229 -0230 NMRpath = -1; -0231 NMRfile = -1; -0232 % for almost all import cases we load a folder ... but not for all -0233 switch label -0234 case {'GGE ascii','GGE field','CoreLab ascii','MOUSE','LIAG single',... -0235 'BGR std','BGR org','BAM TOM','PM25'} -0236 % if there is already a data folder present we start from here -0237 if isfield(import,'path') -0238 NMRpath = uigetdir(import.path,'Choose Data Path'); -0239 else -0240 % otherwise we start at the current working dircetory -0241 % 'NMRpath' holds the name of the choosen data path -0242 here = mfilename('fullpath'); -0243 [pathstr,~,~] = fileparts(here); -0244 NMRpath = uigetdir(pathstr,'Choose Data Path'); -0245 end -0246 case 'LIAG from project' -0247 % if there is already a data folder present we start from here -0248 if isfield(import,'path') -0249 NMRpath = uigetdir(import.path,'Choose Project Path'); -0250 else -0251 % otherwise we start at the current working dircetory -0252 % 'NMRpath' holds the name of the choosen data path -0253 here = mfilename('fullpath'); -0254 [pathstr,~,~] = fileparts(here); -0255 NMRpath = uigetdir(pathstr,'Choose Project Path'); -0256 end -0257 case 'LIAG last project' -0258 % there is already a data folder and we use this one -0259 if isfield(import,'path') -0260 NMRpath = import.path; -0261 else -0262 % otherwise we start at the current working dircetory -0263 % 'NMRpath' holds the name of the choosen data path -0264 here = mfilename('fullpath'); -0265 [pathstr,~,~] = fileparts(here); -0266 NMRpath = uigetdir(pathstr,'Choose Project Path'); -0267 end -0268 case {'GGE Dart','BGR mat','NMR mat'} -0269 % if there is already a data folder present we start from here -0270 if isfield(import,'path') -0271 [NMRfile,NMRpath] = uigetfile(import.path,'Choose Data file'); -0272 else -0273 % otherwise we start at the current working dircetory -0274 % 'foldername' hold s the name of the choosen data path -0275 here = mfilename('fullpath'); -0276 [pathstr,~,~] = fileparts(here); -0277 [NMRfile,NMRpath] = uigetfile(pathstr,'Choose Data file'); -0278 end -0279 case 'PM5' -0280 % if there is already a data folder present we start from here -0281 if isfield(import,'path') -0282 NMRpath = uigetdir(import.path,'Choose PM5 Data Path'); -0283 else -0284 % otherwise we start at the current working dircetory -0285 % 'NMRpath' holds the name of the choosen data path -0286 here = mfilename('fullpath'); -0287 [pathstr,~,~] = fileparts(here); -0288 NMRpath = uigetdir(pathstr,'Choose PM5 Data Path'); -0289 end -0290 -0291 % check if there are multiple inf-files available -0292 % if yes -> choose one -0293 inffiles = dir(fullfile(NMRpath,'*.inf')); -0294 if numel(inffiles) > 1 -0295 [NMRfile,NMRpath] = uigetfile(fullfile(NMRpath,'*.inf'),... -0296 'Choose INF file'); -0297 elseif numel(inffiles) == 1 -0298 NMRfile = inffiles.name; -0299 end -0300 end -0301 -0302 end -0303 -0304 %% -0305 function [data,gui] = importDataBGR(data,gui) -0306 -0307 % first check the subpaths -0308 % there should be 'cpmgfastauto' and 't1test' -0309 t1path = dir(fullfile(data.import.path,'t1test')); -0310 t1path = t1path(~ismember({t1path.name},{'.','..'})); -0311 t2path = dir(fullfile(data.import.path,'cpmgfastauto')); -0312 t2path = t2path(~ismember({t2path.name},{'.','..'})); -0313 -0314 fnames = struct; -0315 % shownames is just a dummy to hold all data file names that -0316 % will be shown in the listbox -0317 shownames = cell(1,1); -0318 -0319 c = 0; -0320 if ~isempty(t1path) -0321 for i = 1:size(t1path,1) -0322 in.T1T2 = 'T1'; -0323 in.path = fullfile(data.import.path,'t1test',t1path(i).name); -0324 in.fileformat = data.import.fileformat; -0325 out = LoadNMRData_driver(in); -0326 -0327 for j = 1:size(out.nmrData,2) -0328 % the individual file names -0329 c = c + 1; -0330 fnames(c).parfile = 'acq.par'; -0331 fnames(c).datafile = out.nmrData{j}.datfile; -0332 -0333 shownames{c} = ['T1_',t1path(i).name,'_',fnames(c).datafile]; -0334 -0335 % the NMR data -0336 % here we fix the time scale from [ms] to [s] -0337 if max(out.nmrData{j}.time) > 100 -0338 out.nmrData{j}.time = out.nmrData{j}.time/1000; -0339 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; -0340 end -0341 data.import.NMR.data{c} = out.nmrData{j}; -0342 data.import.NMR.para{c} = out.parData; -0343 end -0344 end -0345 end -0346 -0347 if ~isempty(t2path) -0348 for i = 1:size(t2path,1) -0349 in.T1T2 = 'T2'; -0350 in.path = fullfile(data.import.path,'cpmgfastauto',t2path(i).name); -0351 in.fileformat = data.import.fileformat; -0352 out = LoadNMRData_driver(in); -0353 -0354 for j = 1:size(out.nmrData,2) -0355 % the individual file names -0356 c = c + 1; -0357 fnames(c).parfile = 'acq.par'; -0358 fnames(c).datafile = out.nmrData{j}.datfile; -0359 fnames(c).T2specfile = ''; -0360 -0361 shownames{c} = ['T2_',t2path(i).name,'_',fnames(c).datafile]; -0362 -0363 % the NMR data -0364 % here we fix the time scale from [ms] to [s] -0365 if max(out.nmrData{j}.time) > 100 -0366 out.nmrData{j}.time = out.nmrData{j}.time/1000; -0367 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; -0368 end -0369 data.import.NMR.data{c} = out.nmrData{j}; -0370 data.import.NMR.para{c} = out.parData; -0371 end -0372 end -0373 end -0374 -0375 if isempty(t1path) && isempty(t2path) -0376 helpdlg('No data folders in the given directory.','onMenuImport: No data.'); -0377 else -0378 % update the global data structure -0379 data.import.NMR.files = fnames; -0380 data.import.NMR.filesShort = shownames; -0381 end -0382 -0383 end -0384 -0385 %% -0386 function [data,gui] = importDataBGRmat(data,gui) -0387 -0388 in.path = fullfile(data.import.path); -0389 in.name = data.import.file; -0390 in.fileformat = data.import.fileformat; -0391 out = LoadNMRData_driver(in); -0392 -0393 fnames = struct; -0394 % shownames is just a dummy to hold all data file names that -0395 % will be shown in the listbox -0396 shownames = cell(1,1); -0397 -0398 c = 0; -0399 table = {true,0,1,'D'}; -0400 for j = 1:size(out.nmrData,2) -0401 % the individual file names -0402 c = c + 1; -0403 fnames(c).parfile = ''; -0404 fnames(c).datafile = data.import.file; -0405 fnames(c).T2specfile = ''; -0406 -0407 shownames{c} = out.parData{j}.file; -0408 -0409 data.import.NMR.data{c} = out.nmrData{j}; -0410 data.import.NMR.para{c} = out.parData{j}; -0411 -0412 if isfield(out,'pressData') -0413 % convert hPa to Pa -0414 table{c,1} = true; -0415 table{c,2} = out.pressData.p(j)*1e2; -0416 table{c,3} = out.pressData.S(j); -0417 table{c,4} = 'D'; -0418 end +0182 % update the list of file names +0183 set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); +0184 set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); +0185 +0186 % create a global INVdata struct for every file in the list +0187 if isstruct(getappdata(fig,'INVdata')) +0188 setappdata(fig,'INVdata',[]); +0189 end +0190 INVdata = cell(length(data.import.NMR.filesShort),1); +0191 setappdata(fig,'INVdata',INVdata); +0192 +0193 % clear all axes +0194 clearAllAxes(gui.figh); +0195 +0196 % update GUI data and interface +0197 setappdata(fig,'data',data); +0198 setappdata(fig,'gui',gui); +0199 enableGUIelements('NMR'); +0200 +0201 % special treatment of LIAG projects +0202 switch label +0203 case {'LIAG from project','LIAG last project'} +0204 cpath = data.import.LIAG.calibrationpath; +0205 % check if this calibration file was already used +0206 isfile = dir(fullfile(cpath,'NUCLEUS_calibData.mat')); +0207 if ~isempty(isfile) +0208 id = 2; +0209 % if data is there reuse it +0210 calib = load(fullfile(cpath,isfile.name),'calib'); +0211 INVdata{id} = calib.calib; +0212 data.calib = INVdata{id}.calib; +0213 data.import.LIAG.Tbulk = INVdata{id}.results.invstd.T2; +0214 setappdata(fig,'INVdata',INVdata); +0215 setappdata(fig,'data',data); +0216 % color the list entry +0217 strL = get(gui.listbox_handles.signal,'String'); +0218 str1 = strL{id}; +0219 str2 = ['<HTML><BODY bgcolor="rgb(',... +0220 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',... +0221 str1,'</BODY></HTML>']; +0222 strL{id} = str2; +0223 set(gui.listbox_handles.signal,'String',strL); +0224 end +0225 otherwise +0226 end +0227 % update GUI interface +0228 NUCLEUSinv_updateInterface; +0229 end +0230 +0231 catch ME +0232 % show error message in case import fails +0233 errmsg = {ME.message;[ME.stack(1).name,' Line: ',num2str(ME.stack(1).line)];... +0234 'Check File Format settings.'}; +0235 errordlg(errmsg,'importNMRdata: Error!'); +0236 +0237 end +0238 +0239 end +0240 +0241 %% +0242 function [NMRpath,NMRfile] = getNMRPathFile(label,import) +0243 +0244 NMRpath = -1; +0245 NMRfile = -1; +0246 % for almost all import cases we load a folder ... but not for all +0247 switch label +0248 case {'GGE ascii','GGE field','CoreLab ascii','MOUSE','LIAG single',... +0249 'BGR std','MouseCPMG','MouseLiftSingle','MouseLiftAll','Helios','BAM TOM','PM25'} +0250 % if there is already a data folder present we start from here +0251 if isfield(import,'path') +0252 NMRpath = uigetdir(import.path,'Choose Data Path'); +0253 else +0254 % otherwise we start at the current working dircetory +0255 % 'NMRpath' holds the name of the choosen data path +0256 here = mfilename('fullpath'); +0257 [pathstr,~,~] = fileparts(here); +0258 NMRpath = uigetdir(pathstr,'Choose Data Path'); +0259 end +0260 case 'LIAG from project' +0261 % if there is already a data folder present we start from here +0262 if isfield(import,'path') +0263 NMRpath = uigetdir(import.path,'Choose Project Path'); +0264 else +0265 % otherwise we start at the current working dircetory +0266 % 'NMRpath' holds the name of the choosen data path +0267 here = mfilename('fullpath'); +0268 [pathstr,~,~] = fileparts(here); +0269 NMRpath = uigetdir(pathstr,'Choose Project Path'); +0270 end +0271 case 'LIAG last project' +0272 % there is already a data folder and we use this one +0273 if isfield(import,'path') +0274 NMRpath = import.path; +0275 else +0276 % otherwise we start at the current working dircetory +0277 % 'NMRpath' holds the name of the choosen data path +0278 here = mfilename('fullpath'); +0279 [pathstr,~,~] = fileparts(here); +0280 NMRpath = uigetdir(pathstr,'Choose Project Path'); +0281 end +0282 case {'GGE Dart','BGR mat','NMR mat'} +0283 % if there is already a data folder present we start from here +0284 if isfield(import,'path') +0285 [NMRfile,NMRpath] = uigetfile(import.path,'Choose Data file'); +0286 else +0287 % otherwise we start at the current working dircetory +0288 % 'foldername' hold s the name of the choosen data path +0289 here = mfilename('fullpath'); +0290 [pathstr,~,~] = fileparts(here); +0291 [NMRfile,NMRpath] = uigetfile(pathstr,'Choose Data file'); +0292 end +0293 case 'PM5' +0294 % if there is already a data folder present we start from here +0295 if isfield(import,'path') +0296 NMRpath = uigetdir(import.path,'Choose PM5 Data Path'); +0297 else +0298 % otherwise we start at the current working dircetory +0299 % 'NMRpath' holds the name of the choosen data path +0300 here = mfilename('fullpath'); +0301 [pathstr,~,~] = fileparts(here); +0302 NMRpath = uigetdir(pathstr,'Choose PM5 Data Path'); +0303 end +0304 +0305 % check if there are multiple inf-files available +0306 % if yes -> choose one +0307 inffiles = dir(fullfile(NMRpath,'*.inf')); +0308 if numel(inffiles) > 1 +0309 [NMRfile,NMRpath] = uigetfile(fullfile(NMRpath,'*.inf'),... +0310 'Choose INF file'); +0311 elseif numel(inffiles) == 1 +0312 NMRfile = inffiles.name; +0313 end +0314 end +0315 +0316 end +0317 +0318 %% +0319 function [data,gui] = importDataMouseCPMG(data,gui) +0320 +0321 csv_t2path = dir(fullfile(data.import.path,'CPMG')); +0322 csv_t2path = csv_t2path(~ismember({csv_t2path.name},{'.','..'})); +0323 +0324 fnames = struct; +0325 % shownames is just a dummy to hold all data file names that +0326 % will be shown in the listbox +0327 shownames = cell(1,1); +0328 +0329 c = 0; +0330 if ~isempty(csv_t2path) +0331 for i = 1:size(csv_t2path,1) +0332 in.T1T2 = 'T2'; +0333 in.path = fullfile(data.import.path,'CPMG',csv_t2path(i).name); +0334 in.fileformat = data.import.fileformat; +0335 out = LoadNMRData_driver(in); +0336 +0337 for j = 1:size(out.nmrData,2) +0338 % the individual file names +0339 c = c + 1; +0340 fnames(c).parfile = 'acqu.par'; +0341 fnames(c).datafile = out.nmrData{j}.datfile; +0342 fnames(c).T2specfile = ''; +0343 +0344 shownames{c} = ['T2_',csv_t2path(i).name,'_',fnames(c).datafile]; +0345 +0346 % the NMR data +0347 % here we fix the time scale from [ms] to [s] +0348 if max(out.nmrData{j}.time) > 100 +0349 out.nmrData{j}.time = out.nmrData{j}.time/1000; +0350 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +0351 end +0352 data.import.NMR.data{c} = out.nmrData{j}; +0353 data.import.NMR.para{c} = out.parData; +0354 end +0355 end +0356 end +0357 +0358 if isempty(csv_t2path) +0359 helpdlg('No data folders in the given directory.','onMenuImport: No data.'); +0360 else +0361 % update the global data structure +0362 data.import.NMR.files = fnames; +0363 data.import.NMR.filesShort = shownames; +0364 end +0365 +0366 end +0367 +0368 %% +0369 function [data,gui] = importDataBGRmat(data,gui) +0370 +0371 in.path = fullfile(data.import.path); +0372 in.name = data.import.file; +0373 in.fileformat = data.import.fileformat; +0374 out = LoadNMRData_driver(in); +0375 +0376 fnames = struct; +0377 % shownames is just a dummy to hold all data file names that +0378 % will be shown in the listbox +0379 shownames = cell(1,1); +0380 +0381 c = 0; +0382 table = {true,0,1,'D'}; +0383 for j = 1:size(out.nmrData,2) +0384 % the individual file names +0385 c = c + 1; +0386 fnames(c).parfile = ''; +0387 fnames(c).datafile = data.import.file; +0388 fnames(c).T2specfile = ''; +0389 +0390 shownames{c} = out.parData{j}.file; +0391 +0392 data.import.NMR.data{c} = out.nmrData{j}; +0393 data.import.NMR.para{c} = out.parData{j}; +0394 +0395 if isfield(out,'pressData') +0396 % convert hPa to Pa +0397 table{c,1} = true; +0398 table{c,2} = out.pressData.p(j)*1e2; +0399 table{c,3} = out.pressData.S(j); +0400 table{c,4} = 'D'; +0401 end +0402 end +0403 +0404 % global Tbulk +0405 data.invstd.Tbulk = out.parData{1}.Tbulk; +0406 % global porosity +0407 data.import.BGR.porosity = out.parData{1}.porosity; +0408 +0409 data.import.NMR.files = fnames; +0410 data.import.NMR.filesShort = shownames; +0411 +0412 % import pressure / saturation data +0413 if isfield(out,'pressData') +0414 data.pressure.unit = 'kPa'; +0415 data.pressure.unitfac = 1e-3; +0416 data.pressure.table = table; +0417 end +0418 0419 end 0420 -0421 % global Tbulk -0422 data.invstd.Tbulk = out.parData{1}.Tbulk; -0423 % global porosity -0424 data.import.BGR.porosity = out.parData{1}.porosity; -0425 -0426 data.import.NMR.files = fnames; -0427 data.import.NMR.filesShort = shownames; -0428 -0429 % import pressure / saturation data -0430 if isfield(out,'pressData') -0431 data.pressure.unit = 'kPa'; -0432 data.pressure.unitfac = 1e-3; -0433 data.pressure.table = table; -0434 end -0435 +0421 %% +0422 function [data,gui] = importDataBGRliftSingle(data,gui) +0423 +0424 % first check whether T1 or T2 was measured... +0425 % by analyzing the name of data folder +0426 indiz = find(data.import.path == filesep); +0427 checkT1T2 = data.import.path(indiz(end-1)+1:indiz(end)-1); +0428 if strcmp(checkT1T2,'t1test') +0429 in.T1T2 = 'T1'; +0430 elseif strcmp(checkT1T2,'cpmgfastautotest') +0431 in.T1T2 = 'T2'; +0432 elseif strcmp(checkT1T2,'cpmgfastauto') +0433 in.T1T2 = 'T2'; +0434 else +0435 disp('Please chose an original Prospa folder for Mouse Lift data!'); 0436 end 0437 -0438 %% -0439 function [data,gui] = importDataDart(data,gui) +0438 % % there should be folders with integer values in their names +0439 % t1t2path = data.import.path; 0440 -0441 in.path = fullfile(data.import.path); -0442 in.name = data.import.file; -0443 in.fileformat = data.import.fileformat; -0444 out = LoadNMRData_driver(in); +0441 fnames = struct; +0442 % shownames is just a dummy to hold all data file names that +0443 % will be shown in the listbox +0444 shownames = cell(1,1); 0445 -0446 fnames = struct; -0447 % shownames is just a dummy to hold all data file names that -0448 % will be shown in the listbox -0449 shownames = cell(1,1); -0450 -0451 c = 0; -0452 for j = 1:size(out.nmrData,2) -0453 % the individual file names -0454 c = c + 1; -0455 fnames(c).parfile = ''; -0456 fnames(c).datafile = data.import.file; -0457 fnames(c).T2specfile = ''; -0458 -0459 shownames{c} = ['depth_',sprintf('%04.3f',out.parData{j}.depth)]; -0460 -0461 % the NMR data -0462 % here we fix the time scale from [ms] to [s] -0463 if max(out.nmrData{j}.time) > 100 -0464 out.nmrData{j}.time = out.nmrData{j}.time/1000; -0465 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; -0466 end -0467 data.import.NMR.data{c} = out.nmrData{j}; -0468 data.import.NMR.para{c} = out.parData{j}; -0469 end -0470 -0471 data.import.NMR.files = fnames; -0472 data.import.NMR.filesShort = shownames; +0446 c = 0; +0447 if ~isempty(data.import.path) +0448 % for i = 1:size(t1t2path,1) +0449 in.path = data.import.path; +0450 in.fileformat = data.import.fileformat; +0451 out = LoadNMRData_driver(in); +0452 +0453 for j = 1:size(out.nmrData,2) +0454 % the individual file names +0455 c = c + 1; +0456 fnames(c).parfile = 'acq.par'; +0457 fnames(c).datafile = out.nmrData{j}.datfile; +0458 +0459 shownames{c} = [in.T1T2,'_',fnames(c).datafile]; +0460 +0461 % the NMR data +0462 % here we fix the time scale from [ms] to [s] +0463 if max(out.nmrData{j}.time) > 100 +0464 out.nmrData{j}.time = out.nmrData{j}.time/1000; +0465 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +0466 end +0467 data.import.NMR.data{c} = out.nmrData{j}; +0468 data.import.NMR.para{c} = out.parData; +0469 end +0470 else +0471 helpdlg('No data folders in the given directory.','onMenuImport: No data.'); +0472 end 0473 -0474 end -0475 -0476 %% -0477 function [data,gui] = importDataGeneral(data,gui) +0474 +0475 % update the global data structure +0476 data.import.NMR.files = fnames; +0477 data.import.NMR.filesShort = shownames; 0478 -0479 % look for all files ending with '.par' -0480 % should exist for T1 and T2 measurements -0481 filenames = dir(fullfile(data.import.path,'*.par')); +0479 end +0480 %% +0481 function [data,gui] = importDataBGRliftAll(data,gui) 0482 -0483 fnames = struct; -0484 % shownames is just a dummy to hold all data file names that -0485 % will be shown in the listbox -0486 shownames = cell(1,1); -0487 -0488 % loop over all found files and import the data -0489 c = 0; -0490 in.path = data.import.path; -0491 for i = 1:size(filenames,1) -0492 ind = strfind(filenames(i).name,'.'); -0493 ind = max(ind); -0494 in.name = filenames(i).name(1:ind-1); -0495 in.fileformat = data.import.fileformat; -0496 out = LoadNMRData_driver(in); -0497 -0498 for j = 1:size(out.nmrData,2) -0499 % the individual file names -0500 c = c + 1; -0501 fnames(c).parfile = filenames(i).name; -0502 fnames(c).datafile = out.nmrData{j}.datfile; -0503 if strcmp(out.nmrData{j}.flag,'T2') && isfield(out.nmrData{j},'specfile') -0504 fnames(c).T2specfile = out.nmrData{j}.specfile; -0505 else -0506 fnames(c).T2specfile = ''; -0507 end -0508 shownames{c} = fnames(c).datafile; -0509 -0510 % the NMR data -0511 % here we fix the time scale from [ms] to [s] -0512 if max(out.nmrData{j}.time) > 100 -0513 out.nmrData{j}.time = out.nmrData{j}.time/1000; -0514 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; -0515 end -0516 data.import.NMR.data{c} = out.nmrData{j}; -0517 data.import.NMR.para{c} = out.parData; -0518 end -0519 end -0520 -0521 switch data.import.fileformat -0522 case 'bamtom' -0523 nslices = out.parData.nSlices; -0524 ncsvfiles = numel(fnames); -0525 -0526 % check for background file ("Leermessung") -0527 has_bg = false; -0528 for i = 1:numel(fnames) -0529 if ~isempty(strfind(fnames(i).datafile,'000')) -0530 has_bg = true; -0531 index_bg = i; -0532 break; -0533 end -0534 end -0535 -0536 if has_bg -0537 % background data set -0538 bg = data.import.NMR.data{index_bg}; -0539 -0540 % vector of indices of the files that get corrected -0541 ind_files = 1:1:ncsvfiles; -0542 ind_files(ind_files==index_bg) = []; -0543 -0544 for i = ind_files -0545 % subtract the background signal -0546 s = data.import.NMR.data{i}.signal; -0547 s_bg = bg.signal; -0548 % if the background signal is longer than the signal cut it -0549 if numel(s_bg) > numel(s) -0550 tmp_s = s_bg(1:numel(s)); -0551 else % if not pad it with zeros -0552 tmp_s = zeros(size(s)); -0553 tmp_s(1:numel(s_bg)) = s_bg; -0554 end -0555 s = complex(real(s)-real(tmp_s),imag(s)-imag(tmp_s)); -0556 % update original data set -0557 data.import.NMR.data{i}.signal = s; -0558 end -0559 -0560 % check if the number of slices is equal to the number of files -0561 % (excluding the background file) -0562 if nslices == numel(ind_files) -0563 % create z-vector -0564 p = out.parData; -0565 zslice = linspace(p.startPos,p.endPos,p.nSlices)'; -0566 data.import.BAM.use_z = true; -0567 data.import.BAM.zslice = zslice; -0568 if numel(zslice) > 1 -0569 c = 0; -0570 for i = ind_files -0571 c = c + 1; -0572 tmp = shownames{i}; -0573 shownames{i} = [tmp,' z:',sprintf('%5.4f',zslice(c))]; -0574 end -0575 end -0576 else -0577 data.import.BAM.use_z = false; -0578 data.import.BAM.zslice = 1:1:max([numel(ind_files) 1]); +0483 % first check whether T1 or T2 was measured +0484 indiz = find(data.import.path == filesep); +0485 checkT1T2 = data.import.path(indiz(end)+1:end); +0486 if strcmp(checkT1T2,'t1test') +0487 in.T1T2 = 'T1'; +0488 elseif strcmp(checkT1T2,'cpmgfastautotest') +0489 in.T1T2 = 'T2'; +0490 elseif strcmp(checkT1T2,'cpmgfastauto') +0491 in.T1T2 = 'T2'; +0492 else +0493 helpdlg('No original data folder','onMenuImport: No data.'); +0494 end +0495 +0496 % there should be folders with integer values in their names +0497 t1t2path = dir(data.import.path); +0498 t1t2path = t1t2path(~ismember({t1t2path.name},{'.','..'})); +0499 +0500 fnames = struct; +0501 % shownames is just a dummy to hold all data file names that +0502 % will be shown in the listbox +0503 shownames = cell(1,1); +0504 +0505 c = 0; +0506 if ~isempty(t1t2path) +0507 for i = 1:size(t1t2path,1) +0508 in.path = fullfile(data.import.path,t1t2path(i).name); +0509 in.fileformat = data.import.fileformat; +0510 out = LoadNMRData_driver(in); +0511 +0512 for j = 1:size(out.nmrData,2) +0513 % the individual file names +0514 c = c + 1; +0515 fnames(c).parfile = 'acq.par'; +0516 fnames(c).datafile = out.nmrData{j}.datfile; +0517 +0518 shownames{c} = [in.T1T2,'_',t1t2path(i).name,'_',fnames(c).datafile]; +0519 +0520 % the NMR data +0521 % here we fix the time scale from [ms] to [s] +0522 if max(out.nmrData{j}.time) > 100 +0523 out.nmrData{j}.time = out.nmrData{j}.time/1000; +0524 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +0525 end +0526 data.import.NMR.data{c} = out.nmrData{j}; +0527 data.import.NMR.para{c} = out.parData; +0528 end +0529 end +0530 else +0531 helpdlg('No data folders in the given directory.','onMenuImport: No data.'); +0532 end +0533 % update the global data structure +0534 data.import.NMR.files = fnames; +0535 data.import.NMR.filesShort = shownames; +0536 +0537 end +0538 +0539 %% +0540 function [data,gui] = importDataHelios(data,gui) +0541 +0542 % first check the subpaths +0543 % there should be some folders with names ... +0544 % ... similar to the data filenames inside them +0545 datpath = dir(data.import.path); +0546 datpath = datpath(~ismember({datpath.name},{'.','..'})); +0547 +0548 fnames = struct; +0549 % shownames is just a dummy to hold all data file names that +0550 % will be shown in the listbox +0551 shownames = cell(1,1); +0552 +0553 c = 0; +0554 if ~isempty(datpath) +0555 for i = 1:size(datpath,1) +0556 % does datpath.name points to a folder? +0557 if datpath(i).isdir +0558 % check if datpath.name includes regular Helios data files +0559 content = dir([data.import.path,filesep,datpath(i).name]); +0560 content = content(~ismember({content.name},{'.','..'})); +0561 for j = 1:size(content,1) +0562 if strcmp(content(j).name,[datpath(i).name,'_1.hrd']) +0563 in.T1T2 = 'T2'; +0564 in.name = content(j).name; +0565 in.path = fullfile(data.import.path,filesep,datpath(i).name); +0566 in.fileformat = data.import.fileformat; +0567 out = LoadNMRData_driver(in); +0568 +0569 % the individual file names +0570 c = c + 1; +0571 fnames(c).parfile = ''; +0572 fnames(c).datafile = out.nmrData.datfile; +0573 fnames(c).T2specfile = ''; +0574 shownames{c} = ['T2_',datpath(i).name]; +0575 +0576 data.import.NMR.data{c} = out.nmrData; +0577 data.import.NMR.para{c} = out.parData; +0578 end 0579 end -0580 -0581 else -0582 if nslices == ncsvfiles -0583 % create z-vector -0584 p = out.parData; -0585 zslice = linspace(p.startPos,p.endPos,p.nSlices)'; -0586 data.import.BAM.use_z = true; -0587 data.import.BAM.zslice = zslice; -0588 if numel(zslice) > 1 -0589 for i = 1:numel(zslice) -0590 tmp = shownames{i}; -0591 shownames{i} = [tmp,' z:',sprintf('%5.4f',zslice(i))]; -0592 end -0593 end -0594 else -0595 data.import.BAM.use_z = false; -0596 data.import.BAM.zslice = 1:1:ncsvfiles; -0597 end -0598 end -0599 end -0600 -0601 % update the global data structure -0602 data.import.NMR.files = fnames; -0603 data.import.NMR.filesShort = shownames; -0604 -0605 end -0606 -0607 %% -0608 function [data,gui] = importDataLIAG(data,gui) -0609 -0610 in.path = fullfile(data.import.path); -0611 in.fileformat = data.import.fileformat; -0612 out = LoadNMRData_driver(in); -0613 -0614 fnames = struct; -0615 % shownames is just a dummy to hold all data file names that -0616 % will be shown in the listbox -0617 shownames = cell(1,1); -0618 -0619 c = 0; -0620 for j = 1:size(out.nmrData,2) -0621 % the individual file names -0622 c = c + 1; -0623 fnames(c).parfile = 'acqu.par'; -0624 fnames(c).datafile = out.nmrData{j}.datfile; -0625 fnames(c).T2specfile = ''; -0626 -0627 shownames{c} = fnames(c).datafile; -0628 -0629 % the NMR data -0630 % here we fix the time scale to [s] if neccessary -0631 TE = out.parData.echoTime; % [µs] -0632 if out.nmrData{j}.time(1)== TE/1e3 -0633 % change [ms] to [s] -0634 out.nmrData{j}.time = out.nmrData{j}.time/1e3; -0635 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1e3; -0636 elseif out.nmrData{j}.time(1)== TE -0637 % change [µs] to [s] -> very unlikely -0638 out.nmrData{j}.time = out.nmrData{j}.time/1e6; -0639 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1e6; -0640 end -0641 data.import.NMR.data{c} = out.nmrData{j}; -0642 data.import.NMR.para{c} = out.parData; -0643 end -0644 -0645 % update the global data structure -0646 data.import.NMR.files = fnames; -0647 data.import.NMR.filesShort = shownames; -0648 -0649 end -0650 -0651 %% -0652 function [data,gui] = importDataLIAGproject(data,gui) -0653 -0654 % 1) show all folders and ask for sample -0655 subdirs = dir(data.import.path); -0656 % remove the dot-dirs -0657 subdirs = subdirs(~ismember({subdirs.name},{'.','..'})); -0658 -0659 fnames = {subdirs.name}; -0660 [indx,~] = listdlg('PromptString','Select Sample:',... -0661 'SelectionMode','single',... -0662 'ListString',fnames); -0663 -0664 if ~isempty(indx) -0665 % 2) check corresponding SampleParameter.par file for reference and -0666 % calibration sample and other parameters -0667 datapath = fullfile(data.import.path,fnames{indx}); -0668 % load SampleParameter file for sample -0669 [SampleParameter] = loadGUIParameters(datapath,'SampleParameters.par'); -0670 if isfield(SampleParameter,'sampleVolume') -0671 dataVol = SampleParameter.sampleVolume; -0672 else -0673 dataVol = 1; -0674 end -0675 -0676 % 2a) background file #1 -0677 backgroundFile1 = SampleParameter.backgroundFile; -0678 if isempty(backgroundFile1) -0679 bnames = {subdirs.name}; -0680 bnames(indx) = []; -0681 [indb,~] = listdlg('PromptString','Select Background File:',... -0682 'SelectionMode','single',... -0683 'ListString',bnames); -0684 if ~isempty(indb) -0685 backgroundFile1 = bnames{indb}; -0686 else -0687 backgroundFile1 = ''; -0688 end -0689 end -0690 if ~isempty(backgroundFile1) -0691 ind = strfind(backgroundFile1,filesep); -0692 if ~isempty(ind) -0693 backgroundFile1 = backgroundFile1(ind(end)+1:end); -0694 end -0695 fileInList = ismember(fnames,{backgroundFile1}); -0696 if any(fileInList) -0697 backgroundpath1 = fullfile(data.import.path,fnames{fileInList}); -0698 end -0699 else -0700 backgroundpath1 = ''; -0701 backgroundFile1 = ''; -0702 end -0703 backVol = 1; -0704 -0705 % 2b) calibration file -0706 c = 0; -0707 if isfield(SampleParameter,'calibrationFile0') -0708 c = c + 1; -0709 calibrationFiles{c} = SampleParameter.calibrationFile0; -0710 end -0711 if isfield(SampleParameter,'calibrationFile1') -0712 c = c + 1; -0713 calibrationFiles{c} = SampleParameter.calibrationFile1; -0714 end -0715 if isfield(SampleParameter,'calibrationFile2') -0716 c = c + 1; -0717 calibrationFiles{c} = SampleParameter.calibrationFile2; -0718 end -0719 if isfield(SampleParameter,'calibrationFile3') -0720 c = c + 1; -0721 calibrationFiles{c} = SampleParameter.calibrationFile3; -0722 end -0723 -0724 % find the non-empty one -0725 ind = ismember(calibrationFiles,{''}); -0726 % there are three possibilities: 0, 1 and more than 1 calib. files -0727 calibrationFiles(ind) = []; -0728 if isempty(calibrationFiles) -0729 cnames = {subdirs.name}; -0730 cnames(indx) = []; -0731 [indc,~] = listdlg('PromptString','Select Calibration File:',... -0732 'SelectionMode','single',... -0733 'ListString',cnames); -0734 if ~isempty(indc) -0735 calibrationFiles = cnames{indc}; -0736 else -0737 calibrationFiles = ''; -0738 end -0739 else -0740 % if there are more than one let the user select the correct one -0741 if numel(calibrationFiles) > 1 -0742 [ind,~] = listdlg('PromptString','Select Calibration File:',... -0743 'SelectionMode','single',... -0744 'ListString',calibrationFiles); -0745 calibrationFiles = calibrationFiles{ind}; -0746 else -0747 calibrationFiles = calibrationFiles{1}; -0748 end -0749 end -0750 if ~isempty(calibrationFiles) -0751 ind = strfind(calibrationFiles,filesep); -0752 if ~isempty(ind) -0753 calibrationFiles = calibrationFiles(ind(end)+1:end); -0754 end -0755 fileInList = ismember(fnames,{calibrationFiles}); -0756 if any(fileInList) -0757 calibrationpath = fullfile(data.import.path,fnames{fileInList}); -0758 end -0759 else -0760 calibrationpath = ''; -0761 calibrationFiles = ''; -0762 end -0763 -0764 if ~isempty(calibrationpath) -0765 [SampleParameterC] = loadGUIParameters(calibrationpath,'SampleParameters.par'); -0766 if isfield(SampleParameterC,'sampleVolume') -0767 calibVol = SampleParameterC.sampleVolume; -0768 else -0769 calibVol = 1; -0770 end -0771 -0772 % 2a) background file #2 -0773 backgroundFile2 = SampleParameterC.backgroundFile; -0774 if isempty(backgroundFile2) -0775 bnames = {subdirs.name}; -0776 bnames(indx) = []; -0777 [indb,~] = listdlg('PromptString','Select Background File for Calibration:',... -0778 'SelectionMode','single',... -0779 'ListString',bnames); -0780 if ~isempty(indb) -0781 backgroundFile2 = bnames{indb}; -0782 else -0783 backgroundFile2 = ''; -0784 end -0785 end -0786 if ~isempty(backgroundFile2) -0787 ind = strfind(backgroundFile2,filesep); -0788 if ~isempty(ind) -0789 backgroundFile2 = backgroundFile2(ind(end)+1:end); -0790 end -0791 fileInList = ismember(fnames,{backgroundFile2}); -0792 if any(fileInList) -0793 backgroundpath2 = fullfile(data.import.path,fnames{fileInList}); -0794 end -0795 else -0796 backgroundpath2 = ''; -0797 backgroundFile2 = ''; -0798 end -0799 else -0800 calibVol = 1; -0801 backgroundpath2 = ''; -0802 backgroundFile2 = ''; -0803 end -0804 -0805 % 3) now load the data -0806 workpaths = {datapath,calibrationpath,backgroundpath1,backgroundpath2}; -0807 workfiles = {fnames{indx},[calibrationFiles,' - calibration'],... -0808 [backgroundFile1,' - backgroundS'],[backgroundFile2,' - backgroundC']}; -0809 out = cell(size(workpaths)); -0810 count = 0; -0811 keep = false(size(workpaths)); -0812 for i = 1:numel(workpaths) -0813 if ~isempty(workpaths{i}) -0814 workdirs = dir(workpaths{i}); -0815 workdirs = workdirs(~ismember({workdirs.name},{'.','..'})); -0816 isT2 = ismember({workdirs.name},{'T2CPMG'}); -0817 if any(isT2) -0818 keep(i) = true; -0819 count = count + 1; -0820 T2path = fullfile(workdirs(isT2).folder,workdirs(isT2).name); -0821 in.path = T2path; -0822 in.fileformat = data.import.fileformat; -0823 out{i} = LoadNMRData_driver(in); -0824 end -0825 end -0826 end -0827 -0828 if isempty(backgroundpath1) -0829 ref1 = struct; -0830 else -0831 background1 = out{3}; -0832 % get the background data 1 -0833 ref1.t = background1.nmrData{1}.time; -0834 ref1.s = background1.nmrData{1}.signal; -0835 [ref1.s,~] = rotateT2phase(ref1.s); -0836 end -0837 -0838 if isempty(backgroundpath2) -0839 ref2 = struct; -0840 else -0841 background2 = out{4}; -0842 % get the background data 2 -0843 ref2.t = background2.nmrData{1}.time; -0844 ref2.s = background2.nmrData{1}.signal; -0845 [ref2.s,~] = rotateT2phase(ref2.s); -0846 end -0847 -0848 workpaths = workpaths(keep); -0849 workfiles = workfiles(keep); -0850 out = out(keep); -0851 -0852 ffnames = struct; -0853 % shownames is just a dummy to hold all data file names that -0854 % will be shown in the listbox -0855 shownames = cell(1,1); -0856 -0857 for i = 1:count -0858 ffnames(i).parfile = 'acqu.par'; -0859 ffnames(i).datafile = workfiles{i}; -0860 ffnames(i).T2specfile = ''; -0861 -0862 shownames{i} = ffnames(i).datafile; -0863 -0864 % the NMR data -0865 % here we fix the time scale to [s] if neccessary -0866 TE = out{i}.parData.echoTime; % [µs] -0867 if out{i}.nmrData{1}.time(1)== TE/1e3 -0868 % change [ms] to [s] -0869 out{i}.nmrData{1}.time = out{i}.nmrData{1}.time/1e3; -0870 out{i}.nmrData{1}.raw.time = out{i}.nmrData{1}.raw.time/1e3; -0871 elseif out{i}.nmrData{1}.time(1)== TE -0872 % change [µs] to [s] -> very unlikely -0873 out{i}.nmrData{1}.time = out{i}.nmrData{1}.time/1e6; -0874 out{i}.nmrData{1}.raw.time = out{i}.nmrData{1}.raw.time/1e6; -0875 end -0876 -0877 data.import.NMR.data{i} = out{i}.nmrData{1}; -0878 data.import.NMR.para{i} = out{i}.parData; -0879 data.import.NMR.para{i}.SampleParameter = SampleParameter; -0880 -0881 % subtract the background signal -0882 s = data.import.NMR.data{i}.signal; -0883 if i==1 && isfield(ref1,'s') -0884 % if the background signal is longer than the signal cut it, if -0885 % not extend it with zeros -0886 if numel(ref1.s) > numel(s) -0887 tmp_r = ref1.s(1:numel(s)); -0888 else -0889 tmp_r = zeros(size(s)); -0890 tmp_r(1:numel(ref1.s)) = ref1.s; -0891 end -0892 s = complex(real(s)-real(tmp_r),imag(s)-imag(tmp_r)); -0893 elseif i==2 && isfield(ref2,'s') -0894 % if the background signal is longer than the signal cut it, if -0895 % not extend it with zeros -0896 if numel(ref2.s) > numel(s) -0897 tmp_r = ref2.s(1:numel(s)); -0898 else -0899 tmp_r = zeros(size(s)); -0900 tmp_r(1:numel(ref2.s)) = ref2.s; -0901 end -0902 s = complex(real(s)-real(tmp_r),imag(s)-imag(tmp_r)); -0903 end -0904 data.import.NMR.data{i}.signal = s; -0905 data.import.NMR.data{i}.raw.signal = s; -0906 -0907 if ~isempty(strfind(workfiles{i},' - background')) -0908 data.import.LIAG.sampleVol(i) = backVol; -0909 elseif ~isempty(strfind(workfiles{i},' - calibration')) -0910 data.import.LIAG.sampleVol(i) = calibVol; -0911 elseif isempty(strfind(workfiles{i},' - background')) &&... -0912 isempty(strfind(workfiles{i},' - calibration')) -0913 data.import.LIAG.sampleVol(i) = dataVol; -0914 end -0915 end -0916 data.import.LIAG.Tbulk = 1e6; -0917 data.import.LIAG.workpaths = workpaths; -0918 data.import.LIAG.datapath = datapath; -0919 data.import.LIAG.calibrationpath = calibrationpath; -0920 data.import.LIAG.backgroundpath1 = backgroundpath1; -0921 data.import.LIAG.backgroundpath2 = backgroundpath2; -0922 -0923 % update the global data structure -0924 data.import.NMR.files = ffnames; -0925 data.import.NMR.filesShort = shownames; -0926 -0927 end -0928 -0929 end -0930 -0931 %% -0932 function [data,gui] = importDataMouse(data,gui) -0933 -0934 in.path = fullfile(data.import.path); -0935 in.fileformat = data.import.fileformat; -0936 out = LoadNMRData_driver(in); -0937 -0938 fnames = struct; -0939 % shownames is just a dummy to hold all data file names that -0940 % will be shown in the listbox -0941 shownames = cell(1,1); -0942 -0943 c = 0; -0944 for j = 1:size(out.nmrData,2) -0945 % the individual file names -0946 c = c + 1; -0947 fnames(c).parfile = 'acq.par'; -0948 fnames(c).datafile = out.nmrData{j}.datfile; -0949 fnames(c).T2specfile = ''; -0950 -0951 shownames{c} = ['T2_',fnames(c).datafile]; -0952 -0953 % the NMR data -0954 % here we fix the time scale from [ms] to [s] -0955 if max(out.nmrData{j}.time) > 100 -0956 out.nmrData{j}.time = out.nmrData{j}.time/1000; -0957 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; -0958 end -0959 data.import.NMR.data{c} = out.nmrData{j}; -0960 data.import.NMR.para{c} = out.parData; -0961 end -0962 -0963 % update the global data structure -0964 data.import.NMR.files = fnames; -0965 data.import.NMR.filesShort = shownames; -0966 -0967 end -0968 -0969 %% -0970 function [data,gui] = importDataIBAC(data,gui) -0971 -0972 in.path = fullfile(data.import.path); -0973 in.name = data.import.file; -0974 in.fileformat = data.import.fileformat; -0975 out = LoadNMRData_driver(in); -0976 -0977 nslices = out.parData.depths; -0978 nfiles = size(out.nmrData,2); -0979 -0980 fnames = struct; -0981 % shownames is just a dummy to hold all data file names that -0982 % will be shown in the listbox -0983 shownames = cell(1,1); -0984 -0985 c = 0; -0986 for j = 1:size(out.nmrData,2) -0987 % the individual file names -0988 c = c + 1; -0989 fnames(c).parfile = out.parData.parfile; -0990 fnames(c).datafile = out.nmrData{j}.datfile; -0991 fnames(c).T2specfile = ''; -0992 -0993 shownames{c} = [fnames(c).datafile]; -0994 -0995 data.import.NMR.data{c} = out.nmrData{j}; -0996 data.import.NMR.para{c} = out.parData; -0997 end -0998 -0999 % check if the number of depth levels is equal to the number of -1000 % measurements -1001 if nslices == nfiles -1002 % create z-vector -1003 p = out.parData; -1004 zslice = linspace(p.initialdepth,p.finaldepth,p.depths)'; -1005 data.import.IBAC.use_z = true; -1006 data.import.IBAC.zslice = zslice; -1007 if numel(zslice) > 1 -1008 c = 0; -1009 for i = 1:nfiles -1010 c = c + 1; -1011 tmp = shownames{i}; -1012 shownames{i} = [tmp,' z:',sprintf('%05d',zslice(c))]; -1013 end -1014 end -1015 else -1016 data.import.IBAC.use_z = false; -1017 data.import.IBAC.zslice = 1:1:max([nfiles 1]); -1018 end -1019 -1020 % update the global data structure -1021 data.import.NMR.files = fnames; -1022 data.import.NMR.filesShort = shownames; -1023 -1024 end -1025 -1026 %% -1027 function data = loadGUIParameters(datapath,fname) -1028 fid = fopen(fullfile(datapath,fname)); -1029 d = textscan(fid,'%s','Delimiter','\n'); -1030 fclose(fid); -1031 data = struct; -1032 for i = 1:size(d{1},1) -1033 str = char(d{1}(i)); -1034 str = fixParameterString(str); -1035 eval(['data.',str,';']); -1036 end -1037 -1038 end -1039 -1040 %% -1041 function data = loadGUIrawdata(data,NMRpath,NMRfile) -1042 -1043 load(fullfile(NMRpath,'NUCLEUSinv_raw.mat'),'savedata'); -1044 -1045 data.import.file = NMRfile; -1046 data.import.path = NMRpath; -1047 data.import.NMR = savedata; -1048 -1049 end -1050 -1051 %------------- END OF CODE -------------- -1052 -1053 %% License: -1054 % MIT License -1055 % -1056 % Copyright (c) 2018 Thomas Hiller -1057 % -1058 % Permission is hereby granted, free of charge, to any person obtaining a copy -1059 % of this software and associated documentation files (the "Software"), to deal -1060 % in the Software without restriction, including without limitation the rights -1061 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -1062 % copies of the Software, and to permit persons to whom the Software is -1063 % furnished to do so, subject to the following conditions: -1064 % -1065 % The above copyright notice and this permission notice shall be included in all -1066 % copies or substantial portions of the Software. -1067 % -1068 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -1069 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -1070 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -1071 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -1072 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -1073 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -1074 % SOFTWARE.

    +0580 end +0581 end +0582 end +0583 +0584 data.import.NMR.files = fnames; +0585 data.import.NMR.filesShort = shownames; +0586 +0587 end +0588 +0589 %% +0590 function [data,gui] = importDataDart(data,gui) +0591 +0592 in.path = fullfile(data.import.path); +0593 in.name = data.import.file; +0594 in.fileformat = data.import.fileformat; +0595 out = LoadNMRData_driver(in); +0596 +0597 fnames = struct; +0598 % shownames is just a dummy to hold all data file names that +0599 % will be shown in the listbox +0600 shownames = cell(1,1); +0601 +0602 c = 0; +0603 for j = 1:size(out.nmrData,2) +0604 % the individual file names +0605 c = c + 1; +0606 fnames(c).parfile = ''; +0607 fnames(c).datafile = data.import.file; +0608 fnames(c).T2specfile = ''; +0609 +0610 shownames{c} = ['depth_',sprintf('%04.3f',out.parData{j}.depth)]; +0611 +0612 % the NMR data +0613 % here we fix the time scale from [ms] to [s] +0614 if max(out.nmrData{j}.time) > 100 +0615 out.nmrData{j}.time = out.nmrData{j}.time/1000; +0616 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +0617 end +0618 data.import.NMR.data{c} = out.nmrData{j}; +0619 data.import.NMR.para{c} = out.parData{j}; +0620 end +0621 +0622 data.import.NMR.files = fnames; +0623 data.import.NMR.filesShort = shownames; +0624 +0625 end +0626 +0627 %% +0628 function [data,gui] = importDataGeneral(data,gui) +0629 +0630 % look for all files ending with '.par' +0631 % should exist for T1 and T2 measurements +0632 filenames = dir(fullfile(data.import.path,'*.par')); +0633 +0634 fnames = struct; +0635 % shownames is just a dummy to hold all data file names that +0636 % will be shown in the listbox +0637 shownames = cell(1,1); +0638 +0639 % loop over all found files and import the data +0640 c = 0; +0641 in.path = data.import.path; +0642 for i = 1:size(filenames,1) +0643 ind = strfind(filenames(i).name,'.'); +0644 ind = max(ind); +0645 in.name = filenames(i).name(1:ind-1); +0646 in.fileformat = data.import.fileformat; +0647 out = LoadNMRData_driver(in); +0648 +0649 for j = 1:size(out.nmrData,2) +0650 % the individual file names +0651 c = c + 1; +0652 fnames(c).parfile = filenames(i).name; +0653 fnames(c).datafile = out.nmrData{j}.datfile; +0654 if strcmp(out.nmrData{j}.flag,'T2') && isfield(out.nmrData{j},'specfile') +0655 fnames(c).T2specfile = out.nmrData{j}.specfile; +0656 else +0657 fnames(c).T2specfile = ''; +0658 end +0659 shownames{c} = fnames(c).datafile; +0660 +0661 % the NMR data +0662 % here we fix the time scale from [ms] to [s] +0663 if max(out.nmrData{j}.time) > 100 +0664 out.nmrData{j}.time = out.nmrData{j}.time/1000; +0665 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +0666 end +0667 data.import.NMR.data{c} = out.nmrData{j}; +0668 data.import.NMR.para{c} = out.parData; +0669 end +0670 end +0671 +0672 switch data.import.fileformat +0673 case 'bamtom' +0674 nslices = out.parData.nSlices; +0675 ncsvfiles = numel(fnames); +0676 +0677 % check for background file ("Leermessung") +0678 has_bg = false; +0679 for i = 1:numel(fnames) +0680 if ~isempty(strfind(fnames(i).datafile,'000')) +0681 has_bg = true; +0682 index_bg = i; +0683 break; +0684 end +0685 end +0686 +0687 if has_bg +0688 % background data set +0689 bg = data.import.NMR.data{index_bg}; +0690 +0691 % vector of indices of the files that get corrected +0692 ind_files = 1:1:ncsvfiles; +0693 ind_files(ind_files==index_bg) = []; +0694 +0695 for i = ind_files +0696 % subtract the background signal +0697 s = data.import.NMR.data{i}.signal; +0698 s_bg = bg.signal; +0699 % if the background signal is longer than the signal cut it +0700 if numel(s_bg) > numel(s) +0701 tmp_s = s_bg(1:numel(s)); +0702 else % if not pad it with zeros +0703 tmp_s = zeros(size(s)); +0704 tmp_s(1:numel(s_bg)) = s_bg; +0705 end +0706 s = complex(real(s)-real(tmp_s),imag(s)-imag(tmp_s)); +0707 % update original data set +0708 data.import.NMR.data{i}.signal = s; +0709 end +0710 +0711 % check if the number of slices is equal to the number of files +0712 % (excluding the background file) +0713 if nslices == numel(ind_files) +0714 % create z-vector +0715 p = out.parData; +0716 zslice = linspace(p.startPos,p.endPos,p.nSlices)'; +0717 data.import.BAM.use_z = true; +0718 data.import.BAM.zslice = zslice; +0719 if numel(zslice) > 1 +0720 c = 0; +0721 for i = ind_files +0722 c = c + 1; +0723 tmp = shownames{i}; +0724 shownames{i} = [tmp,' z:',sprintf('%5.4f',zslice(c))]; +0725 end +0726 end +0727 else +0728 data.import.BAM.use_z = false; +0729 data.import.BAM.zslice = 1:1:max([numel(ind_files) 1]); +0730 end +0731 +0732 else +0733 if nslices == ncsvfiles +0734 % create z-vector +0735 p = out.parData; +0736 zslice = linspace(p.startPos,p.endPos,p.nSlices)'; +0737 data.import.BAM.use_z = true; +0738 data.import.BAM.zslice = zslice; +0739 if numel(zslice) > 1 +0740 for i = 1:numel(zslice) +0741 tmp = shownames{i}; +0742 shownames{i} = [tmp,' z:',sprintf('%5.4f',zslice(i))]; +0743 end +0744 end +0745 else +0746 data.import.BAM.use_z = false; +0747 data.import.BAM.zslice = 1:1:ncsvfiles; +0748 end +0749 end +0750 end +0751 +0752 % update the global data structure +0753 data.import.NMR.files = fnames; +0754 data.import.NMR.filesShort = shownames; +0755 +0756 end +0757 +0758 %% +0759 function [data,gui] = importDataLIAG(data,gui) +0760 +0761 in.path = fullfile(data.import.path); +0762 in.fileformat = data.import.fileformat; +0763 out = LoadNMRData_driver(in); +0764 +0765 fnames = struct; +0766 % shownames is just a dummy to hold all data file names that +0767 % will be shown in the listbox +0768 shownames = cell(1,1); +0769 +0770 c = 0; +0771 for j = 1:size(out.nmrData,2) +0772 % the individual file names +0773 c = c + 1; +0774 fnames(c).parfile = 'acqu.par'; +0775 fnames(c).datafile = out.nmrData{j}.datfile; +0776 fnames(c).T2specfile = ''; +0777 +0778 shownames{c} = fnames(c).datafile; +0779 +0780 % the NMR data +0781 % here we fix the time scale to [s] if neccessary +0782 TE = out.parData.echoTime; % [µs] +0783 if out.nmrData{j}.time(1)== TE/1e3 +0784 % change [ms] to [s] +0785 out.nmrData{j}.time = out.nmrData{j}.time/1e3; +0786 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1e3; +0787 elseif out.nmrData{j}.time(1)== TE +0788 % change [µs] to [s] -> very unlikely +0789 out.nmrData{j}.time = out.nmrData{j}.time/1e6; +0790 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1e6; +0791 end +0792 data.import.NMR.data{c} = out.nmrData{j}; +0793 data.import.NMR.para{c} = out.parData; +0794 end +0795 +0796 % update the global data structure +0797 data.import.NMR.files = fnames; +0798 data.import.NMR.filesShort = shownames; +0799 +0800 end +0801 +0802 %% +0803 function [data,gui] = importDataLIAGproject(data,gui) +0804 +0805 % 1) show all folders and ask for sample +0806 subdirs = dir(data.import.path); +0807 % remove the dot-dirs +0808 subdirs = subdirs(~ismember({subdirs.name},{'.','..'})); +0809 +0810 fnames = {subdirs.name}; +0811 [indx,~] = listdlg('PromptString','Select Sample:',... +0812 'SelectionMode','single',... +0813 'ListString',fnames); +0814 +0815 if ~isempty(indx) +0816 % 2) check corresponding SampleParameter.par file for reference and +0817 % calibration sample and other parameters +0818 datapath = fullfile(data.import.path,fnames{indx}); +0819 % load SampleParameter file for sample +0820 [SampleParameter] = loadGUIParameters(datapath,'SampleParameters.par'); +0821 if isfield(SampleParameter,'sampleVolume') +0822 dataVol = SampleParameter.sampleVolume; +0823 else +0824 dataVol = 1; +0825 end +0826 +0827 % 2a) background file #1 +0828 backgroundFile1 = SampleParameter.backgroundFile; +0829 if isempty(backgroundFile1) +0830 bnames = {subdirs.name}; +0831 bnames(indx) = []; +0832 [indb,~] = listdlg('PromptString','Select Background File:',... +0833 'SelectionMode','single',... +0834 'ListString',bnames); +0835 if ~isempty(indb) +0836 backgroundFile1 = bnames{indb}; +0837 else +0838 backgroundFile1 = ''; +0839 end +0840 end +0841 if ~isempty(backgroundFile1) +0842 ind = strfind(backgroundFile1,filesep); +0843 if ~isempty(ind) +0844 backgroundFile1 = backgroundFile1(ind(end)+1:end); +0845 end +0846 fileInList = ismember(fnames,{backgroundFile1}); +0847 if any(fileInList) +0848 backgroundpath1 = fullfile(data.import.path,fnames{fileInList}); +0849 end +0850 else +0851 backgroundpath1 = ''; +0852 backgroundFile1 = ''; +0853 end +0854 backVol = 1; +0855 +0856 % 2b) calibration file +0857 c = 0; +0858 if isfield(SampleParameter,'calibrationFile0') +0859 c = c + 1; +0860 calibrationFiles{c} = SampleParameter.calibrationFile0; +0861 end +0862 if isfield(SampleParameter,'calibrationFile1') +0863 c = c + 1; +0864 calibrationFiles{c} = SampleParameter.calibrationFile1; +0865 end +0866 if isfield(SampleParameter,'calibrationFile2') +0867 c = c + 1; +0868 calibrationFiles{c} = SampleParameter.calibrationFile2; +0869 end +0870 if isfield(SampleParameter,'calibrationFile3') +0871 c = c + 1; +0872 calibrationFiles{c} = SampleParameter.calibrationFile3; +0873 end +0874 +0875 % find the non-empty one +0876 ind = ismember(calibrationFiles,{''}); +0877 % there are three possibilities: 0, 1 and more than 1 calib. files +0878 calibrationFiles(ind) = []; +0879 if isempty(calibrationFiles) +0880 cnames = {subdirs.name}; +0881 cnames(indx) = []; +0882 [indc,~] = listdlg('PromptString','Select Calibration File:',... +0883 'SelectionMode','single',... +0884 'ListString',cnames); +0885 if ~isempty(indc) +0886 calibrationFiles = cnames{indc}; +0887 else +0888 calibrationFiles = ''; +0889 end +0890 else +0891 % if there are more than one let the user select the correct one +0892 if numel(calibrationFiles) > 1 +0893 [ind,~] = listdlg('PromptString','Select Calibration File:',... +0894 'SelectionMode','single',... +0895 'ListString',calibrationFiles); +0896 calibrationFiles = calibrationFiles{ind}; +0897 else +0898 calibrationFiles = calibrationFiles{1}; +0899 end +0900 end +0901 if ~isempty(calibrationFiles) +0902 ind = strfind(calibrationFiles,filesep); +0903 if ~isempty(ind) +0904 calibrationFiles = calibrationFiles(ind(end)+1:end); +0905 end +0906 fileInList = ismember(fnames,{calibrationFiles}); +0907 if any(fileInList) +0908 calibrationpath = fullfile(data.import.path,fnames{fileInList}); +0909 end +0910 else +0911 calibrationpath = ''; +0912 calibrationFiles = ''; +0913 end +0914 +0915 if ~isempty(calibrationpath) +0916 [SampleParameterC] = loadGUIParameters(calibrationpath,'SampleParameters.par'); +0917 if isfield(SampleParameterC,'sampleVolume') +0918 calibVol = SampleParameterC.sampleVolume; +0919 else +0920 calibVol = 1; +0921 end +0922 +0923 % 2a) background file #2 +0924 backgroundFile2 = SampleParameterC.backgroundFile; +0925 if isempty(backgroundFile2) +0926 bnames = {subdirs.name}; +0927 bnames(indx) = []; +0928 [indb,~] = listdlg('PromptString','Select Background File for Calibration:',... +0929 'SelectionMode','single',... +0930 'ListString',bnames); +0931 if ~isempty(indb) +0932 backgroundFile2 = bnames{indb}; +0933 else +0934 backgroundFile2 = ''; +0935 end +0936 end +0937 if ~isempty(backgroundFile2) +0938 ind = strfind(backgroundFile2,filesep); +0939 if ~isempty(ind) +0940 backgroundFile2 = backgroundFile2(ind(end)+1:end); +0941 end +0942 fileInList = ismember(fnames,{backgroundFile2}); +0943 if any(fileInList) +0944 backgroundpath2 = fullfile(data.import.path,fnames{fileInList}); +0945 end +0946 else +0947 backgroundpath2 = ''; +0948 backgroundFile2 = ''; +0949 end +0950 else +0951 calibVol = 1; +0952 backgroundpath2 = ''; +0953 backgroundFile2 = ''; +0954 end +0955 +0956 % 3) now load the data +0957 workpaths = {datapath,calibrationpath,backgroundpath1,backgroundpath2}; +0958 workfiles = {fnames{indx},[calibrationFiles,' - calibration'],... +0959 [backgroundFile1,' - backgroundS'],[backgroundFile2,' - backgroundC']}; +0960 out = cell(size(workpaths)); +0961 count = 0; +0962 keep = false(size(workpaths)); +0963 for i = 1:numel(workpaths) +0964 if ~isempty(workpaths{i}) +0965 workdirs = dir(workpaths{i}); +0966 workdirs = workdirs(~ismember({workdirs.name},{'.','..'})); +0967 isT2 = ismember({workdirs.name},{'T2CPMG'}); +0968 if any(isT2) +0969 keep(i) = true; +0970 count = count + 1; +0971 T2path = fullfile(workdirs(isT2).folder,workdirs(isT2).name); +0972 in.path = T2path; +0973 in.fileformat = data.import.fileformat; +0974 out{i} = LoadNMRData_driver(in); +0975 end +0976 end +0977 end +0978 +0979 if isempty(backgroundpath1) +0980 ref1 = struct; +0981 else +0982 background1 = out{3}; +0983 % get the background data 1 +0984 ref1.t = background1.nmrData{1}.time; +0985 ref1.s = background1.nmrData{1}.signal; +0986 [ref1.s,~] = rotateT2phase(ref1.s); +0987 end +0988 +0989 if isempty(backgroundpath2) +0990 ref2 = struct; +0991 else +0992 background2 = out{4}; +0993 % get the background data 2 +0994 ref2.t = background2.nmrData{1}.time; +0995 ref2.s = background2.nmrData{1}.signal; +0996 [ref2.s,~] = rotateT2phase(ref2.s); +0997 end +0998 +0999 workpaths = workpaths(keep); +1000 workfiles = workfiles(keep); +1001 out = out(keep); +1002 +1003 ffnames = struct; +1004 % shownames is just a dummy to hold all data file names that +1005 % will be shown in the listbox +1006 shownames = cell(1,1); +1007 +1008 for i = 1:count +1009 ffnames(i).parfile = 'acqu.par'; +1010 ffnames(i).datafile = workfiles{i}; +1011 ffnames(i).T2specfile = ''; +1012 +1013 shownames{i} = ffnames(i).datafile; +1014 +1015 % the NMR data +1016 % here we fix the time scale to [s] if neccessary +1017 TE = out{i}.parData.echoTime; % [µs] +1018 if out{i}.nmrData{1}.time(1)== TE/1e3 +1019 % change [ms] to [s] +1020 out{i}.nmrData{1}.time = out{i}.nmrData{1}.time/1e3; +1021 out{i}.nmrData{1}.raw.time = out{i}.nmrData{1}.raw.time/1e3; +1022 elseif out{i}.nmrData{1}.time(1)== TE +1023 % change [µs] to [s] -> very unlikely +1024 out{i}.nmrData{1}.time = out{i}.nmrData{1}.time/1e6; +1025 out{i}.nmrData{1}.raw.time = out{i}.nmrData{1}.raw.time/1e6; +1026 end +1027 +1028 data.import.NMR.data{i} = out{i}.nmrData{1}; +1029 data.import.NMR.para{i} = out{i}.parData; +1030 data.import.NMR.para{i}.SampleParameter = SampleParameter; +1031 +1032 % subtract the background signal +1033 s = data.import.NMR.data{i}.signal; +1034 if i==1 && isfield(ref1,'s') +1035 % if the background signal is longer than the signal cut it, if +1036 % not extend it with zeros +1037 if numel(ref1.s) > numel(s) +1038 tmp_r = ref1.s(1:numel(s)); +1039 else +1040 tmp_r = zeros(size(s)); +1041 tmp_r(1:numel(ref1.s)) = ref1.s; +1042 end +1043 s = complex(real(s)-real(tmp_r),imag(s)-imag(tmp_r)); +1044 elseif i==2 && isfield(ref2,'s') +1045 % if the background signal is longer than the signal cut it, if +1046 % not extend it with zeros +1047 if numel(ref2.s) > numel(s) +1048 tmp_r = ref2.s(1:numel(s)); +1049 else +1050 tmp_r = zeros(size(s)); +1051 tmp_r(1:numel(ref2.s)) = ref2.s; +1052 end +1053 s = complex(real(s)-real(tmp_r),imag(s)-imag(tmp_r)); +1054 end +1055 data.import.NMR.data{i}.signal = s; +1056 data.import.NMR.data{i}.raw.signal = s; +1057 +1058 if ~isempty(strfind(workfiles{i},' - background')) +1059 data.import.LIAG.sampleVol(i) = backVol; +1060 elseif ~isempty(strfind(workfiles{i},' - calibration')) +1061 data.import.LIAG.sampleVol(i) = calibVol; +1062 elseif isempty(strfind(workfiles{i},' - background')) &&... +1063 isempty(strfind(workfiles{i},' - calibration')) +1064 data.import.LIAG.sampleVol(i) = dataVol; +1065 end +1066 end +1067 data.import.LIAG.Tbulk = 1e6; +1068 data.import.LIAG.workpaths = workpaths; +1069 data.import.LIAG.datapath = datapath; +1070 data.import.LIAG.calibrationpath = calibrationpath; +1071 data.import.LIAG.backgroundpath1 = backgroundpath1; +1072 data.import.LIAG.backgroundpath2 = backgroundpath2; +1073 +1074 % update the global data structure +1075 data.import.NMR.files = ffnames; +1076 data.import.NMR.filesShort = shownames; +1077 +1078 end +1079 +1080 end +1081 +1082 %% +1083 function [data,gui] = importDataMouse(data,gui) +1084 +1085 in.path = fullfile(data.import.path); +1086 in.fileformat = data.import.fileformat; +1087 out = LoadNMRData_driver(in); +1088 +1089 fnames = struct; +1090 % shownames is just a dummy to hold all data file names that +1091 % will be shown in the listbox +1092 shownames = cell(1,1); +1093 +1094 c = 0; +1095 for j = 1:size(out.nmrData,2) +1096 % the individual file names +1097 c = c + 1; +1098 fnames(c).parfile = 'acq.par'; +1099 fnames(c).datafile = out.nmrData{j}.datfile; +1100 fnames(c).T2specfile = ''; +1101 +1102 shownames{c} = ['T2_',fnames(c).datafile]; +1103 +1104 % the NMR data +1105 % here we fix the time scale from [ms] to [s] +1106 if max(out.nmrData{j}.time) > 100 +1107 out.nmrData{j}.time = out.nmrData{j}.time/1000; +1108 out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; +1109 end +1110 data.import.NMR.data{c} = out.nmrData{j}; +1111 data.import.NMR.para{c} = out.parData; +1112 end +1113 +1114 % update the global data structure +1115 data.import.NMR.files = fnames; +1116 data.import.NMR.filesShort = shownames; +1117 +1118 end +1119 +1120 %% +1121 function [data,gui] = importDataIBAC(data,gui) +1122 +1123 in.path = fullfile(data.import.path); +1124 in.name = data.import.file; +1125 in.fileformat = data.import.fileformat; +1126 out = LoadNMRData_driver(in); +1127 +1128 nslices = out.parData.depths; +1129 nfiles = size(out.nmrData,2); +1130 +1131 fnames = struct; +1132 % shownames is just a dummy to hold all data file names that +1133 % will be shown in the listbox +1134 shownames = cell(1,1); +1135 +1136 c = 0; +1137 for j = 1:size(out.nmrData,2) +1138 % the individual file names +1139 c = c + 1; +1140 fnames(c).parfile = out.parData.parfile; +1141 fnames(c).datafile = out.nmrData{j}.datfile; +1142 fnames(c).T2specfile = ''; +1143 +1144 shownames{c} = [fnames(c).datafile]; +1145 +1146 data.import.NMR.data{c} = out.nmrData{j}; +1147 data.import.NMR.para{c} = out.parData; +1148 end +1149 +1150 % check if the number of depth levels is equal to the number of +1151 % measurements +1152 if nslices == nfiles +1153 % create z-vector +1154 p = out.parData; +1155 zslice = linspace(p.initialdepth,p.finaldepth,p.depths)'; +1156 data.import.IBAC.use_z = true; +1157 data.import.IBAC.zslice = zslice; +1158 if numel(zslice) > 1 +1159 c = 0; +1160 for i = 1:nfiles +1161 c = c + 1; +1162 tmp = shownames{i}; +1163 shownames{i} = [tmp,' z:',sprintf('%05d',zslice(c))]; +1164 end +1165 end +1166 else +1167 data.import.IBAC.use_z = false; +1168 data.import.IBAC.zslice = 1:1:max([nfiles 1]); +1169 end +1170 +1171 % update the global data structure +1172 data.import.NMR.files = fnames; +1173 data.import.NMR.filesShort = shownames; +1174 +1175 end +1176 +1177 %% +1178 function data = loadGUIParameters(datapath,fname) +1179 fid = fopen(fullfile(datapath,fname)); +1180 d = textscan(fid,'%s','Delimiter','\n'); +1181 fclose(fid); +1182 data = struct; +1183 for i = 1:size(d{1},1) +1184 str = char(d{1}(i)); +1185 str = fixParameterString(str); +1186 eval(['data.',str,';']); +1187 end +1188 +1189 end +1190 +1191 %% +1192 function data = loadGUIrawdata(data,NMRpath,NMRfile) +1193 +1194 load(fullfile(NMRpath,'NUCLEUSinv_raw.mat'),'savedata'); +1195 +1196 data.import.file = NMRfile; +1197 data.import.path = NMRpath; +1198 data.import.NMR = savedata; +1199 +1200 end +1201 +1202 %------------- END OF CODE -------------- +1203 +1204 %% License: +1205 % MIT License +1206 % +1207 % Copyright (c) 2018 Thomas Hiller +1208 % +1209 % Permission is hereby granted, free of charge, to any person obtaining a copy +1210 % of this software and associated documentation files (the "Software"), to deal +1211 % in the Software without restriction, including without limitation the rights +1212 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +1213 % copies of the Software, and to permit persons to whom the Software is +1214 % furnished to do so, subject to the following conditions: +1215 % +1216 % The above copyright notice and this permission notice shall be included in all +1217 % copies or substantial portions of the Software. +1218 % +1219 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +1220 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +1221 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +1222 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +1223 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +1224 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +1225 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/makeINIfile.html b/doc/nucleus/functions/interface/makeINIfile.html index 2870772..b7de085 100644 --- a/doc/nucleus/functions/interface/makeINIfile.html +++ b/doc/nucleus/functions/interface/makeINIfile.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/menu.html b/doc/nucleus/functions/interface/menu.html index a30ed3d..dd24a64 100644 --- a/doc/nucleus/functions/interface/menu.html +++ b/doc/nucleus/functions/interface/menu.html @@ -18,7 +18,7 @@

    Index for nucleus\functions\interface

    Matlab files in this directory:

    +
  • ConductView
  • PhaseView
  • beautifyAxes
  • calculateGeometry
  • calculateGuiOnMonitorPosition
  • calculateNMR
  • calibratePorosity
  • caluclatePressureSaturation
  • changeColorTheme
  • checkIfInversionExists
  • cleanupGUIData
  • clearAllAxes
  • clearInversion
  • clearSingleAxis
  • displayStatusText
  • enableGUIelements
  • exportData
  • exportGraphics
  • exportINV
  • findParentOfType
  • fixAxes
  • getColorIndex
  • getColorTheme
  • getVersionNoFromString
  • importASCIIdata
  • importCPSdata
  • importCalibrationData
  • importEXCELdata
  • importINV2INV
  • importMOD2INV
  • importMOD2MOD
  • importNMRdata
  • makeINIfile
  • minimizePanel
  • moveEntryInList
  • onFigureSizeChange
  • processNMRData
  • processNMRDataControl
  • readINIfile
  • removeCalculationFields
  • removeInversionFields
  • removeSignalFromList
  • resortDataList
  • runInversionBatch
  • runInversionJoint
  • runInversionStd
  • showExtraGraphics
  • showFitStatistics
  • showParameterInfo
  • switchToolTips
  • uncheckImportMenus
  • updateCPSTable
  • updateCPSTableSize
  • updateInfo
  • updateNMRsignals
  • updatePlotsCPS
  • updatePlotsDistribution
  • updatePlotsDistributionInfo
  • updatePlotsGeometryType
  • updatePlotsJointInversion
  • updatePlotsLcurve
  • updatePlotsNMR
  • updatePlotsPSD
  • updatePlotsSignal
  • updateStatusInformation
  • updateToolTips
  • useSignalAsCalibration
  • Other Matlab-specific files in this directory:

    This function is called by: @@ -110,8 +110,8 @@

    SOURCE CODE ^% none 0033 % 0034 % See also: NUCLEUSinv -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- @@ -205,146 +205,188 @@

    SOURCE CODE ^fitDataFree(data.results.nmrproc.t,... 0128 data.results.nmrproc.s,flag,param,data.invstd.freeDT); 0129 -0130 case 'NNLS' +0130 case 'LU' 0131 param.T1T2 = data.results.nmrproc.T1T2; 0132 param.T1IRfac = data.results.nmrproc.T1IRfac; 0133 param.Tb = data.invstd.Tbulk; -0134 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; -0135 param.regMethod = data.invstd.regtype; +0134 param.Td = data.invstd.Tdiff; +0135 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; 0136 param.Lorder = data.invstd.Lorder; 0137 param.lambda = data.invstd.lambda; 0138 param.noise = data.results.nmrproc.noise; -0139 param.solver = data.info.solver; -0140 if isfield(data.results.nmrproc,'W') -0141 param.W = data.results.nmrproc.W; -0142 end -0143 -0144 invstd = fitDataLSQ(data.results.nmrproc.t,... -0145 data.results.nmrproc.s,param); -0146 -0147 case 'LU' -0148 param.T1T2 = data.results.nmrproc.T1T2; -0149 param.T1IRfac = data.results.nmrproc.T1IRfac; -0150 param.Tb = data.invstd.Tbulk; -0151 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; -0152 param.Lorder = data.invstd.Lorder; -0153 param.lambda = data.invstd.lambda; -0154 param.noise = data.results.nmrproc.noise; -0155 -0156 invstd = fitDataLUdecomp(data.results.nmrproc.t,... -0157 data.results.nmrproc.s,param); -0158 end -0159 -0160 % save inversion results -0161 data.results.invstd = invstd; -0162 if id == 1 -0163 % get possible parameter file information -0164 if isfield(data.import.NMR,'para') -0165 data.info.parameter = data.import.NMR.para{id}; -0166 else -0167 data.info.parameter = 'No parameter data available.'; -0168 end -0169 INVdata{id} = []; -0170 INVdata{id} = data; -0171 INVdata{id} = rmfield(INVdata{id},'import'); -0172 INVdata{id} = rmfield(INVdata{id},'info'); -0173 INVdata{id} = rmfield(INVdata{id},'calib'); -0174 INVdata{id} = rmfield(INVdata{id},'pressure'); -0175 % copy data to all INVdata fields to allocate memory -0176 % this speeds up the overall runtime -0177 INVdata = repmat(INVdata(id),[length(data.import.NMR.filesShort),1]); -0178 else -0179 % save individual results -0180 INVdata{id}.process = data.process; -0181 INVdata{id}.results = data.results; -0182 if isfield(data.import.NMR,'para') -0183 INVdata{id}.info.parameter = data.import.NMR.para{id}; -0184 else -0185 INVdata{id}.info.parameter = 'No parameter data available.'; -0186 end -0187 end -0188 -0189 % color the list entries that already have an inversion result -0190 strL = get(gui.listbox_handles.signal,'String'); -0191 str1 = strL{id}; -0192 str2 = ['<HTML><BODY bgcolor="rgb(',... -0193 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',str1,'</BODY></HTML>']; -0194 strL{id} = str2; -0195 set(gui.listbox_handles.signal,'String',strL); -0196 -0197 % update wait-bar -0198 if wbopts.show -0199 % for a small number of signals always update the wait-bar -0200 if size(INVdata,1) <= 50 -0201 waitbar(id / steps,hwb,['processing ...',num2str(id),... -0202 ' / ',num2str(steps),' NMR signals']); -0203 else -0204 % otherwise only update every 25 signals -0205 % NOTE: Matlab's wait-bar SLOWS DOWN the calculation -0206 % MASSIVELY -0207 if id == 1 || mod(id,25) == 0 -0208 waitbar(id / steps,hwb,['processing ...',num2str(id),... -0209 ' / ',num2str(steps),' NMR signals']); -0210 end -0211 end -0212 end -0213 else -0214 displayStatusText(gui,[infostring,' was canceled']); -0215 % remove temporary data fields -0216 data = removeInversionFields(data); -0217 % remove data from fields not processed -0218 INVdata{id} = []; -0219 end -0220 end -0221 % delete wait-bar -0222 if wbopts.show -0223 delete(hwb); -0224 end -0225 if get(gui.push_handles.invstd_run,'UserData') == 1 % STOP was not pressed -0226 displayStatusText(gui,[infostring,'done']); -0227 end -0228 -0229 % update INVdata to GUI -0230 setappdata(fig,'INVdata',INVdata); -0231 % make the STOP button a RUN again -0232 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... -0233 'BackgroundColor','g','Callback',@onPushRun); -0234 % update GUI data -0235 setappdata(fig,'gui',gui); -0236 % set focus on the first entry in the list -0237 set(gui.listbox_handles.signal,'Value',1); -0238 onListboxData(gui.listbox_handles.signal); -0239 else -0240 helpdlg('Nothing to do because there is no data loaded!',... -0241 'runInversioBatch: Load NMR data first.'); -0242 end -0243 -0244 end -0245 -0246 %------------- END OF CODE -------------- -0247 -0248 %% License: -0249 % MIT License -0250 % -0251 % Copyright (c) 2018 Thomas Hiller -0252 % -0253 % Permission is hereby granted, free of charge, to any person obtaining a copy -0254 % of this software and associated documentation files (the "Software"), to deal -0255 % in the Software without restriction, including without limitation the rights -0256 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0257 % copies of the Software, and to permit persons to whom the Software is -0258 % furnished to do so, subject to the following conditions: -0259 % -0260 % The above copyright notice and this permission notice shall be included in all -0261 % copies or substantial portions of the Software. -0262 % -0263 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0264 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0265 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0266 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0267 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0268 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0269 % SOFTWARE. +0139 +0140 invstd = fitDataLUdecomp(data.results.nmrproc.t,... +0141 data.results.nmrproc.s,param); +0142 +0143 case 'NNLS' +0144 param.T1T2 = data.results.nmrproc.T1T2; +0145 param.T1IRfac = data.results.nmrproc.T1IRfac; +0146 param.Tb = data.invstd.Tbulk; +0147 param.Td = data.invstd.Tdiff; +0148 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0149 param.regMethod = data.invstd.regtype; +0150 param.Lorder = data.invstd.Lorder; +0151 param.lambda = data.invstd.lambda; +0152 param.noise = data.results.nmrproc.noise; +0153 param.solver = data.info.solver; +0154 if isfield(data.results.nmrproc,'W') +0155 param.W = data.results.nmrproc.W; +0156 end +0157 +0158 invstd = fitDataLSQ(data.results.nmrproc.t,... +0159 data.results.nmrproc.s,param); +0160 +0161 case 'MUMO' % N free distribution inversion +0162 param.T1T2 = data.results.nmrproc.T1T2; +0163 param.T1IRfac = data.results.nmrproc.T1IRfac; +0164 param.Tb = data.invstd.Tbulk; +0165 param.Td = data.invstd.Tdiff; +0166 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0167 param.regMethod = data.invstd.regtype; +0168 param.noise = data.results.nmrproc.noise; +0169 param.solver = data.info.solver; +0170 param.optim = data.info.has_optim; +0171 if isfield(data.results.nmrproc,'W') +0172 param.W = data.results.nmrproc.W; +0173 end +0174 +0175 % status bar information +0176 switch data.info.solver +0177 case 'lsqlin' +0178 infostring = 'Inversion using ''Optimization Toolbox'' ... '; +0179 case 'lsqnonneg' +0180 infostring = 'Inversion using ''fminsearchbnd'' ... '; +0181 end +0182 displayStatusText(gui,infostring); +0183 invstd = fitDataMultiModal(data.results.nmrproc.t,... +0184 data.results.nmrproc.s,param,data.invstd.freeDT); +0185 +0186 % estimate uncertainty +0187 if data.invstd.useUncert +0188 % original fit parameter +0189 iparam = param; +0190 % uncertainty parameter +0191 uparam.time = data.results.nmrproc.t; +0192 uparam.signal = data.results.nmrproc.s; +0193 uparam.uncertMethod = data.invstd.uncertMethod; +0194 uparam.uncertThresh = data.invstd.uncertThresh; +0195 uparam.uncertChi2 = data.invstd.uncertChi2; +0196 uparam.uncertN = data.invstd.uncertN; +0197 uparam.uncertMax = data.invstd.uncertMax; +0198 invstd = estimateUncertainty(data.invstd.invtype,invstd,iparam,uparam); +0199 end +0200 end +0201 +0202 % save inversion results +0203 data.results.invstd = invstd; +0204 if id == 1 +0205 % get possible parameter file information +0206 if isfield(data.import.NMR,'para') +0207 data.info.parameter = data.import.NMR.para{id}; +0208 else +0209 data.info.parameter = 'No parameter data available.'; +0210 end +0211 INVdata{id} = []; +0212 INVdata{id} = data; +0213 INVdata{id} = rmfield(INVdata{id},'import'); +0214 INVdata{id} = rmfield(INVdata{id},'info'); +0215 INVdata{id} = rmfield(INVdata{id},'calib'); +0216 INVdata{id} = rmfield(INVdata{id},'pressure'); +0217 % copy data to all INVdata fields to allocate memory +0218 % this speeds up the overall runtime +0219 INVdata = repmat(INVdata(id),[length(data.import.NMR.filesShort),1]); +0220 else +0221 % save individual results +0222 INVdata{id}.process = data.process; +0223 INVdata{id}.results = data.results; +0224 if isfield(data.import.NMR,'para') +0225 INVdata{id}.info.parameter = data.import.NMR.para{id}; +0226 else +0227 INVdata{id}.info.parameter = 'No parameter data available.'; +0228 end +0229 end +0230 +0231 % color the list entries that already have an inversion result +0232 strL = get(gui.listbox_handles.signal,'String'); +0233 str1 = strL{id}; +0234 str2 = ['<HTML><BODY bgcolor="rgb(',... +0235 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',str1,'</BODY></HTML>']; +0236 strL{id} = str2; +0237 set(gui.listbox_handles.signal,'String',strL); +0238 +0239 % update wait-bar +0240 if wbopts.show +0241 % for a small number of signals always update the wait-bar +0242 if size(INVdata,1) <= 50 +0243 waitbar(id / steps,hwb,['processing ...',num2str(id),... +0244 ' / ',num2str(steps),' NMR signals']); +0245 else +0246 % otherwise only update every 25 signals +0247 % NOTE: Matlab's wait-bar SLOWS DOWN the calculation +0248 % MASSIVELY +0249 if id == 1 || mod(id,25) == 0 +0250 waitbar(id / steps,hwb,['processing ...',num2str(id),... +0251 ' / ',num2str(steps),' NMR signals']); +0252 end +0253 end +0254 end +0255 else +0256 displayStatusText(gui,[infostring,' was canceled']); +0257 % remove temporary data fields +0258 data = removeInversionFields(data); +0259 % remove data from fields not processed +0260 INVdata{id} = []; +0261 end +0262 end +0263 % delete wait-bar +0264 if wbopts.show +0265 delete(hwb); +0266 end +0267 if get(gui.push_handles.invstd_run,'UserData') == 1 % STOP was not pressed +0268 displayStatusText(gui,[infostring,'done']); +0269 end +0270 +0271 % update INVdata to GUI +0272 setappdata(fig,'INVdata',INVdata); +0273 % make the STOP button a RUN again +0274 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... +0275 'BackgroundColor','g','Callback',@onPushRun); +0276 % update GUI data +0277 setappdata(fig,'gui',gui); +0278 % set focus on the first entry in the list +0279 set(gui.listbox_handles.signal,'Value',1); +0280 onListboxData(gui.listbox_handles.signal); +0281 else +0282 helpdlg('Nothing to do because there is no data loaded!',... +0283 'runInversioBatch: Load NMR data first.'); +0284 end +0285 +0286 end +0287 +0288 %------------- END OF CODE -------------- +0289 +0290 %% License: +0291 % MIT License +0292 % +0293 % Copyright (c) 2018 Thomas Hiller +0294 % +0295 % Permission is hereby granted, free of charge, to any person obtaining a copy +0296 % of this software and associated documentation files (the "Software"), to deal +0297 % in the Software without restriction, including without limitation the rights +0298 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0299 % copies of the Software, and to permit persons to whom the Software is +0300 % furnished to do so, subject to the following conditions: +0301 % +0302 % The above copyright notice and this permission notice shall be included in all +0303 % copies or substantial portions of the Software. +0304 % +0305 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0306 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0307 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0308 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0309 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0310 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0311 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/runInversionJoint.html b/doc/nucleus/functions/interface/runInversionJoint.html index ba72430..51cfd2f 100644 --- a/doc/nucleus/functions/interface/runInversionJoint.html +++ b/doc/nucleus/functions/interface/runInversionJoint.html @@ -67,7 +67,7 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
  • onPushShowHide shows/hides the INFO column on the right side of NUCLEUSinv
  • onPushStop recognizes that a STOP push button was pressed and resets the
  • checkIfInversionExists checks if any inversion result is stored inside
  • clearSingleAxis clears an individual axis
  • displayStatusText shows status information either in the GUI or on the
  • removeInversionFields deletes all inversion result fields from NUCLEUSinv
  • updateInfo updates the information shown in all information list boxes
  • updatePlotsJointInversion plots the joint-inversion results in NUCLEUSinv
  • updatePlotsLcurve plots the results of the L-curve calculation
  • fcn_JointInvfixed performs the "fixed" joint inversion using the RTD of
  • fcn_JointInvfree jointly estimates the pore size distribution and surface
  • fcn_JointInvshape performs the "shape" joint inversion using the RTD of
  • getChi2 the chi2 of a NMR fit (noise weighted error quality)
  • getLambdaFromLCurve estimates the regularization parameter lambda according
  • getConstants provides some physical constants to the forward calculation
  • getCornerNMRparameter calculates corner parameters (amplitude surface to
  • getGeometryParameter calculates geometric parameters for the three possible
  • getPartialSaturationMatrix calculates the partial saturation matrix to be
  • getSaturationFromPressureBatch
  • This function is called by:
    • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
    @@ -122,8 +122,8 @@

    SOURCE CODE ^% none 0045 % 0046 % See also: NUCLEUSinv -0047 % Author: Thomas Hiller -0048 % email: thomas.hiller[at]leibniz-liag.de +0047 % Author: see AUTHORS.md +0048 % email: see AUTHORS.md 0049 % License: MIT License (at end) 0050 0051 %------------- BEGIN CODE -------------- @@ -354,540 +354,545 @@

    SOURCE CODE ^end 0278 iparam.Tb = data.invstd.Tbulk; -0279 iparam.T1T2 = T1T2flag; -0280 iparam.T1IRfac = T1IRfac; -0281 iparam.L = L; -0282 iparam.lambda = lambda_range(i); -0283 iparam.igeom = igeom; -0284 iparam.IPS = IPS; -0285 iparam.SVdata = SVdata; -0286 iparam.SatImbDrain = SatImbDrain; -0287 -0288 % start values and bounds -0289 rhostart = log10(data.invjoint.rhostart/1e6); -0290 rhobounds = log10(data.invjoint.rhobounds/1e6); -0291 x0 = [zeros(size(igeom.radius))' rhostart]; -0292 lb = [zeros(size(igeom.radius))' rhobounds(1)]; -0293 ub = [ones(size(igeom.radius))' rhobounds(2)]; -0294 -0295 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... -0296 'Jacobian','on','DerivativeCheck','off','FinDiffType','central',... -0297 'Algorithm','levenberg-marquardt',... -0298 'MaxIter',1000); -0299 -0300 [X,~,~,~] = lsqnonlin(@(X)fcn_JointInvfree(X,iparam),x0,lb,ub,options); -0301 [~,~,ig,~] = fcn_JointInvfree(X,iparam); -0302 -0303 if useW -0304 % normalize the fit because the signal was error -0305 % weighted for the inversion -0306 e = diag(iparam.W); -0307 einv = 1./e; -0308 Winv = diag(einv); -0309 ig = Winv * ig; -0310 end -0311 -0312 residual = ig - g'; -0313 iF = X(1:length(X)-1); -0314 -0315 % output data -0316 % get RMS and X^2 -0317 if useW -0318 % weighted RMS error -0319 % residual is weighted with the amount of echoes N per time gate -0320 % NOTE: if "N per gate" is too large, the RMS estimation breaks down -0321 RMS(i) = sqrt (sum(N'.*(residual).^2) / length(residual)); -0322 % X2 estimate -0323 CHI2(i) = getChi2(g',ig,e); -0324 else -0325 % RMS error -0326 RMS(i) = sqrt( sum(residual.^2) / length(residual) ); -0327 % X2 estimate -0328 CHI2(i) = getChi2(g',ig,idata.nmr{levels(1)}.noise); -0329 end -0330 % error norm and model norm -0331 RN(i) = norm(residual,2); -0332 XN(i) = norm(L*iF',2); -0333 % wait-bar update -0334 if wbopts.show -0335 waitbar(i / steps,hwb,['processing ...',num2str(i),' / ',num2str(steps),' lambda steps']); -0336 end -0337 end -0338 end -0339 % delete the wait-bar -0340 if wbopts.show -0341 delete(hwb); -0342 end -0343 -0344 % check if the STOP button was pressed -0345 % if "UserData" is 1 STOP was not pressed -> save data -0346 if get(gui.push_handles.invjoint_run,'UserData') == 1 -0347 lc.lambda = lambda_range; -0348 lc.RMS = RMS; -0349 lc.RN = RN; -0350 lc.XN = XN; -0351 % get optimal lambda -0352 % lc.index = getLambdaFromRMS(lc.lambda,lc.RMS,0); -0353 lc.index = getLambdaFromLCurve(RN,XN,0); -0354 data.results.lcurve = lc; -0355 % update GUI data -0356 setappdata(fig,'data',data); -0357 % update L-curve plots -0358 updatePlotsLcurve; -0359 % status bar information -0360 displayStatusText(gui,[infostring,' done']); -0361 % set focus on results -0362 set(gui.plots.DistPanel,'Selection',1); -0363 else -0364 % status bar information -0365 displayStatusText(gui,[infostring,' was canceled']); -0366 % remove temporary data fields -0367 data = removeInversionFields(data); -0368 end -0369 -0370 case 'manual' -0371 % disable the RUN button to indicate a running inversion -0372 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... -0373 'Enable','inactive'); -0374 -0375 % inversion parameter -0376 iparam.t = t; -0377 iparam.g = g; -0378 if useW -0379 iparam.W = W; -0380 end -0381 iparam.Tb = data.invstd.Tbulk; -0382 iparam.T1T2 = T1T2flag; -0383 iparam.T1IRfac = T1IRfac; -0384 iparam.L = L; -0385 iparam.lambda = data.invjoint.lambda; -0386 iparam.igeom = igeom; -0387 iparam.IPS = IPS; -0388 iparam.SVdata = SVdata; -0389 iparam.SatImbDrain = SatImbDrain; -0390 -0391 % start values and bounds -0392 rhostart = log10(data.invjoint.rhostart/1e6); -0393 rhobounds = log10(data.invjoint.rhobounds/1e6); -0394 x0 = [zeros(size(igeom.radius))' rhostart]; -0395 lb = [zeros(size(igeom.radius))' rhobounds(1)]; -0396 ub = [ones(size(igeom.radius))' rhobounds(2)]; -0397 -0398 % status bar information -0399 infostring = 'Joint Inversion (free) using ''lsqnonlin'' ... '; -0400 displayStatusText(gui,infostring); -0401 -0402 % optimization settings -0403 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... -0404 'Jacobian','on','DerivativeCheck','off','FinDiffType','central',... -0405 'Algorithm','levenberg-marquardt',... -0406 'MaxIter',1000); -0407 [X,~,~,exitflag] = lsqnonlin(@(X)fcn_JointInvfree(X,iparam),x0,lb,ub,options); -0408 % status bar information -0409 displayStatusText(gui,[infostring,'done']); -0410 % get the final fit -0411 [~,~,ig,KK] = fcn_JointInvfree(X,iparam); -0412 -0413 if useW -0414 % normalize the fit because the signal was error -0415 % weighted for the inversion -0416 e = diag(iparam.W); -0417 einv = 1./e; -0418 Winv = diag(einv); -0419 ig = Winv * ig; -0420 end -0421 -0422 % the inverted surface relaxivity and PSD -0423 iF = X(1:length(X)-1); -0424 irho = 10^X(length(X)); -0425 % inversion output -0426 data.results.invjoint.p0 = p0; -0427 data.results.invjoint.S0 = S0; -0428 data.results.invjoint.levels = levels; -0429 data.results.invjoint.T1T2 = T1T2flag; -0430 data.results.invjoint.T1IRfac = T1IRfac; -0431 data.results.invjoint.x0 = x0; -0432 data.results.invjoint.lb = lb; -0433 data.results.invjoint.ub = ub; -0434 data.results.invjoint.iparam = iparam; -0435 data.results.invjoint.iGEOM = igeom; -0436 data.results.invjoint.iSAT = iSAT; -0437 data.results.invjoint.iF = iF'; -0438 data.results.invjoint.irho = irho; -0439 data.results.invjoint.ig = ig; -0440 data.results.invjoint.XX = KK; -0441 data.results.invjoint.errnorm = norm((ig - g')).^2; -0442 data.results.invjoint.residual = ig - g'; -0443 data.results.invjoint.exitflag = exitflag; -0444 -0445 % get RMS and X^2 -0446 residual = data.results.invjoint.residual; -0447 if useW -0448 % weighted RMS error -0449 % residual is weighted with the amount of echoes N per time gate -0450 % NOTE: if "N per gate" is too large, the RMS estimation breaks down -0451 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); -0452 % X2 estimate -0453 data.results.invjoint.chi2 = getChi2(g',ig,e); -0454 else -0455 % RMS error -0456 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); -0457 % X2 estimate -0458 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); -0459 end -0460 -0461 % predict CPS curves for the final model -0462 infostring = 'calculate CPS curve ... '; -0463 displayStatusText(gui,infostring); -0464 ppsddata.r = igeom.radius'; -0465 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); -0466 if min(p) == 0 -0467 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,150); -0468 else -0469 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,150); -0470 end -0471 % waitbar option -0472 wbopts.show = true; -0473 wbopts.tag = 'INV'; -0474 pSAT = getSaturationFromPressureBatch(igeom,p_tmp,ppsddata,getConstants,wbopts); -0475 % save -0476 data.results.invjoint.pSAT = pSAT; -0477 displayStatusText(gui,[infostring,'done']); -0478 end -0479 -0480 case 'fixed' % only invert for rho -0481 % disable the RUN button to indicate a running inversion -0482 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... -0483 'Enable','inactive'); -0484 -0485 % inversion geometry -0486 igeom.type = data.invjoint.geometry_type; -0487 igeom.angles = [data.invjoint.alpha data.invjoint.beta data.invjoint.gamma]; -0488 igeom.polyN = data.invjoint.polyN; -0489 igeom.radius = data.param.rho.*data.param.a.*fullsat.T; % first guess -0490 igeom = getGeometryParameter(igeom); -0491 -0492 iparam.t = t; -0493 iparam.g = g; -0494 if useW -0495 iparam.W = W; -0496 end -0497 iparam.indt = indt; -0498 iparam.Tb = data.invstd.Tbulk; -0499 iparam.T1T2 = T1T2flag; -0500 iparam.T1IRfac = T1IRfac; -0501 iparam.SatImbDrain = SatImbDrain; -0502 iparam.p = p; -0503 iparam.igeom = igeom; -0504 iparam.x = fullsat.T'; -0505 iparam.f = fullsat.F'; -0506 -0507 % start values and bounds -0508 x0 = log10(data.invjoint.rhostart/1e6); -0509 rhobounds = log10(data.invjoint.rhobounds/1e6); -0510 lb = rhobounds(1); -0511 ub = rhobounds(2); -0512 -0513 infostring = 'Joint Inversion (fixed) using ''fminsearchbnd'' ... '; -0514 displayStatusText(gui,infostring); -0515 -0516 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... -0517 'MaxFunEvals',300,'MaxIter',300); -0518 X = fminsearchbnd(@(X) fcn_JointInvfixed(X,iparam),x0,lb,ub,options); +0279 iparam.Td = data.invstd.Tdiff; +0280 iparam.T1T2 = T1T2flag; +0281 iparam.T1IRfac = T1IRfac; +0282 iparam.L = L; +0283 iparam.lambda = lambda_range(i); +0284 iparam.igeom = igeom; +0285 iparam.IPS = IPS; +0286 iparam.SVdata = SVdata; +0287 iparam.SatImbDrain = SatImbDrain; +0288 +0289 % start values and bounds +0290 rhostart = log10(data.invjoint.rhostart/1e6); +0291 rhobounds = log10(data.invjoint.rhobounds/1e6); +0292 x0 = [zeros(size(igeom.radius))' rhostart]; +0293 lb = [zeros(size(igeom.radius))' rhobounds(1)]; +0294 ub = [ones(size(igeom.radius))' rhobounds(2)]; +0295 +0296 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... +0297 'Jacobian','on','DerivativeCheck','off','FinDiffType','central',... +0298 'Algorithm','levenberg-marquardt',... +0299 'MaxIter',1000); +0300 +0301 [X,~,~,~] = lsqnonlin(@(X)fcn_JointInvfree(X,iparam),x0,lb,ub,options); +0302 [~,~,ig,~] = fcn_JointInvfree(X,iparam); +0303 +0304 if useW +0305 % normalize the fit because the signal was error +0306 % weighted for the inversion +0307 e = diag(iparam.W); +0308 einv = 1./e; +0309 Winv = diag(einv); +0310 ig = Winv * ig; +0311 end +0312 +0313 residual = ig - g'; +0314 iF = X(1:length(X)-1); +0315 +0316 % output data +0317 % get RMS and X^2 +0318 if useW +0319 % weighted RMS error +0320 % residual is weighted with the amount of echoes N per time gate +0321 % NOTE: if "N per gate" is too large, the RMS estimation breaks down +0322 RMS(i) = sqrt (sum(N'.*(residual).^2) / length(residual)); +0323 % X2 estimate +0324 CHI2(i) = getChi2(g',ig,e); +0325 else +0326 % RMS error +0327 RMS(i) = sqrt( sum(residual.^2) / length(residual) ); +0328 % X2 estimate +0329 CHI2(i) = getChi2(g',ig,idata.nmr{levels(1)}.noise); +0330 end +0331 % error norm and model norm +0332 RN(i) = norm(residual,2); +0333 XN(i) = norm(L*iF',2); +0334 % wait-bar update +0335 if wbopts.show +0336 waitbar(i / steps,hwb,['processing ...',num2str(i),' / ',num2str(steps),' lambda steps']); +0337 end +0338 end +0339 end +0340 % delete the wait-bar +0341 if wbopts.show +0342 delete(hwb); +0343 end +0344 +0345 % check if the STOP button was pressed +0346 % if "UserData" is 1 STOP was not pressed -> save data +0347 if get(gui.push_handles.invjoint_run,'UserData') == 1 +0348 lc.lambda = lambda_range; +0349 lc.RMS = RMS; +0350 lc.RN = RN; +0351 lc.XN = XN; +0352 % get optimal lambda +0353 % lc.index = getLambdaFromRMS(lc.lambda,lc.RMS,0); +0354 lc.index = getLambdaFromLCurve(RN,XN,0); +0355 data.results.lcurve = lc; +0356 % update GUI data +0357 setappdata(fig,'data',data); +0358 % update L-curve plots +0359 updatePlotsLcurve; +0360 % status bar information +0361 displayStatusText(gui,[infostring,' done']); +0362 % set focus on results +0363 set(gui.plots.DistPanel,'Selection',1); +0364 else +0365 % status bar information +0366 displayStatusText(gui,[infostring,' was canceled']); +0367 % remove temporary data fields +0368 data = removeInversionFields(data); +0369 end +0370 +0371 case 'manual' +0372 % disable the RUN button to indicate a running inversion +0373 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... +0374 'Enable','inactive'); +0375 +0376 % inversion parameter +0377 iparam.t = t; +0378 iparam.g = g; +0379 if useW +0380 iparam.W = W; +0381 end +0382 iparam.Tb = data.invstd.Tbulk; +0383 iparam.Td = data.invstd.Tdiff; +0384 iparam.T1T2 = T1T2flag; +0385 iparam.T1IRfac = T1IRfac; +0386 iparam.L = L; +0387 iparam.lambda = data.invjoint.lambda; +0388 iparam.igeom = igeom; +0389 iparam.IPS = IPS; +0390 iparam.SVdata = SVdata; +0391 iparam.SatImbDrain = SatImbDrain; +0392 +0393 % start values and bounds +0394 rhostart = log10(data.invjoint.rhostart/1e6); +0395 rhobounds = log10(data.invjoint.rhobounds/1e6); +0396 x0 = [zeros(size(igeom.radius))' rhostart]; +0397 lb = [zeros(size(igeom.radius))' rhobounds(1)]; +0398 ub = [ones(size(igeom.radius))' rhobounds(2)]; +0399 +0400 % status bar information +0401 infostring = 'Joint Inversion (free) using ''lsqnonlin'' ... '; +0402 displayStatusText(gui,infostring); +0403 +0404 % optimization settings +0405 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... +0406 'Jacobian','on','DerivativeCheck','off','FinDiffType','central',... +0407 'Algorithm','levenberg-marquardt',... +0408 'MaxIter',1000); +0409 [X,~,~,exitflag] = lsqnonlin(@(X)fcn_JointInvfree(X,iparam),x0,lb,ub,options); +0410 +0411 % status bar information +0412 displayStatusText(gui,[infostring,'done']); +0413 % get the final fit +0414 [~,~,ig,KK] = fcn_JointInvfree(X,iparam); +0415 +0416 if useW +0417 % normalize the fit because the signal was error +0418 % weighted for the inversion +0419 e = diag(iparam.W); +0420 einv = 1./e; +0421 Winv = diag(einv); +0422 ig = Winv * ig; +0423 end +0424 +0425 % the inverted surface relaxivity and PSD +0426 iF = X(1:length(X)-1); +0427 irho = 10^X(length(X)); +0428 % inversion output +0429 data.results.invjoint.p0 = p0; +0430 data.results.invjoint.S0 = S0; +0431 data.results.invjoint.levels = levels; +0432 data.results.invjoint.T1T2 = T1T2flag; +0433 data.results.invjoint.T1IRfac = T1IRfac; +0434 data.results.invjoint.x0 = x0; +0435 data.results.invjoint.lb = lb; +0436 data.results.invjoint.ub = ub; +0437 data.results.invjoint.iparam = iparam; +0438 data.results.invjoint.iGEOM = igeom; +0439 data.results.invjoint.iSAT = iSAT; +0440 data.results.invjoint.iF = iF'; +0441 data.results.invjoint.irho = irho; +0442 data.results.invjoint.ig = ig; +0443 data.results.invjoint.XX = KK; +0444 data.results.invjoint.errnorm = norm((ig - g')).^2; +0445 data.results.invjoint.residual = ig - g'; +0446 data.results.invjoint.exitflag = exitflag; +0447 +0448 % get RMS and X^2 +0449 residual = data.results.invjoint.residual; +0450 if useW +0451 % weighted RMS error +0452 % residual is weighted with the amount of echoes N per time gate +0453 % NOTE: if "N per gate" is too large, the RMS estimation breaks down +0454 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); +0455 % X2 estimate +0456 data.results.invjoint.chi2 = getChi2(g',ig,e); +0457 else +0458 % RMS error +0459 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); +0460 % X2 estimate +0461 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); +0462 end +0463 +0464 % predict CPS curves for the final model +0465 infostring = 'calculate CPS curve ... '; +0466 displayStatusText(gui,infostring); +0467 ppsddata.r = igeom.radius'; +0468 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); +0469 if min(p) == 0 +0470 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,150); +0471 else +0472 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,150); +0473 end +0474 % waitbar option +0475 wbopts.show = true; +0476 wbopts.tag = 'INV'; +0477 pSAT = getSaturationFromPressureBatch(igeom,p_tmp,ppsddata,getConstants,wbopts); +0478 % save +0479 data.results.invjoint.pSAT = pSAT; +0480 displayStatusText(gui,[infostring,'done']); +0481 end +0482 +0483 case 'fixed' % only invert for rho +0484 % disable the RUN button to indicate a running inversion +0485 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... +0486 'Enable','inactive'); +0487 +0488 % inversion geometry +0489 igeom.type = data.invjoint.geometry_type; +0490 igeom.angles = [data.invjoint.alpha data.invjoint.beta data.invjoint.gamma]; +0491 igeom.polyN = data.invjoint.polyN; +0492 igeom.radius = data.param.rho.*data.param.a.*fullsat.T; % first guess +0493 igeom = getGeometryParameter(igeom); +0494 +0495 iparam.t = t; +0496 iparam.g = g; +0497 if useW +0498 iparam.W = W; +0499 end +0500 iparam.indt = indt; +0501 iparam.Tb = data.invstd.Tbulk; +0502 iparam.Td = data.invstd.Tdiff; +0503 iparam.T1T2 = T1T2flag; +0504 iparam.T1IRfac = T1IRfac; +0505 iparam.SatImbDrain = SatImbDrain; +0506 iparam.p = p; +0507 iparam.igeom = igeom; +0508 iparam.x = fullsat.T'; +0509 iparam.f = fullsat.F'; +0510 +0511 % start values and bounds +0512 x0 = log10(data.invjoint.rhostart/1e6); +0513 rhobounds = log10(data.invjoint.rhobounds/1e6); +0514 lb = rhobounds(1); +0515 ub = rhobounds(2); +0516 +0517 infostring = 'Joint Inversion (fixed) using ''fminsearchbnd'' ... '; +0518 displayStatusText(gui,infostring); 0519 -0520 [errnorm,ig,XX,iGEOM,iSAT] = fcn_JointInvfixed(X,iparam); -0521 -0522 displayStatusText(gui,[infostring,'done']); +0520 options = optimset('Display',info,'TolFun',1e-12,'TolX',1e-12,... +0521 'MaxFunEvals',300,'MaxIter',300); +0522 X = fminsearchbnd(@(X) fcn_JointInvfixed(X,iparam),x0,lb,ub,options); 0523 -0524 if useW -0525 % normalize the fit because the signal was error -0526 % weighted for the inversion -0527 e = diag(iparam.W); -0528 einv = 1./e; -0529 Winv = diag(einv); -0530 ig = Winv * ig; -0531 end -0532 -0533 % inverted surface relaxivity -0534 irho = 10^X(1); -0535 % output data -0536 data.results.invjoint.p0 = p0; -0537 data.results.invjoint.S0 = S0; -0538 data.results.invjoint.levels = levels; -0539 data.results.invjoint.T1T2 = T1T2flag; -0540 data.results.invjoint.T1IRfac = T1IRfac; -0541 data.results.invjoint.x0 = x0; -0542 data.results.invjoint.lb = lb; -0543 data.results.invjoint.ub = ub; -0544 data.results.invjoint.iparam = iparam; -0545 data.results.invjoint.iGEOM = iGEOM; -0546 data.results.invjoint.iSAT = iSAT; -0547 data.results.invjoint.iF = fullsat.F; -0548 data.results.invjoint.irho = irho; -0549 data.results.invjoint.ig = ig; -0550 data.results.invjoint.XX = XX; -0551 data.results.invjoint.errnorm = errnorm; -0552 data.results.invjoint.residual = ig-g'; -0553 -0554 % get RMS and X^2 -0555 residual = data.results.invjoint.residual; -0556 if useW -0557 % weighted RMS error -0558 % residual is weighted with the amount of echoes N per time gate -0559 % NOTE: if "N per gate" is too large, the RMS estimation breaks down -0560 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); -0561 % X2 estimate -0562 data.results.invjoint.chi2 = getChi2(g',ig,e); -0563 else -0564 % RMS error -0565 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); -0566 % X2 estimate -0567 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); -0568 end -0569 -0570 % predict CPS curves from final model -0571 infostring = 'calculate CPS curve ... '; -0572 displayStatusText(gui,infostring); -0573 ppsddata.r = iGEOM.radius'; -0574 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); -0575 if min(p) == 0 -0576 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,200); -0577 else -0578 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,200); -0579 end -0580 % waitbar option -0581 wbopts.show = true; -0582 wbopts.tag = 'INV'; -0583 pSAT = getSaturationFromPressureBatch(iGEOM,p_tmp,ppsddata,getConstants,wbopts); -0584 -0585 data.results.invjoint.pSAT = pSAT; -0586 displayStatusText(gui,[infostring,'done']); -0587 -0588 case 'shape' % invert for rho and right angular shape -0589 % disable the RUN button to indicate a running inversion -0590 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... -0591 'Enable','inactive'); -0592 -0593 % inversion geometry -0594 igeom.type = data.invjoint.geometry_type; -0595 igeom.angles = [data.invjoint.alpha data.invjoint.beta data.invjoint.gamma]; -0596 igeom.polyN = data.invjoint.polyN; -0597 igeom.radius = data.param.rho.*data.param.a.*fullsat.T; % first guess -0598 igeom = getGeometryParameter(igeom); -0599 -0600 iparam.t = t; -0601 iparam.g = g; -0602 if useW -0603 iparam.W = W; -0604 end -0605 iparam.indt = indt; -0606 iparam.Tb = data.invstd.Tbulk; -0607 iparam.T1T2 = T1T2flag; -0608 iparam.T1IRfac = T1IRfac; -0609 iparam.SatImbDrain = SatImbDrain; -0610 iparam.p = p; -0611 iparam.igeom = igeom; -0612 iparam.x = fullsat.T'; -0613 iparam.f = fullsat.F'; -0614 -0615 % scale fit parameter between [0 1] -0616 % x0 = [(log10(1e-6)+7)/3 data.invjoint.beta/45]; -0617 % lb = [(log10(1e-7)+7)/3 0.1/45]; -0618 % ub = [(log10(1e-4)+7)/3 45/45]; -0619 % old way -0620 x0 = [log10(data.invjoint.rhostart/1e6) data.invjoint.anglestart]; -0621 rhobounds = log10(data.invjoint.rhobounds/1e6); -0622 lb = [rhobounds(1) 0.1]; -0623 ub = [rhobounds(2) 45]; -0624 -0625 infostring = 'Joint Inversion (shape) using ''fminsearchbnd'' ... '; -0626 displayStatusText(gui,infostring); -0627 options = optimset('Display','iter','TolFun',1e-12,'TolX',1e-12,'MaxIter',500); -0628 options.Algorithm = 'levenberg-marquardt'; -0629 options.MaxFunEvals = 500; -0630 options.DiffMinChange = 1; -0631 -0632 options = optimset('Display',info,'TolFun',1e-9,'TolX',1e-9,... -0633 'MaxFunEvals',300,'MaxIter',300); -0634 X = fminsearchbnd(@(X) fcn_JointInvshape(X,iparam),x0,lb,ub,options); -0635 -0636 [errnorm,ig,XX,iGEOM,iSAT] = fcn_JointInvshape(X,iparam); -0637 -0638 displayStatusText(gui,[infostring,'done']); -0639 -0640 if useW -0641 % normalize the fit because the signal was error -0642 % weighted for the inversion -0643 e = diag(iparam.W); -0644 einv = 1./e; -0645 Winv = diag(einv); -0646 ig = Winv * ig; -0647 end -0648 -0649 irho = 10^X(1); -0650 ibeta = X(2); -0651 -0652 data.results.invjoint.p0 = p0; -0653 data.results.invjoint.S0 = S0; -0654 data.results.invjoint.levels = levels; -0655 data.results.invjoint.T1T2 = T1T2flag; -0656 data.results.invjoint.T1IRfac = T1IRfac; -0657 data.results.invjoint.x0 = x0; -0658 data.results.invjoint.lb = lb; -0659 data.results.invjoint.ub = ub; -0660 data.results.invjoint.iparam = iparam; -0661 data.results.invjoint.iGEOM = iGEOM; -0662 data.results.invjoint.iSAT = iSAT; -0663 data.results.invjoint.iF = fullsat.F; -0664 data.results.invjoint.irho = irho; -0665 data.results.invjoint.ibeta = ibeta; -0666 data.results.invjoint.ig = ig; -0667 data.results.invjoint.XX = XX; -0668 data.results.invjoint.errnorm = errnorm; -0669 data.results.invjoint.residual = ig-g'; -0670 -0671 % get RMS and X^2 -0672 residual = data.results.invjoint.residual; -0673 if useW -0674 % weighted RMS error -0675 % residual is weighted with the amount of echoes N per time gate -0676 % NOTE: if "N per gate" is too large, the RMS estimation breaks down -0677 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); -0678 % X2 estimate -0679 data.results.invjoint.chi2 = getChi2(g',ig,e); -0680 else -0681 % RMS error -0682 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); +0524 [errnorm,ig,XX,iGEOM,iSAT] = fcn_JointInvfixed(X,iparam); +0525 +0526 displayStatusText(gui,[infostring,'done']); +0527 +0528 if useW +0529 % normalize the fit because the signal was error +0530 % weighted for the inversion +0531 e = diag(iparam.W); +0532 einv = 1./e; +0533 Winv = diag(einv); +0534 ig = Winv * ig; +0535 end +0536 +0537 % inverted surface relaxivity +0538 irho = 10^X(1); +0539 % output data +0540 data.results.invjoint.p0 = p0; +0541 data.results.invjoint.S0 = S0; +0542 data.results.invjoint.levels = levels; +0543 data.results.invjoint.T1T2 = T1T2flag; +0544 data.results.invjoint.T1IRfac = T1IRfac; +0545 data.results.invjoint.x0 = x0; +0546 data.results.invjoint.lb = lb; +0547 data.results.invjoint.ub = ub; +0548 data.results.invjoint.iparam = iparam; +0549 data.results.invjoint.iGEOM = iGEOM; +0550 data.results.invjoint.iSAT = iSAT; +0551 data.results.invjoint.iF = fullsat.F; +0552 data.results.invjoint.irho = irho; +0553 data.results.invjoint.ig = ig; +0554 data.results.invjoint.XX = XX; +0555 data.results.invjoint.errnorm = errnorm; +0556 data.results.invjoint.residual = ig-g'; +0557 +0558 % get RMS and X^2 +0559 residual = data.results.invjoint.residual; +0560 if useW +0561 % weighted RMS error +0562 % residual is weighted with the amount of echoes N per time gate +0563 % NOTE: if "N per gate" is too large, the RMS estimation breaks down +0564 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); +0565 % X2 estimate +0566 data.results.invjoint.chi2 = getChi2(g',ig,e); +0567 else +0568 % RMS error +0569 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); +0570 % X2 estimate +0571 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); +0572 end +0573 +0574 % predict CPS curves from final model +0575 infostring = 'calculate CPS curve ... '; +0576 displayStatusText(gui,infostring); +0577 ppsddata.r = iGEOM.radius'; +0578 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); +0579 if min(p) == 0 +0580 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,200); +0581 else +0582 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,200); +0583 end +0584 % waitbar option +0585 wbopts.show = true; +0586 wbopts.tag = 'INV'; +0587 pSAT = getSaturationFromPressureBatch(iGEOM,p_tmp,ppsddata,getConstants,wbopts); +0588 +0589 data.results.invjoint.pSAT = pSAT; +0590 displayStatusText(gui,[infostring,'done']); +0591 +0592 case 'shape' % invert for rho and right angular shape +0593 % disable the RUN button to indicate a running inversion +0594 set(gui.push_handles.invjoint_run,'String','RUNNING ...',... +0595 'Enable','inactive'); +0596 +0597 % inversion geometry +0598 igeom.type = data.invjoint.geometry_type; +0599 igeom.angles = [data.invjoint.alpha data.invjoint.beta data.invjoint.gamma]; +0600 igeom.polyN = data.invjoint.polyN; +0601 igeom.radius = data.param.rho.*data.param.a.*fullsat.T; % first guess +0602 igeom = getGeometryParameter(igeom); +0603 +0604 iparam.t = t; +0605 iparam.g = g; +0606 if useW +0607 iparam.W = W; +0608 end +0609 iparam.indt = indt; +0610 iparam.Tb = data.invstd.Tbulk; +0611 iparam.Td = data.invstd.Tdiff; +0612 iparam.T1T2 = T1T2flag; +0613 iparam.T1IRfac = T1IRfac; +0614 iparam.SatImbDrain = SatImbDrain; +0615 iparam.p = p; +0616 iparam.igeom = igeom; +0617 iparam.x = fullsat.T'; +0618 iparam.f = fullsat.F'; +0619 +0620 % scale fit parameter between [0 1] +0621 % x0 = [(log10(1e-6)+7)/3 data.invjoint.beta/45]; +0622 % lb = [(log10(1e-7)+7)/3 0.1/45]; +0623 % ub = [(log10(1e-4)+7)/3 45/45]; +0624 % old way +0625 x0 = [log10(data.invjoint.rhostart/1e6) data.invjoint.anglestart]; +0626 rhobounds = log10(data.invjoint.rhobounds/1e6); +0627 lb = [rhobounds(1) 0.1]; +0628 ub = [rhobounds(2) 45]; +0629 +0630 infostring = 'Joint Inversion (shape) using ''fminsearchbnd'' ... '; +0631 displayStatusText(gui,infostring); +0632 options = optimset('Display','iter','TolFun',1e-12,'TolX',1e-12,'MaxIter',500); +0633 options.Algorithm = 'levenberg-marquardt'; +0634 options.MaxFunEvals = 500; +0635 options.DiffMinChange = 1; +0636 +0637 options = optimset('Display',info,'TolFun',1e-9,'TolX',1e-9,... +0638 'MaxFunEvals',300,'MaxIter',300); +0639 X = fminsearchbnd(@(X) fcn_JointInvshape(X,iparam),x0,lb,ub,options); +0640 +0641 [errnorm,ig,XX,iGEOM,iSAT] = fcn_JointInvshape(X,iparam); +0642 +0643 displayStatusText(gui,[infostring,'done']); +0644 +0645 if useW +0646 % normalize the fit because the signal was error +0647 % weighted for the inversion +0648 e = diag(iparam.W); +0649 einv = 1./e; +0650 Winv = diag(einv); +0651 ig = Winv * ig; +0652 end +0653 +0654 irho = 10^X(1); +0655 ibeta = X(2); +0656 +0657 data.results.invjoint.p0 = p0; +0658 data.results.invjoint.S0 = S0; +0659 data.results.invjoint.levels = levels; +0660 data.results.invjoint.T1T2 = T1T2flag; +0661 data.results.invjoint.T1IRfac = T1IRfac; +0662 data.results.invjoint.x0 = x0; +0663 data.results.invjoint.lb = lb; +0664 data.results.invjoint.ub = ub; +0665 data.results.invjoint.iparam = iparam; +0666 data.results.invjoint.iGEOM = iGEOM; +0667 data.results.invjoint.iSAT = iSAT; +0668 data.results.invjoint.iF = fullsat.F; +0669 data.results.invjoint.irho = irho; +0670 data.results.invjoint.ibeta = ibeta; +0671 data.results.invjoint.ig = ig; +0672 data.results.invjoint.XX = XX; +0673 data.results.invjoint.errnorm = errnorm; +0674 data.results.invjoint.residual = ig-g'; +0675 +0676 % get RMS and X^2 +0677 residual = data.results.invjoint.residual; +0678 if useW +0679 % weighted RMS error +0680 % residual is weighted with the amount of echoes N per time gate +0681 % NOTE: if "N per gate" is too large, the RMS estimation breaks down +0682 data.results.invjoint.rms = sqrt (sum(N'.*(residual).^2) / length(residual)); 0683 % X2 estimate -0684 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); -0685 end -0686 -0687 % predict CPS curves from final model -0688 infostring = 'calculate CPS curve ... '; -0689 displayStatusText(gui,infostring); -0690 ppsddata.r = iGEOM.radius'; -0691 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); -0692 if min(p) == 0 -0693 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,150); -0694 else -0695 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,150); -0696 end -0697 % wait-bar option -0698 wbopts.show = true; -0699 wbopts.tag = 'INV'; -0700 pSAT = getSaturationFromPressureBatch(iGEOM,p_tmp,ppsddata,getConstants,wbopts); -0701 -0702 data.results.invjoint.pSAT = pSAT; -0703 displayStatusText(gui,[infostring,'done']); -0704 end -0705 -0706 % if the regularization method was not L-curve then post process the -0707 % NMR data fits -0708 if ~strcmp(data.invjoint.regtype,'lcurve') -0709 % get the individual NMR fits -0710 for i = 1:1:numel(levels) -0711 idata.nmr{levels(i)}.fit_t = idata.nmr{levels(i)}.t; -0712 if i == 1 -0713 idata.nmr{levels(i)}.fit_g = ig(1:indt(1)); -0714 else -0715 idata.nmr{levels(i)}.fit_g = ig(sum(indt(1:i-1))+1:sum(indt(1:i-1))+indt(i)); -0716 end -0717 residual = idata.nmr{levels(i)}.fit_g - idata.nmr{levels(i)}.g; -0718 -0719 % get RMS and X^2 -0720 if useW -0721 % weighted RMS error -0722 % residual is weighted with the amount of echoes N per time gate -0723 % NOTE: if "N per gate" is too large, the RMS estimation breaks down -0724 N = idata.nmr{levels(i)}.N; -0725 rms = sqrt (sum(N.*(residual).^2) / length(residual)); -0726 % X2 estimate -0727 chi2 = getChi2(idata.nmr{levels(i)}.g,... -0728 idata.nmr{levels(i)}.fit_g,... -0729 idata.nmr{levels(i)}.e); -0730 else -0731 % RMS error -0732 rms = sqrt( sum(residual.^2) / length(residual) ); -0733 % X2 estimate -0734 chi2 = getChi2(idata.nmr{levels(i)}.g,... -0735 idata.nmr{levels(i)}.fit_g,... -0736 idata.nmr{levels(i)}.noise); -0737 end -0738 -0739 idata.nmr{levels(i)}.residual = residual; -0740 idata.nmr{levels(i)}.rms = rms; -0741 idata.nmr{levels(i)}.chi2 = chi2; -0742 end -0743 % save the GUI data -0744 data.results.invjoint.idata = idata; -0745 setappdata(fig,'data',data); -0746 -0747 % save the "INVdata" -0748 for i = 1:1:numel(levels) -0749 INVdata{levels(i)}.invjoint = data.invjoint; -0750 INVdata{levels(i)}.pressure = data.pressure; -0751 INVdata{levels(i)}.results.invjoint = data.results.invjoint; -0752 end -0753 setappdata(fig,'INVdata',INVdata); -0754 % update the plots -0755 updatePlotsJointInversion; -0756 % update the INFO fields -0757 updateInfo(gui.plots.SignalPanel); -0758 % set focus on results -0759 set(gui.plots.SignalPanel,'Selection',3); -0760 set(gui.plots.DistPanel,'Selection',3); -0761 % open INFO field -0762 set(gui.push_handles.info,'String','<'); -0763 onPushShowHide(gui.push_handles.info); -0764 % activate the ConductView GUI -0765 set(gui.menu.extra_conduct,'Enable','on'); -0766 end -0767 else -0768 if ~InvtypeIsOK -0769 helpdlg({'function: runInversionJoint','Perform standard multi-exponential inversion first.',... -0770 'For ''fixed'' and ''shape'' you need a RTD!'},'No RTD'); -0771 end -0772 if ~GatetypeIsOK -0773 helpdlg({'function: runInversionJoint','Check your ''signal gating'' settings.',... -0774 'All signals need to have the same gating.'},'Wrong gating'); -0775 end -0776 end -0777 else -0778 helpdlg({'function: runInversionJoint','Perform standard inversion first.',... -0779 'For ''fixed'' and ''shape'' you need a RTD!'},'No Data'); -0780 end -0781 -0782 %% at the end, no matter what reset the RUN button -0783 set(gui.push_handles.invjoint_run,'String','<HTML><u>R</u>UN',... -0784 'BackgroundColor','g','Enable','on','Callback',@onPushRun); -0785 setappdata(fig,'gui',gui); +0684 data.results.invjoint.chi2 = getChi2(g',ig,e); +0685 else +0686 % RMS error +0687 data.results.invjoint.rms = sqrt( sum(residual.^2) / length(residual) ); +0688 % X2 estimate +0689 data.results.invjoint.chi2 = getChi2(g',ig,idata.nmr{levels(1)}.noise); +0690 end +0691 +0692 % predict CPS curves from final model +0693 infostring = 'calculate CPS curve ... '; +0694 displayStatusText(gui,infostring); +0695 ppsddata.r = iGEOM.radius'; +0696 ppsddata.psd = data.results.invjoint.iF'./sum(data.results.invjoint.iF); +0697 if min(p) == 0 +0698 p_tmp = logspace(floor(log10(min(p(p>0)))-2),ceil(log10(max(p)))+2,150); +0699 else +0700 p_tmp = logspace(floor(log10(min(p)/2)),ceil(log10(max(p)))+2,150); +0701 end +0702 % wait-bar option +0703 wbopts.show = true; +0704 wbopts.tag = 'INV'; +0705 pSAT = getSaturationFromPressureBatch(iGEOM,p_tmp,ppsddata,getConstants,wbopts); +0706 +0707 data.results.invjoint.pSAT = pSAT; +0708 displayStatusText(gui,[infostring,'done']); +0709 end +0710 +0711 % if the regularization method was not L-curve then post process the +0712 % NMR data fits +0713 if ~strcmp(data.invjoint.regtype,'lcurve') +0714 % get the individual NMR fits +0715 for i = 1:1:numel(levels) +0716 idata.nmr{levels(i)}.fit_t = idata.nmr{levels(i)}.t; +0717 if i == 1 +0718 idata.nmr{levels(i)}.fit_g = ig(1:indt(1)); +0719 else +0720 idata.nmr{levels(i)}.fit_g = ig(sum(indt(1:i-1))+1:sum(indt(1:i-1))+indt(i)); +0721 end +0722 residual = idata.nmr{levels(i)}.fit_g - idata.nmr{levels(i)}.g; +0723 +0724 % get RMS and X^2 +0725 if useW +0726 % weighted RMS error +0727 % residual is weighted with the amount of echoes N per time gate +0728 % NOTE: if "N per gate" is too large, the RMS estimation breaks down +0729 N = idata.nmr{levels(i)}.N; +0730 rms = sqrt (sum(N.*(residual).^2) / length(residual)); +0731 % X2 estimate +0732 chi2 = getChi2(idata.nmr{levels(i)}.g,... +0733 idata.nmr{levels(i)}.fit_g,... +0734 idata.nmr{levels(i)}.e); +0735 else +0736 % RMS error +0737 rms = sqrt( sum(residual.^2) / length(residual) ); +0738 % X2 estimate +0739 chi2 = getChi2(idata.nmr{levels(i)}.g,... +0740 idata.nmr{levels(i)}.fit_g,... +0741 idata.nmr{levels(i)}.noise); +0742 end +0743 +0744 idata.nmr{levels(i)}.residual = residual; +0745 idata.nmr{levels(i)}.rms = rms; +0746 idata.nmr{levels(i)}.chi2 = chi2; +0747 end +0748 % save the GUI data +0749 data.results.invjoint.idata = idata; +0750 setappdata(fig,'data',data); +0751 +0752 % save the "INVdata" +0753 for i = 1:1:numel(levels) +0754 INVdata{levels(i)}.invjoint = data.invjoint; +0755 INVdata{levels(i)}.pressure = data.pressure; +0756 INVdata{levels(i)}.results.invjoint = data.results.invjoint; +0757 end +0758 setappdata(fig,'INVdata',INVdata); +0759 % update the plots +0760 updatePlotsJointInversion; +0761 % update the INFO fields +0762 updateInfo(gui.plots.SignalPanel); +0763 % set focus on results +0764 set(gui.plots.SignalPanel,'Selection',3); +0765 set(gui.plots.DistPanel,'Selection',3); +0766 % open INFO field +0767 set(gui.push_handles.info,'String','<'); +0768 onPushShowHide(gui.push_handles.info); +0769 % activate the ConductView GUI +0770 set(gui.menu.extra_conduct,'Enable','on'); +0771 end +0772 else +0773 if ~InvtypeIsOK +0774 helpdlg({'function: runInversionJoint','Perform standard multi-exponential inversion first.',... +0775 'For ''fixed'' and ''shape'' you need a RTD!'},'No RTD'); +0776 end +0777 if ~GatetypeIsOK +0778 helpdlg({'function: runInversionJoint','Check your ''signal gating'' settings.',... +0779 'All signals need to have the same gating.'},'Wrong gating'); +0780 end +0781 end +0782 else +0783 helpdlg({'function: runInversionJoint','Perform standard inversion first.',... +0784 'For ''fixed'' and ''shape'' you need a RTD!'},'No Data'); +0785 end 0786 -0787 end -0788 -0789 %------------- END OF CODE -------------- -0790 -0791 %% License: -0792 % MIT License -0793 % -0794 % Copyright (c) 2018 Thomas Hiller -0795 % -0796 % Permission is hereby granted, free of charge, to any person obtaining a copy -0797 % of this software and associated documentation files (the "Software"), to deal -0798 % in the Software without restriction, including without limitation the rights -0799 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0800 % copies of the Software, and to permit persons to whom the Software is -0801 % furnished to do so, subject to the following conditions: -0802 % -0803 % The above copyright notice and this permission notice shall be included in all -0804 % copies or substantial portions of the Software. -0805 % -0806 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0807 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0808 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0809 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0810 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0811 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0812 % SOFTWARE. +0787 %% at the end, no matter what reset the RUN button +0788 set(gui.push_handles.invjoint_run,'String','<HTML><u>R</u>UN',... +0789 'BackgroundColor','g','Enable','on','Callback',@onPushRun); +0790 setappdata(fig,'gui',gui); +0791 +0792 end +0793 +0794 %------------- END OF CODE -------------- +0795 +0796 %% License: +0797 % MIT License +0798 % +0799 % Copyright (c) 2018 Thomas Hiller +0800 % +0801 % Permission is hereby granted, free of charge, to any person obtaining a copy +0802 % of this software and associated documentation files (the "Software"), to deal +0803 % in the Software without restriction, including without limitation the rights +0804 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0805 % copies of the Software, and to permit persons to whom the Software is +0806 % furnished to do so, subject to the following conditions: +0807 % +0808 % The above copyright notice and this permission notice shall be included in all +0809 % copies or substantial portions of the Software. +0810 % +0811 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0812 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0813 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0814 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0815 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0816 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0817 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/runInversionStd.html b/doc/nucleus/functions/interface/runInversionStd.html index 9617be0..0e7a5f2 100644 --- a/doc/nucleus/functions/interface/runInversionStd.html +++ b/doc/nucleus/functions/interface/runInversionStd.html @@ -69,15 +69,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • NUCLEUSinv_updateInterface updates all GUI elements
  • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
  • onPushShowHide shows/hides the INFO column on the right side of NUCLEUSinv
  • onPushStop recognizes that a STOP push button was pressed and resets the
  • calibratePorosity determines a sample's porosity from a calibration
  • clearAllAxes clears all axes of a given figure
  • clearSingleAxis clears an individual axis
  • displayStatusText shows status information either in the GUI or on the
  • removeInversionFields deletes all inversion result fields from NUCLEUSinv
  • showFitStatistics shows an extra fit statistics figure (for T2 data)
  • updateInfo updates the information shown in all information list boxes
  • updatePlotsDistribution plots the RTD and PSD curves into NUCLEUSinv
  • updatePlotsLcurve plots the results of the L-curve calculation
  • updatePlotsSignal plots the raw and processed NMR signals in NUCLEUSinv
  • useSignalAsCalibration uses E0 as porosity calibration factor.
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • getLambdaFromLCurve estimates the regularization parameter lambda according
  • This function is called by:
    • onPushRun handles the callbacks to all RUN push buttons in both GUIs and
    @@ -130,8 +130,8 @@

    SOURCE CODE ^% none 0043 % 0044 % See also: NUCLEUSinv -0045 % Author: Thomas Hiller -0046 % email: thomas.hiller[at]leibniz-liag.de +0045 % Author: see AUTHORS.md +0046 % email: see AUTHORS.md 0047 % License: MIT License (at end) 0048 0049 %------------- BEGIN CODE -------------- @@ -189,310 +189,353 @@

    SOURCE CODE ^'manual'; -0106 param.Lorder = data.invstd.Lorder; -0107 param.noise = data.results.nmrproc.noise; -0108 param.TE = data.results.nmrraw.t(2)-data.results.nmrraw.t(1); -0109 if isfield(data.results.nmrproc,'W') -0110 param.W = data.results.nmrproc.W; -0111 end -0112 param.solver = data.info.solver; -0113 -0114 % status bar information -0115 infostring = 'L-curve calculation ... '; -0116 displayStatusText(gui,infostring); -0117 -0118 % wait-bar option -0119 wbopts.show = true; -0120 wbopts.tag = 'INV'; -0121 if wbopts.show -0122 hwb = waitbar(0,'processing ...','Name','L-curve calculation','Visible','off'); -0123 steps = length(lambda_range); -0124 fig = findobj('Tag',wbopts.tag); -0125 if ~isempty(fig) -0126 posf = get(fig,'Position'); -0127 set(hwb,'Units','Pixel') -0128 posw = get(hwb,'Position'); -0129 set(hwb,'Position',[posf(1)+posf(3)/2-posw(3)/2 posf(2)+posf(4)/2-posw(4)/2 posw(3:4)]); -0130 end -0131 set(hwb,'Visible','on'); -0132 end -0133 -0134 % the L-curve calculation -0135 for i = 1:length(lambda_range) -0136 % check if the STOP button was pressed -0137 % if "UserData" is 1 STOP was not pressed -> continue -0138 if get(gui.push_handles.invstd_run,'UserData') == 1 -0139 param.lambda = lambda_range(i); -0140 invdata = fitDataLSQ(data.results.nmrproc.t,data.results.nmrproc.s,param); -0141 % output data -0142 RMS(i) = invdata.rms; -0143 RN(i) = invdata.rn; -0144 XN(i) = invdata.xn; -0145 % wait-bar update -0146 if wbopts.show -0147 waitbar(i / steps,hwb,['processing ...',num2str(i),' / ',num2str(steps),' lambda steps']); -0148 end -0149 end -0150 end -0151 % delete the wait-bar -0152 if wbopts.show -0153 delete(hwb); -0154 end -0155 -0156 % check if the STOP button was pressed -0157 % if "UserData" is 1 STOP was not pressed -> save data -0158 if get(gui.push_handles.invstd_run,'UserData') == 1 -0159 lc.lambda = lambda_range; -0160 lc.RMS = RMS; -0161 lc.RN = RN; -0162 lc.XN = XN; -0163 % get optimal lambda -0164 lc.index = getLambdaFromLCurve(RN,XN,0); -0165 data.results.lcurve = lc; -0166 % update GUI data -0167 setappdata(fig,'data',data); -0168 % update L-curve plots -0169 updatePlotsLcurve; -0170 % status bar information -0171 displayStatusText(gui,[infostring,' done']); -0172 else -0173 % status bar information -0174 displayStatusText(gui,[infostring,' was canceled']); -0175 % remove temporary data fields -0176 data = removeInversionFields(data); -0177 end -0178 -0179 otherwise % normal inversion -0180 % time the whole inversion -0181 tic; -0182 % Do we want inversion output on the command line -0183 switch data.info.InvInfo -0184 case 'on' -0185 param.info = 'final'; -0186 case 'off' -0187 param.info = 'off'; -0188 end -0189 -0190 % disable the RUN button to indicate a running inversion -0191 set(gui.push_handles.invstd_run,'String','RUNNING ...',... -0192 'Enable','inactive'); -0193 % switch depending on inversion method -0194 switch data.invstd.invtype -0195 case 'mono' % mono-exponential inversion -0196 flag = data.results.nmrproc.T1T2; -0197 param.T1IRfac = data.results.nmrproc.T1IRfac; -0198 param.noise = data.results.nmrproc.noise; -0199 param.optim = data.info.has_optim; -0200 if isfield(data.results.nmrproc,'W') -0201 param.W = data.results.nmrproc.W; -0202 end -0203 % status bar information -0204 switch data.info.has_optim -0205 case 'on' -0206 infostring = 'Inversion using ''Optimization Toolbox'' ... '; -0207 case 'off' -0208 infostring = 'Inversion using ''fminsearchbnd'' ... '; -0209 end -0210 displayStatusText(gui,infostring); -0211 invstd = fitDataFree(data.results.nmrproc.t,... -0212 data.results.nmrproc.s,flag,param,1); -0213 -0214 case 'free' % bi-exponential inversion -0215 flag = data.results.nmrproc.T1T2; -0216 param.T1IRfac = data.results.nmrproc.T1IRfac; -0217 param.noise = data.results.nmrproc.noise; -0218 param.optim = data.info.has_optim; -0219 if isfield(data.results.nmrproc,'W') -0220 param.W = data.results.nmrproc.W; -0221 end -0222 % status bar information -0223 switch data.info.has_optim -0224 case 'on' -0225 infostring = 'Inversion using ''Optimization Toolbox'' ... '; -0226 case 'off' -0227 infostring = 'Inversion using ''fminsearchbnd'' ... '; -0228 end -0229 displayStatusText(gui,infostring); -0230 invstd = fitDataFree(data.results.nmrproc.t,... -0231 data.results.nmrproc.s,flag,param,data.invstd.freeDT); -0232 -0233 case 'LU' % multi-exponential inversion -0234 % general parameter -0235 param.T1T2 = data.results.nmrproc.T1T2; -0236 param.T1IRfac = data.results.nmrproc.T1IRfac; -0237 param.Tb = data.invstd.Tbulk; -0238 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; -0239 param.Lorder = data.invstd.Lorder; -0240 param.lambda = data.invstd.lambda; -0241 param.noise = data.results.nmrproc.noise; -0242 if isfield(data.results.nmrproc,'W') -0243 param.W = data.results.nmrproc.W; -0244 end -0245 % status bar information -0246 infostring = 'Inversion using LU decomposition ... '; -0247 displayStatusText(gui,infostring); -0248 invstd = fitDataLUdecomp(data.results.nmrproc.t,... -0249 data.results.nmrproc.s,param); -0250 -0251 case 'NNLS' % multi-exponential inversion with manual regularization -0252 % general parameter -0253 param.T1T2 = data.results.nmrproc.T1T2; -0254 param.T1IRfac = data.results.nmrproc.T1IRfac; -0255 param.Tb = data.invstd.Tbulk; -0256 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; -0257 param.regMethod = data.invstd.regtype; -0258 param.Lorder = data.invstd.Lorder; -0259 param.lambda = data.invstd.lambda; -0260 param.noise = data.results.nmrproc.noise; -0261 param.solver = data.info.solver; -0262 if isfield(data.results.nmrproc,'W') -0263 param.W = data.results.nmrproc.W; -0264 end -0265 -0266 % status bar information -0267 switch data.info.solver -0268 case 'lsqlin' -0269 infostring = 'Inversion using ''Optimization Toolbox'' ... '; -0270 case 'lsqnonneg' -0271 infostring = 'Inversion using ''lsqnonneg'' ... '; -0272 end -0273 displayStatusText(gui,infostring); -0274 invstd = fitDataLSQ(data.results.nmrproc.t,... -0275 data.results.nmrproc.s,param); -0276 end -0277 % normalize to 1 -0278 if data.process.norm == 1 -0279 switch data.invstd.invtype -0280 case {'LU','NNLS'} -0281 % normalization with sum(f*dt) -> area~=1 -0282 % dt = log10(invstd.T1T2me(2))-log10(invstd.T1T2me(1)); -0283 % invstd.T1T2f = invstd.T1T2f/sum(invstd.T1T2f*dt); -0284 % normalization with sum() -> sum(f)=1 -0285 invstd.T1T2f = invstd.T1T2f./sum(invstd.T1T2f); -0286 % normalization with trapz() -> area=1 but sum(f)>1 -0287 % invstd.T1T2f = invstd.T1T2f./trapz(invstd.T1T2me,invstd.T1T2f); -0288 otherwise -0289 % nothing to do -0290 end -0291 end -0292 runtime = toc; -0293 displayStatusText(gui,[infostring,'done | inversion took ',... -0294 sprintf('%5.2f',runtime),' s']); -0295 % save inversion results -0296 data.results.invstd = invstd; -0297 setappdata(fig,'data',data); -0298 end -0299 % as long as the "UserData" is 1 the STOP button was not pressed and -0300 % the inversion result gets saved in "INVdata" -0301 if get(gui.push_handles.invstd_run,'UserData') == 1 && ... -0302 ( isfield(data.results,'invstd') || ... -0303 isfield(data.results,'lcurve') ) -0304 INVdata{id} = []; -0305 INVdata{id} = data; -0306 INVdata{id} = rmfield(INVdata{id},'import'); -0307 INVdata{id} = rmfield(INVdata{id},'info'); -0308 INVdata{id} = rmfield(INVdata{id},'calib'); -0309 INVdata{id} = rmfield(INVdata{id},'pressure'); -0310 -0311 % color the list entries that already have an inversion result -0312 strL = get(gui.listbox_handles.signal,'String'); -0313 str1 = strL{id}; -0314 str2 = ['<HTML><BODY bgcolor="rgb(',... -0315 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',str1,'</BODY></HTML>']; -0316 strL{id} = str2; -0317 set(gui.listbox_handles.signal,'String',strL); -0318 -0319 % update GUI INVdata -0320 setappdata(fig,'INVdata',INVdata); -0321 -0322 % --- -0323 % special treatment for LIAG processing -0324 if isfield(data.import,'LIAG') && ~strcmp(data.invstd.regtype,'lcurve') -0325 if ~isempty(strfind(str1,' - calibration')) -0326 % save Tbulk from the calibration sample -0327 btn1 = 'Yes keep it'; -0328 btn2 = 'No, reset to 1e6 [s]'; -0329 if data.results.invstd.T2 < 0.2 -0330 answer = questdlg(['Tbulk seems very short with ',... -0331 sprintf('%4.3f',data.results.invstd.T2),' [s] ',... -0332 'Do you want to keep it?'], ... -0333 'Keep relaxation time as Tbulk', ... -0334 btn1,btn2,btn2); -0335 % Handle response -0336 switch answer -0337 case btn1 -0338 data.import.LIAG.Tbulk = data.results.invstd.T2; -0339 case btn2 -0340 data.import.LIAG.Tbulk = 1e6; -0341 end -0342 else -0343 data.import.LIAG.Tbulk = data.results.invstd.T2; -0344 end -0345 % update GUI data -0346 setappdata(fig,'data',data); -0347 % save calibration data -0348 useSignalAsCalibration(id); -0349 else -0350 calibratePorosity; -0351 end -0352 end -0353 % --- -0354 else % STOP was pressed (because "UserData" is 0) -0355 % reset "UserData" -0356 set(gui.push_handles.invstd_run,'UserData',1); -0357 end -0358 % update plots and INFO fields -0359 updatePlotsSignal; -0360 updatePlotsDistribution; -0361 updateInfo(gui.plots.SignalPanel); -0362 % set focus on results -0363 set(gui.plots.SignalPanel,'Selection',1); -0364 set(gui.plots.DistPanel,'Selection',1); -0365 % open INFO field -0366 set(gui.push_handles.info,'String','<'); -0367 onPushShowHide(gui.push_handles.info); -0368 if ~isempty(findobj('Tag','FITSTATS')) -0369 showFitStatistics; -0370 end -0371 NUCLEUSinv_updateInterface; -0372 else -0373 helpdlg('Cannot start inversion because no data set is selected!',... -0374 'Select NMR data first.'); -0375 end -0376 -0377 %% at the end, no matter what reset the RUN button -0378 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... -0379 'BackgroundColor','g','Enable','on','Callback',@onPushRun); -0380 setappdata(fig,'gui',gui); -0381 -0382 end -0383 -0384 %------------- END OF CODE -------------- -0385 -0386 %% License: -0387 % MIT License -0388 % -0389 % Copyright (c) 2018 Thomas Hiller -0390 % -0391 % Permission is hereby granted, free of charge, to any person obtaining a copy -0392 % of this software and associated documentation files (the "Software"), to deal -0393 % in the Software without restriction, including without limitation the rights -0394 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0395 % copies of the Software, and to permit persons to whom the Software is -0396 % furnished to do so, subject to the following conditions: -0397 % -0398 % The above copyright notice and this permission notice shall be included in all -0399 % copies or substantial portions of the Software. -0400 % -0401 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0402 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0403 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0404 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0405 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0406 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0407 % SOFTWARE. +0104 param.Td = data.invstd.Tdiff; +0105 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0106 param.regMethod = 'manual'; +0107 param.Lorder = data.invstd.Lorder; +0108 param.noise = data.results.nmrproc.noise; +0109 param.TE = data.results.nmrraw.t(2)-data.results.nmrraw.t(1); +0110 if isfield(data.results.nmrproc,'W') +0111 param.W = data.results.nmrproc.W; +0112 end +0113 param.solver = data.info.solver; +0114 +0115 % status bar information +0116 infostring = 'L-curve calculation ... '; +0117 displayStatusText(gui,infostring); +0118 +0119 % wait-bar option +0120 wbopts.show = true; +0121 wbopts.tag = 'INV'; +0122 if wbopts.show +0123 hwb = waitbar(0,'processing ...','Name','L-curve calculation','Visible','off'); +0124 steps = length(lambda_range); +0125 fig = findobj('Tag',wbopts.tag); +0126 if ~isempty(fig) +0127 posf = get(fig,'Position'); +0128 set(hwb,'Units','Pixel') +0129 posw = get(hwb,'Position'); +0130 set(hwb,'Position',[posf(1)+posf(3)/2-posw(3)/2 posf(2)+posf(4)/2-posw(4)/2 posw(3:4)]); +0131 end +0132 set(hwb,'Visible','on'); +0133 end +0134 +0135 % the L-curve calculation +0136 for i = 1:length(lambda_range) +0137 % check if the STOP button was pressed +0138 % if "UserData" is 1 STOP was not pressed -> continue +0139 if get(gui.push_handles.invstd_run,'UserData') == 1 +0140 param.lambda = lambda_range(i); +0141 invdata = fitDataLSQ(data.results.nmrproc.t,data.results.nmrproc.s,param); +0142 % output data +0143 RMS(i) = invdata.rms; +0144 RN(i) = invdata.rn; +0145 XN(i) = invdata.xn; +0146 % wait-bar update +0147 if wbopts.show +0148 waitbar(i / steps,hwb,['processing ...',num2str(i),' / ',num2str(steps),' lambda steps']); +0149 end +0150 end +0151 end +0152 % delete the wait-bar +0153 if wbopts.show +0154 delete(hwb); +0155 end +0156 +0157 % check if the STOP button was pressed +0158 % if "UserData" is 1 STOP was not pressed -> save data +0159 if get(gui.push_handles.invstd_run,'UserData') == 1 +0160 lc.lambda = lambda_range; +0161 lc.RMS = RMS; +0162 lc.RN = RN; +0163 lc.XN = XN; +0164 % get optimal lambda +0165 lc.index = getLambdaFromLCurve(RN,XN,0); +0166 data.results.lcurve = lc; +0167 % update GUI data +0168 setappdata(fig,'data',data); +0169 % update L-curve plots +0170 updatePlotsLcurve; +0171 % status bar information +0172 displayStatusText(gui,[infostring,' done']); +0173 else +0174 % status bar information +0175 displayStatusText(gui,[infostring,' was canceled']); +0176 % remove temporary data fields +0177 data = removeInversionFields(data); +0178 end +0179 +0180 otherwise % normal inversion +0181 % time the whole inversion +0182 tic; +0183 % Do we want inversion output on the command line +0184 switch data.info.InvInfo +0185 case 'on' +0186 param.info = 'final'; +0187 case 'off' +0188 param.info = 'off'; +0189 end +0190 +0191 % disable the RUN button to indicate a running inversion +0192 set(gui.push_handles.invstd_run,'String','RUNNING ...',... +0193 'Enable','inactive'); +0194 % switch depending on inversion method +0195 switch data.invstd.invtype +0196 case 'mono' % mono-exponential inversion +0197 flag = data.results.nmrproc.T1T2; +0198 param.T1IRfac = data.results.nmrproc.T1IRfac; +0199 param.noise = data.results.nmrproc.noise; +0200 param.optim = data.info.has_optim; +0201 if isfield(data.results.nmrproc,'W') +0202 param.W = data.results.nmrproc.W; +0203 end +0204 % status bar information +0205 switch data.info.has_optim +0206 case 'on' +0207 infostring = 'Inversion using ''Optimization Toolbox'' ... '; +0208 case 'off' +0209 infostring = 'Inversion using ''fminsearchbnd'' ... '; +0210 end +0211 displayStatusText(gui,infostring); +0212 invstd = fitDataFree(data.results.nmrproc.t,... +0213 data.results.nmrproc.s,flag,param,1); +0214 +0215 case 'free' % N free single-exponential inversion +0216 flag = data.results.nmrproc.T1T2; +0217 param.T1IRfac = data.results.nmrproc.T1IRfac; +0218 param.noise = data.results.nmrproc.noise; +0219 param.optim = data.info.has_optim; +0220 if isfield(data.results.nmrproc,'W') +0221 param.W = data.results.nmrproc.W; +0222 end +0223 % status bar information +0224 switch data.info.has_optim +0225 case 'on' +0226 infostring = 'Inversion using ''Optimization Toolbox'' ... '; +0227 case 'off' +0228 infostring = 'Inversion using ''fminsearchbnd'' ... '; +0229 end +0230 displayStatusText(gui,infostring); +0231 invstd = fitDataFree(data.results.nmrproc.t,... +0232 data.results.nmrproc.s,flag,param,data.invstd.freeDT); +0233 +0234 case 'LU' % multi-exponential inversion +0235 % general parameter +0236 param.T1T2 = data.results.nmrproc.T1T2; +0237 param.T1IRfac = data.results.nmrproc.T1IRfac; +0238 param.Tb = data.invstd.Tbulk; +0239 param.Td = data.invstd.Tdiff; +0240 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0241 param.Lorder = data.invstd.Lorder; +0242 param.lambda = data.invstd.lambda; +0243 param.noise = data.results.nmrproc.noise; +0244 if isfield(data.results.nmrproc,'W') +0245 param.W = data.results.nmrproc.W; +0246 end +0247 % status bar information +0248 infostring = 'Inversion using LU decomposition ... '; +0249 displayStatusText(gui,infostring); +0250 invstd = fitDataLUdecomp(data.results.nmrproc.t,... +0251 data.results.nmrproc.s,param); +0252 +0253 case 'NNLS' % multi-exponential inversion with manual regularization +0254 % general parameter +0255 param.T1T2 = data.results.nmrproc.T1T2; +0256 param.T1IRfac = data.results.nmrproc.T1IRfac; +0257 param.Tb = data.invstd.Tbulk; +0258 param.Td = data.invstd.Tdiff; +0259 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0260 param.regMethod = data.invstd.regtype; +0261 param.Lorder = data.invstd.Lorder; +0262 param.lambda = data.invstd.lambda; +0263 param.noise = data.results.nmrproc.noise; +0264 param.solver = data.info.solver; +0265 if isfield(data.results.nmrproc,'W') +0266 param.W = data.results.nmrproc.W; +0267 end +0268 +0269 % status bar information +0270 switch data.info.solver +0271 case 'lsqlin' +0272 infostring = 'Inversion using ''Optimization Toolbox'' ... '; +0273 case 'lsqnonneg' +0274 infostring = 'Inversion using ''lsqnonneg'' ... '; +0275 end +0276 displayStatusText(gui,infostring); +0277 invstd = fitDataLSQ(data.results.nmrproc.t,... +0278 data.results.nmrproc.s,param); +0279 +0280 case 'MUMO' % N free distribution inversion +0281 param.T1T2 = data.results.nmrproc.T1T2; +0282 param.T1IRfac = data.results.nmrproc.T1IRfac; +0283 param.Tb = data.invstd.Tbulk; +0284 param.Td = data.invstd.Tdiff; +0285 param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; +0286 param.noise = data.results.nmrproc.noise; +0287 param.optim = data.info.has_optim; +0288 if isfield(data.results.nmrproc,'W') +0289 param.W = data.results.nmrproc.W; +0290 end +0291 +0292 % status bar information +0293 switch data.info.solver +0294 case 'lsqlin' +0295 infostring = 'Inversion using ''Optimization Toolbox'' ... '; +0296 case 'lsqnonneg' +0297 infostring = 'Inversion using ''fminsearchbnd'' ... '; +0298 end +0299 displayStatusText(gui,infostring); +0300 invstd = fitDataMultiModal(data.results.nmrproc.t,... +0301 data.results.nmrproc.s,param,data.invstd.freeDT); +0302 +0303 % estimate uncertainty +0304 if data.invstd.useUncert +0305 % original fit parameter +0306 iparam = param; +0307 % uncertainty parameter +0308 uparam.time = data.results.nmrproc.t; +0309 uparam.signal = data.results.nmrproc.s; +0310 uparam.uncertMethod = data.invstd.uncertMethod; +0311 uparam.uncertThresh = data.invstd.uncertThresh; +0312 uparam.uncertChi2 = data.invstd.uncertChi2; +0313 uparam.uncertN = data.invstd.uncertN; +0314 uparam.uncertMax = data.invstd.uncertMax; +0315 invstd = estimateUncertainty(data.invstd.invtype,invstd,iparam,uparam); +0316 end +0317 end +0318 % normalize to 1 +0319 if data.process.norm == 1 +0320 switch data.invstd.invtype +0321 case {'LU','NNLS','MUMO'} +0322 % normalization with sum() -> sum(f)=1 +0323 % this is the standard way of doing it +0324 invstd.T1T2f = invstd.T1T2f./sum(invstd.T1T2f); +0325 % alternatives depending on the objective: +0326 % 1.) normalization with sum(f*dt) -> area~=1 +0327 % dt = log10(invstd.T1T2me(2))-log10(invstd.T1T2me(1)); +0328 % invstd.T1T2f = invstd.T1T2f/sum(invstd.T1T2f*dt); +0329 % 2.) normalization with trapz() -> area=1 but sum(f)>1 +0330 % invstd.T1T2f = invstd.T1T2f./trapz(invstd.T1T2me,invstd.T1T2f); +0331 otherwise +0332 % nothing to do +0333 end +0334 end +0335 runtime = toc; +0336 displayStatusText(gui,[infostring,'done | inversion took ',... +0337 sprintf('%5.2f',runtime),' s']); +0338 % save inversion results +0339 data.results.invstd = invstd; +0340 setappdata(fig,'data',data); +0341 end +0342 % as long as the "UserData" is 1 the STOP button was not pressed and +0343 % the inversion result gets saved in "INVdata" +0344 if get(gui.push_handles.invstd_run,'UserData') == 1 && ... +0345 ( isfield(data.results,'invstd') || ... +0346 isfield(data.results,'lcurve') ) +0347 INVdata{id} = []; +0348 INVdata{id} = data; +0349 INVdata{id} = rmfield(INVdata{id},'import'); +0350 INVdata{id} = rmfield(INVdata{id},'info'); +0351 INVdata{id} = rmfield(INVdata{id},'calib'); +0352 INVdata{id} = rmfield(INVdata{id},'pressure'); +0353 +0354 % color the list entries that already have an inversion result +0355 strL = get(gui.listbox_handles.signal,'String'); +0356 str1 = strL{id}; +0357 str2 = ['<HTML><BODY bgcolor="rgb(',... +0358 sprintf('%d,%d,%d',gui.myui.colors.listINV.*255),')">',str1,'</BODY></HTML>']; +0359 strL{id} = str2; +0360 set(gui.listbox_handles.signal,'String',strL); +0361 +0362 % update GUI INVdata +0363 setappdata(fig,'INVdata',INVdata); +0364 +0365 % --- +0366 % special treatment for LIAG processing +0367 if isfield(data.import,'LIAG') && ~strcmp(data.invstd.regtype,'lcurve') +0368 if ~isempty(strfind(str1,' - calibration')) +0369 % save Tbulk from the calibration sample +0370 btn1 = 'Yes keep it'; +0371 btn2 = 'No, reset to 1e6 [s]'; +0372 if data.results.invstd.T2 < 0.2 +0373 answer = questdlg(['Tbulk seems very short with ',... +0374 sprintf('%4.3f',data.results.invstd.T2),' [s] ',... +0375 'Do you want to keep it?'], ... +0376 'Keep relaxation time as Tbulk', ... +0377 btn1,btn2,btn2); +0378 % Handle response +0379 switch answer +0380 case btn1 +0381 data.import.LIAG.Tbulk = data.results.invstd.T2; +0382 case btn2 +0383 data.import.LIAG.Tbulk = 1e6; +0384 end +0385 else +0386 data.import.LIAG.Tbulk = data.results.invstd.T2; +0387 end +0388 % update GUI data +0389 setappdata(fig,'data',data); +0390 % save calibration data +0391 useSignalAsCalibration(id); +0392 else +0393 calibratePorosity; +0394 end +0395 end +0396 % --- +0397 else % STOP was pressed (because "UserData" is 0) +0398 % reset "UserData" +0399 set(gui.push_handles.invstd_run,'UserData',1); +0400 end +0401 % update plots and INFO fields +0402 updatePlotsSignal; +0403 updatePlotsDistribution; +0404 updateInfo(gui.plots.SignalPanel); +0405 % set focus on results +0406 set(gui.plots.SignalPanel,'Selection',1); +0407 set(gui.plots.DistPanel,'Selection',1); +0408 % open INFO field +0409 set(gui.push_handles.info,'String','<'); +0410 onPushShowHide(gui.push_handles.info); +0411 if ~isempty(findobj('Tag','FITSTATS')) +0412 showFitStatistics; +0413 end +0414 NUCLEUSinv_updateInterface; +0415 else +0416 helpdlg('Cannot start inversion because no data set is selected!',... +0417 'Select NMR data first.'); +0418 end +0419 +0420 %% at the end, no matter what reset the RUN button +0421 set(gui.push_handles.invstd_run,'String','<HTML><u>R</u>UN',... +0422 'BackgroundColor','g','Enable','on','Callback',@onPushRun); +0423 setappdata(fig,'gui',gui); +0424 +0425 end +0426 +0427 %------------- END OF CODE -------------- +0428 +0429 %% License: +0430 % MIT License +0431 % +0432 % Copyright (c) 2018 Thomas Hiller +0433 % +0434 % Permission is hereby granted, free of charge, to any person obtaining a copy +0435 % of this software and associated documentation files (the "Software"), to deal +0436 % in the Software without restriction, including without limitation the rights +0437 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0438 % copies of the Software, and to permit persons to whom the Software is +0439 % furnished to do so, subject to the following conditions: +0440 % +0441 % The above copyright notice and this permission notice shall be included in all +0442 % copies or substantial portions of the Software. +0443 % +0444 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0445 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0446 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0447 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0448 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0449 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0450 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/showExtraGraphics.html b/doc/nucleus/functions/interface/showExtraGraphics.html index b8132cc..32acf1f 100644 --- a/doc/nucleus/functions/interface/showExtraGraphics.html +++ b/doc/nucleus/functions/interface/showExtraGraphics.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/showFitStatistics.html b/doc/nucleus/functions/interface/showFitStatistics.html index 17ae4bd..58d5581 100644 --- a/doc/nucleus/functions/interface/showFitStatistics.html +++ b/doc/nucleus/functions/interface/showFitStatistics.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/showParameterInfo.html b/doc/nucleus/functions/interface/showParameterInfo.html index ff04b03..12d4cf5 100644 --- a/doc/nucleus/functions/interface/showParameterInfo.html +++ b/doc/nucleus/functions/interface/showParameterInfo.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/switchToolTips.html b/doc/nucleus/functions/interface/switchToolTips.html index b4ebbc9..1254a45 100644 --- a/doc/nucleus/functions/interface/switchToolTips.html +++ b/doc/nucleus/functions/interface/switchToolTips.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv, NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/uncheckImportMenus.html b/doc/nucleus/functions/interface/uncheckImportMenus.html index a239d5f..fba64b6 100644 --- a/doc/nucleus/functions/interface/uncheckImportMenus.html +++ b/doc/nucleus/functions/interface/uncheckImportMenus.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- @@ -114,46 +114,49 @@

    SOURCE CODE ^'Checked','off'); 0046 set(gui.menu.file_import_lab_liag_project,'Checked','off'); 0047 set(gui.menu.file_import_lab_bgr_std,'Checked','off'); -0048 set(gui.menu.file_import_lab_bgr_org,'Checked','off'); -0049 set(gui.menu.file_import_lab_bgr_mat,'Checked','off'); -0050 set(gui.menu.file_import_lab_bam_tom,'Checked','off'); -0051 set(gui.menu.file_import_lab_ascii_T1,'Checked','off'); -0052 set(gui.menu.file_import_lab_ascii_T2,'Checked','off'); -0053 set(gui.menu.file_import_lab_excel_T1,'Checked','off'); -0054 set(gui.menu.file_import_lab_excel_T2,'Checked','off'); -0055 set(gui.menu.file_import_nmrinv_file,'Checked','off'); -0056 set(gui.menu.file_import_nmrmod_file,'Checked','off'); -0057 set(gui.menu.file_import_nmrmod_gui,'Checked','off'); -0058 -0059 % update GUI data -0060 setappdata(fig,'gui',gui); +0048 set(gui.menu.file_import_lab_bgr_mat,'Checked','off'); +0049 set(gui.menu.file_import_lab_bgr_mouse_cpmg,'Checked','off'); +0050 set(gui.menu.file_import_lab_bgr_mouse_liftsingle,'Checked','off'); +0051 set(gui.menu.file_import_lab_bgr_mouse_liftall,'Checked','off'); +0052 set(gui.menu.file_import_lab_bgr_helios,'Checked','off'); +0053 set(gui.menu.file_import_lab_bam_tom,'Checked','off'); +0054 set(gui.menu.file_import_lab_ascii_T1,'Checked','off'); +0055 set(gui.menu.file_import_lab_ascii_T2,'Checked','off'); +0056 set(gui.menu.file_import_lab_excel_T1,'Checked','off'); +0057 set(gui.menu.file_import_lab_excel_T2,'Checked','off'); +0058 set(gui.menu.file_import_nmrinv_file,'Checked','off'); +0059 set(gui.menu.file_import_nmrmod_file,'Checked','off'); +0060 set(gui.menu.file_import_nmrmod_gui,'Checked','off'); 0061 -0062 end -0063 -0064 %------------- END OF CODE -------------- -0065 -0066 %% License: -0067 % MIT License -0068 % -0069 % Copyright (c) 2018 Thomas Hiller -0070 % -0071 % Permission is hereby granted, free of charge, to any person obtaining a copy -0072 % of this software and associated documentation files (the "Software"), to deal -0073 % in the Software without restriction, including without limitation the rights -0074 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0075 % copies of the Software, and to permit persons to whom the Software is -0076 % furnished to do so, subject to the following conditions: -0077 % -0078 % The above copyright notice and this permission notice shall be included in all -0079 % copies or substantial portions of the Software. +0062 % update GUI data +0063 setappdata(fig,'gui',gui); +0064 +0065 end +0066 +0067 %------------- END OF CODE -------------- +0068 +0069 %% License: +0070 % MIT License +0071 % +0072 % Copyright (c) 2018 Thomas Hiller +0073 % +0074 % Permission is hereby granted, free of charge, to any person obtaining a copy +0075 % of this software and associated documentation files (the "Software"), to deal +0076 % in the Software without restriction, including without limitation the rights +0077 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0078 % copies of the Software, and to permit persons to whom the Software is +0079 % furnished to do so, subject to the following conditions: 0080 % -0081 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0082 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0083 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0084 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0085 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0086 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0087 % SOFTWARE. +0081 % The above copyright notice and this permission notice shall be included in all +0082 % copies or substantial portions of the Software. +0083 % +0084 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0085 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0086 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0087 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0088 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0089 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0090 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/updateCPSTable.html b/doc/nucleus/functions/interface/updateCPSTable.html index a0f8588..e47603f 100644 --- a/doc/nucleus/functions/interface/updateCPSTable.html +++ b/doc/nucleus/functions/interface/updateCPSTable.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updateCPSTableSize.html b/doc/nucleus/functions/interface/updateCPSTableSize.html index 3d0ac0f..3984bfd 100644 --- a/doc/nucleus/functions/interface/updateCPSTableSize.html +++ b/doc/nucleus/functions/interface/updateCPSTableSize.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updateInfo.html b/doc/nucleus/functions/interface/updateInfo.html index 5c518a2..ee3eb50 100644 --- a/doc/nucleus/functions/interface/updateInfo.html +++ b/doc/nucleus/functions/interface/updateInfo.html @@ -50,8 +50,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • NUCLEUSinv_createPanelPlots creates graphics panel
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onListboxData handles the calls from the context menu of the data
  • onMenuJointInversion handles the call from the menu that activates / deactivates
  • onRadioGates selects the re-sampling / gating method ("log", "lin" or "none")
  • onRadioNormalize selects whether to normalize a NMR signal to 1
  • importINV2INV imports a previously saved NUCLEUSinv session
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • +
  • NUCLEUSinv_createPanelPlots creates graphics panel
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onListboxData handles the calls from the context menu of the data
  • onMenuJointInversion handles the call from the menu that activates / deactivates
  • onRadioGates selects the re-sampling / gating method ("log", "lin" or "none")
  • onRadioNormalize selects whether to normalize a NMR signal to 1
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- @@ -134,11 +134,11 @@

    SOURCE CODE ^case 'T2' 0066 switch data.process.gatetype 0067 case 'raw' -0068 info{end+1,1} = ['<HTML><BODY>Echos &nbsp= ',... +0068 info{end+1,1} = ['<HTML><BODY>Echos&nbsp= ',... 0069 sprintf('%d',length(nmrproc.t)),... 0070 ' (',data.process.gatetype,')','</BODY></HTML>']; 0071 otherwise -0072 info{end+1,1} = ['<HTML><BODY>Gates &nbsp= ',... +0072 info{end+1,1} = ['<HTML><BODY>Gates&nbsp= ',... 0073 sprintf('%d',length(nmrproc.t)),... 0074 ' (',data.process.gatetype,')','</BODY></HTML>']; 0075 end @@ -147,12 +147,12 @@

    SOURCE CODE ^switch data.process.norm 0079 case 0 0080 if max(nmrproc.s) < 1e-3 -0081 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... -0082 sprintf('%5.4e',max(nmrproc.s)),... +0081 info{end+1,1} = ['<HTML><BODY>A_max&nbsp= ',... +0082 sprintf('%5.3e',max(nmrproc.s)),... 0083 '</BODY></HTML>']; 0084 else -0085 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... -0086 sprintf('%5.4f',max(nmrproc.s)),... +0085 info{end+1,1} = ['<HTML><BODY>A_max&nbsp= ',... +0086 sprintf('%5.3f',max(nmrproc.s)),... 0087 '</BODY></HTML>']; 0088 end 0089 case 1 @@ -162,12 +162,12 @@

    SOURCE CODE ^'</BODY></HTML>']; 0094 else 0095 info{end+1,1} = ['<HTML><BODY>Normfac = ',... -0096 sprintf('%5.4f',max(data.process.normfac)),... +0096 sprintf('%5.3f',max(data.process.normfac)),... 0097 '</BODY></HTML>']; 0098 end 0099 0100 end -0101 info{end+1,1} = ['<HTML><BODY>noise &nbsp= ',sprintf('%4.3e',nmrproc.noise),'</BODY></HTML>']; +0101 info{end+1,1} = ['<HTML><BODY>noise&nbsp= ',sprintf('%4.2e',nmrproc.noise),'</BODY></HTML>']; 0102 info{end+1,1} = ' '; 0103 0104 % possible inversion results/statistics @@ -181,12 +181,12 @@

    SOURCE CODE ^switch nmrproc.T1T2 0113 case 'T1' 0114 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... -0115 sprintf('%4.3e',sum(invstd.E0)),... -0116 ' &#8723 (',sprintf('%4.3e',ciE0),')','</BODY></HTML>']; +0115 sprintf('%4.2e',sum(invstd.E0)),... +0116 ' &#8723 (',sprintf('%4.2e',ciE0),')','</BODY></HTML>']; 0117 case 'T2' 0118 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... -0119 sprintf('%4.3e',sum(invstd.E0)),... -0120 ' &#8723 (',sprintf('%4.3e',ciE0),')','</BODY></HTML>']; +0119 sprintf('%4.2e',sum(invstd.E0)),... +0120 ' &#8723 (',sprintf('%4.2e',ciE0),')','</BODY></HTML>']; 0121 end 0122 info{end+1,1} = ' '; 0123 if isfield(invstd,'chi2') @@ -195,339 +195,422 @@

    SOURCE CODE ^'<HTML><BODY>',str,'</BODY></HTML>']; 0127 end 0128 end -0129 str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; +0129 str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; 0130 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; -0131 if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 -0132 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; -0133 end -0134 -0135 case 'free' -0136 ciE0s = sum(full(invstd.ci(1:2:end))); -0137 E0 = invstd.E0; -0138 switch nmrproc.T1T2 -0139 case 'T1' -0140 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... -0141 sprintf('%4.3e',sum(E0)),... -0142 ' &#8723 (',sprintf('%4.3e',ciE0s),')','</BODY></HTML>']; -0143 case 'T2' -0144 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... -0145 sprintf('%4.3e',sum(E0)),... -0146 ' &#8723 (',sprintf('%4.3e',ciE0s),')','</BODY></HTML>']; -0147 end -0148 info{end+1,1} = ' '; -0149 if isfield(invstd,'chi2') -0150 if ~isnan(invstd.chi2) -0151 str = ['&Chi<sup><font size="',num2str(subfs),'">2</sup> = ',sprintf('%4.2f',invstd.chi2)]; -0152 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; -0153 end -0154 end -0155 str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; -0156 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; -0157 -0158 if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 -0159 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; -0160 end -0161 -0162 case {'LU','NNLS'} -0163 switch nmrproc.T1T2 -0164 case 'T1' -0165 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... -0166 sprintf('%4.3e',sum(invstd.E0)),'</BODY></HTML>']; -0167 case 'T2' -0168 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... -0169 sprintf('%4.3e',sum(invstd.E0)),'</BODY></HTML>']; -0170 end -0171 info{end+1,1} = ' '; -0172 if isfield(invstd,'chi2') -0173 if ~isnan(invstd.chi2) -0174 str = ['&Chi<sup><font size="',num2str(subfs),'">2</sup> = ',sprintf('%4.2f',invstd.chi2)]; -0175 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; -0176 end -0177 end -0178 str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; -0179 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; -0180 -0181 if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 -0182 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; -0183 end -0184 -0185 info{end+1,1} = ['<HTML><BODY>&lambda = ',sprintf('%6.5f',invstd.lambda_out),'</BODY></HTML>']; -0186 info{end+1,1} = ' '; -0187 end -0188 end -0189 end -0190 -0191 case 2 % RAW -0192 if isfield(data,'results') -0193 % add filename & date information -0194 id = get(gui.listbox_handles.signal,'Value'); -0195 info{end+1,1} = data.import.NMR.filesShort{id}; -0196 info{end+1,1} = data.import.NMR.data{id}.date; -0197 info{end+1,1} = ' '; -0198 info{end+1,1} = ['<HTML><BODY>t_min &nbsp= ',... -0199 sprintf('%4.3e',data.results.nmrraw.t(1)),'</BODY></HTML>']; -0200 info{end+1,1} = ['<HTML><BODY>t_max &nbsp= ',... -0201 sprintf('%4.3e',data.results.nmrraw.t(end)),'</BODY></HTML>']; -0202 switch data.results.nmrproc.T1T2 -0203 case 'T1' -0204 info{end+1,1} = ' '; -0205 info{end+1,1} = ['<HTML><BODY>Sampl. = ',... -0206 sprintf('%d',length(data.results.nmrproc.t)),... -0207 '</BODY></HTML>']; -0208 case 'T2' -0209 info{end+1,1} = ['<HTML><BODY>t_echo = ',... -0210 sprintf('%4.3e',data.results.nmrproc.echotime),'</BODY></HTML>']; -0211 info{end+1,1} = ['<HTML><BODY>t_dead = ',... -0212 sprintf('%4.3e',data.results.nmrproc.dead),'</BODY></HTML>']; -0213 info{end+1,1} = ' '; -0214 info{end+1,1} = ['<HTML><BODY>Echos &nbsp= ',... -0215 sprintf('%d',length(data.results.nmrraw.t)),... -0216 '</BODY></HTML>']; -0217 end -0218 if max(data.results.nmrproc.s) < 1e-3 -0219 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... -0220 sprintf('%4.3e',max(data.results.nmrproc.s)),... -0221 '</BODY></HTML>']; -0222 else -0223 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... -0224 sprintf('%5.4f',max(data.results.nmrproc.s)),... -0225 '</BODY></HTML>']; -0226 end +0131 % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +0132 if nmrproc.noise ~= 0 +0133 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; +0134 end +0135 +0136 case 'free' +0137 ciE0s = sum(full(invstd.ci(1:2:end))); +0138 E0 = invstd.E0; +0139 switch nmrproc.T1T2 +0140 case 'T1' +0141 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... +0142 sprintf('%4.2e',sum(E0)),... +0143 ' &#8723 (',sprintf('%4.2e',ciE0s),')','</BODY></HTML>']; +0144 case 'T2' +0145 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... +0146 sprintf('%4.2e',sum(E0)),... +0147 ' &#8723 (',sprintf('%4.2e',ciE0s),')','</BODY></HTML>']; +0148 end +0149 info{end+1,1} = ' '; +0150 if isfield(invstd,'chi2') +0151 if ~isnan(invstd.chi2) +0152 str = ['&Chi<sup><font size="',num2str(subfs),'">2</sup> = ',sprintf('%4.2f',invstd.chi2)]; +0153 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0154 end +0155 end +0156 str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; +0157 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0158 +0159 % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +0160 if nmrproc.noise ~= 0 +0161 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; +0162 end +0163 +0164 case {'LU','NNLS'} +0165 switch nmrproc.T1T2 +0166 case 'T1' +0167 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... +0168 sprintf('%4.2e',sum(invstd.E0)),'</BODY></HTML>']; +0169 case 'T2' +0170 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... +0171 sprintf('%4.2e',sum(invstd.E0)),'</BODY></HTML>']; +0172 end +0173 info{end+1,1} = ' '; +0174 if isfield(invstd,'chi2') +0175 if ~isnan(invstd.chi2) +0176 str = ['&Chi<sup><font size="',num2str(subfs),'">2</sup> = ',sprintf('%4.2f',invstd.chi2)]; +0177 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0178 end +0179 end +0180 str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; +0181 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0182 +0183 % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +0184 if nmrproc.noise ~= 0 +0185 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; +0186 end +0187 +0188 info{end+1,1} = ['<HTML><BODY>&lambda = ',sprintf('%6.5f',invstd.lambda_out),'</BODY></HTML>']; +0189 info{end+1,1} = ' '; +0190 case {'MUMO'} +0191 switch nmrproc.T1T2 +0192 case 'T1' +0193 info{end+1,1} = ['<HTML><BODY>E<sub>&infin</sub> = ',... +0194 sprintf('%4.2e',sum(invstd.E0)),... +0195 ' &#8723 (',sprintf('%4.2e',invstd.ciE0),')','</BODY></HTML>']; +0196 case 'T2' +0197 info{end+1,1} = ['<HTML><BODY>E<sub><font size="',num2str(subfs),'">0</sub> = ',... +0198 sprintf('%4.2e',sum(invstd.E0)),... +0199 ' &#8723 (',sprintf('%4.2e',invstd.ciE0),')','</BODY></HTML>']; +0200 end +0201 info{end+1,1} = ' '; +0202 +0203 if isfield(invstd,'chi2') +0204 if ~isnan(invstd.chi2) +0205 str = ['&Chi<sup><font size="',num2str(subfs),'">2</sup> = ',sprintf('%4.2f',invstd.chi2)]; +0206 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0207 end +0208 end +0209 str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; +0210 info{end+1,1} = ['<HTML><BODY>',str,'</BODY></HTML>']; +0211 +0212 % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +0213 if nmrproc.noise ~= 0 +0214 info{end+1,1} = ['<HTML><BODY>S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'</BODY></HTML>']; +0215 end +0216 info{end+1,1} = ' '; +0217 end +0218 end +0219 end +0220 +0221 case 2 % RAW +0222 if isfield(data,'results') +0223 % add filename & date information +0224 id = get(gui.listbox_handles.signal,'Value'); +0225 info{end+1,1} = data.import.NMR.filesShort{id}; +0226 info{end+1,1} = data.import.NMR.data{id}.date; 0227 info{end+1,1} = ' '; -0228 -0229 if isfield(data.import,'BAM') -0230 a = data.import.NMR.data{id}.phase_bam; -0231 info{end+1,1} = ['<HTML><BODY>phase TOM&nbsp= ',... -0232 sprintf('%5.2f',rad2deg(a)),... -0233 '°</BODY></HTML>']; -0234 end -0235 info{end+1,1} = ['<HTML><BODY>phase fit&nbsp= ',... -0236 sprintf('%5.2f',rad2deg(data.results.nmrraw.phase)),... -0237 '°</BODY></HTML>']; -0238 info{end+1,1} = ' '; -0239 end -0240 -0241 case 3 % ALL -0242 if isfield(data,'results') -0243 if isfield(data.results,'invjoint') -0244 invjoint = data.results.invjoint; -0245 nmr = invjoint.idata.nmr; -0246 levels = invjoint.levels; -0247 -0248 % global fit error -0249 info{end+1,1} = ['ErrNorm: ',sprintf('%4.3e',invjoint.errnorm)]; -0250 info{end+1,1} = ['RMS: ',sprintf('%4.3e',invjoint.rms)]; -0251 info{end+1,1} = ['<HTML><BODY>&Chi<sup><font size="',num2str(subfs),'">2</sup>: ',... -0252 sprintf('%5.4f',invjoint.chi2),'</BODY></HTML>']; -0253 info{end+1,1} = ' '; -0254 info{end+1,1} = '-----'; -0255 info{end+1,1} = ' '; -0256 -0257 for i = 1:numel(levels) -0258 info{end+1,1} = nmr{levels(i)}.name; -0259 info{end+1,1} = ['RMS: ',sprintf('%4.3e',nmr{levels(i)}.rms)]; -0260 info{end+1,1} = ['<HTML><BODY>&Chi<sup><font size="',num2str(subfs),'">2</sup>: ',... -0261 sprintf('%5.4f',nmr{levels(i)}.chi2),'</BODY></HTML>']; -0262 info{end+1,1} = ' '; -0263 end +0228 info{end+1,1} = ['<HTML><BODY>t_min &nbsp= ',... +0229 sprintf('%4.3e',data.results.nmrraw.t(1)),'</BODY></HTML>']; +0230 info{end+1,1} = ['<HTML><BODY>t_max &nbsp= ',... +0231 sprintf('%4.3e',data.results.nmrraw.t(end)),'</BODY></HTML>']; +0232 switch data.results.nmrproc.T1T2 +0233 case 'T1' +0234 info{end+1,1} = ' '; +0235 info{end+1,1} = ['<HTML><BODY>Sampl. = ',... +0236 sprintf('%d',length(data.results.nmrproc.t)),... +0237 '</BODY></HTML>']; +0238 case 'T2' +0239 info{end+1,1} = ['<HTML><BODY>t_echo = ',... +0240 sprintf('%4.3e',data.results.nmrproc.echotime),'</BODY></HTML>']; +0241 info{end+1,1} = ['<HTML><BODY>t_dead = ',... +0242 sprintf('%4.3e',data.results.nmrproc.dead),'</BODY></HTML>']; +0243 info{end+1,1} = ' '; +0244 info{end+1,1} = ['<HTML><BODY>Echos &nbsp= ',... +0245 sprintf('%d',length(data.results.nmrraw.t)),... +0246 '</BODY></HTML>']; +0247 end +0248 if max(data.results.nmrproc.s) < 1e-3 +0249 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... +0250 sprintf('%4.3e',max(data.results.nmrproc.s)),... +0251 '</BODY></HTML>']; +0252 else +0253 info{end+1,1} = ['<HTML><BODY>A_max &nbsp= ',... +0254 sprintf('%5.4f',max(data.results.nmrproc.s)),... +0255 '</BODY></HTML>']; +0256 end +0257 info{end+1,1} = ' '; +0258 +0259 if isfield(data.import,'BAM') +0260 a = data.import.NMR.data{id}.phase_bam; +0261 info{end+1,1} = ['<HTML><BODY>phase TOM&nbsp= ',... +0262 sprintf('%5.2f',rad2deg(a)),... +0263 '°</BODY></HTML>']; 0264 end -0265 end -0266 end -0267 % and update the text box -0268 set(gui.listbox_handles.info_signal,'String',info,'Value',[],'Max',2,'Min',0); -0269 -0270 %% RTD panel info -0271 clear info; -0272 info{1,1} = ' '; -0273 switch whichdist -0274 case 1 % RTD -0275 if isfield(data,'results') -0276 if isfield(data.results,'invstd') -0277 nmrproc = data.results.nmrproc; -0278 invstd = data.results.invstd; -0279 invtype = data.invstd.invtype; -0280 -0281 switch invtype -0282 case 'mono' -0283 info{end+1,1} = invtype; -0284 info{end+1,1} = ' '; -0285 ciT = sum(full(invstd.ci(2:2:end))); -0286 -0287 switch nmrproc.T1T2 -0288 case 'T1' -0289 info{end+1,1} = ['<HTML><BODY>T<sub><font size="',num2str(subfs),'">1</sub> = ',... -0290 sprintf('%5.4f',invstd.T1),... -0291 ' &#8723 (',sprintf('%5.4f',ciT),')','</BODY></HTML>']; -0292 case 'T2' -0293 info{end+1,1} = ['<HTML><BODY>T<sub><font size="',num2str(subfs),'">2</sub> = ',... -0294 sprintf('%5.4f',invstd.T2),... -0295 ' &#8723 (',sprintf('%5.4f',ciT),')','</BODY></HTML>']; -0296 end -0297 info{end+1,1} = ' '; -0298 -0299 case 'free' -0300 str = [invtype,' ',num2str(data.invstd.freeDT)]; -0301 info{end+1,1} = str; -0302 info{end+1,1} = ' '; -0303 -0304 E0 = invstd.E0; -0305 ciE0 = full(invstd.ci(1:2:end)); -0306 ciT = full(invstd.ci(2:2:end)); -0307 -0308 switch nmrproc.T1T2 -0309 case 'T1' -0310 T = invstd.T1; -0311 case 'T2' -0312 T = invstd.T2; -0313 end -0314 -0315 for i = 1:length(T) -0316 info{end+1,1} = ['<HTML><BODY>T(',num2str(i),') = ',sprintf('%5.4f',T(i)),... -0317 ' &#8723 (',sprintf('%5.4f',ciT(i)),')','</BODY></HTML>']; %#ok<*AGROW> -0318 info{end+1,1} = ['<HTML><BODY>E(',num2str(i),') = ',sprintf('%5.4f',E0(i)),... -0319 ' &#8723 (',sprintf('%5.4f',ciE0(i)),')','</BODY></HTML>']; -0320 end -0321 info{end+1,1} = ' '; -0322 -0323 case {'LU','NNLS'} -0324 % info is a cell array -0325 str = [invtype,' ',data.invstd.regtype,' ',num2str(data.invstd.Lorder)]; -0326 info{end+1,1} = str; -0327 info{end+1,1} = ' '; -0328 -0329 % TLGM -0330 info{end+1,1} = ['<HTML><BODY>TLGM</sub> = ',sprintf('%5.4f',invstd.Tlgm),'</BODY></HTML>']; -0331 info{end+1,1} = ' '; -0332 -0333 % clay-bound water CBW < tcut ms -0334 % irreducible water / capillary water BVI ccut - tcut ms -0335 % movable water BVM > tcut ms -0336 switch data.process.timescale -0337 case 's' -0338 ccut = data.param.CBWcutoff/1000; -0339 tcut = data.param.BVIcutoff/1000; -0340 case 'ms' -0341 ccut = data.param.CBWcutoff; -0342 tcut = data.param.BVIcutoff; -0343 end -0344 por = data.invstd.porosity; -0345 CBW = abs(sum(invstd.T1T2f(invstd.T1T2me<=ccut))/sum(invstd.T1T2f)); -0346 BVI = abs(sum(invstd.T1T2f(invstd.T1T2me>ccut & invstd.T1T2me<=tcut))/sum(invstd.T1T2f)); -0347 BVM = abs(sum(invstd.T1T2f(invstd.T1T2me>tcut))/sum(invstd.T1T2f)); -0348 info{end+1,1} = ['CBW(',sprintf('%2d',data.param.CBWcutoff),... -0349 ') = ',sprintf('%5.2f',por*CBW*100),' [vol. %]']; -0350 -0351 info{end+1,1} = ['BVI(',sprintf('%2d',data.param.BVIcutoff),... -0352 ') = ',sprintf('%5.2f',por*BVI*100),' [vol. %]']; -0353 info{end+1,1} = ['BVM = ',sprintf('%5.2f',por*BVM*100),' [vol. %]']; +0265 if isfield(data.results.nmrraw,'phase') +0266 info{end+1,1} = ['<HTML><BODY>phase fit&nbsp= ',... +0267 sprintf('%5.2f',rad2deg(data.results.nmrraw.phase)),... +0268 '°</BODY></HTML>']; +0269 end +0270 info{end+1,1} = ' '; +0271 end +0272 +0273 case 3 % ALL +0274 if isfield(data,'results') +0275 if isfield(data.results,'invjoint') +0276 invjoint = data.results.invjoint; +0277 nmr = invjoint.idata.nmr; +0278 levels = invjoint.levels; +0279 +0280 % global fit error +0281 info{end+1,1} = ['ErrNorm: ',sprintf('%4.3e',invjoint.errnorm)]; +0282 info{end+1,1} = ['RMS: ',sprintf('%4.3e',invjoint.rms)]; +0283 info{end+1,1} = ['<HTML><BODY>&Chi<sup><font size="',num2str(subfs),'">2</sup>: ',... +0284 sprintf('%5.4f',invjoint.chi2),'</BODY></HTML>']; +0285 info{end+1,1} = ' '; +0286 info{end+1,1} = '-----'; +0287 info{end+1,1} = ' '; +0288 +0289 for i = 1:numel(levels) +0290 info{end+1,1} = nmr{levels(i)}.name; +0291 info{end+1,1} = ['RMS: ',sprintf('%4.3e',nmr{levels(i)}.rms)]; +0292 info{end+1,1} = ['<HTML><BODY>&Chi<sup><font size="',num2str(subfs),'">2</sup>: ',... +0293 sprintf('%5.4f',nmr{levels(i)}.chi2),'</BODY></HTML>']; +0294 info{end+1,1} = ' '; +0295 end +0296 end +0297 end +0298 end +0299 % and update the text box +0300 set(gui.listbox_handles.info_signal,'String',info,'Value',[],'Max',2,'Min',0); +0301 +0302 %% RTD panel info +0303 clear info; +0304 info{1,1} = ' '; +0305 switch whichdist +0306 case 1 % RTD +0307 if isfield(data,'results') +0308 if isfield(data.results,'invstd') +0309 nmrproc = data.results.nmrproc; +0310 invstd = data.results.invstd; +0311 invtype = data.invstd.invtype; +0312 +0313 switch invtype +0314 case 'mono' +0315 info{end+1,1} = invtype; +0316 info{end+1,1} = ' '; +0317 ciT = sum(full(invstd.ci(2:2:end))); +0318 +0319 switch nmrproc.T1T2 +0320 case 'T1' +0321 info{end+1,1} = ['<HTML><BODY>T<sub><font size="',num2str(subfs),'">1</sub> = ',... +0322 sprintf('%5.4f',invstd.T1),... +0323 ' &#8723 (',sprintf('%5.4f',ciT),')','</BODY></HTML>']; +0324 case 'T2' +0325 info{end+1,1} = ['<HTML><BODY>T<sub><font size="',num2str(subfs),'">2</sub> = ',... +0326 sprintf('%5.4f',invstd.T2),... +0327 ' &#8723 (',sprintf('%5.4f',ciT),')','</BODY></HTML>']; +0328 end +0329 info{end+1,1} = ' '; +0330 +0331 case 'free' +0332 str = [invtype,' ',num2str(data.invstd.freeDT)]; +0333 info{end+1,1} = str; +0334 info{end+1,1} = ' '; +0335 +0336 E0 = invstd.E0; +0337 ciE0 = full(invstd.ci(1:2:end)); +0338 ciT = full(invstd.ci(2:2:end)); +0339 +0340 switch nmrproc.T1T2 +0341 case 'T1' +0342 T = invstd.T1; +0343 case 'T2' +0344 T = invstd.T2; +0345 end +0346 +0347 for i = 1:length(T) +0348 info{end+1,1} = ['<HTML><BODY>T(',num2str(i),') = ',sprintf('%5.4f',T(i)),... +0349 ' &#8723 (',sprintf('%5.4f',ciT(i)),')','</BODY></HTML>']; %#ok<*AGROW> +0350 info{end+1,1} = ['<HTML><BODY>E(',num2str(i),') = ',sprintf('%5.4f',E0(i)),... +0351 ' &#8723 (',sprintf('%5.4f',ciE0(i)),')','</BODY></HTML>']; +0352 info{end+1,1} = ' '; +0353 end 0354 -0355 end -0356 end -0357 end -0358 -0359 case 2 % PSD -0360 info{end+1,1} = 'PSD'; -0361 info{end+1,1} = ' '; -0362 -0363 case 3 % PSDJ -0364 if isfield(data,'results') -0365 if isfield(data.results,'invjoint') -0366 invjoint = data.results.invjoint; -0367 -0368 info{end+1,1} = ['Inversion type: ',data.invjoint.invtype]; -0369 info{end+1,1} = ' '; -0370 switch data.invjoint.geometry_type -0371 case 'cyl' -0372 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type]; -0373 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; -0374 case 'ang' -0375 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type,' ',... -0376 num2str(invjoint.iGEOM.angles(1)),' ',... -0377 num2str(invjoint.iGEOM.angles(2)),' ',... -0378 num2str(invjoint.iGEOM.angles(3))]; -0379 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; -0380 case 'poly' -0381 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type,... -0382 ' ',num2str(data.invjoint.polyN),' sides']; -0383 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; -0384 end -0385 info{end+1,1} = ' '; -0386 info{end+1,1} = ['rho (INV): ',sprintf('%5.3f',invjoint.irho*1e6),' [µm/s]']; -0387 if isfield(data.import,'NMRMOD') -0388 info{end+1,1} = ['rho (MOD): ',... -0389 sprintf('%5.3f',data.import.NMRMOD.nmr.rho*1e6),' [µm/s]']; -0390 end -0391 end -0392 end -0393 end -0394 % and update the text box -0395 set(gui.listbox_handles.info_dist,'String',info,'Value',[],'Max',2,'Min',0); -0396 -0397 %% CPS panel info -0398 % check if the CPS panel is maximized -0399 isminimized = get(gui.plots.CPSPanel,'Minimized'); -0400 if ~isminimized -0401 clear info; -0402 info{1,1} = ' '; -0403 if isfield(data,'results') -0404 if isfield(data.results,'invjoint') -0405 invjoint = data.results.invjoint; -0406 nmr = invjoint.idata.nmr; -0407 levels = invjoint.levels; -0408 -0409 for i = 1:numel(levels) -0410 info{end+1,1} = nmr{levels(i)}.name; -0411 info{end+1,1} = ['press. : ',... -0412 sprintf('%5.4f',invjoint.p0(levels(i))*data.pressure.unitfac),... -0413 ' [',data.pressure.unit,']']; -0414 switch invjoint.T1T2 -0415 case 'T1' -0416 info{end+1,1} = ['sat. (INV) : ',... -0417 sprintf('%4.2f',invjoint.idata.nmr{levels(i)}.fit_g(end))]; -0418 case 'T2' -0419 info{end+1,1} = ['sat. (INV) : ',... -0420 sprintf('%4.2f',invjoint.idata.nmr{levels(i)}.fit_g(1))]; -0421 end -0422 info{end+1,1} = ['sat. (MOD) : ',... -0423 sprintf('%4.2f',invjoint.S0(levels(i)))]; -0424 info{end+1,1} = ' '; -0425 end -0426 end -0427 end -0428 set(gui.listbox_handles.info_cps,'String',info,'Value',[],'Max',2,'Min',0); -0429 end -0430 -0431 % update GUI data -0432 setappdata(fig,'data',data); -0433 setappdata(fig,'gui',gui); -0434 end -0435 -0436 end -0437 -0438 %------------- END OF CODE -------------- -0439 -0440 %% License: -0441 % MIT License -0442 % -0443 % Copyright (c) 2018 Thomas Hiller -0444 % -0445 % Permission is hereby granted, free of charge, to any person obtaining a copy -0446 % of this software and associated documentation files (the "Software"), to deal -0447 % in the Software without restriction, including without limitation the rights -0448 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0449 % copies of the Software, and to permit persons to whom the Software is -0450 % furnished to do so, subject to the following conditions: -0451 % -0452 % The above copyright notice and this permission notice shall be included in all -0453 % copies or substantial portions of the Software. -0454 % -0455 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0456 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0457 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0458 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0459 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0460 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0461 % SOFTWARE. +0355 case {'LU','NNLS'} +0356 % info is a cell array +0357 str = [invtype,' ',data.invstd.regtype,' ',num2str(data.invstd.Lorder)]; +0358 info{end+1,1} = str; +0359 info{end+1,1} = ' '; +0360 +0361 % TLGM +0362 info{end+1,1} = ['<HTML><BODY>TLGM</sub> = ',sprintf('%5.4f',invstd.Tlgm),'</BODY></HTML>']; +0363 info{end+1,1} = ' '; +0364 +0365 % clay-bound water CBW < tcut ms +0366 % irreducible water / capillary water BVI ccut - tcut ms +0367 % movable water BVM > tcut ms +0368 switch data.process.timescale +0369 case 's' +0370 ccut = data.param.CBWcutoff/1000; +0371 tcut = data.param.BVIcutoff/1000; +0372 case 'ms' +0373 ccut = data.param.CBWcutoff; +0374 tcut = data.param.BVIcutoff; +0375 end +0376 por = data.invstd.porosity; +0377 CBW = abs(sum(invstd.T1T2f(invstd.T1T2me<=ccut))/sum(invstd.T1T2f)); +0378 BVI = abs(sum(invstd.T1T2f(invstd.T1T2me>ccut & invstd.T1T2me<=tcut))/sum(invstd.T1T2f)); +0379 BVM = abs(sum(invstd.T1T2f(invstd.T1T2me>tcut))/sum(invstd.T1T2f)); +0380 info{end+1,1} = ['CBW(',sprintf('%2d',data.param.CBWcutoff),... +0381 ') = ',sprintf('%5.2f',por*CBW*100),' [vol. %]']; +0382 +0383 info{end+1,1} = ['BVI(',sprintf('%2d',data.param.BVIcutoff),... +0384 ') = ',sprintf('%5.2f',por*BVI*100),' [vol. %]']; +0385 info{end+1,1} = ['BVM = ',sprintf('%5.2f',por*BVM*100),' [vol. %]']; +0386 +0387 case {'MUMO'} +0388 +0389 % info is a cell array +0390 str = [invtype,' ',num2str(data.invstd.freeDT)]; +0391 info{end+1,1} = str; +0392 info{end+1,1} = ' '; +0393 +0394 % TLGM +0395 info{end+1,1} = ['<HTML><BODY>TLGM</sub> = ',sprintf('%5.4f',invstd.Tlgm),'</BODY></HTML>']; +0396 info{end+1,1} = ' '; +0397 +0398 % clay-bound water CBW < tcut ms +0399 % irreducible water / capillary water BVI ccut - tcut ms +0400 % movable water BVM > tcut ms +0401 switch data.process.timescale +0402 case 's' +0403 ccut = data.param.CBWcutoff/1000; +0404 tcut = data.param.BVIcutoff/1000; +0405 case 'ms' +0406 ccut = data.param.CBWcutoff; +0407 tcut = data.param.BVIcutoff; +0408 end +0409 por = data.invstd.porosity; +0410 CBW = abs(sum(invstd.T1T2f(invstd.T1T2me<=ccut))/sum(invstd.T1T2f)); +0411 BVI = abs(sum(invstd.T1T2f(invstd.T1T2me>ccut & invstd.T1T2me<=tcut))/sum(invstd.T1T2f)); +0412 BVM = abs(sum(invstd.T1T2f(invstd.T1T2me>tcut))/sum(invstd.T1T2f)); +0413 info{end+1,1} = ['CBW(',sprintf('%2d',data.param.CBWcutoff),... +0414 ') = ',sprintf('%5.2f',por*CBW*100),' [vol. %]']; +0415 +0416 info{end+1,1} = ['BVI(',sprintf('%2d',data.param.BVIcutoff),... +0417 ') = ',sprintf('%5.2f',por*BVI*100),' [vol. %]']; +0418 info{end+1,1} = ['BVM = ',sprintf('%5.2f',por*BVM*100),' [vol. %]']; +0419 info{end+1,1} = ' '; +0420 +0421 % values for T, sigma and amplitude +0422 T = invstd.T; ciT = invstd.ci(1:3:end); +0423 S = invstd.S; ciS = invstd.ci(2:3:end); +0424 E = invstd.E; ciE = invstd.ci(3:3:end); +0425 % transform ciT because it is in log scale +0426 tmpT = log(T)-ciT'; +0427 ciT = T - exp(tmpT); +0428 +0429 for i = 1:length(T) +0430 info{end+1,1} = ['<HTML><BODY>T(',num2str(i),') = ',sprintf('%5.4f',T(i)),... +0431 ' &#8723 (',sprintf('%5.4f',ciT(i)),')','</BODY></HTML>']; %#ok<*AGROW> +0432 info{end+1,1} = ['<HTML><BODY>S(',num2str(i),') = ',sprintf('%5.4f',S(i)),... +0433 ' &#8723 (',sprintf('%5.4f',ciS(i)),')','</BODY></HTML>']; +0434 info{end+1,1} = ['<HTML><BODY>E(',num2str(i),') = ',sprintf('%5.4f',E(i)),... +0435 ' &#8723 (',sprintf('%5.4f',ciE(i)),')','</BODY></HTML>']; +0436 info{end+1,1} = ' '; +0437 end +0438 end +0439 end +0440 end +0441 +0442 case 2 % PSD +0443 info{end+1,1} = 'PSD'; +0444 info{end+1,1} = ' '; +0445 +0446 case 3 % PSDJ +0447 if isfield(data,'results') +0448 if isfield(data.results,'invjoint') +0449 invjoint = data.results.invjoint; +0450 +0451 info{end+1,1} = ['Inversion type: ',data.invjoint.invtype]; +0452 info{end+1,1} = ' '; +0453 switch data.invjoint.geometry_type +0454 case 'cyl' +0455 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type]; +0456 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; +0457 case 'ang' +0458 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type,' ',... +0459 num2str(invjoint.iGEOM.angles(1)),' ',... +0460 num2str(invjoint.iGEOM.angles(2)),' ',... +0461 num2str(invjoint.iGEOM.angles(3))]; +0462 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; +0463 case 'poly' +0464 info{end+1,1} = ['Shape: ',data.invjoint.geometry_type,... +0465 ' ',num2str(data.invjoint.polyN),' sides']; +0466 info{end+1,1} = ['Geom.: ',sprintf('%4.2f',invjoint.iGEOM.a)]; +0467 end +0468 info{end+1,1} = ' '; +0469 info{end+1,1} = ['rho (INV): ',sprintf('%5.3f',invjoint.irho*1e6),' [µm/s]']; +0470 if isfield(data.import,'NMRMOD') +0471 info{end+1,1} = ['rho (MOD): ',... +0472 sprintf('%5.3f',data.import.NMRMOD.nmr.rho*1e6),' [µm/s]']; +0473 end +0474 end +0475 end +0476 end +0477 % and update the text box +0478 set(gui.listbox_handles.info_dist,'String',info,'Value',[],'Max',2,'Min',0); +0479 +0480 %% CPS panel info +0481 % check if the CPS panel is maximized +0482 isminimized = get(gui.plots.CPSPanel,'Minimized'); +0483 if ~isminimized +0484 clear info; +0485 info{1,1} = ' '; +0486 if isfield(data,'results') +0487 if isfield(data.results,'invjoint') +0488 invjoint = data.results.invjoint; +0489 nmr = invjoint.idata.nmr; +0490 levels = invjoint.levels; +0491 +0492 for i = 1:numel(levels) +0493 info{end+1,1} = nmr{levels(i)}.name; +0494 info{end+1,1} = ['press. : ',... +0495 sprintf('%5.4f',invjoint.p0(levels(i))*data.pressure.unitfac),... +0496 ' [',data.pressure.unit,']']; +0497 switch invjoint.T1T2 +0498 case 'T1' +0499 info{end+1,1} = ['sat. (INV) : ',... +0500 sprintf('%4.2f',invjoint.idata.nmr{levels(i)}.fit_g(end))]; +0501 case 'T2' +0502 info{end+1,1} = ['sat. (INV) : ',... +0503 sprintf('%4.2f',invjoint.idata.nmr{levels(i)}.fit_g(1))]; +0504 end +0505 info{end+1,1} = ['sat. (MOD) : ',... +0506 sprintf('%4.2f',invjoint.S0(levels(i)))]; +0507 info{end+1,1} = ' '; +0508 end +0509 end +0510 end +0511 set(gui.listbox_handles.info_cps,'String',info,'Value',[],'Max',2,'Min',0); +0512 end +0513 +0514 % update GUI data +0515 setappdata(fig,'data',data); +0516 setappdata(fig,'gui',gui); +0517 end +0518 +0519 end +0520 +0521 %------------- END OF CODE -------------- +0522 +0523 %% License: +0524 % MIT License +0525 % +0526 % Copyright (c) 2018 Thomas Hiller +0527 % +0528 % Permission is hereby granted, free of charge, to any person obtaining a copy +0529 % of this software and associated documentation files (the "Software"), to deal +0530 % in the Software without restriction, including without limitation the rights +0531 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0532 % copies of the Software, and to permit persons to whom the Software is +0533 % furnished to do so, subject to the following conditions: +0534 % +0535 % The above copyright notice and this permission notice shall be included in all +0536 % copies or substantial portions of the Software. +0537 % +0538 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0539 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0540 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0541 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0542 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0543 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0544 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/updateNMRsignals.html b/doc/nucleus/functions/interface/updateNMRsignals.html index d760015..6f26e6f 100644 --- a/doc/nucleus/functions/interface/updateNMRsignals.html +++ b/doc/nucleus/functions/interface/updateNMRsignals.html @@ -43,6 +43,7 @@

    DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • updatePlotsNMR plots the forward modeled NMR data in NUCLEUSmod
  • addNoiseToSignal adds noise with mean 'mu' and standard deviation 'sigma' to
  • This function is called by:
      -
    • onEditValue updates all edit field values, checks for wrong inputs and
    • calculateNMR calculates the NMR signals for the full and partially saturated
    +
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onPopupNMRNoiseType selects the noise type to be aplied to the forward
  • calculateNMR calculates the NMR signals for the full and partially saturated
  • @@ -86,80 +87,108 @@

    SOURCE CODE ^% 0017 % Other m-files required: 0018 % addNoiseToSignal -0019 % -0020 % Subfunctions: -0021 % none -0022 % -0023 % MAT-files required: -0024 % none -0025 % -0026 % See also: NUCLEUSmod -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de -0029 % License: MIT License (at end) -0030 -0031 %------------- BEGIN CODE -------------- -0032 -0033 %% get GUI handle and data -0034 fig = findobj('Tag','MOD'); -0035 data = getappdata(fig,'data'); -0036 -0037 %% only proceed if the noise is larger than 0 -0038 if data.nmr.noise > 0 -0039 % scale noise by porosity -0040 noise = data.nmr.noise/data.nmr.porosity; -0041 % add noise to NMR signals -0042 [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noise); -0043 [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noise); -0044 [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noise); -0045 [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noise); -0046 else -0047 % reset the NMR signals with the raw data (without noise) -0048 data.results.NMR.EiT1 = data.results.NMR.raw.EiT1; -0049 data.results.NMR.EdT1 = data.results.NMR.raw.EdT1; -0050 data.results.NMR.EiT2 = data.results.NMR.raw.EiT2; -0051 data.results.NMR.EdT2 = data.results.NMR.raw.EdT2; -0052 end -0053 -0054 % scale NMR signals by porosity -0055 data.results.NMR.EiT1 = data.nmr.porosity.*data.results.NMR.EiT1; -0056 data.results.NMR.EdT1 = data.nmr.porosity.*data.results.NMR.EdT1; -0057 data.results.NMR.EiT2 = data.nmr.porosity.*data.results.NMR.EiT2; -0058 data.results.NMR.EdT2 = data.nmr.porosity.*data.results.NMR.EdT2; -0059 -0060 % save the noise value -0061 data.results.NMR.noise = data.nmr.noise; -0062 % save the porosity value -0063 data.results.NMR.porosity = data.nmr.porosity; -0064 % update the GUI data -0065 setappdata(fig,'data',data); -0066 -0067 end -0068 -0069 %------------- END OF CODE -------------- -0070 -0071 %% License: -0072 % MIT License -0073 % -0074 % Copyright (c) 2018 Thomas Hiller -0075 % -0076 % Permission is hereby granted, free of charge, to any person obtaining a copy -0077 % of this software and associated documentation files (the "Software"), to deal -0078 % in the Software without restriction, including without limitation the rights -0079 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0080 % copies of the Software, and to permit persons to whom the Software is -0081 % furnished to do so, subject to the following conditions: -0082 % -0083 % The above copyright notice and this permission notice shall be included in all -0084 % copies or substantial portions of the Software. -0085 % -0086 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0087 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0088 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0089 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0090 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0091 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0092 % SOFTWARE. +0019 % updatePlotsNMR +0020 % +0021 % Subfunctions: +0022 % none +0023 % +0024 % MAT-files required: +0025 % none +0026 % +0027 % See also: NUCLEUSmod +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md +0030 % License: MIT License (at end) +0031 +0032 %------------- BEGIN CODE -------------- +0033 +0034 %% get GUI handle and data +0035 fig = findobj('Tag','MOD'); +0036 data = getappdata(fig,'data'); +0037 +0038 %% first check what is the noise type +0039 +0040 switch data.nmr.noisetype +0041 case 'level' +0042 noise = data.nmr.noise; +0043 case 'SNR' +0044 SNR = data.nmr.noise; +0045 noise = 1./SNR; +0046 end +0047 +0048 %% only proceed if the noise is larger than 0 +0049 if noise > 0 +0050 switch data.nmr.noisetype +0051 case 'level' +0052 % scale noise by porosity +0053 noise = noise/data.nmr.porosity; +0054 % add noise to NMR signals +0055 [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noise); +0056 [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noise); +0057 [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noise); +0058 [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noise); +0059 noiseM = noise*ones(size(data.results.NMR.EiT1,2),4); +0060 case 'SNR' +0061 SNR = data.nmr.noise; +0062 noiseM(:,1) = data.results.NMR.EiT1(:,end)./SNR; +0063 [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noiseM(:,1)); +0064 noiseM(:,2) = data.results.NMR.EdT1(:,end)./SNR; +0065 [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noiseM(:,2)); +0066 noiseM(:,3) = data.results.NMR.EiT2(:,1)./SNR; +0067 [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noiseM(:,3)); +0068 noiseM(:,4) = data.results.NMR.EdT2(:,1)./SNR; +0069 [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noiseM(:,4)); +0070 end +0071 else +0072 % reset the NMR signals with the raw data (without noise) +0073 data.results.NMR.EiT1 = data.results.NMR.raw.EiT1; +0074 data.results.NMR.EdT1 = data.results.NMR.raw.EdT1; +0075 data.results.NMR.EiT2 = data.results.NMR.raw.EiT2; +0076 data.results.NMR.EdT2 = data.results.NMR.raw.EdT2; +0077 noiseM = zeros(size(data.results.NMR.EiT1,2),4); +0078 end +0079 +0080 % scale NMR signals by porosity +0081 data.results.NMR.EiT1 = data.nmr.porosity.*data.results.NMR.EiT1; +0082 data.results.NMR.EdT1 = data.nmr.porosity.*data.results.NMR.EdT1; +0083 data.results.NMR.EiT2 = data.nmr.porosity.*data.results.NMR.EiT2; +0084 data.results.NMR.EdT2 = data.nmr.porosity.*data.results.NMR.EdT2; +0085 +0086 % save the noise matrix values +0087 data.results.NMR.noise = noiseM; +0088 % save the porosity value +0089 data.results.NMR.porosity = data.nmr.porosity; +0090 % update the GUI data +0091 setappdata(fig,'data',data); +0092 % update the NMR plot window +0093 updatePlotsNMR; +0094 +0095 end +0096 +0097 %------------- END OF CODE -------------- +0098 +0099 %% License: +0100 % MIT License +0101 % +0102 % Copyright (c) 2018 Thomas Hiller +0103 % +0104 % Permission is hereby granted, free of charge, to any person obtaining a copy +0105 % of this software and associated documentation files (the "Software"), to deal +0106 % in the Software without restriction, including without limitation the rights +0107 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0108 % copies of the Software, and to permit persons to whom the Software is +0109 % furnished to do so, subject to the following conditions: +0110 % +0111 % The above copyright notice and this permission notice shall be included in all +0112 % copies or substantial portions of the Software. +0113 % +0114 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0115 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0116 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0117 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0118 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0119 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0120 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/updatePlotsCPS.html b/doc/nucleus/functions/interface/updatePlotsCPS.html index abb38c1..8f86f96 100644 --- a/doc/nucleus/functions/interface/updatePlotsCPS.html +++ b/doc/nucleus/functions/interface/updatePlotsCPS.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsDistribution.html b/doc/nucleus/functions/interface/updatePlotsDistribution.html index f90c2ac..8d920cd 100644 --- a/doc/nucleus/functions/interface/updatePlotsDistribution.html +++ b/doc/nucleus/functions/interface/updatePlotsDistribution.html @@ -50,8 +50,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=clearSingleAxis clears an individual axis
  • updatePlotsDistributionInfo plots cut-offs and diffusion regime lines
  • This function is called by: +
  • onContextPlotsPSD checks the label of the distribution axis context menu
  • onContextPlotsRTD checks the label of the distribution axis context menu
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onListboxData handles the calls from the context menu of the data
  • calibratePorosity determines a sample's porosity from a calibration
  • changeColorTheme changes the color theme of the calling figure
  • runInversionStd controls the standard inversion process to invert a
  • SUBFUNCTIONS ^

    @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- @@ -149,233 +149,499 @@

    SOURCE CODE ^case 'free' 0080 mymark = {'o-','+-','s-','d-','x-'}; -0081 mycols = [22 140 75;22 87 140;140 22 87;140 75 22;64 64 64]./255; -0082 switch nmrproc.T1T2 -0083 case 'T1' -0084 T = invstd.T1; -0085 case 'T2' -0086 T = invstd.T2; -0087 end -0088 -0089 lgdstr = cell(1,1); -0090 for i = 1:data.invstd.freeDT -0091 stem(T(i),invstd.E0(i),mymark{i},'Color',mycols(i,:),'LineWidth',2,'Parent',ax); -0092 lgdstr{i} = ['T',num2str(i)]; -0093 end -0094 -0095 % limits -0096 ticks = floor(log10(min(T)))-2 :1: ceil(log10(max(T)))+2; -0097 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); -0098 set(ax,'YScale','lin','YLim',[0 max(invstd.E0)*1.05]); -0099 % labels -0100 set(get(ax,'XLabel'),'String',xlstring); -0101 set(get(ax,'YLabel'),'String','individual amplitudes Ex [-]'); -0102 % grid -0103 grid(ax,'on'); -0104 -0105 case {'LU','NNLS'} -0106 % scale distribution by porosity -0107 F = invstd.T1T2f; -0108 if sum(F)>0 -0109 F = (data.invstd.porosity*100).*F./sum(F); -0110 ylims = [0 max(F)*1.05]; -0111 else -0112 ylims = [-1 1]; -0113 end -0114 if data.invstd.porosity == 1 -0115 ylab1 = 'amplitudes [-]'; -0116 ylab2 = 'cumulative amplitudes [-]'; -0117 else -0118 ylab1 = 'water content [vol. %]'; -0119 ylab2 = 'cumulative water content [vol. %]'; -0120 end -0121 % F = data.invstd.porosity.*F./trapz(T,F); -0122 -0123 switch data.info.RTDflag -0124 case 'freq' -0125 plot(invstd.T1T2me,F,'o-','Color',col.FIT,... -0126 'LineWidth',2,'Parent',ax); -0127 % find approx. TLGM amplitude -0128 amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); -0129 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... -0130 'LineWidth',2,'Tag','TLGM','Parent',ax); -0131 -0132 % y-limits -0133 set(ax,'YScale','lin','YLim',ylims); -0134 % y-label -0135 set(get(ax,'YLabel'),'String',ylab1); -0136 -0137 case 'cum' -0138 plot(invstd.T1T2me,cumsum(F),'o-','Color',col.FIT,... -0139 'LineWidth',2,'Parent',ax); -0140 % find approx. TLGM amplitude -0141 amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); -0142 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... -0143 'LineWidth',2,'Tag','TLGM','Parent',ax); -0144 -0145 % y-limits -0146 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); -0147 % y-label -0148 set(get(ax,'YLabel'),'String',ylab2); -0149 end -0150 -0151 % x-limits -0152 ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); -0153 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); -0154 % x-label -0155 set(get(ax,'XLabel'),'String',xlstring); -0156 % grid -0157 grid(ax,'on'); -0158 end -0159 -0160 %% PSD -0161 % PSD axis -0162 ax = gui.axes_handles.psd; -0163 clearSingleAxis(ax); -0164 hold(ax,'on'); -0165 set(gui.cm_handles.axes_psd_view,'Enable','on'); -0166 -0167 % x-axis label -0168 switch data.process.timescale -0169 case 's' -0170 xlstring = 'equiv. pore size [m]'; -0171 case 'ms' -0172 xlstring = 'equiv. pore size [mm]'; -0173 end -0174 -0175 rho = data.param.rho/1e6; % surface relaxivity [m/s] -0176 a = data.param.a; % geometry parameter -0177 -0178 switch data.invstd.invtype -0179 case 'mono' -0180 switch nmrproc.T1T2 -0181 case 'T1' -0182 T = invstd.T1; -0183 case 'T2' -0184 T = invstd.T2; -0185 end -0186 -0187 stem(T.*rho.*a,invstd.E0,'o-','Color',col.FIT,'LineWidth',2,'Parent',ax); -0188 -0189 % limits -0190 ticks = floor(log10(min(T.*rho.*a)))-2 :1: ceil(log10(max(T.*rho.*a)))+2; -0191 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); -0192 set(ax,'YScale','lin','YLim',[0 invstd.E0*1.05]); -0193 % labels -0194 set(get(ax,'XLabel'),'String',xlstring); -0195 set(get(ax,'YLabel'),'String','initial amplitude E0'); -0196 % grid -0197 grid(ax,'on'); -0198 -0199 case 'free' -0200 mymark = {'o-','+-','s-','d-','x-'}; -0201 mycols = [22 140 75;22 87 140;140 22 87;140 75 22;64 64 64]./255; -0202 switch nmrproc.T1T2 -0203 case 'T1' -0204 T = invstd.T1; -0205 case 'T2' -0206 T = invstd.T2; -0207 end -0208 -0209 lgdstr = cell(1,1); -0210 for i = 1:data.invstd.freeDT -0211 stem(T(i).*rho.*a,invstd.E0(i),mymark{i},'Color',mycols(i,:),'LineWidth',2,'Parent',ax); -0212 lgdstr{i} = ['T',num2str(i)]; -0213 end -0214 -0215 % limits -0216 ticks = floor(log10(min(T.*rho.*a)))-2 :1: ceil(log10(max(T.*rho.*a)))+2; -0217 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); -0218 set(ax,'YScale','lin','YLim',[0 max(invstd.E0)*1.05]); -0219 % labels -0220 set(get(ax,'XLabel'),'String',xlstring); -0221 set(get(ax,'YLabel'),'String','individual amplitudes Ex [-]'); -0222 % grid -0223 grid(ax,'on'); -0224 -0225 case {'LU','NNLS'} -0226 % very basic RTD to PSD conversion -0227 requiv = invstd.T1T2me.*rho.*a; -0228 Rlgm = invstd.Tlgm.*rho.*a; -0229 -0230 switch data.info.PSDflag -0231 case 'freq' -0232 plot(requiv,F,'o-','Color',col.FIT,... -0233 'LineWidth',2,'Parent',ax); -0234 % find approx. RLGM amplitude -0235 amp = findApproxTlgmAmplitude(requiv,F,Rlgm); -0236 stem(Rlgm,amp,'x-','Color',[0.3 0.3 0.3],'LineWidth',2,'Tag','TLGM','Parent',ax); -0237 -0238 % y-limits -0239 set(ax,'YScale','lin','YLim',ylims); -0240 % y-label -0241 set(get(ax,'YLabel'),'String',ylab1); -0242 -0243 case 'cum' -0244 plot(requiv,cumsum(F),'o-','Color',col.FIT,... -0245 'LineWidth',2,'Parent',ax); -0246 % find approx. RLGM amplitude -0247 amp = findApproxTlgmAmplitude(requiv,cumsum(F),Rlgm); -0248 stem(Rlgm,amp,'x-','Color',[0.3 0.3 0.3],'LineWidth',2,'Tag','TLGM','Parent',ax); -0249 -0250 % y-limits -0251 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); -0252 % y-label -0253 set(get(ax,'YLabel'),'String',ylab2); +0081 mycols = [0.8941 0.1020 0.1098;0.3020 0.6863 0.2902; +0082 0.2157 0.4941 0.7216;0.5961 0.3059 0.6392;0.6510 0.3373 0.1569]; +0083 switch nmrproc.T1T2 +0084 case 'T1' +0085 T = invstd.T1; +0086 case 'T2' +0087 T = invstd.T2; +0088 end +0089 +0090 lgdstr = cell(1,1); +0091 for i = 1:data.invstd.freeDT +0092 stem(T(i),invstd.E0(i),mymark{i},'Color',mycols(i,:),'LineWidth',2,'Parent',ax); +0093 lgdstr{i} = ['T',num2str(i)]; +0094 end +0095 +0096 % limits +0097 ticks = floor(log10(min(T)))-2 :1: ceil(log10(max(T)))+2; +0098 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0099 set(ax,'YScale','lin','YLim',[0 max(invstd.E0)*1.05]); +0100 % labels +0101 set(get(ax,'XLabel'),'String',xlstring); +0102 set(get(ax,'YLabel'),'String','individual amplitudes Ex [-]'); +0103 % grid +0104 grid(ax,'on'); +0105 +0106 case {'LU'} +0107 % scale distribution by porosity +0108 F = invstd.T1T2f; +0109 if sum(F)>0 +0110 F = (data.invstd.porosity*100).*F./sum(F); +0111 ylims = [0 max(F)*1.05]; +0112 else +0113 ylims = [-1 1]; +0114 end +0115 if data.invstd.porosity == 1 +0116 ylab1 = 'amplitudes [-]'; +0117 ylab2 = 'cumulative amplitudes [-]'; +0118 else +0119 ylab1 = 'water content [vol. %]'; +0120 ylab2 = 'cumulative water content [vol. %]'; +0121 end +0122 % F = data.invstd.porosity.*F./trapz(T,F); +0123 +0124 switch data.info.RTDflag +0125 case 'freq' +0126 plot(invstd.T1T2me,F,'o-','Color',col.FIT,... +0127 'LineWidth',2,'Parent',ax); +0128 % find approx. TLGM amplitude +0129 amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); +0130 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0131 'LineWidth',2,'Tag','TLGM','Parent',ax); +0132 +0133 % y-limits +0134 set(ax,'YScale','lin','YLim',ylims); +0135 % y-label +0136 set(get(ax,'YLabel'),'String',ylab1); +0137 +0138 case 'cum' +0139 plot(invstd.T1T2me,cumsum(F),'o-','Color',col.FIT,... +0140 'LineWidth',2,'Parent',ax); +0141 % find approx. TLGM amplitude +0142 amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); +0143 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0144 'LineWidth',2,'Tag','TLGM','Parent',ax); +0145 +0146 % y-limits +0147 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); +0148 % y-label +0149 set(get(ax,'YLabel'),'String',ylab2); +0150 end +0151 +0152 % x-limits +0153 ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); +0154 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0155 % x-label +0156 set(get(ax,'XLabel'),'String',xlstring); +0157 % grid +0158 grid(ax,'on'); +0159 +0160 case {'NNLS'} +0161 % scale distribution by porosity +0162 F = invstd.T1T2f; +0163 if sum(F)>0 +0164 % apply same scaling to the uncertainty patch +0165 if isfield(data.results.invstd,'uncert') +0166 f_min = data.results.invstd.uncert.interp_f_min; +0167 f_max = data.results.invstd.uncert.interp_f_max; +0168 f_min = (data.invstd.porosity*100).*f_min./sum(F); +0169 f_max = (data.invstd.porosity*100).*f_max./sum(F); +0170 end +0171 F = (data.invstd.porosity*100).*F./sum(F); +0172 +0173 ylims = [0 max(F)*1.05]; +0174 else +0175 ylims = [-1 1]; +0176 end +0177 if data.invstd.porosity == 1 +0178 ylab1 = 'amplitudes [-]'; +0179 ylab2 = 'cumulative amplitudes [-]'; +0180 else +0181 ylab1 = 'water content [vol. %]'; +0182 ylab2 = 'cumulative water content [vol. %]'; +0183 end +0184 % F = data.invstd.porosity.*F./trapz(T,F); +0185 +0186 switch data.info.RTDflag +0187 case 'freq' +0188 if isfield(data.results.invstd,'uncert') +0189 % plot uncertainty +0190 for i = 1:size(invstd.uncert.interp_f,1) +0191 plot(invstd.T1T2me,(data.invstd.porosity*100).*invstd.uncert.interp_f(i,:)./sum(invstd.T1T2f),... +0192 '-','Color',[0.5 0.5 0.5],'LineWidth',1,'Parent',ax); +0193 end +0194 % verts = [invstd.T1T2me f_min'; flipud(invstd.T1T2me) flipud(f_max')]; +0195 % faces = 1:1:size(verts,1); +0196 % patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0197 % 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0198 % adjust y-limits +0199 ylims(2) = max([ylims(2) max(f_max)*1.05]); +0200 end +0201 +0202 plot(invstd.T1T2me,F,'-','Color',col.FIT,... +0203 'LineWidth',2,'Parent',ax); +0204 % find approx. TLGM amplitude +0205 amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); +0206 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0207 'LineWidth',2,'Tag','TLGM','Parent',ax); +0208 +0209 % y-limits +0210 set(ax,'YScale','lin','YLim',ylims); +0211 % y-label +0212 set(get(ax,'YLabel'),'String',ylab1); +0213 +0214 case 'cum' +0215 if isfield(data.results.invstd,'uncert') +0216 verts = [invstd.T1T2me cumsum(F)-f_min'; flipud(invstd.T1T2me) flipud(cumsum(F)+f_max')]; +0217 faces = 1:1:size(verts,1); +0218 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0219 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0220 end +0221 plot(invstd.T1T2me,cumsum(F),'-','Color',col.FIT,... +0222 'LineWidth',2,'Parent',ax); +0223 % find approx. TLGM amplitude +0224 amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); +0225 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0226 'LineWidth',2,'Tag','TLGM','Parent',ax); +0227 +0228 % y-limits +0229 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); +0230 % y-label +0231 set(get(ax,'YLabel'),'String',ylab2); +0232 end +0233 +0234 % x-limits +0235 ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); +0236 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0237 % x-label +0238 set(get(ax,'XLabel'),'String',xlstring); +0239 % grid +0240 grid(ax,'on'); +0241 +0242 case {'MUMO'} +0243 % single distributions for different T, sigma and amplitude +0244 dist = zeros(length(invstd.x)/3,numel(invstd.T1T2me)); +0245 for i = 1:length(invstd.x)/3 +0246 mu = invstd.T(i); +0247 sigma = invstd.S(i); +0248 amp = invstd.E(i); +0249 +0250 tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(invstd.T1T2me) - log(mu))/ sqrt(2)/sigma).^2); +0251 +0252 % scale to amplitude +0253 dist(i,:) = (tmp/sum(tmp)) * amp; 0254 end 0255 -0256 % x-limits -0257 ticks = floor(log10(min(requiv))) :1: ceil(log10(max(requiv))); -0258 % set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); -0259 set(ax,'XScale','log','XLim',[min(requiv) max(requiv)],'XTick',10.^ticks); -0260 % x-label -0261 set(get(ax,'XLabel'),'String',xlstring); -0262 % grid -0263 grid(ax,'on'); -0264 end -0265 -0266 % show CBW and diffusion regime lines -0267 updatePlotsDistributionInfo; -0268 end -0269 -0270 end -0271 -0272 %% -0273 function amp = findApproxTlgmAmplitude(t,f,TLGM) -0274 -0275 if isnan(TLGM) -0276 amp = NaN; -0277 else -0278 index = find(abs(t-TLGM)==min(abs(t-TLGM))); -0279 amp = interp1(t(index-1:index+1),f(index-1:index+1),TLGM); -0280 end -0281 -0282 end -0283 -0284 %------------- END OF CODE -------------- -0285 -0286 %% License: -0287 % MIT License -0288 % -0289 % Copyright (c) 2018 Thomas Hiller -0290 % -0291 % Permission is hereby granted, free of charge, to any person obtaining a copy -0292 % of this software and associated documentation files (the "Software"), to deal -0293 % in the Software without restriction, including without limitation the rights -0294 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0295 % copies of the Software, and to permit persons to whom the Software is -0296 % furnished to do so, subject to the following conditions: -0297 % -0298 % The above copyright notice and this permission notice shall be included in all -0299 % copies or substantial portions of the Software. -0300 % -0301 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0302 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0303 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0304 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0305 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0306 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0307 % SOFTWARE. +0256 % scale distribution by porosity +0257 F = invstd.T1T2f; +0258 if sum(F)>0 +0259 for i = 1:length(invstd.x)/3 +0260 dist(i,:) = (data.invstd.porosity*100).*dist(i,:)./sum(F); +0261 end +0262 % apply same scaling to the uncertainty patch +0263 if isfield(data.results.invstd,'uncert') +0264 f_min = data.results.invstd.uncert.interp_f_min; +0265 f_max = data.results.invstd.uncert.interp_f_max; +0266 f_min = (data.invstd.porosity*100).*f_min./sum(F); +0267 f_max = (data.invstd.porosity*100).*f_max./sum(F); +0268 end +0269 F = (data.invstd.porosity*100).*F./sum(F); +0270 +0271 ylims = [0 max(F)*1.05]; +0272 else +0273 ylims = [-1 1]; +0274 end +0275 if data.invstd.porosity == 1 +0276 ylab1 = 'amplitudes [-]'; +0277 ylab2 = 'cumulative amplitudes [-]'; +0278 else +0279 ylab1 = 'water content [vol. %]'; +0280 ylab2 = 'cumulative water content [vol. %]'; +0281 end +0282 % F = data.invstd.porosity.*F./trapz(T,F); +0283 +0284 mycols = [0.2157 0.4941 0.7216;0.3020 0.6863 0.2902; +0285 0.5961 0.3059 0.6392;0.6510 0.3373 0.1569;0.8941 0.1020 0.1098]; +0286 +0287 switch data.info.RTDflag +0288 case 'freq' +0289 if isfield(data.results.invstd,'uncert') +0290 % plot uncertainty +0291 % lines +0292 % for i = 1:size(invstd.TDIST,1) +0293 % plot(invstd.T1T2me,(data.invstd.porosity*100).*invstd.TDIST(i,:)./sum(invstd.T1T2f),... +0294 % '-','Color',[0.5 0.5 0.5],'LineWidth',1,'Parent',ax); +0295 % end +0296 % patch +0297 % TDIST = invstd.TDIST; +0298 % for i = 1:size(invstd.TDIST,1) +0299 % TDIST(i,:) = (data.invstd.porosity*100).*invstd.TDIST(i,:)./sum(invstd.T1T2f); +0300 % end +0301 % TDISTmin = min(TDIST); +0302 % TDISTmax = max(TDIST); +0303 % verts = [nmrproc.t(nmrproc.t>0) s_min; flipud(nmrproc.t(nmrproc.t>0)) flipud(s_max)]; +0304 % faces = 1:1:size(verts,1); +0305 % +0306 verts = [invstd.T1T2me f_min'; flipud(invstd.T1T2me) flipud(f_max')]; +0307 faces = 1:1:size(verts,1); +0308 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0309 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0310 % adjust y-limits +0311 ylims(2) = max([ylims(2) max(f_max)*1.05]); +0312 end +0313 +0314 % plot total RTD +0315 plot(invstd.T1T2me,F,'-','Color',col.FIT,... +0316 'LineWidth',2,'Parent',ax); +0317 % plot individual RTDs +0318 for i = 1:length(invstd.x)/3 +0319 plot(invstd.T1T2me,dist(i,:),'--','Color',mycols(i,:),... +0320 'LineWidth',2,'Parent',ax); +0321 end +0322 % find approx. TLGM amplitude +0323 amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); +0324 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0325 'LineWidth',2,'Tag','TLGM','Parent',ax); +0326 +0327 % y-limits +0328 set(ax,'YScale','lin','YLim',ylims); +0329 % y-label +0330 set(get(ax,'YLabel'),'String',ylab1); +0331 +0332 case 'cum' +0333 if isfield(data.results.invstd,'uncert') +0334 verts = [invstd.T1T2me cumsum(F)-f_min'; flipud(invstd.T1T2me) flipud(cumsum(F)+f_max')]; +0335 faces = 1:1:size(verts,1); +0336 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0337 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0338 end +0339 plot(invstd.T1T2me,cumsum(F),'-','Color',col.FIT,... +0340 'LineWidth',2,'Parent',ax); +0341 for i = 1:length(invstd.x)/3 +0342 plot(invstd.T1T2me,cumsum(dist(i,:)),'--','Color',mycols(i,:),... +0343 'LineWidth',2,'Parent',ax); +0344 end +0345 % find approx. TLGM amplitude +0346 amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); +0347 stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... +0348 'LineWidth',2,'Tag','TLGM','Parent',ax); +0349 +0350 % y-limits +0351 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); +0352 % y-label +0353 set(get(ax,'YLabel'),'String',ylab2); +0354 end +0355 +0356 % x-limits +0357 ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); +0358 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0359 % x-label +0360 set(get(ax,'XLabel'),'String',xlstring); +0361 % grid +0362 grid(ax,'on'); +0363 end +0364 +0365 %% PSD +0366 % PSD axis +0367 ax = gui.axes_handles.psd; +0368 clearSingleAxis(ax); +0369 hold(ax,'on'); +0370 set(gui.cm_handles.axes_psd_view,'Enable','on'); +0371 +0372 % x-axis label +0373 switch data.process.timescale +0374 case 's' +0375 xlstring = 'equiv. pore size [m]'; +0376 case 'ms' +0377 xlstring = 'equiv. pore size [mm]'; +0378 end +0379 +0380 rho = data.param.rho/1e6; % surface relaxivity [m/s] +0381 a = data.param.a; % geometry parameter +0382 +0383 switch data.invstd.invtype +0384 case 'mono' +0385 switch nmrproc.T1T2 +0386 case 'T1' +0387 T = invstd.T1; +0388 case 'T2' +0389 T = invstd.T2; +0390 end +0391 +0392 stem(T.*rho.*a,invstd.E0,'o-','Color',col.FIT,'LineWidth',2,'Parent',ax); +0393 +0394 % limits +0395 ticks = floor(log10(min(T.*rho.*a)))-2 :1: ceil(log10(max(T.*rho.*a)))+2; +0396 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0397 set(ax,'YScale','lin','YLim',[0 invstd.E0*1.05]); +0398 % labels +0399 set(get(ax,'XLabel'),'String',xlstring); +0400 set(get(ax,'YLabel'),'String','initial amplitude E0'); +0401 % grid +0402 grid(ax,'on'); +0403 +0404 case 'free' +0405 mymark = {'o-','+-','s-','d-','x-'}; +0406 mycols = [0.8941 0.1020 0.1098;0.3020 0.6863 0.2902; +0407 0.2157 0.4941 0.7216;0.5961 0.3059 0.6392;0.6510 0.3373 0.1569]; +0408 switch nmrproc.T1T2 +0409 case 'T1' +0410 T = invstd.T1; +0411 case 'T2' +0412 T = invstd.T2; +0413 end +0414 +0415 lgdstr = cell(1,1); +0416 for i = 1:data.invstd.freeDT +0417 stem(T(i).*rho.*a,invstd.E0(i),mymark{i},'Color',mycols(i,:),'LineWidth',2,'Parent',ax); +0418 lgdstr{i} = ['T',num2str(i)]; +0419 end +0420 +0421 % limits +0422 ticks = floor(log10(min(T.*rho.*a)))-2 :1: ceil(log10(max(T.*rho.*a)))+2; +0423 set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0424 set(ax,'YScale','lin','YLim',[0 max(invstd.E0)*1.05]); +0425 % labels +0426 set(get(ax,'XLabel'),'String',xlstring); +0427 set(get(ax,'YLabel'),'String','individual amplitudes Ex [-]'); +0428 % grid +0429 grid(ax,'on'); +0430 +0431 case {'LU','NNLS'} +0432 % very basic RTD to PSD conversion +0433 requiv = invstd.T1T2me.*rho.*a; +0434 Rlgm = invstd.Tlgm.*rho.*a; +0435 +0436 switch data.info.PSDflag +0437 case 'freq' +0438 plot(requiv,F,'o-','Color',col.FIT,... +0439 'LineWidth',2,'Parent',ax); +0440 % find approx. RLGM amplitude +0441 amp = findApproxTlgmAmplitude(requiv,F,Rlgm); +0442 stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); +0443 +0444 % y-limits +0445 set(ax,'YScale','lin','YLim',ylims); +0446 % y-label +0447 set(get(ax,'YLabel'),'String',ylab1); +0448 +0449 case 'cum' +0450 plot(requiv,cumsum(F),'o-','Color',col.FIT,... +0451 'LineWidth',2,'Parent',ax); +0452 % find approx. RLGM amplitude +0453 amp = findApproxTlgmAmplitude(requiv,cumsum(F),Rlgm); +0454 stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); +0455 +0456 % y-limits +0457 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); +0458 % y-label +0459 set(get(ax,'YLabel'),'String',ylab2); +0460 end +0461 +0462 % x-limits +0463 ticks = floor(log10(min(requiv))) :1: ceil(log10(max(requiv))); +0464 % set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0465 set(ax,'XScale','log','XLim',[min(requiv) max(requiv)],'XTick',10.^ticks); +0466 % x-label +0467 set(get(ax,'XLabel'),'String',xlstring); +0468 % grid +0469 grid(ax,'on'); +0470 +0471 case {'MUMO'} +0472 % very basic RTD to PSD conversion +0473 requiv = invstd.T1T2me.*rho.*a; +0474 Rlgm = invstd.Tlgm.*rho.*a; +0475 +0476 switch data.info.PSDflag +0477 case 'freq' +0478 if isfield(data.results.invstd,'uncert') +0479 verts = [requiv f_min'; flipud(requiv) flipud(f_max')]; +0480 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0481 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0482 % adjust y-limits +0483 ylims(2) = max([ylims(2) max(f_max)*1.05]); +0484 end +0485 plot(requiv,F,'-','Color',col.FIT,... +0486 'LineWidth',2,'Parent',ax); +0487 for i = 1:length(invstd.x)/3 +0488 plot(requiv,dist(i,:),'--','Color',mycols(i,:),... +0489 'LineWidth',2,'Parent',ax); +0490 end +0491 % find approx. RLGM amplitude +0492 amp = findApproxTlgmAmplitude(requiv,F,Rlgm); +0493 stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); +0494 +0495 % y-limits +0496 set(ax,'YScale','lin','YLim',ylims); +0497 % y-label +0498 set(get(ax,'YLabel'),'String',ylab1); +0499 +0500 case 'cum' +0501 if isfield(data.results.invstd,'uncert') +0502 verts = [requiv cumsum(F)-f_min'; flipud(requiv) flipud(cumsum(F)+f_max')]; +0503 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0504 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0505 end +0506 plot(requiv,cumsum(F),'-','Color',col.FIT,... +0507 'LineWidth',2,'Parent',ax); +0508 for i = 1:length(invstd.x)/3 +0509 plot(requiv,cumsum(dist(i,:)),'--','Color',mycols(i,:),... +0510 'LineWidth',2,'Parent',ax); +0511 end +0512 % find approx. RLGM amplitude +0513 amp = findApproxTlgmAmplitude(requiv,cumsum(F),Rlgm); +0514 stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); +0515 +0516 % y-limits +0517 set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); +0518 % y-label +0519 set(get(ax,'YLabel'),'String',ylab2); +0520 end +0521 +0522 % x-limits +0523 ticks = floor(log10(min(requiv))) :1: ceil(log10(max(requiv))); +0524 % set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); +0525 set(ax,'XScale','log','XLim',[min(requiv) max(requiv)],'XTick',10.^ticks); +0526 % x-label +0527 set(get(ax,'XLabel'),'String',xlstring); +0528 % grid +0529 grid(ax,'on'); +0530 end +0531 +0532 % show CBW and diffusion regime lines +0533 updatePlotsDistributionInfo; +0534 end +0535 +0536 end +0537 +0538 %% +0539 function amp = findApproxTlgmAmplitude(t,f,TLGM) +0540 +0541 if isnan(TLGM) +0542 amp = NaN; +0543 else +0544 index = find(abs(t-TLGM)==min(abs(t-TLGM))); +0545 amp = interp1(t(index-1:index+1),f(index-1:index+1),TLGM); +0546 end +0547 +0548 end +0549 +0550 %------------- END OF CODE -------------- +0551 +0552 %% License: +0553 % MIT License +0554 % +0555 % Copyright (c) 2018 Thomas Hiller +0556 % +0557 % Permission is hereby granted, free of charge, to any person obtaining a copy +0558 % of this software and associated documentation files (the "Software"), to deal +0559 % in the Software without restriction, including without limitation the rights +0560 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0561 % copies of the Software, and to permit persons to whom the Software is +0562 % furnished to do so, subject to the following conditions: +0563 % +0564 % The above copyright notice and this permission notice shall be included in all +0565 % copies or substantial portions of the Software. +0566 % +0567 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0568 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0569 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0570 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0571 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0572 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0573 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/updatePlotsDistributionInfo.html b/doc/nucleus/functions/interface/updatePlotsDistributionInfo.html index d420e2f..90de642 100644 --- a/doc/nucleus/functions/interface/updatePlotsDistributionInfo.html +++ b/doc/nucleus/functions/interface/updatePlotsDistributionInfo.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsGeometryType.html b/doc/nucleus/functions/interface/updatePlotsGeometryType.html index 03d66ec..b446367 100644 --- a/doc/nucleus/functions/interface/updatePlotsGeometryType.html +++ b/doc/nucleus/functions/interface/updatePlotsGeometryType.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsJointInversion.html b/doc/nucleus/functions/interface/updatePlotsJointInversion.html index b679f41..b80ef6e 100644 --- a/doc/nucleus/functions/interface/updatePlotsJointInversion.html +++ b/doc/nucleus/functions/interface/updatePlotsJointInversion.html @@ -52,8 +52,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=beautifyAxes can be used to globally change the general appearance of axes
  • clearSingleAxis clears an individual axis
  • getColorIndex exports graphics from both GUIs into various formats
  • This function is called by: +
  • onContextAxisLogLin changes the label of an axis context menu which allows to
  • onContextPlotsPSDJ checks the label of the distribution axis context menu
  • onListboxData handles the calls from the context menu of the data
  • changeColorTheme changes the color theme of the calling figure
  • runInversionJoint controls the joint inversion process to infer a pore size
  • @@ -96,8 +96,8 @@

    SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSinv -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsLcurve.html b/doc/nucleus/functions/interface/updatePlotsLcurve.html index 53c1ad7..0a3638d 100644 --- a/doc/nucleus/functions/interface/updatePlotsLcurve.html +++ b/doc/nucleus/functions/interface/updatePlotsLcurve.html @@ -51,8 +51,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=beautifyAxes can be used to globally change the general appearance of axes
  • clearSingleAxis clears an individual axis
  • This function is called by: +
  • onListboxData handles the calls from the context menu of the data
  • runInversionJoint controls the joint inversion process to infer a pore size
  • runInversionStd controls the standard inversion process to invert a
  • @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsNMR.html b/doc/nucleus/functions/interface/updatePlotsNMR.html index 5e600ea..b5b1223 100644 --- a/doc/nucleus/functions/interface/updatePlotsNMR.html +++ b/doc/nucleus/functions/interface/updatePlotsNMR.html @@ -50,8 +50,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=beautifyAxes can be used to globally change the general appearance of axes
  • clearSingleAxis clears an individual axis
  • getColorIndex exports graphics from both GUIs into various formats
  • This function is called by: +
  • onContextAxisLogLin changes the label of an axis context menu which allows to
  • onContextAxisT1T2 checks the axis context menu for plotting either T1 or
  • onContextTableSelect selects or deselects whole drainage and imbibition
  • onEditCPSTable updates entries made in the CPS table of NUCLEUSinv
  • calculateNMR calculates the NMR signals for the full and partially saturated
  • changeColorTheme changes the color theme of the calling figure
  • importMOD2MOD imports previously saved NUCLEUSmod data back into the GUI
  • updateNMRsignals adds noise to the forward NMR signals and scales the
  • @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsPSD.html b/doc/nucleus/functions/interface/updatePlotsPSD.html index 54e2311..e190f48 100644 --- a/doc/nucleus/functions/interface/updatePlotsPSD.html +++ b/doc/nucleus/functions/interface/updatePlotsPSD.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: NUCLEUSmod -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updatePlotsSignal.html b/doc/nucleus/functions/interface/updatePlotsSignal.html index 32b13e5..eb00eb4 100644 --- a/doc/nucleus/functions/interface/updatePlotsSignal.html +++ b/doc/nucleus/functions/interface/updatePlotsSignal.html @@ -51,8 +51,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=beautifyAxes can be used to globally change the general appearance of axes
  • clearSingleAxis clears an individual axis
  • getColorIndex exports graphics from both GUIs into various formats
  • This function is called by: +
  • onContextAxisLogLin changes the label of an axis context menu which allows to
  • onEditCPSTable updates entries made in the CPS table of NUCLEUSinv
  • onEditValue updates all edit field values, checks for wrong inputs and
  • onListboxData handles the calls from the context menu of the data
  • onMenuJointInversion handles the call from the menu that activates / deactivates
  • onRadioGates selects the re-sampling / gating method ("log", "lin" or "none")
  • onRadioNormalize selects whether to normalize a NMR signal to 1
  • onRadioTimescale selects whether the time scale should be "s" or "ms"
  • changeColorTheme changes the color theme of the calling figure
  • clearInversion removes inversion results from the internal data structure
  • runInversionStd controls the standard inversion process to invert a
  • @@ -94,8 +94,8 @@

    SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -191,283 +191,324 @@

    SOURCE CODE ^'LineStyle','--','LineWidth',1,'Color','k','Parent',axI); 0122 imag_mean = mean(imag(nmrraw.s)); 0123 imag_std = std(imag(nmrraw.s)); -0124 yticks = linspace(min(imag(nmrraw.s)),max(imag(nmrraw.s)),3); -0125 set(axI,'XTickLabel','','YTick',yticks,'YTickLabelMode','auto'); -0126 switch loglinx -0127 case 'x-axis -> lin' % log axes -0128 set(axI,'XScale','log','XLim',xlims); -0129 case 'x-axis -> log' % lin axes -0130 set(axI,'XScale','lin','XLim',xlims); -0131 end -0132 set(get(axI,'YLabel'),'String','\Immag'); -0133 end -0134 -0135 % grid -0136 grid(ax,'on'); -0137 -0138 %% PROC data axis -0139 ax = gui.axes_handles.proc; -0140 axE = gui.axes_handles.err; -0141 clearSingleAxis(ax); -0142 clearSingleAxis(axE); -0143 hold(ax,'on'); -0144 hold(axE,'on'); -0145 -0146 % data -0147 plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); -0148 if isfield(data.results,'invstd') -0149 plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); -0150 if nmrproc.noise > 0 -0151 plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... -0152 'LineWidth',1,'Parent',axE); -0153 else -0154 plot(nmrproc.t,invstd.residual,'Color',col.IM,... -0155 'LineWidth',1,'Parent',axE); -0156 end -0157 end -0158 -0159 % limits & ticks -0160 xlimraw = get(gui.axes_handles.raw,'XLim'); -0161 loglinx = get(gui.cm_handles.axes_proc_xaxis,'Label'); -0162 switch loglinx -0163 case 'x-axis -> lin' % log axes -0164 ticks = floor(min(log10(nmrproc.t(nmrproc.t>0)))):1:ceil(log10(nmrproc.t(end))); -0165 set(ax,'XScale','log','XLim',xlimraw,'XTick',10.^ticks); -0166 case 'x-axis -> log' % lin axes -0167 set(ax,'XScale','lin','XLim',xlimraw,'XTickMode','auto'); -0168 end -0169 logliny = get(gui.cm_handles.axes_proc_yaxis,'Label'); -0170 switch nmrproc.T1T2 -0171 case 'T1' -0172 ymin = min(real(nmrproc.s)); -0173 ymax = max(real(nmrproc.s)); -0174 if isfield(data.results,'invstd') -0175 ymin = min([ymin min(invstd.residual)]); -0176 ymax = max([ymax max(data.results.invstd.fit_s)]); -0177 end -0178 if ymin>0 -0179 ymin = ymin*0.8; -0180 else -0181 ymin = ymin*1.2; -0182 end -0183 switch logliny -0184 case 'y-axis -> lin' % log axes -0185 ticks = floor(min(log10(nmrproc.s(nmrproc.s>0))))-1:1:ceil(log10(nmrproc.s(end))); -0186 set(ax,'YScale','log','YLim',[10^(ticks(1)) 10^(ticks(end))],... -0187 'YTick',10.^ticks); -0188 case 'y-axis -> log' % lin axes -0189 set(ax,'YScale','lin','YLim',[ymin ymax*1.05],... -0190 'YTickMode','auto'); -0191 end -0192 case 'T2' -0193 ymin = min([min(real(nmrraw.s)) min(imag(nmrraw.s))]); -0194 ymax = max(real(nmrraw.s)); -0195 if isfield(data.results,'invstd') -0196 ymin = min([ymin min(invstd.residual)]); -0197 ymax = max([ymax max(data.results.invstd.fit_s)]); -0198 end -0199 switch logliny -0200 case 'y-axis -> lin' % log axes -0201 ticks = floor(log10(ymin))-1 :1: ceil(log10(ymax)); -0202 set(ax,'YScale','log','YLim',[10^(ticks(1)) ymax*1.05],... -0203 'YTick',10.^ticks); -0204 case 'y-axis -> log' % lin axes -0205 set(ax,'YScale','lin','YLim',[ymin ymax*1.05],... -0206 'YTickMode','auto'); -0207 end -0208 end -0209 if isfield(data.results,'invstd') -0210 ylims = get(ax,'YLim'); -0211 set(gui.axes_handles.raw,'YLim',ylims); -0212 end -0213 -0214 % labels -0215 if strcmp(data.process.timescale,'s') -0216 set(get(ax,'XLabel'),'String','time [s]'); -0217 else -0218 set(get(ax,'XLabel'),'String','time [ms]'); -0219 end -0220 set(get(ax,'YLabel'),'String','amplitude [a.u.]'); -0221 -0222 % legend -0223 if isfield(data.results,'invstd') -0224 lgdstr = {'signal','fit'}; -0225 switch nmrproc.T1T2 -0226 case 'T1' -0227 lgh = legend(ax,lgdstr,'Location','NorthWest',... -0228 'Tag','fitlegend','FontSize',10); -0229 case 'T2' -0230 lgh = legend(ax,lgdstr,'Location','NorthEast',... -0231 'Tag','fitlegend','FontSize',10); -0232 end -0233 set(lgh,'TextColor',gui.myui.colors.panelFG); -0234 end -0235 -0236 % grid -0237 grid(ax,'on'); -0238 -0239 %% residual plot -0240 if isfield(data.results,'invstd') -0241 col = gui.myui.colors.axisL; -0242 xlims = get(ax,'XLim'); -0243 line(xlims,[0 0],'LineStyle','--','LineWidth',1,'Color',col,'Parent',axE); -0244 if nmrproc.noise > 0 -0245 err_mean = mean(invstd.residual./nmrproc.e); -0246 err_std = std(invstd.residual./nmrproc.e); -0247 line(xlims,[err_mean-err_std err_mean-err_std],... -0248 'LineStyle','--','LineWidth',1,'Color','r','Parent',axE); -0249 line(xlims,[err_mean+err_std err_mean+err_std],... -0250 'LineStyle','--','LineWidth',1,'Color','r','Parent',axE); -0251 line(xlims,[-1 -1],'LineStyle','-.','LineWidth',1,... -0252 'Color',col,'Parent',axE); -0253 line(xlims,[1 1],'LineStyle','-.','LineWidth',1,... -0254 'Color',col,'Parent',axE); -0255 set(axE,'XTickLabel',''); -0256 set(axE,'YLim',[-2 2]); -0257 set(axE,'YTick',[-1 0 1],'YTickLabelMode','auto'); -0258 set(get(axE,'YLabel'),'String',{'noise';'weighted';'residuals'},... -0259 'FontWeight','normal'); -0260 else -0261 err_mean = mean(invstd.residual); -0262 err_std = std(invstd.residual); -0263 line(xlims,[err_mean-1*err_std err_mean-1*err_std],... -0264 'LineStyle','-.','LineWidth',1,'Color',col,'Parent',axE); -0265 line(xlims,[err_mean+1*err_std err_mean+1*err_std],... -0266 'LineStyle','-.','LineWidth',1,'Color',col,'Parent',axE); -0267 set(axE,'XTickLabel',''); -0268 set(axE,'YLim',[err_mean-3*err_std err_mean+3*err_std]); -0269 set(axE,'YTick',[err_mean-3*err_std 0 err_mean+3*err_std],... -0270 'YTickLabelMode','auto') -0271 set(get(axE,'YLabel'),'String','residuals',... -0272 'FontWeight','normal'); +0124 % yticks = linspace(min(imag(nmrraw.s)),max(imag(nmrraw.s)),3); +0125 yticks = [imag_mean-imag_std*2 0 imag_mean+imag_std*2]; +0126 ylim = [imag_mean-imag_std*3 imag_mean+imag_std*3]; +0127 set(axI,'XTickLabel','','YLim',ylim,'YTick',yticks,'YTickLabelMode','auto'); +0128 switch loglinx +0129 case 'x-axis -> lin' % log axes +0130 set(axI,'XScale','log','XLim',xlims); +0131 case 'x-axis -> log' % lin axes +0132 set(axI,'XScale','lin','XLim',xlims); +0133 end +0134 set(get(axI,'YLabel'),'String','\Immag'); +0135 end +0136 +0137 % grid +0138 grid(ax,'on'); +0139 +0140 %% PROC data axis +0141 ax = gui.axes_handles.proc; +0142 axE = gui.axes_handles.err; +0143 clearSingleAxis(ax); +0144 clearSingleAxis(axE); +0145 hold(ax,'on'); +0146 hold(axE,'on'); +0147 +0148 % data +0149 switch data.invstd.invtype +0150 case {'MUMO','NNLS'} +0151 if isfield(data.results,'invstd') && isfield(data.results.invstd,'uncert') +0152 % uncertainty patch created from min max of uncertainty +0153 % data +0154 s_min = data.results.invstd.uncert.interp_s_min; +0155 s_max = data.results.invstd.uncert.interp_s_max; +0156 t = data.results.invstd.uncert.interp_t; +0157 verts = [t(t>0) s_min(t>0); flipud(t(t>0)) flipud(s_max(t>0))]; +0158 faces = 1:1:size(verts,1); +0159 patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +0160 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); +0161 end +0162 +0163 if isfield(data.results,'invstd') +0164 plot(nmrproc.t,nmrproc.s,'-','Color',col.RE,'LineWidth',1,'Parent',ax); +0165 plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); +0166 if nmrproc.noise > 0 +0167 plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... +0168 'LineWidth',1,'Parent',axE); +0169 else +0170 plot(nmrproc.t,invstd.residual,'Color',col.IM,... +0171 'LineWidth',1,'Parent',axE); +0172 end +0173 else +0174 plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); +0175 end +0176 otherwise +0177 if isfield(data.results,'invstd') +0178 plot(nmrproc.t,nmrproc.s,'-','Color',col.RE,'LineWidth',1,'Parent',ax); +0179 plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); +0180 if nmrproc.noise > 0 +0181 plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... +0182 'LineWidth',1,'Parent',axE); +0183 else +0184 plot(nmrproc.t,invstd.residual,'Color',col.IM,... +0185 'LineWidth',1,'Parent',axE); +0186 end +0187 else +0188 plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); +0189 end +0190 end +0191 +0192 % limits & ticks +0193 xlimraw = get(gui.axes_handles.raw,'XLim'); +0194 loglinx = get(gui.cm_handles.axes_proc_xaxis,'Label'); +0195 switch loglinx +0196 case 'x-axis -> lin' % log axes +0197 ticks = floor(min(log10(nmrproc.t(nmrproc.t>0)))):1:ceil(log10(nmrproc.t(end))); +0198 set(ax,'XScale','log','XLim',xlimraw,'XTick',10.^ticks); +0199 case 'x-axis -> log' % lin axes +0200 set(ax,'XScale','lin','XLim',xlimraw,'XTickMode','auto'); +0201 end +0202 logliny = get(gui.cm_handles.axes_proc_yaxis,'Label'); +0203 switch nmrproc.T1T2 +0204 case 'T1' +0205 ymin = min(real(nmrproc.s)); +0206 ymax = max(real(nmrproc.s)); +0207 if isfield(data.results,'invstd') +0208 ymin = min([ymin min(invstd.residual)]); +0209 ymax = max([ymax max(data.results.invstd.fit_s)]); +0210 end +0211 if ymin>0 +0212 ymin = ymin*0.8; +0213 else +0214 ymin = ymin*1.2; +0215 end +0216 switch logliny +0217 case 'y-axis -> lin' % log axes +0218 ticks = floor(min(log10(nmrproc.s(nmrproc.s>0))))-1:1:ceil(log10(nmrproc.s(end))); +0219 set(ax,'YScale','log','YLim',[10^(ticks(1)) 10^(ticks(end))],... +0220 'YTick',10.^ticks); +0221 case 'y-axis -> log' % lin axes +0222 set(ax,'YScale','lin','YLim',[ymin ymax*1.05],... +0223 'YTickMode','auto'); +0224 end +0225 case 'T2' +0226 ymin = min([min(real(nmrraw.s)) min(imag(nmrraw.s))]); +0227 ymax = max(real(nmrraw.s)); +0228 if isfield(data.results,'invstd') +0229 ymin = min([ymin min(invstd.residual)]); +0230 ymax = max([ymax max(data.results.invstd.fit_s)]); +0231 end +0232 switch logliny +0233 case 'y-axis -> lin' % log axes +0234 ticks = floor(log10(ymin))-1 :1: ceil(log10(ymax)); +0235 set(ax,'YScale','log','YLim',[10^(ticks(1)) ymax*1.1],... +0236 'YTick',10.^ticks); +0237 case 'y-axis -> log' % lin axes +0238 set(ax,'YScale','lin','YLim',[ymin ymax*1.1],... +0239 'YTickMode','auto'); +0240 end +0241 end +0242 if isfield(data.results,'invstd') +0243 ylims = get(ax,'YLim'); +0244 set(gui.axes_handles.raw,'YLim',ylims); +0245 end +0246 +0247 % labels +0248 if strcmp(data.process.timescale,'s') +0249 set(get(ax,'XLabel'),'String','time [s]'); +0250 else +0251 set(get(ax,'XLabel'),'String','time [ms]'); +0252 end +0253 set(get(ax,'YLabel'),'String','amplitude [a.u.]'); +0254 +0255 % legend +0256 if isfield(data.results,'invstd') +0257 if isfield(data.results,'invstd') && isfield(data.results.invstd,'uncert') +0258 lgdstr = {'uncert','signal','fit'}; +0259 else +0260 lgdstr = {'signal','fit'}; +0261 end +0262 % switch data.invstd.invtype +0263 % case {'MUMO','NNLS'} +0264 % otherwise +0265 % end +0266 switch nmrproc.T1T2 +0267 case 'T1' +0268 lgh = legend(ax,lgdstr,'Location','NorthWest',... +0269 'Tag','fitlegend','FontSize',10); +0270 case 'T2' +0271 lgh = legend(ax,lgdstr,'Location','NorthEast',... +0272 'Tag','fitlegend','FontSize',10); 0273 end -0274 -0275 switch loglinx -0276 case 'x-axis -> lin' % log axes -0277 set(axE,'XScale','log','XLim',xlims); -0278 case 'x-axis -> log' % lin axes -0279 set(axE,'XScale','lin','XLim',xlims); -0280 end -0281 end -0282 % finalize -0283 beautifyAxes(fig); -0284 end -0285 -0286 %% if joint inversion is activated all NMR signals are plotted into the -0287 % "ALL (joint)" panel - this is just a rough overview if no joint inversion -0288 % result is yet availabe -0289 if isjoint && ~isfield(data.results,'invjoint') && ... -0290 ~strcmp(data.invstd.regtype,'lcurve') -0291 -0292 ax = gui.axes_handles.all; -0293 clearSingleAxis(ax); -0294 -0295 INVdata = getappdata(fig,'INVdata'); -0296 -0297 nINV = size(INVdata,1); -0298 E0 = zeros(nINV,1); -0299 c = 0; -0300 invlevels = 0; -0301 for i = 1:nINV -0302 if isstruct(INVdata{i}) -0303 c = c + 1; -0304 invlevels(c) = i; %#ok<AGROW> -0305 E0(i,1) = sum(INVdata{i}.results.invstd.E0); -0306 end -0307 end -0308 -0309 % the pressure / saturation data -0310 table = data.pressure.table; -0311 -0312 if size(table,1) == 1 & invlevels ~= 0 %#ok<AND2> -0313 % apparently no CPS data was loaded but joint inversion is -0314 % activated ... so just plot the data -0315 if invlevels == 0 -0316 levels = []; -0317 else -0318 levels = invlevels; -0319 S = E0(invlevels)./max(E0(invlevels)); -0320 end -0321 else -0322 uselevel = cell2mat(table(:,1)); -0323 tablelevels = 1:size(table,1); -0324 tablelevels = tablelevels(uselevel); -0325 S = cell2mat(table(:,3)); -0326 % if numel(S)~=nINV -0327 % % S() -0328 % end -0329 [isin,levels] = ismember(invlevels,tablelevels); -0330 levels = tablelevels(levels(isin)); -0331 end -0332 -0333 if ~isempty(levels) && levels(1) > 0 && c > 0 -0334 hold(ax,'on'); -0335 -0336 mycol = flipud(parula(128)); -0337 for i = 1:numel(levels) -0338 t = INVdata{levels(i)}.results.invstd.fit_t; -0339 s = INVdata{levels(i)}.results.invstd.fit_s; -0340 s = S(levels(i)).*(s./sum(INVdata{levels(i)}.results.invstd.E0)); -0341 if i == 1 -0342 xlims = [min(t) max(t)]; -0343 ylims = [min(s) max(s)]; -0344 else -0345 xlims = [min([xlims(1) min(t)]) max([xlims(2) max(t)])]; -0346 ylims = [min([ylims(1) min(s)]) max([ylims(2) max(s)])]; -0347 end -0348 colind = getColorIndex(S(levels(i)),128); -0349 plot(t,s,'LineStyle','-','Color',mycol(colind,:),'Parent',ax); -0350 end -0351 set(ax,'YLim',ylims); -0352 -0353 loglinx = get(gui.cm_handles.axes_all_xaxis,'Label'); -0354 switch loglinx -0355 case 'x-axis -> lin' % log axes -0356 ticks = floor(min(log10(nmrproc.t(nmrproc.t>0)))):1:ceil(log10(nmrproc.t(end))); -0357 set(ax,'XScale','log','XLim',xlims,'XTick',10.^ticks); -0358 case 'x-axis -> log' % lin axes -0359 set(ax,'XScale','lin','XLim',xlims,'XTickMode','auto'); -0360 end -0361 logliny = get(gui.cm_handles.axes_all_yaxis,'Label'); -0362 switch logliny -0363 case 'y-axis -> lin' % log axes -0364 set(ax,'YScale','log'); -0365 case 'y-axis -> log' % lin axes -0366 set(ax,'YScale','lin'); -0367 end -0368 end -0369 end -0370 -0371 % update GUI data -0372 setappdata(fig,'data',data); -0373 setappdata(fig,'gui',gui); -0374 -0375 end -0376 -0377 %------------- END OF CODE -------------- -0378 -0379 %% License: -0380 % MIT License -0381 % -0382 % Copyright (c) 2018 Thomas Hiller -0383 % -0384 % Permission is hereby granted, free of charge, to any person obtaining a copy -0385 % of this software and associated documentation files (the "Software"), to deal -0386 % in the Software without restriction, including without limitation the rights -0387 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0388 % copies of the Software, and to permit persons to whom the Software is -0389 % furnished to do so, subject to the following conditions: -0390 % -0391 % The above copyright notice and this permission notice shall be included in all -0392 % copies or substantial portions of the Software. -0393 % -0394 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0395 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0396 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0397 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0398 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0399 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0400 % SOFTWARE. +0274 set(lgh,'TextColor',gui.myui.colors.panelFG); +0275 end +0276 +0277 % grid +0278 grid(ax,'on'); +0279 +0280 %% residual plot +0281 if isfield(data.results,'invstd') +0282 col = gui.myui.colors.axisL; +0283 xlims = get(ax,'XLim'); +0284 line(xlims,[0 0],'LineStyle','--','LineWidth',1,'Color',col,'Parent',axE); +0285 if nmrproc.noise > 0 +0286 err_mean = mean(invstd.residual./nmrproc.e); +0287 err_std = std(invstd.residual./nmrproc.e); +0288 line(xlims,[err_mean-err_std err_mean-err_std],... +0289 'LineStyle','--','LineWidth',1,'Color','r','Parent',axE); +0290 line(xlims,[err_mean+err_std err_mean+err_std],... +0291 'LineStyle','--','LineWidth',1,'Color','r','Parent',axE); +0292 line(xlims,[-1 -1],'LineStyle','-.','LineWidth',1,... +0293 'Color',col,'Parent',axE); +0294 line(xlims,[1 1],'LineStyle','-.','LineWidth',1,... +0295 'Color',col,'Parent',axE); +0296 set(axE,'XTickLabel',''); +0297 set(axE,'YLim',[-2 2]); +0298 set(axE,'YTick',[-1 0 1],'YTickLabelMode','auto'); +0299 set(get(axE,'YLabel'),'String',{'noise';'weighted';'residuals'},... +0300 'FontWeight','normal'); +0301 else +0302 err_mean = mean(invstd.residual); +0303 err_std = std(invstd.residual); +0304 line(xlims,[err_mean-1*err_std err_mean-1*err_std],... +0305 'LineStyle','-.','LineWidth',1,'Color',col,'Parent',axE); +0306 line(xlims,[err_mean+1*err_std err_mean+1*err_std],... +0307 'LineStyle','-.','LineWidth',1,'Color',col,'Parent',axE); +0308 set(axE,'XTickLabel',''); +0309 set(axE,'YLim',[err_mean-3*err_std err_mean+3*err_std]); +0310 set(axE,'YTick',[err_mean-3*err_std 0 err_mean+3*err_std],... +0311 'YTickLabelMode','auto') +0312 set(get(axE,'YLabel'),'String','residuals',... +0313 'FontWeight','normal'); +0314 end +0315 +0316 switch loglinx +0317 case 'x-axis -> lin' % log axes +0318 set(axE,'XScale','log','XLim',xlims); +0319 case 'x-axis -> log' % lin axes +0320 set(axE,'XScale','lin','XLim',xlims); +0321 end +0322 end +0323 % finalize +0324 beautifyAxes(fig); +0325 end +0326 +0327 %% if joint inversion is activated all NMR signals are plotted into the +0328 % "ALL (joint)" panel - this is just a rough overview if no joint inversion +0329 % result is yet availabe +0330 if isjoint && ~isfield(data.results,'invjoint') && ... +0331 ~strcmp(data.invstd.regtype,'lcurve') +0332 +0333 ax = gui.axes_handles.all; +0334 clearSingleAxis(ax); +0335 +0336 INVdata = getappdata(fig,'INVdata'); +0337 +0338 nINV = size(INVdata,1); +0339 E0 = zeros(nINV,1); +0340 c = 0; +0341 invlevels = 0; +0342 for i = 1:nINV +0343 if isstruct(INVdata{i}) +0344 c = c + 1; +0345 invlevels(c) = i; %#ok<AGROW> +0346 E0(i,1) = sum(INVdata{i}.results.invstd.E0); +0347 end +0348 end +0349 +0350 % the pressure / saturation data +0351 table = data.pressure.table; +0352 +0353 if size(table,1) == 1 & invlevels ~= 0 %#ok<AND2> +0354 % apparently no CPS data was loaded but joint inversion is +0355 % activated ... so just plot the data +0356 if invlevels == 0 +0357 levels = []; +0358 else +0359 levels = invlevels; +0360 S = E0(invlevels)./max(E0(invlevels)); +0361 end +0362 else +0363 uselevel = cell2mat(table(:,1)); +0364 tablelevels = 1:size(table,1); +0365 tablelevels = tablelevels(uselevel); +0366 S = cell2mat(table(:,3)); +0367 % if numel(S)~=nINV +0368 % % S() +0369 % end +0370 [isin,levels] = ismember(invlevels,tablelevels); +0371 levels = tablelevels(levels(isin)); +0372 end +0373 +0374 if ~isempty(levels) && levels(1) > 0 && c > 0 +0375 hold(ax,'on'); +0376 +0377 mycol = flipud(parula(128)); +0378 for i = 1:numel(levels) +0379 t = INVdata{levels(i)}.results.invstd.fit_t; +0380 s = INVdata{levels(i)}.results.invstd.fit_s; +0381 s = S(levels(i)).*(s./sum(INVdata{levels(i)}.results.invstd.E0)); +0382 if i == 1 +0383 xlims = [min(t) max(t)]; +0384 ylims = [min(s) max(s)]; +0385 else +0386 xlims = [min([xlims(1) min(t)]) max([xlims(2) max(t)])]; +0387 ylims = [min([ylims(1) min(s)]) max([ylims(2) max(s)])]; +0388 end +0389 colind = getColorIndex(S(levels(i)),128); +0390 plot(t,s,'LineStyle','-','Color',mycol(colind,:),'Parent',ax); +0391 end +0392 set(ax,'YLim',ylims); +0393 +0394 loglinx = get(gui.cm_handles.axes_all_xaxis,'Label'); +0395 switch loglinx +0396 case 'x-axis -> lin' % log axes +0397 ticks = floor(min(log10(nmrproc.t(nmrproc.t>0)))):1:ceil(log10(nmrproc.t(end))); +0398 set(ax,'XScale','log','XLim',xlims,'XTick',10.^ticks); +0399 case 'x-axis -> log' % lin axes +0400 set(ax,'XScale','lin','XLim',xlims,'XTickMode','auto'); +0401 end +0402 logliny = get(gui.cm_handles.axes_all_yaxis,'Label'); +0403 switch logliny +0404 case 'y-axis -> lin' % log axes +0405 set(ax,'YScale','log'); +0406 case 'y-axis -> log' % lin axes +0407 set(ax,'YScale','lin'); +0408 end +0409 end +0410 end +0411 +0412 % update GUI data +0413 setappdata(fig,'data',data); +0414 setappdata(fig,'gui',gui); +0415 +0416 end +0417 +0418 %------------- END OF CODE -------------- +0419 +0420 %% License: +0421 % MIT License +0422 % +0423 % Copyright (c) 2018 Thomas Hiller +0424 % +0425 % Permission is hereby granted, free of charge, to any person obtaining a copy +0426 % of this software and associated documentation files (the "Software"), to deal +0427 % in the Software without restriction, including without limitation the rights +0428 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0429 % copies of the Software, and to permit persons to whom the Software is +0430 % furnished to do so, subject to the following conditions: +0431 % +0432 % The above copyright notice and this permission notice shall be included in all +0433 % copies or substantial portions of the Software. +0434 % +0435 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0436 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0437 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0438 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0439 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0440 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0441 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/updateStatusInformation.html b/doc/nucleus/functions/interface/updateStatusInformation.html index 56521a0..eb31287 100644 --- a/doc/nucleus/functions/interface/updateStatusInformation.html +++ b/doc/nucleus/functions/interface/updateStatusInformation.html @@ -50,8 +50,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv, NUCLEUSmod -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/interface/updateToolTips.html b/doc/nucleus/functions/interface/updateToolTips.html index 9b6f925..69b26ea 100644 --- a/doc/nucleus/functions/interface/updateToolTips.html +++ b/doc/nucleus/functions/interface/updateToolTips.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: NUCLEUSinv -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- @@ -114,120 +114,126 @@

    SOURCE CODE ^'<u>Available options:</u><br>',... 0045 '<b>Mono exp.</b> Mono-exponential fitting.<br>',... 0046 '<b>Several free exp. (2-5)</b> Multi-exponential fitting with up to 5 free relaxation times.<br>',... -0047 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting with Optimization Toolbox.<br>',... -0048 '<b>Multi exp. (LU decomp.)</b> Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.<br><br>',... -0049 'Depending on the chosen method there are additional options available.<br><br>',... -0050 '<u>Default value:</u><br>',... -0051 '<b>Multi exp. (LSQ)</b><br>']; -0052 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... -0053 '<u>Available options:</u><br>',... -0054 '<font color="red">Mono exp.:<br>',... -0055 '<font color="black"><b>none</b><br>',... -0056 '<font color="red">Several free exp. (2-5):<br>',... -0057 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... -0058 '<font color="red">Multi exp. (LSQ):<br>',... -0059 '<font color="black"><b>Manual</b> Manual regularization.<br>',... -0060 '<font color="black"><b>Tikhonov (GCV)</b> Tikhonov regularization (SVD-Toolbox).<br>',... -0061 '<font color="black"><b>TSVD (GCV)</b> Regularization via Truncated SVD (SVD-Toolbox).<br>',... -0062 '<font color="black"><b>DSVD (GCV)</b> Regularization via Damped SVD (SVD-Toolbox).<br>',... -0063 '<font color="black"><b>Discrep.</b> Regularization according to discrepancy principle (SVD-Toolbox).<br>',... -0064 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br>',... -0065 '<font color="red">Multi exp. (LU decomp.):<br>',... -0066 '<font color="black"><b>Manual</b> Manual regularization.<br>',... -0067 '<font color="black"><b>Automatic</b> Automatic regularization.<br><br>',... -0068 '<u>Default value:</u><br>',... -0069 '<b>Manual</b><br>']; -0070 case 'lsqnonneg' -0071 inv_tstr = ['<HTML>Choose between different inversion (fitting) methods.<br><br>',... -0072 '<u>Available options:</u><br>',... -0073 '<b>Mono exp.</b> Mono-exponential fitting.<br>',... -0074 '<b>Several free exp. (2-5)</b> Multi-exponential fitting with up to 5 free relaxation times.<br>',... -0075 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting with Non Negative Least Squares (LSQNONNEG).<br>',... -0076 '<b>Multi exp. (LU decomp.)</b> Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.<br><br>',... -0077 'Depending on the chosen method there are additional options available.<br><br>',... -0078 '<u>Default value:</u><br>',... -0079 '<b>Multi exp. (LSQ)</b><br>']; -0080 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... -0081 '<u>Available options:</u><br>',... -0082 '<font color="red">Mono exp.:<br>',... -0083 '<font color="black"><b>none</b><br>',... -0084 '<font color="red">Several free exp. (2-5):<br>',... -0085 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... -0086 '<font color="red">Multi exp. (LSQ):<br>',... -0087 '<font color="black"><b>Manual</b> Manual regularization.<br>',... -0088 '<font color="black"><b>Tikhonov (GCV)</b> Tikhonov regularization (SVD-Toolbox).<br>',... -0089 '<font color="black"><b>TSVD (GCV)</b> Regularization via Truncated SVD (SVD-Toolbox).<br>',... -0090 '<font color="black"><b>DSVD (GCV)</b> Regularization via Damped SVD (SVD-Toolbox).<br>',... -0091 '<font color="black"><b>Discrep.</b> Regularization according to discrepancy principle (SVD-Toolbox).<br>',... -0092 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br>',... -0093 '<font color="red">Multi exp. (LU decomp.):<br>',... -0094 '<font color="black"><b>Manual</b> Manual regularization.<br>',... -0095 '<font color="black"><b>Automatic</b> Automatic regularization.<br><br>',... -0096 '<u>Default value:</u><br>',... -0097 '<b>Manual</b><br>']; -0098 end -0099 case 'off' -0100 inv_tstr = ['<HTML>Choose between different inversion (fitting) methods.<br><br>',... -0101 '<u>Available options:</u><br>',... -0102 '<b>Mono exp.</b> Mono-exponential fitting.<br>',... -0103 '<b>Several free exp. (2-5).</b> Multi-exponential fitting with up to 5 free relaxation times.<br>',... -0104 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting with Non Negative Least Squares (LSQNONNEG).<br><br>',... -0105 'Depending on the chosen method there are additional options available.<br><br>',... -0106 '<u>Default value:</u><br>',... -0107 '<b>Multi exp. (LSQ)</b><br>']; -0108 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... -0109 '<u>Available options:</u><br>',... -0110 '<font color="red">Mono exp.:<br>',... -0111 '<font color="black"><b>none</b><br>',... -0112 '<font color="red">Several free exp. (2-5):<br>',... -0113 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... -0114 '<font color="red">Multi exp. (LSQ):<br>',... -0115 '<font color="black"><b>Manual</b> Manual regularization.<br>',... -0116 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br><br>',... -0117 '<u>Default value:</u><br>',... -0118 '<b>Manual</b><br>']; -0119 end -0120 % update the tool tips -0121 set(gui.popup_handles.invstd_InvType,'UserData',struct('Tooltipstr',inv_tstr)); -0122 set(gui.popup_handles.invstd_InvTypeOpt,'UserData',struct('Tooltipstr',reg_tstr)); -0123 -0124 % update GUI data -0125 setappdata(fig,'gui',gui); -0126 setappdata(fig,'data',data); -0127 % if tool tips are activated they need to be updated on-the-fly -0128 switch data.info.ToolTips -0129 case 'on' -0130 switchToolTips(gui,'on'); -0131 otherwise -0132 % nothing to do -0133 end -0134 -0135 end -0136 -0137 %------------- END OF CODE -------------- -0138 -0139 %% License: -0140 % MIT License -0141 % -0142 % Copyright (c) 2019 Thomas Hiller -0143 % -0144 % Permission is hereby granted, free of charge, to any person obtaining a copy -0145 % of this software and associated documentation files (the "Software"), to deal -0146 % in the Software without restriction, including without limitation the rights -0147 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0148 % copies of the Software, and to permit persons to whom the Software is -0149 % furnished to do so, subject to the following conditions: -0150 % -0151 % The above copyright notice and this permission notice shall be included in all -0152 % copies or substantial portions of the Software. -0153 % -0154 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0155 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0156 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0157 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0158 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0159 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0160 % SOFTWARE. +0047 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting using the Optimization Toolbox.<br>',... +0048 '<b>Multi exp. (LU decomp.)</b> Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.<br>',... +0049 '<b>Multi modal</b> Multi modal fitting using the Optimization Toolbox.<br><br>',... +0050 'Depending on the chosen method there are additional options available.<br><br>',... +0051 '<u>Default value:</u><br>',... +0052 '<b>Multi exp. (LSQ)</b><br>']; +0053 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... +0054 '<u>Available options:</u><br>',... +0055 '<font color="red">Mono exp.:<br>',... +0056 '<font color="black"><b>none</b><br>',... +0057 '<font color="red">Several free exp. (2-5):<br>',... +0058 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... +0059 '<font color="red">Multi exp. (LSQ):<br>',... +0060 '<font color="black"><b>Manual</b> Manual regularization.<br>',... +0061 '<font color="black"><b>Tikhonov (GCV)</b> Tikhonov regularization (SVD-Toolbox).<br>',... +0062 '<font color="black"><b>TSVD (GCV)</b> Regularization via Truncated SVD (SVD-Toolbox).<br>',... +0063 '<font color="black"><b>DSVD (GCV)</b> Regularization via Damped SVD (SVD-Toolbox).<br>',... +0064 '<font color="black"><b>Discrep.</b> Regularization according to discrepancy principle (SVD-Toolbox).<br>',... +0065 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br>',... +0066 '<font color="red">Multi exp. (LU decomp.):<br>',... +0067 '<font color="black"><b>Manual</b> Manual regularization.<br>',... +0068 '<font color="black"><b>Automatic</b> Automatic regularization.<br>',... +0069 '<font color="red">Multi modal:<br>',... +0070 '<font color="black"><b>1-4</b> choose how many modes to use.<br><br>',... +0071 '<u>Default value:</u><br>',... +0072 '<b>Manual</b><br>']; +0073 case 'lsqnonneg' +0074 inv_tstr = ['<HTML>Choose between different inversion (fitting) methods.<br><br>',... +0075 '<u>Available options:</u><br>',... +0076 '<b>Mono exp.</b> Mono-exponential fitting.<br>',... +0077 '<b>Several free exp. (2-5)</b> Multi-exponential fitting with up to 5 free relaxation times.<br>',... +0078 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting with Non Negative Least Squares (LSQNONNEG).<br>',... +0079 '<b>Multi exp. (LU decomp.)</b> Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.<br>',... +0080 '<b>Multi modal</b> Multi modal fitting using fminsearchbnd.<br><br>',... +0081 'Depending on the chosen method there are additional options available.<br><br>',... +0082 '<u>Default value:</u><br>',... +0083 '<b>Multi exp. (LSQ)</b><br>']; +0084 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... +0085 '<u>Available options:</u><br>',... +0086 '<font color="red">Mono exp.:<br>',... +0087 '<font color="black"><b>none</b><br>',... +0088 '<font color="red">Several free exp. (2-5):<br>',... +0089 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... +0090 '<font color="red">Multi exp. (LSQ):<br>',... +0091 '<font color="black"><b>Manual</b> Manual regularization.<br>',... +0092 '<font color="black"><b>Tikhonov (GCV)</b> Tikhonov regularization (SVD-Toolbox).<br>',... +0093 '<font color="black"><b>TSVD (GCV)</b> Regularization via Truncated SVD (SVD-Toolbox).<br>',... +0094 '<font color="black"><b>DSVD (GCV)</b> Regularization via Damped SVD (SVD-Toolbox).<br>',... +0095 '<font color="black"><b>Discrep.</b> Regularization according to discrepancy principle (SVD-Toolbox).<br>',... +0096 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br>',... +0097 '<font color="red">Multi exp. (LU decomp.):<br>',... +0098 '<font color="black"><b>Manual</b> Manual regularization.<br>',... +0099 '<font color="black"><b>Automatic</b> Automatic regularization.<br>',... +0100 '<font color="red">Multi modal:<br>',... +0101 '<font color="black"><b>1-4</b> choose how many modes to use.<br><br>',... +0102 '<u>Default value:</u><br>',... +0103 '<b>Manual</b><br>']; +0104 end +0105 case 'off' +0106 inv_tstr = ['<HTML>Choose between different inversion (fitting) methods.<br><br>',... +0107 '<u>Available options:</u><br>',... +0108 '<b>Mono exp.</b> Mono-exponential fitting.<br>',... +0109 '<b>Several free exp. (2-5).</b> Multi-exponential fitting with up to 5 free relaxation times.<br>',... +0110 '<b>Multi exp. (LSQ)</b> Multi-exponential fitting with Non Negative Least Squares (LSQNONNEG).<br><br>',... +0111 'Depending on the chosen method there are additional options available.<br><br>',... +0112 '<u>Default value:</u><br>',... +0113 '<b>Multi exp. (LSQ)</b><br>']; +0114 reg_tstr = ['<HTML>Choose additional options depending on the chosen inversion (fitting) method.<br><br>',... +0115 '<u>Available options:</u><br>',... +0116 '<font color="red">Mono exp.:<br>',... +0117 '<font color="black"><b>none</b><br>',... +0118 '<font color="red">Several free exp. (2-5):<br>',... +0119 '<font color="black"><b>1-5</b> choose how many free relaxation times to use.<br>',... +0120 '<font color="red">Multi exp. (LSQ):<br>',... +0121 '<font color="black"><b>Manual</b> Manual regularization.<br>',... +0122 '<font color="black"><b>L-curve</b> Perform the L-curve test to find optimal regularization parameter lambda.<br><br>',... +0123 '<u>Default value:</u><br>',... +0124 '<b>Manual</b><br>']; +0125 end +0126 % update the tool tips +0127 set(gui.popup_handles.invstd_InvType,'UserData',struct('Tooltipstr',inv_tstr)); +0128 set(gui.popup_handles.invstd_InvTypeOpt,'UserData',struct('Tooltipstr',reg_tstr)); +0129 +0130 % update GUI data +0131 setappdata(fig,'gui',gui); +0132 setappdata(fig,'data',data); +0133 % if tool tips are activated they need to be updated on-the-fly +0134 switch data.info.ToolTips +0135 case 'on' +0136 switchToolTips(gui,'on'); +0137 otherwise +0138 % nothing to do +0139 end +0140 +0141 end +0142 +0143 %------------- END OF CODE -------------- +0144 +0145 %% License: +0146 % MIT License +0147 % +0148 % Copyright (c) 2019 Thomas Hiller +0149 % +0150 % Permission is hereby granted, free of charge, to any person obtaining a copy +0151 % of this software and associated documentation files (the "Software"), to deal +0152 % in the Software without restriction, including without limitation the rights +0153 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0154 % copies of the Software, and to permit persons to whom the Software is +0155 % furnished to do so, subject to the following conditions: +0156 % +0157 % The above copyright notice and this permission notice shall be included in all +0158 % copies or substantial portions of the Software. +0159 % +0160 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0161 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0162 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0163 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0164 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0165 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0166 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/interface/useSignalAsCalibration.html b/doc/nucleus/functions/interface/useSignalAsCalibration.html index af5e10c..6d94f18 100644 --- a/doc/nucleus/functions/interface/useSignalAsCalibration.html +++ b/doc/nucleus/functions/interface/useSignalAsCalibration.html @@ -50,15 +50,15 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • calibratePorosity determines a sample's porosity from a calibration
  • displayStatusText shows status information either in the GUI or on the
  • This function is called by: @@ -92,8 +92,8 @@

    SOURCE CODE ^% none 0024 % 0025 % See also: NUCLEUSinv -0026 % Author: Thomas Hiller -0027 % email: thomas.hiller[at]leibniz-liag.de +0026 % Author: see AUTHORS.md +0027 % email: see AUTHORS.md 0028 % License: MIT License (at end) 0029 0030 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/applyGatesToSignal.html b/doc/nucleus/functions/inversion/applyGatesToSignal.html index 5779130..57ac6db 100644 --- a/doc/nucleus/functions/inversion/applyGatesToSignal.html +++ b/doc/nucleus/functions/inversion/applyGatesToSignal.html @@ -59,8 +59,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0033 % 0034 % See also: -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/applyRegularization.html b/doc/nucleus/functions/inversion/applyRegularization.html index e2b8cd3..a2db257 100644 --- a/doc/nucleus/functions/inversion/applyRegularization.html +++ b/doc/nucleus/functions/inversion/applyRegularization.html @@ -64,8 +64,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0038 % 0039 % See also: -0040 % Author: Thomas Hiller -0041 % email: thomas.hiller[at]leibniz-liag.de +0040 % Author: see AUTHORS.md +0041 % email: see AUTHORS.md 0042 % License: MIT License (at end) 0043 0044 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/createKernelMatrix.html b/doc/nucleus/functions/inversion/createKernelMatrix.html index d562c4c..fa9799a 100644 --- a/doc/nucleus/functions/inversion/createKernelMatrix.html +++ b/doc/nucleus/functions/inversion/createKernelMatrix.html @@ -23,7 +23,7 @@

    PURPOSE ^ creates a Kernel matrix from signal time vector "t"

    SYNOPSIS ^

    -
    function K = createKernelMatrix(t,T,Tbulk,Tflag,T1IRfac)
    +
    function K = createKernelMatrix(t,T,Tbulk,Tdiff,Tflag,T1IRfac)

    DESCRIPTION ^

    createKernelMatrix creates a Kernel matrix from signal time vector "t"
    @@ -36,6 +36,7 @@ 

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • +
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • fcn_fitMultiModal is the objective function for N free distribution
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • SOURCE CODE ^

    -
    0001 function K = createKernelMatrix(t,T,Tbulk,Tflag,T1IRfac)
    +
    0001 function K = createKernelMatrix(t,T,Tbulk,Tdiff,Tflag,T1IRfac)
     0002 %createKernelMatrix creates a Kernel matrix from signal time vector "t"
     0003 %and relaxation time vector "T"
     0004 %
    @@ -83,70 +84,71 @@ 

    SOURCE CODE ^% t - signal time vector 0010 % T - relaxation times vector 0011 % Tbulk - bulk relaxation time -0012 % Tflag - 'T1' or 'T2' -0013 % T1IRfac - 1 or 2 (Sat. or Inv. Recovery) -0014 % -0015 % Outputs: -0016 % K - Kernel matrix size(length(t),length(T)) -0017 % -0018 % Example: -0019 % K = createKernelMatrix(t,T,2,'T1') -0020 % -0021 % Other m-files required: -0022 % none -0023 % -0024 % Subfunctions: -0025 % none -0026 % -0027 % MAT-files required: -0028 % none -0029 % -0030 % See also: -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de -0033 % License: MIT License (at end) -0034 -0035 %------------- BEGIN CODE -------------- -0036 -0037 %% init data -0038 K = zeros(length(t),length(T)); -0039 tr = repmat(t(:),[1,numel(T)]); -0040 Tr = repmat(T,[numel(t),1]); -0041 -0042 %% calculate K -0043 switch Tflag -0044 case 'T1' -0045 K = 1-T1IRfac.*(exp(-tr./Tr).*exp(-tr./Tbulk)); -0046 case 'T2' -0047 K = exp(-tr./Tr).*exp(-tr./Tbulk); -0048 end -0049 -0050 return -0051 -0052 %------------- END OF CODE -------------- -0053 -0054 %% License: -0055 % MIT License -0056 % -0057 % Copyright (c) 2018 Thomas Hiller -0058 % -0059 % Permission is hereby granted, free of charge, to any person obtaining a copy -0060 % of this software and associated documentation files (the "Software"), to deal -0061 % in the Software without restriction, including without limitation the rights -0062 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0063 % copies of the Software, and to permit persons to whom the Software is -0064 % furnished to do so, subject to the following conditions: -0065 % -0066 % The above copyright notice and this permission notice shall be included in all -0067 % copies or substantial portions of the Software. -0068 % -0069 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0070 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0071 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0072 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0073 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0074 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0075 % SOFTWARE.

    +0012 % Tdiff - diffusion relaxation time +0013 % Tflag - 'T1' or 'T2' +0014 % T1IRfac - 1 or 2 (Sat. or Inv. Recovery) +0015 % +0016 % Outputs: +0017 % K - Kernel matrix size(length(t),length(T)) +0018 % +0019 % Example: +0020 % K = createKernelMatrix(t,T,2,3,'T1',1) +0021 % +0022 % Other m-files required: +0023 % none +0024 % +0025 % Subfunctions: +0026 % none +0027 % +0028 % MAT-files required: +0029 % none +0030 % +0031 % See also: +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md +0034 % License: MIT License (at end) +0035 +0036 %------------- BEGIN CODE -------------- +0037 +0038 %% init data +0039 K = zeros(length(t),length(T)); +0040 tr = repmat(t(:),[1,numel(T)]); +0041 Tr = repmat(T,[numel(t),1]); +0042 +0043 %% calculate K +0044 switch Tflag +0045 case 'T1' +0046 K = 1-T1IRfac.*(exp(-tr./Tr).*exp(-tr./Tbulk).*exp(-tr./Tdiff)); +0047 case 'T2' +0048 K = exp(-tr./Tr).*exp(-tr./Tbulk).*exp(-tr./Tdiff); +0049 end +0050 +0051 return +0052 +0053 %------------- END OF CODE -------------- +0054 +0055 %% License: +0056 % MIT License +0057 % +0058 % Copyright (c) 2018 Thomas Hiller +0059 % +0060 % Permission is hereby granted, free of charge, to any person obtaining a copy +0061 % of this software and associated documentation files (the "Software"), to deal +0062 % in the Software without restriction, including without limitation the rights +0063 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0064 % copies of the Software, and to permit persons to whom the Software is +0065 % furnished to do so, subject to the following conditions: +0066 % +0067 % The above copyright notice and this permission notice shall be included in all +0068 % copies or substantial portions of the Software. +0069 % +0070 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0071 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0072 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0073 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0074 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0075 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0076 % SOFTWARE.

    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/estimateJacobian.html b/doc/nucleus/functions/inversion/estimateJacobian.html new file mode 100644 index 0000000..0e2f4e0 --- /dev/null +++ b/doc/nucleus/functions/inversion/estimateJacobian.html @@ -0,0 +1,153 @@ + + + + Description of estimateJacobian + + + + + + + + + + + +

    estimateJacobian +

    + +

    PURPOSE ^

    +
    numerically estimates (in a very simple manner) a
    + +

    SYNOPSIS ^

    +
    function J = estimateJacobian(f,x)
    + +

    DESCRIPTION ^

    +
    estimateJacobian numerically estimates (in a very simple manner) a
    +Jacobian at point x for a given function f
    +
    + Syntax:
    +       estimateJacobian
    +
    + Inputs:
    +       f - function handle
    +       x - point on function f
    +
    + Outputs:
    +       J - Jacobian
    +
    + Example:
    +       estimateJacobian(@(x)fcn_fitMultiModal(x,iparam),x);
    +
    + Other m-files required:
    +       none
    +
    + Subfunctions:
    +       none
    +
    + MAT-files required:
    +       none
    +
    + See also: NUCLEUSinv
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: +
      +
    +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function J = estimateJacobian(f,x)
    +0002 %estimateJacobian numerically estimates (in a very simple manner) a
    +0003 %Jacobian at point x for a given function f
    +0004 %
    +0005 % Syntax:
    +0006 %       estimateJacobian
    +0007 %
    +0008 % Inputs:
    +0009 %       f - function handle
    +0010 %       x - point on function f
    +0011 %
    +0012 % Outputs:
    +0013 %       J - Jacobian
    +0014 %
    +0015 % Example:
    +0016 %       estimateJacobian(@(x)fcn_fitMultiModal(x,iparam),x);
    +0017 %
    +0018 % Other m-files required:
    +0019 %       none
    +0020 %
    +0021 % Subfunctions:
    +0022 %       none
    +0023 %
    +0024 % MAT-files required:
    +0025 %       none
    +0026 %
    +0027 % See also: NUCLEUSinv
    +0028 % Author: see AUTHORS.md
    +0029 % email: see AUTHORS.md
    +0030 % License: MIT License (at end)
    +0031 
    +0032 %------------- BEGIN CODE --------------
    +0033 
    +0034 % define increment
    +0035 delta = 1e-7*sqrt(norm(x));
    +0036 % evaluate function at point x
    +0037 y = feval(f,x);
    +0038 % get dimensions of J
    +0039 n = length(y);
    +0040 m = length(x);
    +0041 % initialize J
    +0042 J = zeros(n,m);
    +0043 % loop over paramters
    +0044 for i = 1:m
    +0045     dx = zeros(1,m);
    +0046     dx(i) = delta/2;
    +0047     % evaluate single parameters at x+dx and x-dx
    +0048     % and divide the differenece by the increment
    +0049     col = (feval(f,x+dx)-feval(f,x-dx))/delta;
    +0050     % save result
    +0051     J(:,i) = col;
    +0052 end
    +0053 
    +0054 return
    +0055 
    +0056 %------------- END OF CODE --------------
    +0057 
    +0058 %% License:
    +0059 % MIT License
    +0060 %
    +0061 % Copyright (c) 2022 Thomas Hiller
    +0062 %
    +0063 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0064 % of this software and associated documentation files (the "Software"), to deal
    +0065 % in the Software without restriction, including without limitation the rights
    +0066 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0067 % copies of the Software, and to permit persons to whom the Software is
    +0068 % furnished to do so, subject to the following conditions:
    +0069 %
    +0070 % The above copyright notice and this permission notice shall be included in all
    +0071 % copies or substantial portions of the Software.
    +0072 %
    +0073 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0074 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0075 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0076 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0077 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0078 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0079 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/estimateUncertainty.html b/doc/nucleus/functions/inversion/estimateUncertainty.html new file mode 100644 index 0000000..ccbded2 --- /dev/null +++ b/doc/nucleus/functions/inversion/estimateUncertainty.html @@ -0,0 +1,606 @@ + + + + Description of estimateUncertainty + + + + + + + + + + + +

    estimateUncertainty +

    + +

    PURPOSE ^

    +
    calculates pseudo uncertainty estimates for multi
    + +

    SYNOPSIS ^

    +
    function [invstd,uncert] = estimateUncertainty(invtype,invstd,iparam,parameter)
    + +

    DESCRIPTION ^

    +
    estimateUncertainty calculates pseudo uncertainty estimates for multi
    +modal and LSQ inversion results
    +
    + Syntax:
    +       estimateUncertainty(invtype,invstd,iparam,parameter)
    +
    + Inputs:
    +       invtype - string indicating the inversion method of the optimal
    +                 fit ('NNLS' or 'MUMO')
    +       invstd - struct holding inversion results of the optimal fit
    +       iparam - struct holding original inversion settings
    +       parameter - struct that holds settings:
    +                   uncertMethod : which calculation method to use for
    +                                  'MUMO' the options are 'thresh' and 'ci' for
    +                                  'NNLS' the options are 'RTD_var', 'Lambda',
    +                                  'RMS_bound' and 'RMS_free'
    +                   uncertThresh : threshold for uncertainty search range
    +                   uncertChi2   : stop criteria for the chi2 deviation
    +                   uncertN      : number of models to calculate
    +                   uncertMax    : total number of unsuccessful attempts
    +                                  after which the calculation is stopped
    +
    + Outputs:
    +       invstd - same as input struct
    +       uncert - uncertainty data
    +
    + Example:
    +       [invstd] = estimateUncertainty('MUMO',invstd,iparam,uparam)
    +
    + Other m-files required:
    +       createKernelMatrix
    +       displayStatusText
    +       fitDataLSQ
    +       getFitErrors
    +
    + Subfunctions:
    +       none
    +
    + MAT-files required:
    +       none
    +
    + See also:
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function [invstd,uncert] = estimateUncertainty(invtype,invstd,iparam,parameter)
    +0002 %estimateUncertainty calculates pseudo uncertainty estimates for multi
    +0003 %modal and LSQ inversion results
    +0004 %
    +0005 % Syntax:
    +0006 %       estimateUncertainty(invtype,invstd,iparam,parameter)
    +0007 %
    +0008 % Inputs:
    +0009 %       invtype - string indicating the inversion method of the optimal
    +0010 %                 fit ('NNLS' or 'MUMO')
    +0011 %       invstd - struct holding inversion results of the optimal fit
    +0012 %       iparam - struct holding original inversion settings
    +0013 %       parameter - struct that holds settings:
    +0014 %                   uncertMethod : which calculation method to use for
    +0015 %                                  'MUMO' the options are 'thresh' and 'ci' for
    +0016 %                                  'NNLS' the options are 'RTD_var', 'Lambda',
    +0017 %                                  'RMS_bound' and 'RMS_free'
    +0018 %                   uncertThresh : threshold for uncertainty search range
    +0019 %                   uncertChi2   : stop criteria for the chi2 deviation
    +0020 %                   uncertN      : number of models to calculate
    +0021 %                   uncertMax    : total number of unsuccessful attempts
    +0022 %                                  after which the calculation is stopped
    +0023 %
    +0024 % Outputs:
    +0025 %       invstd - same as input struct
    +0026 %       uncert - uncertainty data
    +0027 %
    +0028 % Example:
    +0029 %       [invstd] = estimateUncertainty('MUMO',invstd,iparam,uparam)
    +0030 %
    +0031 % Other m-files required:
    +0032 %       createKernelMatrix
    +0033 %       displayStatusText
    +0034 %       fitDataLSQ
    +0035 %       getFitErrors
    +0036 %
    +0037 % Subfunctions:
    +0038 %       none
    +0039 %
    +0040 % MAT-files required:
    +0041 %       none
    +0042 %
    +0043 % See also:
    +0044 % Author: see AUTHORS.md
    +0045 % email: see AUTHORS.md
    +0046 % License: MIT License (at end)
    +0047 
    +0048 %------------- BEGIN CODE --------------
    +0049 
    +0050 %% get GUI handle and data
    +0051 fig = findobj('Tag','INV');
    +0052 if ~isempty(fig)
    +0053     gui = getappdata(fig,'gui');
    +0054 else
    +0055     % this routine will sill call 'displayStatusText' but then the output
    +0056     % is displayed at the command line
    +0057     gui = 0;
    +0058 end
    +0059 
    +0060 % get the main parameters
    +0061 uncertMethod = parameter.uncertMethod;
    +0062 uncertChi2 = parameter.uncertChi2;
    +0063 uncertThresh = parameter.uncertThresh;
    +0064 uncertN = parameter.uncertN;
    +0065 uncertMax = parameter.uncertMax;
    +0066 
    +0067 % original data that was fitted
    +0068 time = parameter.time;
    +0069 signal = parameter.signal;
    +0070 
    +0071 % kernel matrices for pure (single) E0 estimation and a second one
    +0072 % that extends the original time vector to "0" -> needed for nicer plots of
    +0073 % uncertainty towards shorter times
    +0074 switch iparam.T1T2
    +0075     case 'T1'
    +0076         K0 = createKernelMatrix(10*time(end),invstd.T1T2me',...
    +0077             iparam.Tb,iparam.Td,'T1',iparam.T1IRfac);
    +0078         
    +0079         time0 = [time' 2*time(end) 5*time(end) 10*time(end)];
    +0080         K0f = createKernelMatrix(time0,invstd.T1T2me',...
    +0081             iparam.Tb,iparam.Td,'T1',iparam.T1IRfac);
    +0082     case 'T2'
    +0083         K0 = createKernelMatrix(0,invstd.T1T2me',iparam.Tb,...
    +0084             iparam.Td,'T2',iparam.T1IRfac);
    +0085         
    +0086         time0 = [0 1e-6 time(1)/10 time(1)/5 time(1)/3 time(1)/2 time'];
    +0087         K0f = createKernelMatrix(time0,invstd.T1T2me',iparam.Tb,...
    +0088             iparam.Td,'T2',iparam.T1IRfac);
    +0089 end
    +0090 
    +0091 % switch depending on inversion method
    +0092 switch invtype
    +0093     case 'MUMO'
    +0094         % data needed from the optimal fit
    +0095         T = invstd.T;
    +0096         S = invstd.S;
    +0097         E = invstd.E;
    +0098         x = invstd.x;
    +0099         lb = invstd.lb;
    +0100         ub = invstd.ub;
    +0101         ci = invstd.ci;
    +0102         
    +0103         % kernel for the original fit needed for comparison of the uncertainty models
    +0104         K = createKernelMatrix(time,invstd.T1T2me',iparam.Tb,iparam.Td,...
    +0105             iparam.T1T2,iparam.T1IRfac);
    +0106         
    +0107         % counter
    +0108         count = 0;
    +0109         countm = 0;
    +0110         
    +0111         % initialize variables
    +0112         TDIST = zeros(uncertN,numel(invstd.T1T2me));
    +0113         SINTERP = zeros(numel(time0),uncertN);
    +0114         E0INTERP = zeros(uncertN,1);
    +0115         % calculate uncertainty models
    +0116         while count < uncertN
    +0117             switch uncertMethod
    +0118                 case 'thresh'
    +0119                     % randomly vary all parameters +- thresh
    +0120                     a = 1-uncertThresh;
    +0121                     b = 1+uncertThresh;
    +0122                     rr = (b-a).*rand(1,length(ci)) + a;
    +0123                     Ti = log(T).*rr(1:3:end); % do it on log-scale
    +0124                     Si = S.*rr(2:3:end);
    +0125                     Ei = E.*rr(3:3:end);
    +0126                     
    +0127                     xi = zeros(size(x));
    +0128                     xi(1:3:end) = Ti;
    +0129                     xi(2:3:end) = Si;
    +0130                     xi(3:3:end) = Ei;
    +0131                 case 'ci'
    +0132                     % randomly vary parameters within confidence interval
    +0133                     rr = 2.*rand(1,length(ci))-1;
    +0134                     xi = zeros(size(x));
    +0135                     xi(1:3:end) = log(T);
    +0136                     xi(2:3:end) = S;
    +0137                     xi(3:3:end) = E;
    +0138                     xi = xi+ci'.*rr;
    +0139             end
    +0140             
    +0141             % adjust for bounds if necessary
    +0142             xi(xi<lb) = lb(xi<lb);
    +0143             xi(xi>ub) = lb(xi>ub);
    +0144             
    +0145             % temporary values
    +0146             Ti = exp(xi(1:3:end)); % transform back to lin-scale
    +0147             Si = xi(2:3:end);
    +0148             Ei = xi(3:3:end);
    +0149             
    +0150             % create a temporary distribution with the new parameters
    +0151             TdistI = 0;
    +0152             for i = 1:numel(T)
    +0153                 tmp = 1./( Si(i)*sqrt(2*pi)).*exp(-((log(invstd.T1T2me') - log(Ti(i)))/ sqrt(2)/Si(i)).^2);
    +0154                 % scale to amplitude
    +0155                 if sum(tmp)>0
    +0156                     tmp = (tmp/sum(tmp)) * Ei(i);
    +0157                 end
    +0158                 % add the tmp per mu to Tdist
    +0159                 TdistI = TdistI + tmp;
    +0160             end
    +0161             % calculate temporary signal(s)
    +0162             s_interp = K*TdistI';
    +0163             s0_interp = K0f*TdistI';
    +0164             
    +0165             % get residuals and error measures
    +0166             if isfield(iparam,'W')
    +0167                 % when signal gating was used the error estimates need to be adjusted
    +0168                 outI = getFitErrors(signal,s_interp,iparam.noise,iparam.W);
    +0169             else
    +0170                 outI = getFitErrors(signal,s_interp,iparam.noise);
    +0171             end
    +0172             
    +0173             % check if the temporary chi2 is within the desired limit
    +0174             if abs(1-outI.chi2/invstd.chi2) <= uncertChi2
    +0175                 % if YES then keep it
    +0176                 count = count + 1;
    +0177                 % save RTD
    +0178                 TDIST(count,:) = TdistI;
    +0179                 % save signal
    +0180                 SINTERP(:,count) = s0_interp;
    +0181                 % save E0
    +0182                 E0INTERP(count,1) = K0*TdistI';
    +0183                 % status bar info
    +0184                 infostring = ['Calculating uncertainty models: ',...
    +0185                     num2str(count),' / ',num2str(uncertN)];
    +0186                 displayStatusText(gui,infostring);
    +0187                 % reset max counter
    +0188                 countm = 0;
    +0189             else
    +0190                 % as long as we did not find a model keep counting
    +0191                 if count < 1
    +0192                     countm = countm + 1;
    +0193                     infostring = ['Trying to find uncertainty model: ',...
    +0194                         num2str(countm),' / ',num2str(uncertMax)];
    +0195                     displayStatusText(gui,infostring);
    +0196                 end
    +0197             end
    +0198             % after to many unsuccessful attempts STOP
    +0199             if countm > uncertMax
    +0200                 infostring = ['No uncertainty model found: ',...
    +0201                     num2str(countm),' / ',num2str(uncertMax)];
    +0202                 displayStatusText(gui,infostring);
    +0203                 break;
    +0204             end
    +0205         end
    +0206         
    +0207         % output data
    +0208         % simple E0 confidence interval
    +0209         invstd.ciE0 = 2*std(E0INTERP);
    +0210         
    +0211         % uncertainty calculation results
    +0212         uncert.interp_t = time0(:);
    +0213         uncert.interp_E0 = E0INTERP;
    +0214         uncert.interp_f = TDIST;
    +0215         uncert.interp_s = SINTERP;
    +0216         
    +0217         % uncertainty patch for fitted signal
    +0218         uncert.interp_s_min = min(SINTERP,[],2);
    +0219         uncert.interp_s_max = max(SINTERP,[],2);
    +0220         
    +0221         % uncertainty patch for fitted RTD
    +0222         uncert.interp_f_min = min(TDIST);
    +0223         uncert.interp_f_max = max(TDIST);
    +0224         
    +0225         invstd.uncert = uncert;
    +0226         
    +0227     case 'NNLS'
    +0228         
    +0229         % 'RTD_var' | 'Lambda' | 'RMS_bound' | 'RMS_free'
    +0230         % uncertMethod = 'RMS_bound';
    +0231         
    +0232         switch uncertMethod
    +0233             case 'RTD_var'
    +0234                 % find uncertainty models by varying the optimal RTD bins
    +0235                 % individually
    +0236                 
    +0237                 % uncertN = 5;
    +0238                 % uncertThresh = 0.1;
    +0239                 % uncertChi2 = 0.05;
    +0240                 f_final = invstd.T1T2f;
    +0241                 
    +0242                 % initialize variables
    +0243                 % NOTE: we save 'uncertN' models for each RTD bin
    +0244                 TDIST = zeros(uncertN*numel(invstd.T1T2me),numel(invstd.T1T2me));
    +0245                 SINTERP = zeros(numel(time0),uncertN*numel(invstd.T1T2me));
    +0246                 E0INTERP = zeros(uncertN*numel(invstd.T1T2me),1);
    +0247                 % loop over all RTD bins
    +0248                 for i1 = 1:numel(f_final)
    +0249                     count = 0;
    +0250                     while count < uncertN
    +0251                         % start RTD is always the optimal one
    +0252                         x0 = f_final;
    +0253                         % global bounds
    +0254                         lb = zeros(size(x0));
    +0255                         ub = max(f_final).*3.*ones(size(x0));
    +0256                         % now draw a random value for the current RTD bin
    +0257                         a = 1-uncertThresh;
    +0258                         b = 1+uncertThresh;
    +0259                         rr = (b-a).*rand(1,1) + a;
    +0260                         % adjust the initial model and bounds accordingly
    +0261                         x0(i1) = f_final(i1)*rr;
    +0262                         lb(i1) = x0(i1);
    +0263                         ub(i1) = x0(i1);
    +0264                         
    +0265                         % save bounds for the LSQ inversion
    +0266                         iparam.bounds.lb = lb;
    +0267                         iparam.bounds.ub = ub;
    +0268                         iparam.bounds.f0 = x0;
    +0269                         
    +0270                         % calculate solution
    +0271                         invtmp = fitDataLSQ(time,signal,iparam);
    +0272                         s0_interp = K0f*invtmp.T1T2f;
    +0273                         
    +0274                         % check if the temporary chi2 and model norm are
    +0275                         % within the desired limits
    +0276                         if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 &&  ...
    +0277                                 abs(1-invtmp.xn/invstd.xn) <= uncertChi2/2
    +0278                             % if YES then keep it
    +0279                             count = count + 1;
    +0280                             % save RTD
    +0281                             TDIST(i1*uncertN-(uncertN-count),:) = invtmp.T1T2f;
    +0282                             % save signal
    +0283                             SINTERP(:,i1*uncertN-(uncertN-count)) = s0_interp;
    +0284                             % save E0
    +0285                             E0INTERP(i1*uncertN-(uncertN-count),1) = invtmp.E0;
    +0286                             % status bar info
    +0287                             infostring = ['Calculating uncertainty models: ',...
    +0288                                 num2str(count),' / ',num2str(uncertN),...
    +0289                                 ' for RTD bin: ',num2str(i1),' / ',num2str(numel(f_final))];
    +0290                             displayStatusText(gui,infostring);
    +0291                         end
    +0292                     end
    +0293                 end
    +0294                 
    +0295             case 'Lambda'
    +0296                 % find uncertainty models by varying the optimal
    +0297                 % regularization parameter lambda
    +0298                 
    +0299                 % set regularization to 'manual' just in case
    +0300                 iparam.regMethod = 'manual';
    +0301                 
    +0302                 % lambda search range
    +0303                 a = log10(invstd.lambda_out/100);
    +0304                 b = log10(invstd.lambda_out*10);
    +0305                 
    +0306                 % counter
    +0307                 count = 0;
    +0308                 countm = 0;
    +0309                 
    +0310                 % initialize variables
    +0311                 TDIST = zeros(uncertN,numel(invstd.T1T2me));
    +0312                 SINTERP = zeros(numel(time0),uncertN);
    +0313                 E0INTERP = zeros(uncertN,1);
    +0314                 % calculate uncertainty models
    +0315                 while count < uncertN
    +0316                     % draw a random lambda value
    +0317                     rr = (b-a).*rand(1,1) + a;
    +0318                     iparam.lambda = 10^(rr);
    +0319                     % calculate solution
    +0320                     invtmp = fitDataLSQ(time,signal,iparam);
    +0321                     s0_interp = K0f*invtmp.T1T2f;
    +0322                     
    +0323                     % check if temporary chi2 and model norm are within the
    +0324                     % desired limits
    +0325                     if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 &&...
    +0326                             abs(1-invtmp.xn/invstd.xn) <= uncertChi2*10
    +0327                         % if YES then keep it
    +0328                         count = count + 1;
    +0329                         % save RTD
    +0330                         TDIST(count,:) = invtmp.T1T2f;
    +0331                         % save signal
    +0332                         SINTERP(:,count) = s0_interp;
    +0333                         % save E0
    +0334                         E0INTERP(count,1) = invtmp.E0;
    +0335                         % status bar info
    +0336                         infostring = ['Calculating uncertainty models: ',...
    +0337                             num2str(count),' / ',num2str(uncertN)];
    +0338                         displayStatusText(gui,infostring);
    +0339                         % reset max counter
    +0340                         countm = 0;
    +0341                     else
    +0342 %                         % update the lambda search range
    +0343 %                         if invtmp.xn > invstd.xn % lambda was too small
    +0344 %                             % new lower lambda search bound
    +0345 %                             a = rr;
    +0346 %                         end
    +0347 %                         if invtmp.chi2 > invstd.chi2 % lambda was too big
    +0348 %                             % new upper lambda search bound
    +0349 %                             b = rr;
    +0350 %                         end
    +0351                         % as long as we did not find a model keep counting
    +0352                         if count < 1
    +0353                             countm = countm + 1;
    +0354                             infostring = ['Trying to find uncertainty model: ',...
    +0355                                 num2str(countm),' / ',num2str(uncertMax)];
    +0356                             displayStatusText(gui,infostring);
    +0357                         end
    +0358                     end
    +0359                     % after to many unsuccessful attempts STOP
    +0360                     if countm > uncertMax
    +0361                         infostring = ['No uncertainty model found: ',...
    +0362                             num2str(countm),' / ',num2str(uncertMax)];
    +0363                         displayStatusText(gui,infostring);
    +0364                         break;
    +0365                     end                    
    +0366                 end
    +0367                 
    +0368             case 'RMS_bound'
    +0369                 % find uncertainty models by adding random noise (based on
    +0370                 % fit RMS) on the optimal fit to create new "raw data" and
    +0371                 % invert them with the original optimal inversion settings
    +0372                 % select models based on chi2 and model norm bounds
    +0373                 
    +0374                 % set regularization to 'manual' just in case
    +0375                 iparam.regMethod = 'manual';
    +0376                 
    +0377                 % counter
    +0378                 count = 0;
    +0379                 countm = 0;
    +0380                 
    +0381                 % initialize variables
    +0382                 TDIST = zeros(uncertN,numel(invstd.T1T2me));
    +0383                 SINTERP = zeros(numel(time0),uncertN);
    +0384                 E0INTERP = zeros(uncertN,1);
    +0385                 % calculate uncertainty models
    +0386                 while count < uncertN
    +0387                     % the original fit
    +0388                     sig1 = invstd.fit_s;
    +0389                     % create some random noise based on original fit RMS
    +0390                     [signalN,~] = addNoiseToSignal(sig1,0,invstd.rms);
    +0391                     % calculate solution
    +0392                     invtmp = fitDataLSQ(time,signalN,iparam);
    +0393                     s0_interp = K0f*invtmp.T1T2f;
    +0394                     
    +0395                     % check if temporary chi2 and model norm are within the
    +0396                     % desired limits
    +0397                     if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 &&...
    +0398                             abs(1-invtmp.xn/invstd.xn) <= uncertChi2*10
    +0399                         % if YES then keep it
    +0400                         count = count + 1;
    +0401                         % save RTD
    +0402                         TDIST(count,:) = invtmp.T1T2f;
    +0403                         % save signal
    +0404                         SINTERP(:,count) = s0_interp;
    +0405                         % save E0
    +0406                         E0INTERP(count,1) = invtmp.E0;
    +0407                         % status bar info
    +0408                         infostring = ['Calculating uncertainty models: ',...
    +0409                             num2str(count),' / ',num2str(uncertN)];
    +0410                         displayStatusText(gui,infostring);
    +0411                         % reset max counter
    +0412                         countm = 0;
    +0413                     else
    +0414                         % as long as we did not find a model keep counting
    +0415                         if count < 1
    +0416                             countm = countm + 1;
    +0417                             infostring = ['Trying to find uncertainty model: ',...
    +0418                                 num2str(countm),' / ',num2str(uncertMax)];
    +0419                             displayStatusText(gui,infostring);
    +0420                         end
    +0421                     end
    +0422                     % after to many unsuccessful attempts STOP
    +0423                     if countm > uncertMax
    +0424                         infostring = ['No uncertainty model found: ',...
    +0425                             num2str(countm),' / ',num2str(uncertMax)];
    +0426                         displayStatusText(gui,infostring);
    +0427                         break;
    +0428                     end
    +0429                 end
    +0430                    
    +0431             case 'RMS_free'
    +0432                 % find uncertainty models by adding random noise (based on
    +0433                 % fit RMS) on the optimal fit to create new "raw data" and
    +0434                 % invert them with the original optimal inversion settings
    +0435                 
    +0436                 % set regularization to 'manual' just in case
    +0437                 iparam.regMethod = 'manual';
    +0438                 
    +0439                 % initialize variables
    +0440                 TDIST = zeros(uncertN,numel(invstd.T1T2me));
    +0441                 SINTERP = zeros(numel(time0),uncertN);
    +0442                 E0INTERP = zeros(uncertN,1);
    +0443                 % calculate uncertainty models
    +0444                 for count = 1:uncertN
    +0445                     % the original fit
    +0446                     sig1 = invstd.fit_s;
    +0447                     % create some random noise based on original fit RMS
    +0448                     [signalN,~] = addNoiseToSignal(sig1,0,invstd.rms);
    +0449                     
    +0450                     % calculate solution
    +0451                     invtmp = fitDataLSQ(time,signalN,iparam);
    +0452                     s0_interp = K0f*invtmp.T1T2f;
    +0453                     % save RTD
    +0454                     TDIST(count,:) = invtmp.T1T2f;
    +0455                     % save signal
    +0456                     SINTERP(:,count) = s0_interp;
    +0457                     % save E0
    +0458                     E0INTERP(count,1) = invtmp.E0;
    +0459                     % status bar info
    +0460                     infostring = ['Calculating uncertainty models: ',...
    +0461                         num2str(count),' / ',num2str(uncertN)];
    +0462                     displayStatusText(gui,infostring);
    +0463                 end
    +0464         end
    +0465         
    +0466         % output data
    +0467         % simple E0 confidence interval
    +0468         invstd.ciE0 = 2*std(E0INTERP);
    +0469         
    +0470         % uncertainty calculation results
    +0471         uncert.interp_t = time0(:);
    +0472         uncert.interp_E0 = E0INTERP;
    +0473         uncert.interp_f = TDIST;
    +0474         uncert.interp_s = SINTERP;
    +0475         
    +0476         % uncertainty patch for fitted signal
    +0477         uncert.interp_s_min = min(SINTERP,[],2);
    +0478         uncert.interp_s_max = max(SINTERP,[],2);
    +0479         
    +0480         % uncertainty patch for fitted RTD
    +0481         uncert.interp_f_min = min(TDIST);
    +0482         uncert.interp_f_max = max(TDIST);
    +0483         
    +0484         invstd.uncert = uncert;
    +0485         
    +0486     otherwise
    +0487         % nothing to do
    +0488         uncert = [];
    +0489 end
    +0490 
    +0491 return
    +0492 
    +0493 %------------- END OF CODE --------------
    +0494 
    +0495 %% License:
    +0496 % MIT License
    +0497 %
    +0498 % Copyright (c) 2022 Thomas Hiller
    +0499 %
    +0500 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0501 % of this software and associated documentation files (the "Software"), to deal
    +0502 % in the Software without restriction, including without limitation the rights
    +0503 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0504 % copies of the Software, and to permit persons to whom the Software is
    +0505 % furnished to do so, subject to the following conditions:
    +0506 %
    +0507 % The above copyright notice and this permission notice shall be included in all
    +0508 % copies or substantial portions of the Software.
    +0509 %
    +0510 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0511 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0512 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0513 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0514 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0515 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0516 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fcn_JointInvfixed.html b/doc/nucleus/functions/inversion/fcn_JointInvfixed.html index 50e2ab4..4131afb 100644 --- a/doc/nucleus/functions/inversion/fcn_JointInvfixed.html +++ b/doc/nucleus/functions/inversion/fcn_JointInvfixed.html @@ -40,6 +40,7 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% g : signal vector (all NMR signals) 0014 % indt : number of echoes/points per NMR signal 0015 % Tb : bulk relaxation time -0016 % T1T2 : flag between 'T1' or 'T2' inversion -0017 % T1IRfac : either '1' or '2' depending on T1 method -0018 % SatImbDrain : string indicating if a NMR signal is from -0019 % the drainage or imbibition branch (e.g. 'DDID') -0020 % p : pressure values -0021 % igeom : geometry struct -0022 % x : relaxation time vector -0023 % f : relaxation time distribution (RTD) -0024 % -0025 % Outputs: -0026 % F - norm of the residual vector -0027 % varargout - cell that holds several more data -0028 % ig : fitted NMR signals -0029 % XX : Kernel matrix -0030 % igeom : final geometry struct -0031 % iSAT : final pressure/saturation struct -0032 % -0033 % Example: -0034 % F = fcn_JointInvfixed(X,iparam) -0035 % -0036 % Other m-files required: -0037 % getConstants -0038 % getCornerNMRparameter -0039 % getGeometryParameter -0040 % getPartialSaturationMatrix -0041 % getSaturationFromPressureBatch -0042 % -0043 % Subfunctions: -0044 % none -0045 % -0046 % MAT-files required: -0047 % none -0048 % -0049 % See also: -0050 % Author: Thomas Hiller -0051 % email: thomas.hiller[at]leibniz-liag.de -0052 % License: MIT License (at end) -0053 -0054 %------------- BEGIN CODE -------------- -0055 -0056 %% input parameters -0057 t = iparam.t; -0058 g = iparam.g; -0059 indt = iparam.indt; -0060 Tb = iparam.Tb; -0061 T1T2 = iparam.T1T2; -0062 T1IRfac = iparam.T1IRfac; -0063 SatImbDrain = iparam.SatImbDrain; -0064 p = iparam.p; -0065 igeom = iparam.igeom; -0066 x = iparam.x; -0067 f = iparam.f; -0068 constants = getConstants; -0069 -0070 %% wait-bar option -0071 wbopts.show = false; -0072 -0073 %% surface relaxivity as log10 value -0074 rhos = 10^X(1); -0075 -0076 %% switch depending on geometry -0077 switch igeom.type -0078 case 'cyl' -0079 % new PSD with updated rhos -0080 ipsddata.r = x.*2.*rhos; -0081 ipsddata.psd = f; -0082 % new saturation state -0083 igeom.radius = ipsddata.r'; -0084 igeom = getGeometryParameter(igeom); -0085 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); -0086 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); -0087 -0088 % get the surface-to-volume ratio -0089 SV = igeom.P0./igeom.A0; -0090 -0091 % Kernel matrix -0092 Kf = zeros(length(t),length(SV)); -0093 switch T1T2 -0094 case 'T1' -0095 for i=1:length(SV) -0096 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); -0097 end -0098 case 'T2' -0099 for i=1:length(SV) -0100 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0101 end -0102 end -0103 -0104 K = Kf; -0105 % Kernel matrix times saturation matrix -0106 XX = K.*IPS; -0107 -0108 case {'ang','poly'} -0109 % new PSD with updated rhos -0110 ipsddata.r = x.*igeom.a.*rhos; -0111 ipsddata.psd = f; -0112 igeom.radius = ipsddata.r'; -0113 % new saturation state -0114 igeom = getGeometryParameter(igeom); -0115 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); -0116 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); -0117 -0118 % get the amplitudes and surface-to-volume ratios for the partially -0119 % saturated corners -0120 SVdata = getCornerNMRparameter(igeom,iSAT,indt,SatImbDrain); -0121 SVdata.TT = repmat(t',[1 length(SVdata.SVF)]); -0122 -0123 SV = SVdata.SVF'; -0124 SVC = SVdata.SVC; -0125 Amp = SVdata.Ampl; -0126 TT = SVdata.TT; -0127 -0128 % Kernel matrix -0129 Kf = zeros(length(t),length(SV)); -0130 switch T1T2 -0131 case 'T1' -0132 for i=1:length(SV) -0133 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); -0134 end -0135 % Kernel matrix for partial saturation -0136 Kc = zeros(length(t),length(SV)); -0137 for i=1:size(SVC,1) -0138 Kc = Kc + ( squeeze(Amp(i,:,:)) .* ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); -0139 end -0140 case 'T2' -0141 for i=1:length(SV) -0142 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0143 end -0144 % Kernel matrix for partial saturation -0145 Kc = zeros(length(t),length(SV)); -0146 for i=1:size(SVC,1) -0147 Kc = Kc + ( squeeze(Amp(i,:,:)) .* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); -0148 end -0149 end -0150 -0151 K = Kf; -0152 K(IPS~=1) = Kc(IPS~=1); -0153 % Kernel matrix times saturation matrix -0154 XX = K; -0155 -0156 otherwise -0157 % nothing to do -0158 end -0159 -0160 %% weighting -0161 if isfield(iparam,'W') -0162 g = iparam.W*g'; -0163 XX = iparam.W*XX; -0164 g = g'; -0165 end -0166 -0167 %% corresponding signal g = Kf -0168 ig = XX*f'; -0169 -0170 %% residual -0171 F = norm(ig - g'); -0172 -0173 %% output -0174 if nargout > 1 -0175 varargout{1} = ig; -0176 varargout{2} = XX; -0177 varargout{3} = igeom; -0178 varargout{4} = iSAT; -0179 end -0180 -0181 return -0182 -0183 %------------- END OF CODE -------------- +0016 % Td : diffusion relaxation time +0017 % T1T2 : flag between 'T1' or 'T2' inversion +0018 % T1IRfac : either '1' or '2' depending on T1 method +0019 % SatImbDrain : string indicating if a NMR signal is from +0020 % the drainage or imbibition branch (e.g. 'DDID') +0021 % p : pressure values +0022 % igeom : geometry struct +0023 % x : relaxation time vector +0024 % f : relaxation time distribution (RTD) +0025 % +0026 % Outputs: +0027 % F - norm of the residual vector +0028 % varargout - cell that holds several more data +0029 % ig : fitted NMR signals +0030 % XX : Kernel matrix +0031 % igeom : final geometry struct +0032 % iSAT : final pressure/saturation struct +0033 % +0034 % Example: +0035 % F = fcn_JointInvfixed(X,iparam) +0036 % +0037 % Other m-files required: +0038 % getConstants +0039 % getCornerNMRparameter +0040 % getGeometryParameter +0041 % getPartialSaturationMatrix +0042 % getSaturationFromPressureBatch +0043 % +0044 % Subfunctions: +0045 % none +0046 % +0047 % MAT-files required: +0048 % none +0049 % +0050 % See also: +0051 % Author: see AUTHORS.md +0052 % email: see AUTHORS.md +0053 % License: MIT License (at end) +0054 +0055 %------------- BEGIN CODE -------------- +0056 +0057 %% input parameters +0058 t = iparam.t; +0059 g = iparam.g; +0060 indt = iparam.indt; +0061 Tb = iparam.Tb; +0062 Td = iparam.Td; +0063 T1T2 = iparam.T1T2; +0064 T1IRfac = iparam.T1IRfac; +0065 SatImbDrain = iparam.SatImbDrain; +0066 p = iparam.p; +0067 igeom = iparam.igeom; +0068 x = iparam.x; +0069 f = iparam.f; +0070 constants = getConstants; +0071 +0072 %% wait-bar option +0073 wbopts.show = false; +0074 +0075 %% surface relaxivity as log10 value +0076 rhos = 10^X(1); +0077 +0078 %% switch depending on geometry +0079 switch igeom.type +0080 case 'cyl' +0081 % new PSD with updated rhos +0082 ipsddata.r = x.*2.*rhos; +0083 ipsddata.psd = f; +0084 % new saturation state +0085 igeom.radius = ipsddata.r'; +0086 igeom = getGeometryParameter(igeom); +0087 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); +0088 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); +0089 +0090 % get the surface-to-volume ratio +0091 SV = igeom.P0./igeom.A0; +0092 +0093 % Kernel matrix +0094 Kf = zeros(length(t),length(SV)); +0095 switch T1T2 +0096 case 'T1' +0097 for i=1:length(SV) +0098 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0099 end +0100 case 'T2' +0101 for i=1:length(SV) +0102 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0103 end +0104 end +0105 +0106 K = Kf; +0107 % Kernel matrix times saturation matrix +0108 XX = K.*IPS; +0109 +0110 case {'ang','poly'} +0111 % new PSD with updated rhos +0112 ipsddata.r = x.*igeom.a.*rhos; +0113 ipsddata.psd = f; +0114 igeom.radius = ipsddata.r'; +0115 % new saturation state +0116 igeom = getGeometryParameter(igeom); +0117 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); +0118 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); +0119 +0120 % get the amplitudes and surface-to-volume ratios for the partially +0121 % saturated corners +0122 SVdata = getCornerNMRparameter(igeom,iSAT,indt,SatImbDrain); +0123 SVdata.TT = repmat(t',[1 length(SVdata.SVF)]); +0124 +0125 SV = SVdata.SVF'; +0126 SVC = SVdata.SVC; +0127 Amp = SVdata.Ampl; +0128 TT = SVdata.TT; +0129 +0130 % Kernel matrix +0131 Kf = zeros(length(t),length(SV)); +0132 switch T1T2 +0133 case 'T1' +0134 for i=1:length(SV) +0135 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0136 end +0137 % Kernel matrix for partial saturation +0138 Kc = zeros(length(t),length(SV)); +0139 for i=1:size(SVC,1) +0140 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0141 ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); +0142 end +0143 case 'T2' +0144 for i=1:length(SV) +0145 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0146 end +0147 % Kernel matrix for partial saturation +0148 Kc = zeros(length(t),length(SV)); +0149 for i=1:size(SVC,1) +0150 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0151 exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); +0152 end +0153 end +0154 +0155 K = Kf; +0156 K(IPS~=1) = Kc(IPS~=1); +0157 % Kernel matrix times saturation matrix +0158 XX = K; +0159 +0160 otherwise +0161 % nothing to do +0162 end +0163 +0164 %% weighting +0165 if isfield(iparam,'W') +0166 g = iparam.W*g'; +0167 XX = iparam.W*XX; +0168 g = g'; +0169 end +0170 +0171 %% corresponding signal g = Kf +0172 ig = XX*f'; +0173 +0174 %% residual +0175 F = norm(ig - g'); +0176 +0177 %% output +0178 if nargout > 1 +0179 varargout{1} = ig; +0180 varargout{2} = XX; +0181 varargout{3} = igeom; +0182 varargout{4} = iSAT; +0183 end 0184 -0185 %% License: -0186 % MIT License -0187 % -0188 % Copyright (c) 2018 Thomas Hiller -0189 % -0190 % Permission is hereby granted, free of charge, to any person obtaining a copy -0191 % of this software and associated documentation files (the "Software"), to deal -0192 % in the Software without restriction, including without limitation the rights -0193 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0194 % copies of the Software, and to permit persons to whom the Software is -0195 % furnished to do so, subject to the following conditions: -0196 % -0197 % The above copyright notice and this permission notice shall be included in all -0198 % copies or substantial portions of the Software. -0199 % -0200 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0201 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0202 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0203 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0204 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0205 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0206 % SOFTWARE.

    +0185 return +0186 +0187 %------------- END OF CODE -------------- +0188 +0189 %% License: +0190 % MIT License +0191 % +0192 % Copyright (c) 2018 Thomas Hiller +0193 % +0194 % Permission is hereby granted, free of charge, to any person obtaining a copy +0195 % of this software and associated documentation files (the "Software"), to deal +0196 % in the Software without restriction, including without limitation the rights +0197 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0198 % copies of the Software, and to permit persons to whom the Software is +0199 % furnished to do so, subject to the following conditions: +0200 % +0201 % The above copyright notice and this permission notice shall be included in all +0202 % copies or substantial portions of the Software. +0203 % +0204 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0205 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0206 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0207 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0208 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0209 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0210 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fcn_JointInvfree.html b/doc/nucleus/functions/inversion/fcn_JointInvfree.html index 46b190d..de4b3e7 100644 --- a/doc/nucleus/functions/inversion/fcn_JointInvfree.html +++ b/doc/nucleus/functions/inversion/fcn_JointInvfree.html @@ -38,6 +38,7 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% t : augmented time vector 0012 % g : augmented signal vector 0013 % Tb : bulk relaxation time -0014 % T1T2 : 'T1' / 'T2' flag -0015 % T1IRfac : either '1' or '2' depending on T1 method -0016 % L : smoothness constraint -0017 % lambda : regularization parameter -0018 % igeom : geometry structure data -0019 % IPS : saturation status matrix -0020 % SVdata : corner saturation data -0021 % -0022 % Outputs: -0023 % F - residual -0024 % J - Jacobian (optional) -0025 % ig - fitted signal (optional) -0026 % XX - augmented Kernel matrix (optional) -0027 % -0028 % Example: -0029 % [F,J,ig,XX] = fcn_JointInvfree(X,iparam) -0030 % -0031 % Other m-files required: -0032 % none -0033 % -0034 % Subfunctions: -0035 % none -0036 % -0037 % MAT-files required: -0038 % none -0039 % -0040 % See also: -0041 % Author: Thomas Hiller -0042 % email: thomas.hiller[at]leibniz-liag.de -0043 % License: MIT License (at end) -0044 -0045 %------------- BEGIN CODE -------------- -0046 -0047 %% input parameters -0048 t = iparam.t; -0049 g = iparam.g; -0050 Tb = iparam.Tb; -0051 T1T2 = iparam.T1T2; -0052 T1IRfac = iparam.T1IRfac; -0053 L = iparam.L; -0054 lambda = iparam.lambda; -0055 igeom = iparam.igeom; -0056 IPS = iparam.IPS; -0057 -0058 % length of relaxation time distr. -0059 n = length(X)-1; -0060 % amplitude of relaxation time distr. -0061 x = X(1:n); -0062 % surface relaxivity as scaled log10 value -0063 rhos = 10^X(n+1); -0064 -0065 %% switch depending on geometry -0066 switch igeom.type -0067 case 'cyl' -0068 % for cylindrical pores SV is simply the full saturated S/V ratio -0069 SV = iparam.SVdata.SVF; -0070 -0071 % Kernel matrix -0072 Kf = zeros(length(t),length(SV)); -0073 switch T1T2 -0074 case 'T1' -0075 for i=1:length(SV) -0076 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); -0077 end -0078 case 'T2' -0079 for i=1:length(SV) -0080 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0081 end -0082 end -0083 K = Kf; -0084 % Kernel matrix times saturation matrix -0085 XX = K.*IPS; -0086 -0087 case {'ang','poly'} -0088 % for angular and polygonal pores there are full saturation S/V and -0089 % partial saturation S/V ratios and amplitudes -0090 SV = iparam.SVdata.SVF; -0091 SVC = iparam.SVdata.SVC; -0092 Amp = iparam.SVdata.Ampl; -0093 TT = iparam.SVdata.TT; -0094 -0095 % Kernel matrix -0096 Kf = zeros(length(t),length(SV)); -0097 switch T1T2 -0098 case 'T1' -0099 for i=1:length(SV) -0100 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); -0101 end -0102 % Kernel matrix for partial saturation -0103 Kc = zeros(length(t),length(SV)); -0104 for i=1:size(SVC,1) -0105 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... -0106 ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); -0107 end -0108 case 'T2' -0109 for i=1:length(SV) -0110 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0111 end -0112 % Kernel matrix for partial saturation -0113 Kc = zeros(length(t),length(SV)); -0114 for i=1:size(SVC,1) -0115 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... -0116 exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); -0117 end -0118 end -0119 % full saturation matrix -0120 K = Kf; -0121 % for partial saturation points use the Kc values -0122 K(IPS~=1) = Kc(IPS~=1); -0123 % Kernel matrix times saturation matrix -0124 XX = K; -0125 end -0126 -0127 % error weighing -0128 if isfield(iparam,'W') -0129 g = iparam.W*g'; -0130 XX = iparam.W*XX; -0131 g = g'; -0132 end -0133 -0134 % corresponding signal g = Kf -0135 ig = XX*x'; -0136 % residual -0137 F1 = (ig - g'); -0138 % regularization -0139 F2 = (lambda*L)*x'; -0140 % fcn should return the residual as output for lsqnonlin -0141 % see e.g. Aster et al. S. 240 eq.10.4 -0142 F = [F1; F2]; -0143 -0144 % jacobian - speeds up inversion! -0145 J = 0; -0146 if nargout > 1 -0147 % see Mohnke, 2014 WRR paper for info -0148 -0149 % J = dGAMMA/df -0150 J = XX; -0151 -0152 switch T1T2 -0153 case 'T1' -0154 % Jr = dGAMMA/drho -0155 % for T1 it's a bit more tricky because -0156 % d/drho 1-IR*exp(-t*rho*SV) = IR*t*SV * exp(-t*rho*SV) -0157 % where exp(-t*rho*SV) is essentially the T2 Kernel matrix -0158 -0159 % DD = exp(-t*rho*SV) -0160 switch igeom.type -0161 case 'cyl' -0162 DD = zeros(length(t),length(SV)); -0163 for i=1:length(SV) -0164 DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0165 end -0166 -0167 case {'ang','poly'} -0168 % Kernel matrix for full saturation -0169 DD = zeros(length(t),length(SV)); -0170 for i=1:length(SV) -0171 DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0172 end -0173 % Kernel matrix for partial saturation -0174 D = zeros(length(t),length(SV)); -0175 for i=1:size(SVC,1) -0176 D = D + ( squeeze(Amp(i,:,:)).* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); -0177 end -0178 DD(IPS~=1) = D(IPS~=1); -0179 end -0180 -0181 % and now using DD in the derivative: -0182 Jr = zeros(1,length(t)); -0183 for i = 1:length(t) -0184 Jr(i) = t(i).*sum(x.*T1IRfac.*SV.*rhos.*DD(i,:)); -0185 end -0186 -0187 case 'T2' -0188 % Jr = dGAMMA/drho -0189 % in the case of T2 the derivate of dGAMMA/drho is simple -0190 Jr = zeros(1,length(t)); -0191 for i = 1:length(t) -0192 Jr(i) = t(i).*sum(-x.*SV.*rhos.*XX(i,:)); -0193 end -0194 end -0195 -0196 JJ = [J Jr']; -0197 LL = [lambda*L 0*L(:,1)]; -0198 J = [JJ;LL]; -0199 end -0200 -0201 return -0202 -0203 %------------- END OF CODE -------------- -0204 -0205 %% License: -0206 % MIT License -0207 % -0208 % Copyright (c) 2018 Thomas Hiller -0209 % -0210 % Permission is hereby granted, free of charge, to any person obtaining a copy -0211 % of this software and associated documentation files (the "Software"), to deal -0212 % in the Software without restriction, including without limitation the rights -0213 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0214 % copies of the Software, and to permit persons to whom the Software is -0215 % furnished to do so, subject to the following conditions: -0216 % -0217 % The above copyright notice and this permission notice shall be included in all -0218 % copies or substantial portions of the Software. +0014 % Td : diffusion relaxation time +0015 % T1T2 : 'T1' / 'T2' flag +0016 % T1IRfac : either '1' or '2' depending on T1 method +0017 % L : smoothness constraint +0018 % lambda : regularization parameter +0019 % igeom : geometry structure data +0020 % IPS : saturation status matrix +0021 % SVdata : corner saturation data +0022 % +0023 % Outputs: +0024 % F - residual +0025 % J - Jacobian (optional) +0026 % ig - fitted signal (optional) +0027 % XX - augmented Kernel matrix (optional) +0028 % +0029 % Example: +0030 % [F,J,ig,XX] = fcn_JointInvfree(X,iparam) +0031 % +0032 % Other m-files required: +0033 % none +0034 % +0035 % Subfunctions: +0036 % none +0037 % +0038 % MAT-files required: +0039 % none +0040 % +0041 % See also: +0042 % Author: see AUTHORS.md +0043 % email: see AUTHORS.md +0044 % License: MIT License (at end) +0045 +0046 %------------- BEGIN CODE -------------- +0047 +0048 %% input parameters +0049 t = iparam.t; +0050 g = iparam.g; +0051 Tb = iparam.Tb; +0052 Td = iparam.Td; +0053 T1T2 = iparam.T1T2; +0054 T1IRfac = iparam.T1IRfac; +0055 L = iparam.L; +0056 lambda = iparam.lambda; +0057 igeom = iparam.igeom; +0058 IPS = iparam.IPS; +0059 +0060 % length of relaxation time distr. +0061 n = length(X)-1; +0062 % amplitude of relaxation time distr. +0063 x = X(1:n); +0064 % surface relaxivity as scaled log10 value +0065 rhos = 10^X(n+1); +0066 +0067 %% switch depending on geometry +0068 switch igeom.type +0069 case 'cyl' +0070 % for cylindrical pores SV is simply the full saturated S/V ratio +0071 SV = iparam.SVdata.SVF; +0072 +0073 % Kernel matrix +0074 Kf = zeros(length(t),length(SV)); +0075 switch T1T2 +0076 case 'T1' +0077 for i=1:length(SV) +0078 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0079 end +0080 case 'T2' +0081 for i=1:length(SV) +0082 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0083 end +0084 end +0085 K = Kf; +0086 % Kernel matrix times saturation matrix +0087 XX = K.*IPS; +0088 +0089 case {'ang','poly'} +0090 % for angular and polygonal pores there are full saturation S/V and +0091 % partial saturation S/V ratios and amplitudes +0092 SV = iparam.SVdata.SVF; +0093 SVC = iparam.SVdata.SVC; +0094 Amp = iparam.SVdata.Ampl; +0095 TT = iparam.SVdata.TT; +0096 +0097 % Kernel matrix +0098 Kf = zeros(length(t),length(SV)); +0099 switch T1T2 +0100 case 'T1' +0101 for i=1:length(SV) +0102 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0103 end +0104 % Kernel matrix for partial saturation +0105 Kc = zeros(length(t),length(SV)); +0106 for i=1:size(SVC,1) +0107 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0108 ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); +0109 end +0110 case 'T2' +0111 for i=1:length(SV) +0112 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0113 end +0114 % Kernel matrix for partial saturation +0115 Kc = zeros(length(t),length(SV)); +0116 for i=1:size(SVC,1) +0117 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0118 exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); +0119 end +0120 end +0121 % full saturation matrix +0122 K = Kf; +0123 % for partial saturation points use the Kc values +0124 K(IPS~=1) = Kc(IPS~=1); +0125 % Kernel matrix times saturation matrix +0126 XX = K; +0127 end +0128 +0129 % error weighing +0130 if isfield(iparam,'W') +0131 g = iparam.W*g'; +0132 XX = iparam.W*XX; +0133 g = g'; +0134 end +0135 +0136 % corresponding signal g = Kf +0137 ig = XX*x'; +0138 % residual +0139 F1 = (ig - g'); +0140 % regularization +0141 F2 = (lambda*L)*x'; +0142 % fcn should return the residual as output for lsqnonlin +0143 % see e.g. Aster et al. S. 240 eq.10.4 +0144 F = [F1; F2]; +0145 +0146 % jacobian - speeds up inversion! +0147 J = 0; +0148 if nargout > 1 +0149 % see Mohnke, 2014 WRR paper for info +0150 +0151 % J = dGAMMA/df +0152 J = XX; +0153 +0154 switch T1T2 +0155 case 'T1' +0156 % Jr = dGAMMA/drho +0157 % for T1 it's a bit more tricky because +0158 % d/drho 1-IR*exp(-t*rho*SV) = IR*t*SV * exp(-t*rho*SV) +0159 % where exp(-t*rho*SV) is essentially the T2 Kernel matrix +0160 +0161 % DD = exp(-t*rho*SV) +0162 switch igeom.type +0163 case 'cyl' +0164 DD = zeros(length(t),length(SV)); +0165 for i=1:length(SV) +0166 DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0167 end +0168 +0169 case {'ang','poly'} +0170 % Kernel matrix for full saturation +0171 DD = zeros(length(t),length(SV)); +0172 for i=1:length(SV) +0173 DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0174 end +0175 % Kernel matrix for partial saturation +0176 D = zeros(length(t),length(SV)); +0177 for i=1:size(SVC,1) +0178 D = D + ( squeeze(Amp(i,:,:)).*... +0179 exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); +0180 end +0181 DD(IPS~=1) = D(IPS~=1); +0182 end +0183 +0184 % and now using DD in the derivative: +0185 Jr = zeros(1,length(t)); +0186 for i = 1:length(t) +0187 Jr(i) = t(i).*sum(x.*T1IRfac.*SV.*rhos.*DD(i,:)); +0188 end +0189 +0190 case 'T2' +0191 % Jr = dGAMMA/drho +0192 % in the case of T2 the derivate of dGAMMA/drho is simple +0193 Jr = zeros(1,length(t)); +0194 for i = 1:length(t) +0195 Jr(i) = t(i).*sum(-x.*SV.*rhos.*XX(i,:)); +0196 end +0197 end +0198 +0199 JJ = [J Jr']; +0200 LL = [lambda*L 0*L(:,1)]; +0201 J = [JJ;LL]; +0202 end +0203 +0204 return +0205 +0206 %------------- END OF CODE -------------- +0207 +0208 %% License: +0209 % MIT License +0210 % +0211 % Copyright (c) 2018 Thomas Hiller +0212 % +0213 % Permission is hereby granted, free of charge, to any person obtaining a copy +0214 % of this software and associated documentation files (the "Software"), to deal +0215 % in the Software without restriction, including without limitation the rights +0216 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0217 % copies of the Software, and to permit persons to whom the Software is +0218 % furnished to do so, subject to the following conditions: 0219 % -0220 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0221 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0222 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0223 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0224 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0225 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0226 % SOFTWARE. +0220 % The above copyright notice and this permission notice shall be included in all +0221 % copies or substantial portions of the Software. +0222 % +0223 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0224 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0225 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0226 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0227 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0228 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0229 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fcn_JointInvshape.html b/doc/nucleus/functions/inversion/fcn_JointInvshape.html index 6e7c230..5f25011 100644 --- a/doc/nucleus/functions/inversion/fcn_JointInvshape.html +++ b/doc/nucleus/functions/inversion/fcn_JointInvshape.html @@ -42,6 +42,7 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% g : signal vector (all NMR signals) 0016 % indt : number of echoes/points per NMR signal 0017 % Tb : bulk relaxation time -0018 % T1T2 : flag between 'T1' or 'T2' inversion -0019 % T1IRfac : either '1' or '2' depending on T1 method -0020 % SatImbDrain : string indicating if a NMR signal is from -0021 % the drainage or imbibition branch (e.g. 'DDID') -0022 % p : pressure values -0023 % igeom : geometry struct -0024 % x : relaxation time vector -0025 % f : relaxation time distribution (RTD) -0026 % -0027 % Outputs: -0028 % F - norm of the residual vector -0029 % varargout - cell that holds several more data -0030 % ig : fitted NMR signals -0031 % XX : Kernel matrix -0032 % igeom : final geometry struct -0033 % iSAT : final pressure/saturation struct -0034 % -0035 % Example: -0036 % F = fcn_JointInvshape(X,iparam) -0037 % -0038 % Other m-files required: -0039 % getConstants -0040 % getCornerNMRparameter -0041 % getGeometryParameter -0042 % getPartialSaturationMatrix -0043 % getSaturationFromPressureBatch -0044 % -0045 % Subfunctions: -0046 % none -0047 % -0048 % MAT-files required: -0049 % none -0050 % -0051 % See also: -0052 % Author: Thomas Hiller -0053 % email: thomas.hiller[at]leibniz-liag.de -0054 % License: MIT License (at end) -0055 -0056 %------------- BEGIN CODE -------------- -0057 -0058 %% input parameters -0059 t = iparam.t; -0060 g = iparam.g; -0061 indt = iparam.indt; -0062 Tb = iparam.Tb; -0063 T1T2 = iparam.T1T2; -0064 T1IRfac = iparam.T1IRfac; -0065 SatImbDrain = iparam.SatImbDrain; -0066 p = iparam.p; -0067 igeom = iparam.igeom; -0068 x = iparam.x; -0069 f = iparam.f; -0070 constants = getConstants; -0071 -0072 %% waitbar option -0073 wbopts.show = false; -0074 -0075 %% surface relaxivity as log10 value and "second" angle -0076 rhos = 10^X(1); -0077 beta = X(2); -0078 -0079 %% only works for right angular triangles -0080 switch igeom.type -0081 case 'ang' -0082 % get a new geometry parameter "a" (only shape dependent) -0083 % to shift the PSD -0084 tmp.type = igeom.type; -0085 tmp.radius = 1; -0086 tmp.angles = [igeom.angles(1) beta igeom.angles(1)-beta]; -0087 tmp = getGeometryParameter(tmp); -0088 -0089 % new PSD with updated "rhos" and "a" -0090 ipsddata.r = x.*tmp.a.*rhos; -0091 ipsddata.psd = f; -0092 igeom.radius = ipsddata.r'; -0093 % new saturation state -0094 igeom.angles(2) = beta; -0095 igeom.angles(3) = igeom.angles(1)-igeom.angles(2); -0096 igeom = getGeometryParameter(igeom); -0097 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); -0098 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); -0099 -0100 % get the amplitudes and surface-to-volume ratios for the partially -0101 % saturated corners -0102 SVdata = getCornerNMRparameter(igeom,iSAT,indt,SatImbDrain); -0103 SVdata.TT = repmat(t',[1 length(SVdata.SVF)]); -0104 -0105 SV = SVdata.SVF'; -0106 SVC = SVdata.SVC; -0107 Amp = SVdata.Ampl; -0108 TT = SVdata.TT; -0109 -0110 % Kernel matrix -0111 Kf = zeros(length(t),length(SV)); -0112 switch T1T2 -0113 case 'T1' -0114 for i=1:length(SV) -0115 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); -0116 end -0117 % Kernel matrix for partial saturation -0118 Kc = zeros(length(t),length(SV)); -0119 for i=1:size(SVC,1) -0120 Kc = Kc + ( squeeze(Amp(i,:,:)) .* ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); -0121 end -0122 case 'T2' -0123 for i=1:length(SV) -0124 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); -0125 end -0126 % Kernel matrix for partial saturation -0127 Kc = zeros(length(t),length(SV)); -0128 for i=1:size(SVC,1) -0129 Kc = Kc + ( squeeze(Amp(i,:,:)) .* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); -0130 end -0131 end -0132 -0133 K = Kf; -0134 K(IPS~=1) = Kc(IPS~=1); -0135 % Kernel matrix times saturation matrix -0136 XX = K; -0137 -0138 otherwise -0139 % nothing to do -0140 end -0141 -0142 %% weighting -0143 if isfield(iparam,'W') -0144 g = iparam.W*g'; -0145 XX = iparam.W*XX; -0146 g = g'; -0147 end -0148 -0149 %% corresponding signal g = Kf -0150 ig = XX*f'; -0151 -0152 %% residual -0153 F = norm(ig - g'); -0154 -0155 %% output -0156 if nargout > 1 -0157 varargout{1} = ig; -0158 varargout{2} = XX; -0159 varargout{3} = igeom; -0160 varargout{4} = iSAT; -0161 end -0162 -0163 return -0164 -0165 %------------- END OF CODE -------------- +0018 % Td : diffusion relaxation time +0019 % T1T2 : flag between 'T1' or 'T2' inversion +0020 % T1IRfac : either '1' or '2' depending on T1 method +0021 % SatImbDrain : string indicating if a NMR signal is from +0022 % the drainage or imbibition branch (e.g. 'DDID') +0023 % p : pressure values +0024 % igeom : geometry struct +0025 % x : relaxation time vector +0026 % f : relaxation time distribution (RTD) +0027 % +0028 % Outputs: +0029 % F - norm of the residual vector +0030 % varargout - cell that holds several more data +0031 % ig : fitted NMR signals +0032 % XX : Kernel matrix +0033 % igeom : final geometry struct +0034 % iSAT : final pressure/saturation struct +0035 % +0036 % Example: +0037 % F = fcn_JointInvshape(X,iparam) +0038 % +0039 % Other m-files required: +0040 % getConstants +0041 % getCornerNMRparameter +0042 % getGeometryParameter +0043 % getPartialSaturationMatrix +0044 % getSaturationFromPressureBatch +0045 % +0046 % Subfunctions: +0047 % none +0048 % +0049 % MAT-files required: +0050 % none +0051 % +0052 % See also: +0053 % Author: see AUTHORS.md +0054 % email: see AUTHORS.md +0055 % License: MIT License (at end) +0056 +0057 %------------- BEGIN CODE -------------- +0058 +0059 %% input parameters +0060 t = iparam.t; +0061 g = iparam.g; +0062 indt = iparam.indt; +0063 Tb = iparam.Tb; +0064 Td = iparam.Td; +0065 T1T2 = iparam.T1T2; +0066 T1IRfac = iparam.T1IRfac; +0067 SatImbDrain = iparam.SatImbDrain; +0068 p = iparam.p; +0069 igeom = iparam.igeom; +0070 x = iparam.x; +0071 f = iparam.f; +0072 constants = getConstants; +0073 +0074 %% waitbar option +0075 wbopts.show = false; +0076 +0077 %% surface relaxivity as log10 value and "second" angle +0078 rhos = 10^X(1); +0079 beta = X(2); +0080 +0081 %% only works for right angular triangles +0082 switch igeom.type +0083 case 'ang' +0084 % get a new geometry parameter "a" (only shape dependent) +0085 % to shift the PSD +0086 tmp.type = igeom.type; +0087 tmp.radius = 1; +0088 tmp.angles = [igeom.angles(1) beta igeom.angles(1)-beta]; +0089 tmp = getGeometryParameter(tmp); +0090 +0091 % new PSD with updated "rhos" and "a" +0092 ipsddata.r = x.*tmp.a.*rhos; +0093 ipsddata.psd = f; +0094 igeom.radius = ipsddata.r'; +0095 % new saturation state +0096 igeom.angles(2) = beta; +0097 igeom.angles(3) = igeom.angles(1)-igeom.angles(2); +0098 igeom = getGeometryParameter(igeom); +0099 iSAT = getSaturationFromPressureBatch(igeom,p,ipsddata,constants,wbopts); +0100 IPS = getPartialSaturationMatrix(iSAT,indt,SatImbDrain); +0101 +0102 % get the amplitudes and surface-to-volume ratios for the partially +0103 % saturated corners +0104 SVdata = getCornerNMRparameter(igeom,iSAT,indt,SatImbDrain); +0105 SVdata.TT = repmat(t',[1 length(SVdata.SVF)]); +0106 +0107 SV = SVdata.SVF'; +0108 SVC = SVdata.SVC; +0109 Amp = SVdata.Ampl; +0110 TT = SVdata.TT; +0111 +0112 % Kernel matrix +0113 Kf = zeros(length(t),length(SV)); +0114 switch T1T2 +0115 case 'T1' +0116 for i=1:length(SV) +0117 Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0118 end +0119 % Kernel matrix for partial saturation +0120 Kc = zeros(length(t),length(SV)); +0121 for i=1:size(SVC,1) +0122 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0123 ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); +0124 end +0125 case 'T2' +0126 for i=1:length(SV) +0127 Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); +0128 end +0129 % Kernel matrix for partial saturation +0130 Kc = zeros(length(t),length(SV)); +0131 for i=1:size(SVC,1) +0132 Kc = Kc + ( squeeze(Amp(i,:,:)) .*... +0133 exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); +0134 end +0135 end +0136 +0137 K = Kf; +0138 K(IPS~=1) = Kc(IPS~=1); +0139 % Kernel matrix times saturation matrix +0140 XX = K; +0141 +0142 otherwise +0143 % nothing to do +0144 end +0145 +0146 %% weighting +0147 if isfield(iparam,'W') +0148 g = iparam.W*g'; +0149 XX = iparam.W*XX; +0150 g = g'; +0151 end +0152 +0153 %% corresponding signal g = Kf +0154 ig = XX*f'; +0155 +0156 %% residual +0157 F = norm(ig - g'); +0158 +0159 %% output +0160 if nargout > 1 +0161 varargout{1} = ig; +0162 varargout{2} = XX; +0163 varargout{3} = igeom; +0164 varargout{4} = iSAT; +0165 end 0166 -0167 %% License: -0168 % MIT License -0169 % -0170 % Copyright (c) 2018 Thomas Hiller -0171 % -0172 % Permission is hereby granted, free of charge, to any person obtaining a copy -0173 % of this software and associated documentation files (the "Software"), to deal -0174 % in the Software without restriction, including without limitation the rights -0175 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0176 % copies of the Software, and to permit persons to whom the Software is -0177 % furnished to do so, subject to the following conditions: -0178 % -0179 % The above copyright notice and this permission notice shall be included in all -0180 % copies or substantial portions of the Software. -0181 % -0182 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0183 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0184 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0185 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0186 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0187 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0188 % SOFTWARE. +0167 return +0168 +0169 %------------- END OF CODE -------------- +0170 +0171 %% License: +0172 % MIT License +0173 % +0174 % Copyright (c) 2018 Thomas Hiller +0175 % +0176 % Permission is hereby granted, free of charge, to any person obtaining a copy +0177 % of this software and associated documentation files (the "Software"), to deal +0178 % in the Software without restriction, including without limitation the rights +0179 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0180 % copies of the Software, and to permit persons to whom the Software is +0181 % furnished to do so, subject to the following conditions: +0182 % +0183 % The above copyright notice and this permission notice shall be included in all +0184 % copies or substantial portions of the Software. +0185 % +0186 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0187 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0188 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0189 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0190 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0191 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0192 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fcn_fitFreeT1.html b/doc/nucleus/functions/inversion/fcn_fitFreeT1.html index c066d9e..cec3bf4 100644 --- a/doc/nucleus/functions/inversion/fcn_fitFreeT1.html +++ b/doc/nucleus/functions/inversion/fcn_fitFreeT1.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/fcn_fitFreeT1_fmin.html b/doc/nucleus/functions/inversion/fcn_fitFreeT1_fmin.html index dcd3eec..7a8086a 100644 --- a/doc/nucleus/functions/inversion/fcn_fitFreeT1_fmin.html +++ b/doc/nucleus/functions/inversion/fcn_fitFreeT1_fmin.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/fcn_fitFreeT2.html b/doc/nucleus/functions/inversion/fcn_fitFreeT2.html index b6c1a48..600a985 100644 --- a/doc/nucleus/functions/inversion/fcn_fitFreeT2.html +++ b/doc/nucleus/functions/inversion/fcn_fitFreeT2.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/fcn_fitFreeT2_fmin.html b/doc/nucleus/functions/inversion/fcn_fitFreeT2_fmin.html index 719f909..37afeca 100644 --- a/doc/nucleus/functions/inversion/fcn_fitFreeT2_fmin.html +++ b/doc/nucleus/functions/inversion/fcn_fitFreeT2_fmin.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/fcn_fitFreeT2w.html b/doc/nucleus/functions/inversion/fcn_fitFreeT2w.html index 0a20d0a..a274a35 100644 --- a/doc/nucleus/functions/inversion/fcn_fitFreeT2w.html +++ b/doc/nucleus/functions/inversion/fcn_fitFreeT2w.html @@ -58,8 +58,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=getFitFreeJacobian calculates the Jacobi matrix for the NMR inversion This function is called by:
      -
    • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
    + @@ -108,8 +108,8 @@

    SOURCE CODE ^% none 0032 % 0033 % See also: -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/fcn_fitMultiModal.html b/doc/nucleus/functions/inversion/fcn_fitMultiModal.html new file mode 100644 index 0000000..a624621 --- /dev/null +++ b/doc/nucleus/functions/inversion/fcn_fitMultiModal.html @@ -0,0 +1,197 @@ + + + + Description of fcn_fitMultiModal + + + + + + + + + + + +

    fcn_fitMultiModal +

    + +

    PURPOSE ^

    +
    is the objective function for N free distribution
    + +

    SYNOPSIS ^

    +
    function F = fcn_fitMultiModal(x,iparam)
    + +

    DESCRIPTION ^

    +
    fcn_fitMultiModal is the objective function for N free distribution
    +fitting that is minimized with 'lsqnonlin'
    +
    + Syntax:
    +       fcn_fitMultiModal(x,iparam)
    +
    + Inputs:
    +       x - parameter vector
    +           x(3*i-2) = mu (relaxation time)
    +           x(3*i-1) = sigma (width of distribution)
    +           x(3*i) = amp (relative amplitude)
    +       iparam - struct that holds additional settings:
    +                t : time vector
    +                s : signal vector
    +                T : relaxation times
    +                e : noise vector / error weights (optional)
    +
    + Outputs:
    +       F - residual
    +
    + Example:
    +       F = fcn_fitMultiModal(x,params)
    +
    + Other m-files required:
    +       createKernelMatrix
    +
    + Subfunctions:
    +       none
    +
    + MAT-files required:
    +       none
    +
    + See also:
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function F = fcn_fitMultiModal(x,iparam)
    +0002 %fcn_fitMultiModal is the objective function for N free distribution
    +0003 %fitting that is minimized with 'lsqnonlin'
    +0004 %
    +0005 % Syntax:
    +0006 %       fcn_fitMultiModal(x,iparam)
    +0007 %
    +0008 % Inputs:
    +0009 %       x - parameter vector
    +0010 %           x(3*i-2) = mu (relaxation time)
    +0011 %           x(3*i-1) = sigma (width of distribution)
    +0012 %           x(3*i) = amp (relative amplitude)
    +0013 %       iparam - struct that holds additional settings:
    +0014 %                t : time vector
    +0015 %                s : signal vector
    +0016 %                T : relaxation times
    +0017 %                e : noise vector / error weights (optional)
    +0018 %
    +0019 % Outputs:
    +0020 %       F - residual
    +0021 %
    +0022 % Example:
    +0023 %       F = fcn_fitMultiModal(x,params)
    +0024 %
    +0025 % Other m-files required:
    +0026 %       createKernelMatrix
    +0027 %
    +0028 % Subfunctions:
    +0029 %       none
    +0030 %
    +0031 % MAT-files required:
    +0032 %       none
    +0033 %
    +0034 % See also:
    +0035 % Author: see AUTHORS.md
    +0036 % email: see AUTHORS.md
    +0037 % License: MIT License (at end)
    +0038 
    +0039 %------------- BEGIN CODE --------------
    +0040 
    +0041 % get all neccessary parameters
    +0042 flag = iparam.flag;
    +0043 T1IRfac = iparam.T1IRfac;
    +0044 nModes = iparam.nModes;
    +0045 t = iparam.t;
    +0046 s = iparam.s;
    +0047 T = iparam.T;
    +0048 Tb = iparam.Tb;
    +0049 Td = iparam.Td;
    +0050 
    +0051 % get the global (combined) RTD distribution
    +0052 Tdist = 0;
    +0053 for i = 1:nModes
    +0054     mu = exp(x(3*i-2));
    +0055     sigma = x(3*i-1);
    +0056     amp = x(3*i);
    +0057     
    +0058     % get the temporary RTD with current mu and sigma
    +0059     tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(T) - log(mu))/ sqrt(2)/sigma).^2);
    +0060     
    +0061     % scale the temporary RDT to current amplitude
    +0062     tmp = (tmp/sum(tmp)) * amp;
    +0063     
    +0064     % add the current temporary RTD to the global Tdist
    +0065     Tdist = Tdist + tmp;   
    +0066 end
    +0067 
    +0068 % get the kernel function to calculate the signal out of the global RTD
    +0069 K = createKernelMatrix(t,T,Tb,Td,flag,T1IRfac);
    +0070 si = K*Tdist';
    +0071 
    +0072 
    +0073 % get error weights if available
    +0074 if isfield(iparam,'e')
    +0075     e = iparam.e;
    +0076 else
    +0077     e = ones(size(s));
    +0078 end
    +0079 
    +0080 % change output depending on solver
    +0081 switch iparam.optim
    +0082     case 'on' % lsqnonlin
    +0083         % scale the residual
    +0084         F = e.*(si - s);
    +0085     case 'off' % fminsearchbnd
    +0086         F = e.*(si - s);
    +0087         SSE = sum(F.^2);
    +0088         F = SSE;
    +0089 end
    +0090 
    +0091 return
    +0092 
    +0093 %------------- END OF CODE --------------
    +0094 
    +0095 %% License:
    +0096 % MIT License
    +0097 %
    +0098 % Copyright (c) 2022 Thomas Hiller
    +0099 %
    +0100 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0101 % of this software and associated documentation files (the "Software"), to deal
    +0102 % in the Software without restriction, including without limitation the rights
    +0103 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0104 % copies of the Software, and to permit persons to whom the Software is
    +0105 % furnished to do so, subject to the following conditions:
    +0106 %
    +0107 % The above copyright notice and this permission notice shall be included in all
    +0108 % copies or substantial portions of the Software.
    +0109 %
    +0110 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0111 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0112 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0113 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0114 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0115 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0116 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fitDataFree.html b/doc/nucleus/functions/inversion/fitDataFree.html index bacf961..56bfbab 100644 --- a/doc/nucleus/functions/inversion/fitDataFree.html +++ b/doc/nucleus/functions/inversion/fitDataFree.html @@ -81,18 +81,18 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • fcn_fitFreeT1 is the objective function for T1 mono- and free exponential
  • fcn_fitFreeT1_fmin is the objective function for T1 mono- and free exponential
  • fcn_fitFreeT2 is the objective function for T2 mono- and free exponential
  • fcn_fitFreeT2_fmin is the objective function for T2 mono- and free exponential
  • getConfInterval calculates the confidence interval for the inversion
  • getFitErrors calculates all relevant fitting errors for the NMR inversion
  • getFitFreeJacobian calculates the Jacobi matrix for the NMR inversion
  • This function is called by: +
  • LoadNMRData_driver loads NMR raw data from different file formats
  • importASCIIdata imports NMR data from ASCII files
  • runInversionBatch batch processes the inversion using for all NMR signals
  • runInversionStd controls the standard inversion process to invert a
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • @@ -154,8 +154,8 @@

    SOURCE CODE ^% none 0055 % 0056 % See also: -0057 % Author: Thomas Hiller -0058 % email: thomas.hiller[at]leibniz-liag.de +0057 % Author: see AUTHORS.md +0058 % email: see AUTHORS.md 0059 % License: MIT License (at end) 0060 0061 %------------- BEGIN CODE -------------- @@ -185,138 +185,139 @@

    SOURCE CODE ^% start values for E and T 0086 x0 = zeros(1,2*nExp); 0087 for i = 1:nExp -0088 x0(2*i-1) = max(signal)/nExp; -0089 x0(2*i) = i*max(t)/4; -0090 end -0091 -0092 switch parameter.optim -0093 case 'on' -0094 switch flag -0095 case 'T1' -0096 % solver options -0097 options = optimoptions('lsqcurvefit'); -0098 options.Display = parameter.info; -0099 options.OptimalityTolerance = 1e-18; -0100 options.StepTolerance = 1e-18; -0101 % options.MaxIterations = 1e3; -0102 [x,~,~,~,output,~,jacobian] = lsqcurvefit(@(x,t)fcn_fitFreeT1(x,t,IRfac),... -0103 x0,t,s,zeros(size(x0)),[],options); -0104 case 'T2' -0105 % solver options -0106 options = optimoptions('lsqnonlin'); -0107 options.Algorithm = 'levenberg-marquardt'; -0108 options.Display = parameter.info; -0109 options.OptimalityTolerance = 1e-18; -0110 options.StepTolerance = 1e-18; -0111 % options.MaxIterations = 1e3; -0112 -0113 iparam.t = t; -0114 iparam.s = s; -0115 [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitFreeT2w(x,iparam),... -0116 x0,zeros(size(x0)),[],options); -0117 % [x,~,~,~,output,~,jacobian] = lsqcurvefit(@fcn_fitFreeT2,... -0118 % x0,t,s,zeros(size(x0)),[],options); -0119 end -0120 case 'off' -0121 % solver options -0122 options = optimset('Display',parameter.info,'MaxFunEvals',10^6,... -0123 'MaxIter',5000,'TolFun',1e-12,'TolX',1e-12); -0124 switch flag -0125 case 'T1' -0126 [x,~,~,output] = fminsearchbnd(@(x) fcn_fitFreeT1_fmin(x,t,s,IRfac),... -0127 x0,zeros(size(x0)),[],options); -0128 case 'T2' -0129 [x,~,~,output] = fminsearchbnd(@(x) fcn_fitFreeT2_fmin(x,t,s,e),... -0130 x0,zeros(size(x0)),[],options); -0131 end -0132 end -0133 -0134 % get the fit -0135 fit_t = t; -0136 switch flag -0137 case 'T1' -0138 fit_s = fcn_fitFreeT1(x,fit_t,IRfac); -0139 case 'T2' -0140 fit_s = fcn_fitFreeT2(x,fit_t); -0141 end -0142 -0143 % get residuals and error measures -0144 if isfield(parameter,'W') -0145 % when signal gating was used the error estimates need to be adjusted -0146 out = getFitErrors(signal,fit_s,parameter.noise,parameter.W); -0147 else -0148 out = getFitErrors(signal,fit_s,parameter.noise); -0149 end -0150 -0151 % get Jacobian -0152 switch parameter.optim -0153 case 'on' -0154 % nothing to do because the Optim. Toolbox gives the jacobian as -0155 % output -0156 case 'off' -0157 jacobian = getFitFreeJacobian(x,t,flag,IRfac); -0158 end -0159 -0160 % confidence interval -0161 ci = getConfInterval(out.resnorm,jacobian,0.05); -0162 -0163 % sort the relaxation times in ascending order -0164 E0 = x(1:2:end); -0165 T = x(2:2:end); -0166 [T,idx] = sort(T); -0167 E0 = E0(idx); -0168 ciT = ci(2:2:end); -0169 ciE = ci(1:2:end); -0170 ciT = ciT(idx); -0171 ciE = ciE(idx); -0172 ci(2:2:end) = ciT; -0173 ci(1:2:end) = ciE; -0174 -0175 % output struct -0176 fitdata.E0 = E0; -0177 switch flag -0178 case 'T1' -0179 fitdata.T1 = T; -0180 case 'T2' -0181 fitdata.T2 = T; -0182 end -0183 fitdata.fit_t = fit_t; -0184 fitdata.fit_s = fit_s; -0185 fitdata.resnorm = out.resnorm; -0186 fitdata.residual = out.residual; -0187 fitdata.errornorm = out.errnorm1; -0188 fitdata.rms = out.rms; -0189 fitdata.chi2 = out.chi2; -0190 fitdata.ci = ci; -0191 fitdata.x = x; -0192 fitdata.output = output; -0193 -0194 return -0195 -0196 %------------- END OF CODE -------------- -0197 -0198 %% License: -0199 % MIT License -0200 % -0201 % Copyright (c) 2018 Thomas Hiller -0202 % -0203 % Permission is hereby granted, free of charge, to any person obtaining a copy -0204 % of this software and associated documentation files (the "Software"), to deal -0205 % in the Software without restriction, including without limitation the rights -0206 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0207 % copies of the Software, and to permit persons to whom the Software is -0208 % furnished to do so, subject to the following conditions: -0209 % -0210 % The above copyright notice and this permission notice shall be included in all -0211 % copies or substantial portions of the Software. -0212 % -0213 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0214 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0215 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0216 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0217 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0218 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0219 % SOFTWARE. +0088 x0(2*i-1) = i*max(signal)/nExp; +0089 x0(2*i) = i*max(t)/nExp; +0090 % x0(2*i) = exp(i*max(log(t))/nExp); +0091 end +0092 +0093 switch parameter.optim +0094 case 'on' +0095 switch flag +0096 case 'T1' +0097 % solver options +0098 options = optimoptions('lsqcurvefit'); +0099 options.Display = parameter.info; +0100 options.OptimalityTolerance = 1e-18; +0101 options.StepTolerance = 1e-18; +0102 % options.MaxIterations = 1e3; +0103 [x,~,~,~,output,~,jacobian] = lsqcurvefit(@(x,t)fcn_fitFreeT1(x,t,IRfac),... +0104 x0,t,s,zeros(size(x0)),[],options); +0105 case 'T2' +0106 % solver options +0107 options = optimoptions('lsqnonlin'); +0108 options.Algorithm = 'levenberg-marquardt'; +0109 options.Display = parameter.info; +0110 options.OptimalityTolerance = 1e-18; +0111 options.StepTolerance = 1e-18; +0112 % options.MaxIterations = 1e3; +0113 +0114 iparam.t = t; +0115 iparam.s = s; +0116 % [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitFreeT2w(x,iparam),... +0117 % x0,zeros(size(x0)),[],options); +0118 [x,~,~,~,output,~,jacobian] = lsqcurvefit(@fcn_fitFreeT2,... +0119 x0,t,s,zeros(size(x0)),[],options); +0120 end +0121 case 'off' +0122 % solver options +0123 options = optimset('Display',parameter.info,'MaxFunEvals',10^6,... +0124 'MaxIter',5000,'TolFun',1e-12,'TolX',1e-12); +0125 switch flag +0126 case 'T1' +0127 [x,~,~,output] = fminsearchbnd(@(x) fcn_fitFreeT1_fmin(x,t,s,IRfac),... +0128 x0,zeros(size(x0)),[],options); +0129 case 'T2' +0130 [x,~,~,output] = fminsearchbnd(@(x) fcn_fitFreeT2_fmin(x,t,s,e),... +0131 x0,zeros(size(x0)),[],options); +0132 end +0133 end +0134 +0135 % get the fit +0136 fit_t = t; +0137 switch flag +0138 case 'T1' +0139 fit_s = fcn_fitFreeT1(x,fit_t,IRfac); +0140 case 'T2' +0141 fit_s = fcn_fitFreeT2(x,fit_t); +0142 end +0143 +0144 % get residuals and error measures +0145 if isfield(parameter,'W') +0146 % when signal gating was used the error estimates need to be adjusted +0147 out = getFitErrors(signal,fit_s,parameter.noise,parameter.W); +0148 else +0149 out = getFitErrors(signal,fit_s,parameter.noise); +0150 end +0151 +0152 % get Jacobian +0153 switch parameter.optim +0154 case 'on' +0155 % nothing to do because the Optim. Toolbox gives the jacobian as +0156 % output +0157 case 'off' +0158 jacobian = getFitFreeJacobian(x,t,flag,IRfac); +0159 end +0160 +0161 % confidence interval +0162 ci = getConfInterval(out.resnorm,jacobian,0.05); +0163 +0164 % sort the relaxation times in ascending order +0165 E0 = x(1:2:end); +0166 T = x(2:2:end); +0167 [T,idx] = sort(T); +0168 E0 = E0(idx); +0169 ciT = ci(2:2:end); +0170 ciE = ci(1:2:end); +0171 ciT = ciT(idx); +0172 ciE = ciE(idx); +0173 ci(2:2:end) = ciT; +0174 ci(1:2:end) = ciE; +0175 +0176 % output struct +0177 fitdata.E0 = E0; +0178 switch flag +0179 case 'T1' +0180 fitdata.T1 = T; +0181 case 'T2' +0182 fitdata.T2 = T; +0183 end +0184 fitdata.fit_t = fit_t; +0185 fitdata.fit_s = fit_s; +0186 fitdata.resnorm = out.resnorm; +0187 fitdata.residual = out.residual; +0188 fitdata.errornorm = out.errnorm1; +0189 fitdata.rms = out.rms; +0190 fitdata.chi2 = out.chi2; +0191 fitdata.ci = ci; +0192 fitdata.x = x; +0193 fitdata.output = output; +0194 +0195 return +0196 +0197 %------------- END OF CODE -------------- +0198 +0199 %% License: +0200 % MIT License +0201 % +0202 % Copyright (c) 2018 Thomas Hiller +0203 % +0204 % Permission is hereby granted, free of charge, to any person obtaining a copy +0205 % of this software and associated documentation files (the "Software"), to deal +0206 % in the Software without restriction, including without limitation the rights +0207 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0208 % copies of the Software, and to permit persons to whom the Software is +0209 % furnished to do so, subject to the following conditions: +0210 % +0211 % The above copyright notice and this permission notice shall be included in all +0212 % copies or substantial portions of the Software. +0213 % +0214 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0215 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0216 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0217 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0218 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0219 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0220 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fitDataLSQ.html b/doc/nucleus/functions/inversion/fitDataLSQ.html index 8fbca76..6f1f33a 100644 --- a/doc/nucleus/functions/inversion/fitDataLSQ.html +++ b/doc/nucleus/functions/inversion/fitDataLSQ.html @@ -41,6 +41,7 @@

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls: +
  • applyRegularization applies regularization procedures from the
  • createKernelMatrix creates a Kernel matrix from signal time vector "t"
  • getFitErrors calculates all relevant fitting errors for the NMR inversion
  • getTLogMean calculates the T logmean value out of a relaxation time
  • This function is called by: +
  • runInversionBatch batch processes the inversion using for all NMR signals
  • runInversionStd controls the standard inversion process to invert a
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • @@ -124,220 +127,238 @@

    SOURCE CODE ^% T1T2 : flag between 'T1' or 'T2' inversion 0015 % T1IRfac : either '1' or '2' depending on T1 method 0016 % Tb : bulk relaxation time -0017 % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] -0018 % regMethod: 'manual', 'gcv_tikh', 'gcv_trunc', -0019 % 'gcv_damp', 'discrep' -0020 % Lorder : smoothness constraint (derivative matrix) -0021 % lambda : regularization parameter (for 'manual') -0022 % noise : noise level needed for 'discrep' discrepancy -0023 % principle -0024 % W : error weighting matrix (optional) -0025 % solver : LSQ solver ('lsqlin' or 'lsqnonneg') -0026 % -0027 % Outputs: -0028 % fitdata - struct that holds the inversion results: -0029 % fit_t : time vector for plotting -0030 % fit_s : signal vector for plotting -0031 % T1T2me : relaxation time values -0032 % T1T2f : relaxation time spectrum -0033 % Tlgm : T logmean -0034 % E0 : initial amplitude at t=0 (T2) or t=inf (T1) -0035 % resnorm : residual norm -0036 % residual : vector of residuals -0037 % chi2 : chi square error -0038 % rms : RMS error -0039 % lambda_out : regularization parameter lambda determined -0040 % by the different options from the 'regu' -0041 % toolbox -0042 % KK : Kernel matrix -0043 % L : derivative matrix -0044 % xn : model norm |L*x|_2 -0045 % rn : residual norm |A*x-b|_2 -0046 % -0047 % Example: -0048 % [fitdata] = fitDataLSQ(t,s,parameter) +0017 % Td : diffusion relaxation time +0018 % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] +0019 % regMethod: 'manual', 'gcv_tikh', 'gcv_trunc', +0020 % 'gcv_damp', 'discrep' +0021 % Lorder : smoothness constraint (derivative matrix) +0022 % lambda : regularization parameter (for 'manual') +0023 % noise : noise level needed for 'discrep' discrepancy +0024 % principle +0025 % W : error weighting matrix (optional) +0026 % solver : LSQ solver ('lsqlin' or 'lsqnonneg') +0027 % bounds : predefined lower and upper bounds and start +0028 % model (optional and only for 'lsqlin') +0029 % +0030 % Outputs: +0031 % fitdata - struct that holds the inversion results: +0032 % fit_t : time vector for plotting +0033 % fit_s : signal vector for plotting +0034 % T1T2me : relaxation time values +0035 % T1T2f : relaxation time spectrum +0036 % Tlgm : T log-mean +0037 % E0 : initial amplitude at t=0 (T2) or t=inf (T1) +0038 % resnorm : residual norm +0039 % residual : vector of residuals +0040 % chi2 : chi square error +0041 % rms : RMS error +0042 % lambda_out : regularization parameter lambda determined +0043 % by the different options from the 'regu' +0044 % toolbox +0045 % KK : Kernel matrix +0046 % L : derivative matrix +0047 % xn : model norm |L*x|_2 +0048 % rn : residual norm |A*x-b|_2 0049 % -0050 % Other m-files required: -0051 % Optimization Toolbox from Mathworks (optional) -0052 % Regularization Toolbox -0053 % applyRegularization -0054 % createKernelMatrix -0055 % getFitErrors -0056 % getTLogMean -0057 % lsqnonneg -0058 % lsqlin (optional) -0059 % -0060 % Subfunctions: -0061 % none +0050 % Example: +0051 % [fitdata] = fitDataLSQ(t,s,parameter) +0052 % +0053 % Other m-files required: +0054 % Optimization Toolbox from Mathworks (optional) +0055 % Regularization Toolbox +0056 % applyRegularization +0057 % createKernelMatrix +0058 % getFitErrors +0059 % getTLogMean +0060 % lsqnonneg +0061 % lsqlin (optional) 0062 % -0063 % MAT-files required: +0063 % Subfunctions: 0064 % none 0065 % -0066 % See also: -0067 % Author: Thomas Hiller -0068 % email: thomas.hiller[at]leibniz-liag.de -0069 % License: MIT License (at end) -0070 -0071 %------------- BEGIN CODE -------------- -0072 -0073 % make input column vectors -0074 time = time(:); -0075 signal = signal(:); -0076 -0077 % get the maximum value of the signal to scale the signal for the inversion -0078 % to 1 -0079 maxS = max(signal); -0080 -0081 % temporary variables -0082 t = time; -0083 g = signal./maxS; -0084 -0085 % get the input parameters -0086 flag = parameter.T1T2; % T1/T2 switch -0087 T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor -0088 Tb = parameter.Tb; % bulk relaxation time -0089 tstart = parameter.Tint(1); % log10 value -0090 tend = parameter.Tint(2); % log10 value -0091 N = parameter.Tint(3); % N per decade -0092 regMethod = parameter.regMethod; % regularization method -0093 order = parameter.Lorder; % smoothness constraint -0094 lambda = parameter.lambda; % regularization parameter -0095 noise = parameter.noise; % noise -0096 -0097 % create the relaxation time vector -0098 T1T2me = logspace(tstart,tend,(tend-tstart)*N); -0099 -0100 % create the Kernel matrix for inversion -0101 K = createKernelMatrix(t,T1T2me,Tb,flag,T1IRfac); -0102 -0103 if strcmp(parameter.solver,'lsqlin') -0104 % initial T2 amplitudes -0105 f0 = zeros(size(T1T2me)); -0106 f0_lb = f0; -0107 f0_ub = 1.5*max(g)*ones(size(T1T2me)); -0108 end -0109 -0110 % derivative matrix -0111 L = get_l(length(T1T2me),order); -0112 -0113 % scale the noise and error matrix W accordingly -0114 noise = noise./maxS; -0115 if isfield(parameter,'W') -0116 e = diag(parameter.W); -0117 e = e./maxS; -0118 W = diag(e); -0119 end -0120 -0121 % apply error weight matrix -0122 if isfield(parameter,'W') -0123 g = W*g; -0124 K = W*K; -0125 end +0066 % MAT-files required: +0067 % none +0068 % +0069 % See also: +0070 % Author: see AUTHORS.md +0071 % email: see AUTHORS.md +0072 % License: MIT License (at end) +0073 +0074 %------------- BEGIN CODE -------------- +0075 +0076 % make input column vectors +0077 time = time(:); +0078 signal = signal(:); +0079 +0080 % get the maximum value of the signal to scale the signal for the inversion +0081 % to 1 +0082 maxS = max(signal); +0083 +0084 % temporary variables +0085 t = time; +0086 g = signal./maxS; +0087 +0088 % get the input parameters +0089 flag = parameter.T1T2; % T1/T2 switch +0090 T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor +0091 Tb = parameter.Tb; % bulk relaxation time +0092 Td = parameter.Td; % diffusion relaxation time +0093 tstart = parameter.Tint(1); % log10 value +0094 tend = parameter.Tint(2); % log10 value +0095 N = parameter.Tint(3); % N per decade +0096 regMethod = parameter.regMethod; % regularization method +0097 order = parameter.Lorder; % smoothness constraint +0098 lambda = parameter.lambda; % regularization parameter +0099 noise = parameter.noise; % noise +0100 +0101 % create the relaxation time vector +0102 T1T2me = logspace(tstart,tend,(tend-tstart)*N); +0103 +0104 % create the Kernel matrix for inversion +0105 K = createKernelMatrix(t,T1T2me,Tb,Td,flag,T1IRfac); +0106 +0107 if strcmp(parameter.solver,'lsqlin') +0108 if isfield(parameter,'bounds') +0109 f0 = parameter.bounds.f0; +0110 f0_lb = parameter.bounds.lb; +0111 f0_ub = parameter.bounds.ub; +0112 else +0113 % initial T2 amplitudes +0114 f0 = zeros(size(T1T2me)); +0115 f0_lb = f0; +0116 f0_ub = 1.5*max(g)*ones(size(T1T2me)); +0117 % T2 measurements: cut everything < first considered echo to zero +0118 f0_ub(T1T2me < time(1)/5) = 0; +0119 % T1 measurements: cut everything > 5*time of last point in SR-curve +0120 % f0_ub(T1T2me > time(end)) = 0; +0121 end +0122 end +0123 +0124 % derivative matrix +0125 L = get_l(length(T1T2me),order); 0126 -0127 % extend K and apply regularization -0128 % 'manual' | 'gcv_tikh' | 'gcv_trunc' | 'gcv_damp' | 'discrep' -0129 [KK,lambda_out] = applyRegularization(K,g,L,lambda,regMethod,order,noise); -0130 -0131 % extend g accordingly -0132 gg = g; -0133 gg(length(g)+1:length(g)+size(L,1),1) = 0; +0127 % scale the noise and error matrix W accordingly +0128 noise = noise./maxS; +0129 if isfield(parameter,'W') +0130 e = diag(parameter.W); +0131 e = e./maxS; +0132 W = diag(e); +0133 end 0134 -0135 switch parameter.solver -0136 case 'lsqlin' -0137 options = optimoptions('lsqlin'); -0138 options.Display = parameter.info; -0139 options.OptimalityTolerance = 1e-18; -0140 options.StepTolerance = 1e-18; -0141 [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... -0142 f0_lb,f0_ub,[],options); -0143 case 'lsqnonneg' -0144 options = optimset('Display',parameter.info,'TolX',1e-12); -0145 [f,~,~,~,~,~] = lsqnonneg(KK,gg,options); -0146 end -0147 -0148 % rescale f so that the sum(f)= unscaled E0 -0149 f = (f.*maxS); -0150 -0151 % the 'inverted' signal -0152 gg_fit = KK*f; -0153 % cut off the end which was needed for regularization -0154 s_fit = gg_fit(1:length(t),1); -0155 -0156 % get residuals and error measures -0157 if isfield(parameter,'W') -0158 % normalize the fit because the signal was error weighted for the -0159 % inversion -0160 e = diag(W); -0161 einv = 1./e; -0162 Winv = diag(einv); -0163 s_fit = Winv * s_fit; -0164 -0165 % because signal and s_fit are unscaled the initial values for noise -0166 % and W are used to get the error estimates -0167 out = getFitErrors(signal,s_fit,parameter.noise,parameter.W); -0168 else -0169 out = getFitErrors(signal,s_fit,parameter.noise); -0170 end -0171 -0172 % L-curve parameter -0173 % model norm |L*x|_2 -0174 xn = norm(L*f,2); -0175 % residual norm |A*x-b|_2 -0176 rn = norm(out.residual,2); -0177 -0178 % get "initial" value E0 -0179 if strcmp(flag,'T1') -0180 t2 = 10*time(end); -0181 K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); -0182 elseif strcmp(flag,'T2') -0183 t2 = 0; -0184 K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); -0185 end -0186 E0 = K2*f; -0187 -0188 % output struct -0189 fitdata.fit_t = time(:); -0190 fitdata.fit_s = s_fit(:); -0191 fitdata.T1T2me = T1T2me(:); -0192 fitdata.T1T2f = f(:); -0193 fitdata.Tlgm = getTLogMean(T1T2me,f); -0194 fitdata.E0 = E0; -0195 fitdata.resnorm = out.resnorm; -0196 fitdata.residual = out.residual; -0197 fitdata.chi2 = out.chi2; -0198 fitdata.rms = out.rms; -0199 fitdata.lambda_out = lambda_out; -0200 fitdata.KK = KK; -0201 fitdata.L = L; -0202 fitdata.xn = xn; -0203 fitdata.rn = rn; +0135 % apply error weight matrix +0136 if isfield(parameter,'W') +0137 g = W*g; +0138 K = W*K; +0139 end +0140 +0141 % extend K and apply regularization +0142 % 'manual' | 'gcv_tikh' | 'gcv_trunc' | 'gcv_damp' | 'discrep' +0143 [KK,lambda_out] = applyRegularization(K,g,L,lambda,regMethod,order,noise); +0144 +0145 % extend g accordingly +0146 gg = g; +0147 gg(length(g)+1:length(g)+size(L,1),1) = 0; +0148 +0149 switch parameter.solver +0150 case 'lsqlin' +0151 options = optimoptions('lsqlin'); +0152 options.Display = parameter.info; +0153 options.OptimalityTolerance = 1e-18; +0154 options.StepTolerance = 1e-18; +0155 if isfield(parameter,'bounds') +0156 [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... +0157 f0_lb,f0_ub,f0,options); +0158 else +0159 [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... +0160 f0_lb,f0_ub,[],options); +0161 end +0162 case 'lsqnonneg' +0163 options = optimset('Display',parameter.info,'TolX',1e-12); +0164 [f,~,~,~,~,~] = lsqnonneg(KK,gg,options); +0165 end +0166 +0167 % rescale f so that the sum(f) = unscaled E0 +0168 f = (f.*maxS); +0169 +0170 % the 'inverted' signal +0171 gg_fit = KK*f; +0172 % cut off the end which was needed for regularization +0173 s_fit = gg_fit(1:length(t),1); +0174 +0175 % get residuals and error measures +0176 if isfield(parameter,'W') +0177 % normalize the fit because the signal was error weighted for the +0178 % inversion +0179 e = diag(W); +0180 einv = 1./e; +0181 Winv = diag(einv); +0182 s_fit = Winv * s_fit; +0183 +0184 % because signal and s_fit are unscaled the initial values for noise +0185 % and W are used to get the error estimates +0186 out = getFitErrors(signal,s_fit,parameter.noise,parameter.W); +0187 else +0188 out = getFitErrors(signal,s_fit,parameter.noise); +0189 end +0190 +0191 % L-curve parameter +0192 % model norm |L*x|_2 +0193 xn = norm(L*f,2); +0194 % residual norm |A*x-b|_2 +0195 rn = norm(out.residual,2); +0196 +0197 % get "initial" value E0 +0198 if strcmp(flag,'T1') +0199 K0 = createKernelMatrix(10*time(end),T1T2me,Tb,Td,flag,T1IRfac); +0200 elseif strcmp(flag,'T2') +0201 K0 = createKernelMatrix(0,T1T2me,Tb,Td,flag,T1IRfac); +0202 end +0203 E0 = K0*f; 0204 -0205 return -0206 -0207 %------------- END OF CODE -------------- -0208 -0209 %% License: -0210 % MIT License -0211 % -0212 % Copyright (c) 2018 Thomas Hiller -0213 % -0214 % Permission is hereby granted, free of charge, to any person obtaining a copy -0215 % of this software and associated documentation files (the "Software"), to deal -0216 % in the Software without restriction, including without limitation the rights -0217 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0218 % copies of the Software, and to permit persons to whom the Software is -0219 % furnished to do so, subject to the following conditions: -0220 % -0221 % The above copyright notice and this permission notice shall be included in all -0222 % copies or substantial portions of the Software. -0223 % -0224 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0225 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0226 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0227 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0228 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0229 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0230 % SOFTWARE. +0205 % output struct +0206 fitdata.fit_t = time(:); +0207 fitdata.fit_s = s_fit(:); +0208 fitdata.T1T2me = T1T2me(:); +0209 fitdata.T1T2f = f(:); +0210 fitdata.Tlgm = getTLogMean(T1T2me,f); +0211 fitdata.E0 = E0; +0212 fitdata.ciE0 = NaN; +0213 fitdata.resnorm = out.resnorm; +0214 fitdata.residual = out.residual; +0215 fitdata.chi2 = out.chi2; +0216 fitdata.rms = out.rms; +0217 fitdata.lambda_out = lambda_out; +0218 fitdata.KK = KK; +0219 fitdata.L = L; +0220 fitdata.xn = xn; +0221 fitdata.rn = rn; +0222 +0223 return +0224 +0225 %------------- END OF CODE -------------- +0226 +0227 %% License: +0228 % MIT License +0229 % +0230 % Copyright (c) 2018 Thomas Hiller +0231 % +0232 % Permission is hereby granted, free of charge, to any person obtaining a copy +0233 % of this software and associated documentation files (the "Software"), to deal +0234 % in the Software without restriction, including without limitation the rights +0235 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0236 % copies of the Software, and to permit persons to whom the Software is +0237 % furnished to do so, subject to the following conditions: +0238 % +0239 % The above copyright notice and this permission notice shall be included in all +0240 % copies or substantial portions of the Software. +0241 % +0242 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0243 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0244 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0245 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0246 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0247 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0248 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fitDataLUdecomp.html b/doc/nucleus/functions/inversion/fitDataLUdecomp.html index 38f6098..e19f71b 100644 --- a/doc/nucleus/functions/inversion/fitDataLUdecomp.html +++ b/doc/nucleus/functions/inversion/fitDataLUdecomp.html @@ -39,6 +39,7 @@

    DESCRIPTION ^DESCRIPTION ^DESCRIPTION ^CROSS-REFERENCE INFORMATION ^

    This function calls:
      -
    • createKernelMatrix creates a Kernel matrix from signal time vector "t"
    • getFitErrors calculates all relevant fitting errors for the NMR inversion
    • getTLogMean calculates the T logmean value out of a relaxation time
    +
  • createKernelMatrix creates a Kernel matrix from signal time vector "t"
  • getFitErrors calculates all relevant fitting errors for the NMR inversion
  • getTLogMean calculates the T logmean value out of a relaxation time
  • This function is called by: @@ -113,185 +114,187 @@

    SOURCE CODE ^% T1T2 : flag between 'T1' or 'T2' inversion 0013 % T1IRfac : either '1' or '2' depending on T1 method 0014 % Tb : bulk relaxation time -0015 % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] -0016 % Lorder : smoothness constraint (derivative matrix) -0017 % lambda : regularization parameter (if -1 automatic -0018 % regularization) -0019 % noise : noise level -0020 % -0021 % Outputs: -0022 % fitdata - struct that holds the inversion results: -0023 % fit_t : time vector for plotting -0024 % fit_s : signal vector for plotting -0025 % T1T2me : relaxation time values -0026 % T1T2f : relaxation time spectrum -0027 % Tlgm : T logmean -0028 % E0 : initial amplitude at t=0 (T2) or t=max (T1) -0029 % resnorm : residual norm -0030 % residual : vector of residuals -0031 % chi2 : chi square error -0032 % rms : RMS error -0033 % lambda_out : regularization parameter lambda determined -0034 % by the different options from the 'regu' -0035 % toolbox -0036 % KK : Kernel matrix -0037 % L : derivative matrix -0038 % xn : model norm |L*x|_2 -0039 % rn : residual norm |A*x-b|_2 -0040 % -0041 % Example: -0042 % [fitdata] = fitDataLUdecomp(t,s,parameter) -0043 % -0044 % Other m-files required: -0045 % createKernelMatrix -0046 % getFitErrors -0047 % getTLogMean -0048 % get_l (from 'Regularization Toolbox') -0049 % -0050 % Subfunctions: -0051 % none -0052 % -0053 % MAT-files required: -0054 % none -0055 % -0056 % See also: -0057 % Author: Thomas Hiller -0058 % email: thomas.hiller[at]leibniz-liag.de -0059 % NOTE: I harvested this routine partly from the Internet but forgot where -0060 % I found the routines ... so there is no warranty at all -0061 -0062 %------------- BEGIN CODE -------------- -0063 -0064 % make input column vectors -0065 time = time(:); -0066 signal = signal(:); -0067 -0068 % get the maximum value of the signal to scale the signal for the inversion -0069 % to 1 -0070 maxS = max(signal); -0071 -0072 % temporary variables -0073 t = time; -0074 g = signal./maxS; -0075 -0076 % get the input parameters -0077 flag = parameter.T1T2; % T1/T2 switch -0078 T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor -0079 Tb = parameter.Tb; % bulk relaxation time -0080 tstart = parameter.Tint(1); % log10 value -0081 tend = parameter.Tint(2); % log10 value -0082 N = parameter.Tint(3); % N per decade -0083 order = parameter.Lorder; % smoothness constraint -0084 lambda = parameter.lambda; % regularization parameter -0085 noise = parameter.noise; % noise -0086 -0087 % create the relaxation time vector -0088 T1T2me = logspace(tstart,tend,(tend-tstart)*N); -0089 -0090 % create the Kernel matrix -0091 K = createKernelMatrix(t,T1T2me,Tb,flag,T1IRfac); -0092 -0093 % calculate reg matrix H -0094 m = length(T1T2me); -0095 B = get_l(m,order); -0096 H = B'*B; -0097 -0098 % scale the noise and error matrix W accordingly -0099 noise = noise./maxS; -0100 if isfield(parameter,'W') -0101 e = diag(parameter.W); -0102 e = e./maxS; -0103 W = diag(e); -0104 end -0105 -0106 % apply error weight matrix -0107 if isfield(parameter,'W') -0108 g = W*g; -0109 K = W*K; -0110 end -0111 -0112 % automatic regularization -0113 if lambda == -1 -0114 lambda = trace(K'*K)/trace(H); -0115 end -0116 -0117 % calculate A = K'*K + lambda*H -0118 A = K'*K + lambda*H; -0119 % calculate y = K'*g -0120 y = K'*g; -0121 % calculate f by LU decomposition -0122 [L,U] = lu(A); -0123 f = U\(L\y); -0124 -0125 % now iterate, mapping negative values to zero. -0126 e = 2/max(eig(A)); -0127 A = K'*K; % no regularization now -0128 for i = 1:1000 -0129 f = (f>0).*f; % map neg to zero -0130 f =(eye(m)-e*A)*f+e*y; -0131 end -0132 f = (f>0).*f; % map neg to zero again -0133 -0134 % rescale f so that the sum(f)= unscaled E0 -0135 f = (f.*maxS); -0136 -0137 % the inverted signal -0138 s_fit = K*f; -0139 s_fit = s_fit(1:length(t),1); -0140 -0141 % get residuals and error measures -0142 if isfield(parameter,'W') -0143 % normalize the fit because the signal was error weighted for the -0144 % inversion -0145 e = diag(W); -0146 einv = 1./e; -0147 Winv = diag(einv); -0148 s_fit = Winv * s_fit; -0149 -0150 % because signal and s_fit are unscaled the initial values for noise -0151 % and W are used to get the error estimates -0152 out = getFitErrors(signal,s_fit,parameter.noise,parameter.W); -0153 else -0154 out = getFitErrors(signal,s_fit,parameter.noise); -0155 end -0156 -0157 % derivative matrix -0158 L = get_l(length(T1T2me),order); -0159 % L-curve parameter -0160 % model norm |L*x|_2 -0161 xn = norm(L*f,2); -0162 % residual norm |A*x-b|_2 -0163 rn = norm(out.residual,2); -0164 -0165 % get "initial" value E0 -0166 if strcmp(flag,'T1') -0167 t2 = 10*time(end); -0168 K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); -0169 elseif strcmp(flag,'T2') -0170 t2 = 0; -0171 K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); -0172 end -0173 E0 = K2*f; -0174 -0175 % output struct -0176 fitdata.fit_t = time(:); -0177 fitdata.fit_s = s_fit(:); -0178 fitdata.T1T2me = T1T2me(:); -0179 fitdata.T1T2f = f(:); -0180 fitdata.Tlgm = getTLogMean(T1T2me,f); -0181 fitdata.E0 = E0; -0182 fitdata.residual = out.residual; -0183 fitdata.chi2 = out.chi2; -0184 fitdata.rms = out.rms; -0185 fitdata.lambda_out = lambda; -0186 fitdata.KK = K; -0187 fitdata.L = L; -0188 fitdata.xn = xn; -0189 fitdata.rn = rn; -0190 -0191 return +0015 % Td : diffusion relaxation time +0016 % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] +0017 % Lorder : smoothness constraint (derivative matrix) +0018 % lambda : regularization parameter (if -1 automatic +0019 % regularization) +0020 % noise : noise level +0021 % +0022 % Outputs: +0023 % fitdata - struct that holds the inversion results: +0024 % fit_t : time vector for plotting +0025 % fit_s : signal vector for plotting +0026 % T1T2me : relaxation time values +0027 % T1T2f : relaxation time spectrum +0028 % Tlgm : T logmean +0029 % E0 : initial amplitude at t=0 (T2) or t=max (T1) +0030 % resnorm : residual norm +0031 % residual : vector of residuals +0032 % chi2 : chi square error +0033 % rms : RMS error +0034 % lambda_out : regularization parameter lambda determined +0035 % by the different options from the 'regu' +0036 % toolbox +0037 % KK : Kernel matrix +0038 % L : derivative matrix +0039 % xn : model norm |L*x|_2 +0040 % rn : residual norm |A*x-b|_2 +0041 % +0042 % Example: +0043 % [fitdata] = fitDataLUdecomp(t,s,parameter) +0044 % +0045 % Other m-files required: +0046 % createKernelMatrix +0047 % getFitErrors +0048 % getTLogMean +0049 % get_l (from 'Regularization Toolbox') +0050 % +0051 % Subfunctions: +0052 % none +0053 % +0054 % MAT-files required: +0055 % none +0056 % +0057 % See also: +0058 % Author: see AUTHORS.md +0059 % email: see AUTHORS.md +0060 % NOTE: I harvested this routine partly from the Internet but forgot where +0061 % I found the routines ... so there is no warranty at all +0062 +0063 %------------- BEGIN CODE -------------- +0064 +0065 % make input column vectors +0066 time = time(:); +0067 signal = signal(:); +0068 +0069 % get the maximum value of the signal to scale the signal for the inversion +0070 % to 1 +0071 maxS = max(signal); +0072 +0073 % temporary variables +0074 t = time; +0075 g = signal./maxS; +0076 +0077 % get the input parameters +0078 flag = parameter.T1T2; % T1/T2 switch +0079 T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor +0080 Tb = parameter.Tb; % bulk relaxation time +0081 Td = parameter.Td; % diffusion relaxation time +0082 tstart = parameter.Tint(1); % log10 value +0083 tend = parameter.Tint(2); % log10 value +0084 N = parameter.Tint(3); % N per decade +0085 order = parameter.Lorder; % smoothness constraint +0086 lambda = parameter.lambda; % regularization parameter +0087 noise = parameter.noise; % noise +0088 +0089 % create the relaxation time vector +0090 T1T2me = logspace(tstart,tend,(tend-tstart)*N); +0091 +0092 % create the Kernel matrix +0093 K = createKernelMatrix(t,T1T2me,Tb,Td,flag,T1IRfac); +0094 +0095 % calculate reg matrix H +0096 m = length(T1T2me); +0097 B = get_l(m,order); +0098 H = B'*B; +0099 +0100 % scale the noise and error matrix W accordingly +0101 noise = noise./maxS; +0102 if isfield(parameter,'W') +0103 e = diag(parameter.W); +0104 e = e./maxS; +0105 W = diag(e); +0106 end +0107 +0108 % apply error weight matrix +0109 if isfield(parameter,'W') +0110 g = W*g; +0111 K = W*K; +0112 end +0113 +0114 % automatic regularization +0115 if lambda == -1 +0116 lambda = trace(K'*K)/trace(H); +0117 end +0118 +0119 % calculate A = K'*K + lambda*H +0120 A = K'*K + lambda*H; +0121 % calculate y = K'*g +0122 y = K'*g; +0123 % calculate f by LU decomposition +0124 [L,U] = lu(A); +0125 f = U\(L\y); +0126 +0127 % now iterate, mapping negative values to zero. +0128 e = 2/max(eig(A)); +0129 A = K'*K; % no regularization now +0130 for i = 1:1000 +0131 f = (f>0).*f; % map neg to zero +0132 f =(eye(m)-e*A)*f+e*y; +0133 end +0134 f = (f>0).*f; % map neg to zero again +0135 +0136 % rescale f so that the sum(f)= unscaled E0 +0137 f = (f.*maxS); +0138 +0139 % the inverted signal +0140 s_fit = K*f; +0141 s_fit = s_fit(1:length(t),1); +0142 +0143 % get residuals and error measures +0144 if isfield(parameter,'W') +0145 % normalize the fit because the signal was error weighted for the +0146 % inversion +0147 e = diag(W); +0148 einv = 1./e; +0149 Winv = diag(einv); +0150 s_fit = Winv * s_fit; +0151 +0152 % because signal and s_fit are unscaled the initial values for noise +0153 % and W are used to get the error estimates +0154 out = getFitErrors(signal,s_fit,parameter.noise,parameter.W); +0155 else +0156 out = getFitErrors(signal,s_fit,parameter.noise); +0157 end +0158 +0159 % derivative matrix +0160 L = get_l(length(T1T2me),order); +0161 % L-curve parameter +0162 % model norm |L*x|_2 +0163 xn = norm(L*f,2); +0164 % residual norm |A*x-b|_2 +0165 rn = norm(out.residual,2); +0166 +0167 % get "initial" value E0 +0168 if strcmp(flag,'T1') +0169 t2 = 10*time(end); +0170 K2 = createKernelMatrix(t2,T1T2me,Tb,Td,flag,T1IRfac); +0171 elseif strcmp(flag,'T2') +0172 t2 = 0; +0173 K2 = createKernelMatrix(t2,T1T2me,Tb,Td,flag,T1IRfac); +0174 end +0175 E0 = K2*f; +0176 +0177 % output struct +0178 fitdata.fit_t = time(:); +0179 fitdata.fit_s = s_fit(:); +0180 fitdata.T1T2me = T1T2me(:); +0181 fitdata.T1T2f = f(:); +0182 fitdata.Tlgm = getTLogMean(T1T2me,f); +0183 fitdata.E0 = E0; +0184 fitdata.residual = out.residual; +0185 fitdata.chi2 = out.chi2; +0186 fitdata.rms = out.rms; +0187 fitdata.lambda_out = lambda; +0188 fitdata.KK = K; +0189 fitdata.L = L; +0190 fitdata.xn = xn; +0191 fitdata.rn = rn; 0192 -0193 %------------- END OF CODE -------------- +0193 return +0194 +0195 %------------- END OF CODE --------------
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/fitDataMultiModal.html b/doc/nucleus/functions/inversion/fitDataMultiModal.html new file mode 100644 index 0000000..bd0ae6e --- /dev/null +++ b/doc/nucleus/functions/inversion/fitDataMultiModal.html @@ -0,0 +1,415 @@ + + + + Description of fitDataMultiModal + + + + + + + + + + + +

    fitDataMultiModal +

    + +

    PURPOSE ^

    +
    is a control routine that uses either 'lsqnonlin' or
    + +

    SYNOPSIS ^

    +
    function [fitdata] = fitDataMultiModal(time,signal,parameter,nModes)
    + +

    DESCRIPTION ^

    +
    fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
    +'fminsearchbnd' to fit NMR data with 'nModes' multi modal relaxation time
    +distributions (T1 or T2)
    +
    + Syntax:
    +       fitDataMultiModal(time,signal,flag,parameter,nExp)
    +
    + InputsR:
    +       time - time vector
    +       signal - NMR signal vector (no complex data allowed!)
    +       parameter - struct that holds additional settings:
    +                   T1T2     : flag between 'T1' or 'T2' inversion
    +                   T1IRfac  : either '1' or '2' depending on T1 method
    +                   Tb       : bulk relaxation time
    +                   Td       : diffusion relaxation time
    +                   Tint     : relaxation times [log10(tmin) log10(tmax) Ndec]
    +                   noise    : noise level needed for 'discrep' discrepancy
    +                              principle
    +                   optim    : 'on' or 'off' (Optimization Toolbox)
    +                   W        : error weighting matrix (optional)
    +       nModes - No. of free distributions
    +
    + Outputs:
    +       fitdata - struct that holds the inversion results:
    +                   fit_t   : time vector for plotting
    +                   fit_s   : signal vector for plotting
    +                   T1T2me  : relaxation time values
    +                   T1T2f   : relaxation time spectrum
    +                   Tlgm    : T log-mean
    +                   E0      : initial amplitude at t=0 (T2) or t=max (T1)
    +                   ciE     : E0 confidence interval (NaN as placeholder)
    +                   resnorm : residual norm
    +                   residual: vector of residuals
    +                   errnorm : error norm
    +                   lambda_out : dummy 0
    +                   rms     : RMS error
    +                   chi2    : chi square error
    +                   ci      : confidence interval
    +                   T       : relaxation times per mode
    +                   S       : width per mode
    +                   E       : amplitude per mode;
    +                   x       : all parameters combined
    +                   lb      : lower bounds
    +                   ub      : upper bounds
    +                   output  : output struct (output from 'lsqnonlin' or
    +                             'fminsearchbnd')
    +
    + Example:
    +       [fitdata] = fitDataMultiModal(t,s,parameter,2)
    +
    + Other m-files required:
    +       createKernelMatrix
    +       estimateJacobian
    +       fcn_fitMultiModal
    +       fitDataFree
    +       fminsearchbnd
    +       getFitErrors
    +       getFitFreeJacobian
    +       getConfInterval
    +       lsqnonlin (Optimization Toolbox)
    +
    + Subfunctions:
    +       none
    +
    + MAT-files required:
    +       none
    +
    + See also:
    + Author: see AUTHORS.md
    + email: see AUTHORS.md
    + License: MIT License (at end)
    + + +

    CROSS-REFERENCE INFORMATION ^

    +This function calls: + +This function is called by: + + + + + +

    SOURCE CODE ^

    +
    0001 function [fitdata] = fitDataMultiModal(time,signal,parameter,nModes)
    +0002 %fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
    +0003 %'fminsearchbnd' to fit NMR data with 'nModes' multi modal relaxation time
    +0004 %distributions (T1 or T2)
    +0005 %
    +0006 % Syntax:
    +0007 %       fitDataMultiModal(time,signal,flag,parameter,nExp)
    +0008 %
    +0009 % InputsR:
    +0010 %       time - time vector
    +0011 %       signal - NMR signal vector (no complex data allowed!)
    +0012 %       parameter - struct that holds additional settings:
    +0013 %                   T1T2     : flag between 'T1' or 'T2' inversion
    +0014 %                   T1IRfac  : either '1' or '2' depending on T1 method
    +0015 %                   Tb       : bulk relaxation time
    +0016 %                   Td       : diffusion relaxation time
    +0017 %                   Tint     : relaxation times [log10(tmin) log10(tmax) Ndec]
    +0018 %                   noise    : noise level needed for 'discrep' discrepancy
    +0019 %                              principle
    +0020 %                   optim    : 'on' or 'off' (Optimization Toolbox)
    +0021 %                   W        : error weighting matrix (optional)
    +0022 %       nModes - No. of free distributions
    +0023 %
    +0024 % Outputs:
    +0025 %       fitdata - struct that holds the inversion results:
    +0026 %                   fit_t   : time vector for plotting
    +0027 %                   fit_s   : signal vector for plotting
    +0028 %                   T1T2me  : relaxation time values
    +0029 %                   T1T2f   : relaxation time spectrum
    +0030 %                   Tlgm    : T log-mean
    +0031 %                   E0      : initial amplitude at t=0 (T2) or t=max (T1)
    +0032 %                   ciE     : E0 confidence interval (NaN as placeholder)
    +0033 %                   resnorm : residual norm
    +0034 %                   residual: vector of residuals
    +0035 %                   errnorm : error norm
    +0036 %                   lambda_out : dummy 0
    +0037 %                   rms     : RMS error
    +0038 %                   chi2    : chi square error
    +0039 %                   ci      : confidence interval
    +0040 %                   T       : relaxation times per mode
    +0041 %                   S       : width per mode
    +0042 %                   E       : amplitude per mode;
    +0043 %                   x       : all parameters combined
    +0044 %                   lb      : lower bounds
    +0045 %                   ub      : upper bounds
    +0046 %                   output  : output struct (output from 'lsqnonlin' or
    +0047 %                             'fminsearchbnd')
    +0048 %
    +0049 % Example:
    +0050 %       [fitdata] = fitDataMultiModal(t,s,parameter,2)
    +0051 %
    +0052 % Other m-files required:
    +0053 %       createKernelMatrix
    +0054 %       estimateJacobian
    +0055 %       fcn_fitMultiModal
    +0056 %       fitDataFree
    +0057 %       fminsearchbnd
    +0058 %       getFitErrors
    +0059 %       getFitFreeJacobian
    +0060 %       getConfInterval
    +0061 %       lsqnonlin (Optimization Toolbox)
    +0062 %
    +0063 % Subfunctions:
    +0064 %       none
    +0065 %
    +0066 % MAT-files required:
    +0067 %       none
    +0068 %
    +0069 % See also:
    +0070 % Author: see AUTHORS.md
    +0071 % email: see AUTHORS.md
    +0072 % License: MIT License (at end)
    +0073 
    +0074 %------------- BEGIN CODE --------------
    +0075 
    +0076 % make column vector
    +0077 t = time(:);
    +0078 s = signal(:);
    +0079 
    +0080 % error weights after gating
    +0081 if isfield(parameter,'W')
    +0082     e = diag(parameter.W);
    +0083     iparam.e = sqrt(e);
    +0084 end
    +0085 
    +0086 % get the input parameters
    +0087 % T1/T2 switch
    +0088 flag = parameter.T1T2;
    +0089 % T1 Sat/Inv Recovery factor
    +0090 T1IRfac = parameter.T1IRfac;
    +0091 % bulk relaxation time
    +0092 Tb = parameter.Tb;
    +0093 % diffusion relaxation time
    +0094 Td = parameter.Td;
    +0095 % smallest value in RTD (log10 value)
    +0096 tstart = parameter.Tint(1);
    +0097 % largest value in RTD (log10 value)
    +0098 tend = parameter.Tint(2);
    +0099 % N per decade in RTD
    +0100 N = parameter.Tint(3);
    +0101 
    +0102 % get boundary values for mu, sigma and amp by first applying a free
    +0103 % exponential fit
    +0104 param0.T1IRfac = T1IRfac;
    +0105 param0.noise = parameter.noise;
    +0106 param0.optim = parameter.optim;
    +0107 if isfield(parameter,'W')
    +0108     param0.W = parameter.W;
    +0109 end
    +0110 % free exponential fit to get some reasonable start values
    +0111 invstd0 = fitDataFree(t,s,flag,param0,nModes);
    +0112 
    +0113 % start values for E and T
    +0114 x0 = zeros(1,3*nModes);
    +0115 lb = zeros(1,3*nModes);
    +0116 ub = zeros(1,3*nModes);
    +0117 for i = 1:nModes
    +0118     % initial values for T, sigma and E
    +0119     x0(3*i-2) = log(invstd0.x(2*i));
    +0120     x0(3*i-1) = 1;
    +0121     x0(3*i) = invstd0.x(2*i-1);
    +0122     
    +0123     % lower bounds for T, sigma and E
    +0124     lb(3*i-2) = log(1e-6);%log(invstd0.x(2*i)*0.8);%log(invstd0.x(2*i) - 10*invstd0.ci(2*i));
    +0125     lb(3*i-1) = 0.01;
    +0126     lb(3*i) = invstd0.x(2*i-1)*0.8;%invstd0.x(2*i-1) - 10*invstd0.ci(2*i-1);
    +0127     
    +0128     % upper bounds for T, sigma and E
    +0129     ub(3*i-2) = log(10);%log(invstd0.x(2*i) + 50*invstd0.ci(2*i));
    +0130     ub(3*i-1) = 3.5;
    +0131     ub(3*i) = max(invstd0.E0)*1.1;%invstd0.x(2*i-1) + 50*invstd0.ci(2*i-1);
    +0132 end
    +0133 
    +0134 % switch off output if no option is given via 'parameter'
    +0135 if ~isfield(parameter,'info')
    +0136     parameter.info = 'off';
    +0137 end
    +0138 
    +0139 % create the relaxation time vector
    +0140 T1T2me = logspace(tstart,tend,(tend-tstart)*N);
    +0141 
    +0142 % just needed for debugging the Optimization Toolbox availability
    +0143 % parameter.optim = 'off';
    +0144 
    +0145 switch parameter.optim
    +0146     case 'on'
    +0147         % solver options
    +0148         options = optimoptions('lsqnonlin');
    +0149         options.Algorithm = 'levenberg-marquardt';
    +0150         options.Display = parameter.info;
    +0151         options.OptimalityTolerance = 1e-12;
    +0152         options.StepTolerance = 1e-12;
    +0153         
    +0154         iparam.optim = parameter.optim;
    +0155         iparam.flag = flag;
    +0156         iparam.T1IRfac = T1IRfac;
    +0157         iparam.nModes = nModes;
    +0158         iparam.t = t;
    +0159         iparam.s = s;
    +0160         iparam.T = T1T2me;
    +0161         iparam.Tb = Tb;
    +0162         iparam.Td = Td;
    +0163         [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitMultiModal(x,iparam),...
    +0164             x0,lb,ub,options);
    +0165         
    +0166     case 'off'
    +0167         % solver options
    +0168         options = optimset('Display',parameter.info,'MaxFunEvals',10^6,...
    +0169             'MaxIter',5000,'TolFun',1e-12,'TolX',1e-12);
    +0170         
    +0171         iparam.optim = parameter.optim;
    +0172         iparam.flag = flag;
    +0173         iparam.T1IRfac = T1IRfac;
    +0174         iparam.nModes = nModes;
    +0175         iparam.t = t;
    +0176         iparam.s = s;
    +0177         iparam.T = T1T2me;
    +0178         iparam.Tb = Tb;
    +0179         iparam.Td = Td;
    +0180         [x,~,~,output] = fminsearchbnd(@(x) fcn_fitMultiModal(x,iparam),...
    +0181             x0,lb,ub,options);
    +0182         
    +0183         % get Jacobian
    +0184         % therefore we need to switch the 'optim' on to get the correct
    +0185         % output of 'fcn_fitMultiModal'
    +0186         iparam.optim = 'on';
    +0187         jacobian = estimateJacobian(@(x)fcn_fitMultiModal(x,iparam),x);
    +0188 end
    +0189 
    +0190 % assemble the final RTD
    +0191 Tdist = 0;
    +0192 for i = 1:length(x)/3
    +0193     mu = exp(x(3*i-2)); % T
    +0194     sigma = x(3*i-1); % S
    +0195     amp = x(3*i); % E
    +0196     
    +0197     tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(T1T2me) - log(mu))/ sqrt(2)/sigma).^2);
    +0198     
    +0199     % scale to amplitude
    +0200     tmp = (tmp/sum(tmp)) * amp;
    +0201     % add the tmp per mu to Tdist
    +0202     Tdist = Tdist + tmp;
    +0203 end
    +0204 f = Tdist;
    +0205 % the fitted signal
    +0206 K = createKernelMatrix(t,T1T2me,Tb,Td,flag,1);
    +0207 si = K*f';
    +0208 
    +0209 % get the fit
    +0210 fit_t = t;
    +0211 fit_s = si;
    +0212 
    +0213 % get residuals and error measures
    +0214 if isfield(parameter,'W')
    +0215     % when signal gating was used the error estimates need to be adjusted
    +0216     out = getFitErrors(signal,fit_s,parameter.noise,parameter.W);
    +0217 else
    +0218     out = getFitErrors(signal,fit_s,parameter.noise);
    +0219 end
    +0220 
    +0221 % confidence interval
    +0222 ci = getConfInterval(out.resnorm,jacobian,0.05);
    +0223 
    +0224 % sort the relaxation times in ascending order and adjust the confidence
    +0225 % interval accordingly
    +0226 T = exp(x(1:3:end));
    +0227 S = x(2:3:end);
    +0228 E = x(3:3:end);
    +0229 [T,idx] = sort(T);
    +0230 S  = S(idx);
    +0231 E  = E(idx);
    +0232 ciT = ci(1:3:end);
    +0233 ciS = ci(2:3:end);
    +0234 ciE = ci(3:3:end);
    +0235 ciT = ciT(idx);
    +0236 ciS = ciS(idx);
    +0237 ciE = ciE(idx);
    +0238 ci(1:3:end) = ciT;
    +0239 ci(2:3:end) = ciS;
    +0240 ci(3:3:end) = ciE;
    +0241 
    +0242 % get "initial" value E0
    +0243 switch flag
    +0244     case 'T1'
    +0245         K0 = createKernelMatrix(10*time(end),T1T2me,Tb,Td,flag,T1IRfac);
    +0246     case 'T2'
    +0247         K0 = createKernelMatrix(0,T1T2me,Tb,Td,flag,T1IRfac);
    +0248 end
    +0249 E0 = K0*f';
    +0250 
    +0251 % output struct
    +0252 fitdata.fit_t = fit_t;
    +0253 fitdata.fit_s = fit_s;
    +0254 fitdata.T1T2me = T1T2me(:);
    +0255 fitdata.T1T2f = f(:);
    +0256 fitdata.Tlgm = getTLogMean(T1T2me,f);
    +0257 fitdata.E0 = E0;
    +0258 fitdata.ciE0 = NaN;
    +0259 fitdata.resnorm = out.resnorm;
    +0260 fitdata.residual = out.residual;
    +0261 fitdata.errornorm = out.errnorm1;
    +0262 fitdata.lambda_out = 0;
    +0263 fitdata.rms = out.rms;
    +0264 fitdata.chi2 = out.chi2;
    +0265 fitdata.ci = ci;
    +0266 fitdata.T = T;
    +0267 fitdata.S = S;
    +0268 fitdata.E = E;
    +0269 fitdata.x = x;
    +0270 fitdata.lb = lb;
    +0271 fitdata.ub = ub;
    +0272 fitdata.output = output;
    +0273 
    +0274 return
    +0275 
    +0276 %------------- END OF CODE --------------
    +0277 
    +0278 %% License:
    +0279 % MIT License
    +0280 %
    +0281 % Copyright (c) 2022 Thomas Hiller
    +0282 %
    +0283 % Permission is hereby granted, free of charge, to any person obtaining a copy
    +0284 % of this software and associated documentation files (the "Software"), to deal
    +0285 % in the Software without restriction, including without limitation the rights
    +0286 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    +0287 % copies of the Software, and to permit persons to whom the Software is
    +0288 % furnished to do so, subject to the following conditions:
    +0289 %
    +0290 % The above copyright notice and this permission notice shall be included in all
    +0291 % copies or substantial portions of the Software.
    +0292 %
    +0293 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    +0294 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    +0295 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    +0296 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    +0297 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    +0298 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    +0299 % SOFTWARE.
    +
    Generated by m2html © 2005
    + + \ No newline at end of file diff --git a/doc/nucleus/functions/inversion/getChi2.html b/doc/nucleus/functions/inversion/getChi2.html index b1dbac0..c76f596 100644 --- a/doc/nucleus/functions/inversion/getChi2.html +++ b/doc/nucleus/functions/inversion/getChi2.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getConfInterval.html b/doc/nucleus/functions/inversion/getConfInterval.html index ecf0574..100e87d 100644 --- a/doc/nucleus/functions/inversion/getConfInterval.html +++ b/doc/nucleus/functions/inversion/getConfInterval.html @@ -58,8 +58,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=getStudentInvCDF calculates the inverse of Student's t CDF using the This function is called by:
      -
    • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
    +
  • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • @@ -108,8 +108,8 @@

    SOURCE CODE ^% 0032 % See also: "Parameter Estimation and Inverse Problems", 2nd Ed. 0033 % by Aster et. al p.32 ff -0034 % Author: Thomas Hiller -0035 % email: thomas.hiller[at]leibniz-liag.de +0034 % Author: see AUTHORS.md +0035 % email: see AUTHORS.md 0036 % License: MIT License (at end) 0037 0038 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getFitErrors.html b/doc/nucleus/functions/inversion/getFitErrors.html index 9ae2332..864b29b 100644 --- a/doc/nucleus/functions/inversion/getFitErrors.html +++ b/doc/nucleus/functions/inversion/getFitErrors.html @@ -61,8 +61,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 <li><a href=getChi2 the chi2 of a NMR fit (noise weighted error quality) This function is called by:
      -
    • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
    • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
    • fitDataLUdecomp is a control routine that uses a LU decomposition and the
    +
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • fitDataFree is a control routine that uses 'lsqcurvefit' to fit NMR data
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • @@ -114,8 +114,8 @@

    SOURCE CODE ^% none 0035 % 0036 % See also: -0037 % Author: Thomas Hiller -0038 % email: thomas.hiller[at]leibniz-liag.de +0037 % Author: see AUTHORS.md +0038 % email: see AUTHORS.md 0039 % License: MIT License (at end) 0040 0041 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getFitFreeJacobian.html b/doc/nucleus/functions/inversion/getFitFreeJacobian.html index 51e01a2..c833e42 100644 --- a/doc/nucleus/functions/inversion/getFitFreeJacobian.html +++ b/doc/nucleus/functions/inversion/getFitFreeJacobian.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getLambdaFromLCurve.html b/doc/nucleus/functions/inversion/getLambdaFromLCurve.html index ab43996..70dc25d 100644 --- a/doc/nucleus/functions/inversion/getLambdaFromLCurve.html +++ b/doc/nucleus/functions/inversion/getLambdaFromLCurve.html @@ -53,8 +53,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0027 % 0028 % See also: -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getLambdaFromRMS.html b/doc/nucleus/functions/inversion/getLambdaFromRMS.html index dd0d526..88e1e48 100644 --- a/doc/nucleus/functions/inversion/getLambdaFromRMS.html +++ b/doc/nucleus/functions/inversion/getLambdaFromRMS.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getStudentInvCDF.html b/doc/nucleus/functions/inversion/getStudentInvCDF.html index 8b336d8..dde9125 100644 --- a/doc/nucleus/functions/inversion/getStudentInvCDF.html +++ b/doc/nucleus/functions/inversion/getStudentInvCDF.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/getTLogMean.html b/doc/nucleus/functions/inversion/getTLogMean.html index 53c6a20..1c1783d 100644 --- a/doc/nucleus/functions/inversion/getTLogMean.html +++ b/doc/nucleus/functions/inversion/getTLogMean.html @@ -53,8 +53,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • +
  • fitDataLSQ is a control routine that fits NMR data multi-exponentially;
  • fitDataLUdecomp is a control routine that uses a LU decomposition and the
  • fitDataMultiModal is a control routine that uses either 'lsqnonlin' or
  • @@ -98,8 +98,8 @@

    SOURCE CODE ^% none 0027 % 0028 % See also: -0029 % Author: Thomas Hiller -0030 % email: thomas.hiller[at]leibniz-liag.de +0029 % Author: see AUTHORS.md +0030 % email: see AUTHORS.md 0031 % License: MIT License (at end) 0032 0033 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/inversion/menu.html b/doc/nucleus/functions/inversion/menu.html index 3d7aa17..da2396f 100644 --- a/doc/nucleus/functions/inversion/menu.html +++ b/doc/nucleus/functions/inversion/menu.html @@ -18,7 +18,7 @@

    Index for nucleus\functions\inversion

    Matlab files in this directory:

    +
  • applyGatesToSignal
  • applyRegularization
  • createKernelMatrix
  • estimateJacobian
  • estimateUncertainty
  • fcn_JointInvfixed
  • fcn_JointInvfree
  • fcn_JointInvshape
  • fcn_fitFreeT1
  • fcn_fitFreeT1_fmin
  • fcn_fitFreeT2
  • fcn_fitFreeT2_fmin
  • fcn_fitFreeT2w
  • fcn_fitMultiModal
  • fitDataFree
  • fitDataLSQ
  • fitDataLUdecomp
  • fitDataMultiModal
  • getChi2
  • getConfInterval
  • getFitErrors
  • getFitFreeJacobian
  • getLambdaFromLCurve
  • getLambdaFromRMS
  • getStudentInvCDF
  • getTLogMean
  • diff --git a/doc/nucleus/functions/modeling/addNoiseToSignal.html b/doc/nucleus/functions/modeling/addNoiseToSignal.html index 7ff6cea..533d50b 100644 --- a/doc/nucleus/functions/modeling/addNoiseToSignal.html +++ b/doc/nucleus/functions/modeling/addNoiseToSignal.html @@ -54,8 +54,8 @@

    DESCRIPTION ^CROSS-REFERENCE INFORMATION ^
 </ul>
 This function is called by:
 <ul style= -
  • updateNMRsignals adds noise to the forward NMR signals and scales the
  • +
  • updateNMRsignals adds noise to the forward NMR signals and scales the
  • estimateUncertainty calculates pseudo uncertainty estimates for multi
  • @@ -100,8 +100,8 @@

    SOURCE CODE ^% none 0028 % 0029 % See also: -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/createPSD.html b/doc/nucleus/functions/modeling/createPSD.html index bb7227b..e31aea9 100644 --- a/doc/nucleus/functions/modeling/createPSD.html +++ b/doc/nucleus/functions/modeling/createPSD.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getAngularityFactor.html b/doc/nucleus/functions/modeling/getAngularityFactor.html index b3ab41a..d9fc04a 100644 --- a/doc/nucleus/functions/modeling/getAngularityFactor.html +++ b/doc/nucleus/functions/modeling/getAngularityFactor.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getAreaFactor.html b/doc/nucleus/functions/modeling/getAreaFactor.html index b42707b..02c67fd 100644 --- a/doc/nucleus/functions/modeling/getAreaFactor.html +++ b/doc/nucleus/functions/modeling/getAreaFactor.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% 0026 % See also: 0027 % Tuller & Or, 2001, WRR, Vol. 37(5), 1257-1276 -0028 % Author: Stepahn Costabel -0029 % email: stephan.costabel[at]bgr.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getConduct.html b/doc/nucleus/functions/modeling/getConduct.html index 2e4f5d1..03d82c7 100644 --- a/doc/nucleus/functions/modeling/getConduct.html +++ b/doc/nucleus/functions/modeling/getConduct.html @@ -59,8 +59,8 @@

    DESCRIPTION ^SOURCE CODE ^% See also: 0033 % Tuller & Or, 2001, WRR, Vol. 37(5), 1257-1276 0034 % Patzek & Silin, 2001, JColIntSci, Vol. 236(2), 295-304 -0035 % Author: Stepahn Costabel -0036 % email: stephan.costabel[at]bgr.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getConstants.html b/doc/nucleus/functions/modeling/getConstants.html index d7e2623..120e1e7 100644 --- a/doc/nucleus/functions/modeling/getConstants.html +++ b/doc/nucleus/functions/modeling/getConstants.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getCornerNMRparameter.html b/doc/nucleus/functions/modeling/getCornerNMRparameter.html index d441dc2..58e1c5e 100644 --- a/doc/nucleus/functions/modeling/getCornerNMRparameter.html +++ b/doc/nucleus/functions/modeling/getCornerNMRparameter.html @@ -59,8 +59,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0033 % 0034 % See also: -0035 % Author: Thomas Hiller -0036 % email: thomas.hiller[at]leibniz-liag.de +0035 % Author: see AUTHORS.md +0036 % email: see AUTHORS.md 0037 % License: MIT License (at end) 0038 0039 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getCornerSaturation.html b/doc/nucleus/functions/modeling/getCornerSaturation.html index 0141d60..8018ff8 100644 --- a/doc/nucleus/functions/modeling/getCornerSaturation.html +++ b/doc/nucleus/functions/modeling/getCornerSaturation.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getCriticalPressure.html b/doc/nucleus/functions/modeling/getCriticalPressure.html index feede2f..91fa56e 100644 --- a/doc/nucleus/functions/modeling/getCriticalPressure.html +++ b/doc/nucleus/functions/modeling/getCriticalPressure.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getGeometryParameter.html b/doc/nucleus/functions/modeling/getGeometryParameter.html index 067af3f..cca189d 100644 --- a/doc/nucleus/functions/modeling/getGeometryParameter.html +++ b/doc/nucleus/functions/modeling/getGeometryParameter.html @@ -65,8 +65,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0039 % 0040 % See also: -0041 % Author: Thomas Hiller -0042 % email: thomas.hiller[at]leibniz-liag.de +0041 % Author: see AUTHORS.md +0042 % email: see AUTHORS.md 0043 % License: MIT License (at end) 0044 0045 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getNMRSignal.html b/doc/nucleus/functions/modeling/getNMRSignal.html index 664b827..e3534ae 100644 --- a/doc/nucleus/functions/modeling/getNMRSignal.html +++ b/doc/nucleus/functions/modeling/getNMRSignal.html @@ -36,6 +36,7 @@

    DESCRIPTION ^DESCRIPTION ^SOURCE CODE ^% nmr - structure containing fields: 0010 % t : time vector [s] 0011 % Tb : bulk relaxation time [s] -0012 % rho : surface relaxivity [m/s] -0013 % type - either 'cyl', 'ang' and 'poly' -0014 % SatData - structure (output from 'getSaturationfromPressure') -0015 % psdData - structure containing fields: -0016 % psd : amplitudes of the distribution -0017 % r : sample points of the distribution -0018 % if psd = 1 and r is a scalar value then a single pore -0019 % is assumed -0020 % wbopts - show a wait-bar -0021 % -0022 % Outputs: -0023 % nmr - structure containing new fields: -0024 % EiT1 : T1 NMR signal for imbibition -0025 % EiT2 : T2 NMR signal for imbibition -0026 % EdT1 : T1 NMR signal for drainage -0027 % EdT2 : T2 NMR signal for drainage -0028 % with NMR signals for each available pressure / saturation step -0029 % -0030 % Example: -0031 % nmr = getNMRSignal(nmr,type,SatData,psdData,wbopts) -0032 % -0033 % Other m-files required: -0034 % none -0035 % -0036 % Subfunctions: -0037 % none -0038 % -0039 % MAT-files required: -0040 % none -0041 % -0042 % See also: -0043 % Author: Thomas Hiller -0044 % email: thomas.hiller[at]leibniz-liag.de -0045 % License: MIT License (at end) -0046 -0047 %------------- BEGIN CODE -------------- -0048 -0049 %% allocate the output NMR signals: -0050 % T1 and T2 for imbibition / drainage -0051 EiT1 = zeros(size(SatData.Sifull,1),numel(nmr.t)); -0052 EiT2 = zeros(size(SatData.Sifull,1),numel(nmr.t)); -0053 EdT1 = zeros(size(SatData.Sdfull,1),numel(nmr.t)); -0054 EdT2 = zeros(size(SatData.Sdfull,1),numel(nmr.t)); -0055 -0056 % get general parameters -0057 t = nmr.t; -0058 Tb = nmr.Tb; -0059 rho = nmr.rho; -0060 -0061 %% some informative wait-bar ;-) -0062 if wbopts.show -0063 hwb = waitbar(0,'processing ...','Name','Calculate NMR','Visible','off'); -0064 steps = numel(SatData.pressure); -0065 % position the wait-bar to the NMRMOD GUI if it is present (assuming the call came -0066 % from the GUI) -0067 fig = findobj('Tag',wbopts.tag); -0068 if ~isempty(fig) -0069 posf = get(fig,'Position'); -0070 set(hwb,'Units','Pixel') -0071 posw = get(hwb,'Position'); -0072 set(hwb,'Position',[posf(1)+posf(3)/2-posw(3)/2 posf(2)+posf(4)/2-posw(4)/2 posw(3:4)]); -0073 end -0074 set(hwb,'Visible','on'); -0075 end -0076 -0077 %% NMR signals depending on geometry type -0078 switch type -0079 case 'cyl' -0080 % surface to volume ratio (2/radius) -0081 SVi = SatData.Pai./SatData.Aai; -0082 SVd = SatData.Pad./SatData.Aad; -0083 SVi(isnan(SVi)) = 0; % get rid of NaNs -0084 SVd(isnan(SVd)) = 0; % get rid of NaNs -0085 -0086 % for all pressure steps -0087 for p = 1:numel(SatData.pressure) -0088 % for all time steps -0089 for j = 1:length(t) -0090 EiT1(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... -0091 (1-exp(-t(j) .* (1./Tb+rho.*SVi(p,:)) ))); -0092 EiT2(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... -0093 exp(-t(j) .* (1./Tb+rho.*SVi(p,:)) ) ); -0094 EdT1(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... -0095 (1-exp(-t(j) .* (1./Tb+rho.*SVd(p,:)) ))); -0096 EdT2(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... -0097 exp(-t(j) .* (1./Tb+rho.*SVd(p,:)) ) ); -0098 end -0099 if wbopts.show -0100 waitbar(p / steps,hwb,['processing ... ',num2str(p),' / ',... -0101 num2str(steps),' pressure steps']); -0102 end -0103 end -0104 -0105 % NMR signals for triangular and polygon capillaries -0106 case {'ang','poly'} -0107 % No of water-filled corners -0108 Ncorners = size(SatData.Aai,ndims(SatData.Aai)); -0109 -0110 % for all pressure steps -0111 for p = 1:numel(SatData.pressure) -0112 % area of water-filled corners is later used for NMR amplitude -0113 % calculation -0114 Aai = squeeze(SatData.Aai(p,:,:)); -0115 Aad = squeeze(SatData.Aad(p,:,:)); -0116 -0117 % surface to volume ratio (S/V) for imbibition / drainage -0118 SVi = squeeze(SatData.Pai(p,:,:)./SatData.Aai(p,:,:)); -0119 SVd = squeeze(SatData.Pad(p,:,:)./SatData.Aad(p,:,:)); -0120 -0121 % temporary NMR signals -0122 sigiT1 = zeros(size(SVi,1),numel(t)); -0123 sigiT2 = zeros(size(SVi,1),numel(t)); -0124 sigdT1 = zeros(size(SVd,1),numel(t)); -0125 sigdT2 = zeros(size(SVd,1),numel(t)); -0126 -0127 % for all pore sizes -0128 for j = 1:numel(psdData.r) -0129 % --- imbibition --- -0130 if SatData.isfullsati(p,j) == 1 % if fully saturated -> Ampl = 1 -0131 sigiT1(j,:) = (1-exp(-t .* (1./Tb + rho.*SVi(j,1)) )); -0132 sigiT2(j,:) = exp(-t .* (1./Tb + rho.*SVi(j,1)) ); -0133 else -0134 % partially saturated pore -> account for corners -0135 for jj = 1:Ncorners -0136 Ampl = Aai(j,jj) / SatData.A0(j); -0137 sigiT1(j,:) = sigiT1(j,:) + (Ampl * (1-exp(-t .* ... -0138 (1./Tb + rho.*SVi(j,jj)))) ); -0139 sigiT2(j,:) = sigiT2(j,:) + (Ampl * exp(-t .* ... -0140 (1./Tb + rho.*SVi(j,jj))) ); -0141 end -0142 end -0143 -0144 % --- drainage --- -0145 if SatData.isfullsatd(p,j) == 1 % if fully saturated -> Ampl = 1 -0146 sigdT1(j,:) = (1-exp(-t .* (1./Tb + rho.*SVd(j,1)) )); -0147 sigdT2(j,:) = exp(-t .* (1./Tb + rho.*SVd(j,1)) ); -0148 else -0149 % partially saturated pore -> account for corners -0150 for jj = 1:Ncorners -0151 Ampl = Aad(j,jj) / SatData.A0(j); -0152 sigdT1(j,:) = sigdT1(j,:) + (Ampl * (1-exp(-t .* ... -0153 (1./Tb + rho.*SVd(j,jj)))) ); -0154 sigdT2(j,:) = sigdT2(j,:) + (Ampl * exp(-t .* ... -0155 (1./Tb + rho.*SVd(j,jj))) ); -0156 end -0157 end -0158 -0159 % account for pore size distribution -0160 sigiT1(j,:) = sigiT1(j,:) * psdData.psd(j); -0161 sigiT2(j,:) = sigiT2(j,:) * psdData.psd(j); -0162 sigdT1(j,:) = sigdT1(j,:) * psdData.psd(j); -0163 sigdT2(j,:) = sigdT2(j,:) * psdData.psd(j); -0164 end -0165 -0166 % sum up all pores into one NMR signal for the current pressure / -0167 % saturation step -0168 if numel(psdData.psd) > 1 -0169 EiT1(p,:) = sum(sigiT1); -0170 EiT2(p,:) = sum(sigiT2); -0171 EdT1(p,:) = sum(sigdT1); -0172 EdT2(p,:) = sum(sigdT2); -0173 else -0174 % single pore case -0175 EiT1(p,:) = sigiT1; -0176 EiT2(p,:) = sigiT2; -0177 EdT1(p,:) = sigdT1; -0178 EdT2(p,:) = sigdT2; -0179 end -0180 -0181 % update wait-bar -0182 if wbopts.show -0183 waitbar(p / steps,hwb,['processing ... ',num2str(p),' / ',num2str(steps),' pressure steps']); -0184 end -0185 end -0186 end -0187 %% delete wait-bar -0188 if wbopts.show -0189 delete(hwb); -0190 end -0191 -0192 %% output data -0193 nmr.EiT1 = EiT1; -0194 nmr.EiT2 = EiT2; -0195 nmr.EdT1 = EdT1; -0196 nmr.EdT2 = EdT2; -0197 -0198 return +0012 % Td : diffusion relaxation time [s] +0013 % rho : surface relaxivity [m/s] +0014 % type - either 'cyl', 'ang' and 'poly' +0015 % SatData - structure (output from 'getSaturationfromPressure') +0016 % psdData - structure containing fields: +0017 % psd : amplitudes of the distribution +0018 % r : sample points of the distribution +0019 % if psd = 1 and r is a scalar value then a single pore +0020 % is assumed +0021 % wbopts - show a wait-bar +0022 % +0023 % Outputs: +0024 % nmr - structure containing new fields: +0025 % EiT1 : T1 NMR signal for imbibition +0026 % EiT2 : T2 NMR signal for imbibition +0027 % EdT1 : T1 NMR signal for drainage +0028 % EdT2 : T2 NMR signal for drainage +0029 % with NMR signals for each available pressure / saturation step +0030 % +0031 % Example: +0032 % nmr = getNMRSignal(nmr,type,SatData,psdData,wbopts) +0033 % +0034 % Other m-files required: +0035 % none +0036 % +0037 % Subfunctions: +0038 % none +0039 % +0040 % MAT-files required: +0041 % none +0042 % +0043 % See also: +0044 % Author: see AUTHORS.md +0045 % email: see AUTHORS.md +0046 % License: MIT License (at end) +0047 +0048 %------------- BEGIN CODE -------------- +0049 +0050 %% allocate the output NMR signals: +0051 % T1 and T2 for imbibition / drainage +0052 EiT1 = zeros(size(SatData.Sifull,1),numel(nmr.t)); +0053 EiT2 = zeros(size(SatData.Sifull,1),numel(nmr.t)); +0054 EdT1 = zeros(size(SatData.Sdfull,1),numel(nmr.t)); +0055 EdT2 = zeros(size(SatData.Sdfull,1),numel(nmr.t)); +0056 +0057 % get general parameters +0058 t = nmr.t; +0059 Tb = nmr.Tb; +0060 Td = nmr.Td; +0061 rho = nmr.rho; +0062 +0063 %% some informative wait-bar ;-) +0064 if wbopts.show +0065 hwb = waitbar(0,'processing ...','Name','Calculate NMR','Visible','off'); +0066 steps = numel(SatData.pressure); +0067 % position the wait-bar to the NMRMOD GUI if it is present (assuming the call came +0068 % from the GUI) +0069 fig = findobj('Tag',wbopts.tag); +0070 if ~isempty(fig) +0071 posf = get(fig,'Position'); +0072 set(hwb,'Units','Pixel') +0073 posw = get(hwb,'Position'); +0074 set(hwb,'Position',[posf(1)+posf(3)/2-posw(3)/2 posf(2)+posf(4)/2-posw(4)/2 posw(3:4)]); +0075 end +0076 set(hwb,'Visible','on'); +0077 end +0078 +0079 %% NMR signals depending on geometry type +0080 switch type +0081 case 'cyl' +0082 % surface to volume ratio (2/radius) +0083 SVi = SatData.Pai./SatData.Aai; +0084 SVd = SatData.Pad./SatData.Aad; +0085 SVi(isnan(SVi)) = 0; % get rid of NaNs +0086 SVd(isnan(SVd)) = 0; % get rid of NaNs +0087 +0088 % for all pressure steps +0089 for p = 1:numel(SatData.pressure) +0090 % for all time steps +0091 for j = 1:length(t) +0092 EiT1(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... +0093 (1-exp(-t(j) .* (1./Td+1./Tb+rho.*SVi(p,:)) ))); +0094 EiT2(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... +0095 exp(-t(j) .* (1./Td+1./Tb+rho.*SVi(p,:)) ) ); +0096 EdT1(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... +0097 (1-exp(-t(j) .* (1./Td+1./Tb+rho.*SVd(p,:)) ))); +0098 EdT2(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... +0099 exp(-t(j) .* (1./Td+1./Tb+rho.*SVd(p,:)) ) ); +0100 end +0101 if wbopts.show +0102 waitbar(p / steps,hwb,['processing ... ',num2str(p),' / ',... +0103 num2str(steps),' pressure steps']); +0104 end +0105 end +0106 +0107 % NMR signals for triangular and polygon capillaries +0108 case {'ang','poly'} +0109 % No of water-filled corners +0110 Ncorners = size(SatData.Aai,ndims(SatData.Aai)); +0111 +0112 % for all pressure steps +0113 for p = 1:numel(SatData.pressure) +0114 % area of water-filled corners is later used for NMR amplitude +0115 % calculation +0116 Aai = squeeze(SatData.Aai(p,:,:)); +0117 Aad = squeeze(SatData.Aad(p,:,:)); +0118 +0119 % surface to volume ratio (S/V) for imbibition / drainage +0120 SVi = squeeze(SatData.Pai(p,:,:)./SatData.Aai(p,:,:)); +0121 SVd = squeeze(SatData.Pad(p,:,:)./SatData.Aad(p,:,:)); +0122 +0123 % temporary NMR signals +0124 sigiT1 = zeros(size(SVi,1),numel(t)); +0125 sigiT2 = zeros(size(SVi,1),numel(t)); +0126 sigdT1 = zeros(size(SVd,1),numel(t)); +0127 sigdT2 = zeros(size(SVd,1),numel(t)); +0128 +0129 % for all pore sizes +0130 for j = 1:numel(psdData.r) +0131 % --- imbibition --- +0132 if SatData.isfullsati(p,j) == 1 % if fully saturated -> Ampl = 1 +0133 sigiT1(j,:) = (1-exp(-t .* (1./Td + 1./Tb + rho.*SVi(j,1)) )); +0134 sigiT2(j,:) = exp(-t .* (1./Td + 1./Tb + rho.*SVi(j,1)) ); +0135 else +0136 % partially saturated pore -> account for corners +0137 for jj = 1:Ncorners +0138 Ampl = Aai(j,jj) / SatData.A0(j); +0139 sigiT1(j,:) = sigiT1(j,:) + (Ampl * (1-exp(-t .* ... +0140 (1./Td + 1./Tb + rho.*SVi(j,jj)))) ); +0141 sigiT2(j,:) = sigiT2(j,:) + (Ampl * exp(-t .* ... +0142 (1./Td + 1./Tb + rho.*SVi(j,jj))) ); +0143 end +0144 end +0145 +0146 % --- drainage --- +0147 if SatData.isfullsatd(p,j) == 1 % if fully saturated -> Ampl = 1 +0148 sigdT1(j,:) = (1-exp(-t .* (1./Td + 1./Tb + rho.*SVd(j,1)) )); +0149 sigdT2(j,:) = exp(-t .* (1./Td + 1./Tb + rho.*SVd(j,1)) ); +0150 else +0151 % partially saturated pore -> account for corners +0152 for jj = 1:Ncorners +0153 Ampl = Aad(j,jj) / SatData.A0(j); +0154 sigdT1(j,:) = sigdT1(j,:) + (Ampl * (1-exp(-t .* ... +0155 (1./Td + 1./Tb + rho.*SVd(j,jj)))) ); +0156 sigdT2(j,:) = sigdT2(j,:) + (Ampl * exp(-t .* ... +0157 (1./Td + 1./Tb + rho.*SVd(j,jj))) ); +0158 end +0159 end +0160 +0161 % account for pore size distribution +0162 sigiT1(j,:) = sigiT1(j,:) * psdData.psd(j); +0163 sigiT2(j,:) = sigiT2(j,:) * psdData.psd(j); +0164 sigdT1(j,:) = sigdT1(j,:) * psdData.psd(j); +0165 sigdT2(j,:) = sigdT2(j,:) * psdData.psd(j); +0166 end +0167 +0168 % sum up all pores into one NMR signal for the current pressure / +0169 % saturation step +0170 if numel(psdData.psd) > 1 +0171 EiT1(p,:) = sum(sigiT1); +0172 EiT2(p,:) = sum(sigiT2); +0173 EdT1(p,:) = sum(sigdT1); +0174 EdT2(p,:) = sum(sigdT2); +0175 else +0176 % single pore case +0177 EiT1(p,:) = sigiT1; +0178 EiT2(p,:) = sigiT2; +0179 EdT1(p,:) = sigdT1; +0180 EdT2(p,:) = sigdT2; +0181 end +0182 +0183 % update wait-bar +0184 if wbopts.show +0185 waitbar(p / steps,hwb,['processing ... ',num2str(p),' / ',num2str(steps),' pressure steps']); +0186 end +0187 end +0188 end +0189 %% delete wait-bar +0190 if wbopts.show +0191 delete(hwb); +0192 end +0193 +0194 %% output data +0195 nmr.EiT1 = EiT1; +0196 nmr.EiT2 = EiT2; +0197 nmr.EdT1 = EdT1; +0198 nmr.EdT2 = EdT2; 0199 -0200 %------------- END OF CODE -------------- +0200 return 0201 -0202 %% License: -0203 % MIT License -0204 % -0205 % Copyright (c) 2018 Thomas Hiller +0202 %------------- END OF CODE -------------- +0203 +0204 %% License: +0205 % MIT License 0206 % -0207 % Permission is hereby granted, free of charge, to any person obtaining a copy -0208 % of this software and associated documentation files (the "Software"), to deal -0209 % in the Software without restriction, including without limitation the rights -0210 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -0211 % copies of the Software, and to permit persons to whom the Software is -0212 % furnished to do so, subject to the following conditions: -0213 % -0214 % The above copyright notice and this permission notice shall be included in all -0215 % copies or substantial portions of the Software. -0216 % -0217 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -0218 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -0219 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -0220 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -0221 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -0222 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -0223 % SOFTWARE. +0207 % Copyright (c) 2018 Thomas Hiller +0208 % +0209 % Permission is hereby granted, free of charge, to any person obtaining a copy +0210 % of this software and associated documentation files (the "Software"), to deal +0211 % in the Software without restriction, including without limitation the rights +0212 % to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +0213 % copies of the Software, and to permit persons to whom the Software is +0214 % furnished to do so, subject to the following conditions: +0215 % +0216 % The above copyright notice and this permission notice shall be included in all +0217 % copies or substantial portions of the Software. +0218 % +0219 % THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +0220 % IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +0221 % FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +0222 % AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +0223 % LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +0224 % OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +0225 % SOFTWARE.
    Generated by m2html © 2005
    \ No newline at end of file diff --git a/doc/nucleus/functions/modeling/getNMRTimeVector.html b/doc/nucleus/functions/modeling/getNMRTimeVector.html index 0527457..255ca08 100644 --- a/doc/nucleus/functions/modeling/getNMRTimeVector.html +++ b/doc/nucleus/functions/modeling/getNMRTimeVector.html @@ -62,8 +62,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0036 % 0037 % See also: -0038 % Author: Thomas Hiller -0039 % email: thomas.hiller[at]leibniz-liag.de +0038 % Author: see AUTHORS.md +0039 % email: see AUTHORS.md 0040 % License: MIT License (at end) 0041 0042 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getPartialSaturationMatrix.html b/doc/nucleus/functions/modeling/getPartialSaturationMatrix.html index 24c8ce9..e04ff99 100644 --- a/doc/nucleus/functions/modeling/getPartialSaturationMatrix.html +++ b/doc/nucleus/functions/modeling/getPartialSaturationMatrix.html @@ -55,8 +55,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0029 % 0030 % See also: -0031 % Author: Thomas Hiller -0032 % email: thomas.hiller[at]leibniz-liag.de +0031 % Author: see AUTHORS.md +0032 % email: see AUTHORS.md 0033 % License: MIT License (at end) 0034 0035 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getPointCoordinates.html b/doc/nucleus/functions/modeling/getPointCoordinates.html index cd91c0e..e40a897 100644 --- a/doc/nucleus/functions/modeling/getPointCoordinates.html +++ b/doc/nucleus/functions/modeling/getPointCoordinates.html @@ -52,8 +52,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0026 % 0027 % See also: -0028 % Author: Thomas Hiller -0029 % email: thomas.hiller[at]leibniz-liag.de +0028 % Author: see AUTHORS.md +0029 % email: see AUTHORS.md 0030 % License: MIT License (at end) 0031 0032 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getPressureRangeFromPSD.html b/doc/nucleus/functions/modeling/getPressureRangeFromPSD.html index 34903f4..b6ad04a 100644 --- a/doc/nucleus/functions/modeling/getPressureRangeFromPSD.html +++ b/doc/nucleus/functions/modeling/getPressureRangeFromPSD.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0028 % 0029 % See also: -0030 % Author: Thomas Hiller -0031 % email: thomas.hiller[at]leibniz-liag.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getRaRaEps.html b/doc/nucleus/functions/modeling/getRaRaEps.html index 5039df6..0ab2403 100644 --- a/doc/nucleus/functions/modeling/getRaRaEps.html +++ b/doc/nucleus/functions/modeling/getRaRaEps.html @@ -54,8 +54,8 @@

    DESCRIPTION ^SOURCE CODE ^% 0028 % See also: 0029 % Ransohoff & Radke, 1988, JColIntSci, Vol. 121(2), 392-401 -0030 % Author: Stepahn Costabel -0031 % email: stephan.costabel[at]bgr.de +0030 % Author: see AUTHORS.md +0031 % email: see AUTHORS.md 0032 % License: MIT License (at end) 0033 0034 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getSaturationFromPressure.html b/doc/nucleus/functions/modeling/getSaturationFromPressure.html index 917d06b..64c37af 100644 --- a/doc/nucleus/functions/modeling/getSaturationFromPressure.html +++ b/doc/nucleus/functions/modeling/getSaturationFromPressure.html @@ -65,8 +65,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0039 % 0040 % See also: -0041 % Author: Thomas Hiller, Stephan Costabel -0042 % email: thomas.hiller[at]leibniz-liag.de +0041 % Author: see AUTHORS.md +0042 % email: see AUTHORS.md 0043 % License: MIT License (at end) 0044 0045 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getSaturationFromPressureBatch.html b/doc/nucleus/functions/modeling/getSaturationFromPressureBatch.html index 687204a..1678648 100644 --- a/doc/nucleus/functions/modeling/getSaturationFromPressureBatch.html +++ b/doc/nucleus/functions/modeling/getSaturationFromPressureBatch.html @@ -73,8 +73,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0047 % 0048 % See also: -0049 % Author: Thomas Hiller, Stephan Costabel -0050 % email: thomas.hiller[at]leibniz-liag.de +0049 % Author: see AUTHORS.md +0050 % email: see AUTHORS.md 0051 % License: MIT License (at end) 0052 0053 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getSaturationLevelData.html b/doc/nucleus/functions/modeling/getSaturationLevelData.html index 2736c54..4fac7c2 100644 --- a/doc/nucleus/functions/modeling/getSaturationLevelData.html +++ b/doc/nucleus/functions/modeling/getSaturationLevelData.html @@ -56,8 +56,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0030 % 0031 % See also: -0032 % Author: Thomas Hiller -0033 % email: thomas.hiller[at]leibniz-liag.de +0032 % Author: see AUTHORS.md +0033 % email: see AUTHORS.md 0034 % License: MIT License (at end) 0035 0036 %------------- BEGIN CODE -------------- diff --git a/doc/nucleus/functions/modeling/getShapeFactor.html b/doc/nucleus/functions/modeling/getShapeFactor.html index 8e7323b..c9b4e31 100644 --- a/doc/nucleus/functions/modeling/getShapeFactor.html +++ b/doc/nucleus/functions/modeling/getShapeFactor.html @@ -51,8 +51,8 @@

    DESCRIPTION ^SOURCE CODE ^% none 0025 % 0026 % See also: -0027 % Author: Thomas Hiller -0028 % email: thomas.hiller[at]leibniz-liag.de +0027 % Author: see AUTHORS.md +0028 % email: see AUTHORS.md 0029 % License: MIT License (at end) 0030 0031 %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_bamtom.m b/functions/import/LoadNMRData_bamtom.m index f1d396a..b375602 100644 --- a/functions/import/LoadNMRData_bamtom.m +++ b/functions/import/LoadNMRData_bamtom.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_bgr.m b/functions/import/LoadNMRData_bgr.m index 1855978..108a728 100644 --- a/functions/import/LoadNMRData_bgr.m +++ b/functions/import/LoadNMRData_bgr.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_bgrmat.m b/functions/import/LoadNMRData_bgrmat.m index b72e248..ecc85f9 100644 --- a/functions/import/LoadNMRData_bgrmat.m +++ b/functions/import/LoadNMRData_bgrmat.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_corelab.m b/functions/import/LoadNMRData_corelab.m index 9041219..d4a333a 100644 --- a/functions/import/LoadNMRData_corelab.m +++ b/functions/import/LoadNMRData_corelab.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_dart.m b/functions/import/LoadNMRData_dart.m index 1d0b305..bbf6c97 100644 --- a/functions/import/LoadNMRData_dart.m +++ b/functions/import/LoadNMRData_dart.m @@ -31,8 +31,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_driver.m b/functions/import/LoadNMRData_driver.m index b1f4a69..6faab9f 100644 --- a/functions/import/LoadNMRData_driver.m +++ b/functions/import/LoadNMRData_driver.m @@ -27,8 +27,9 @@ % LoadNMRData_mouse % LoadNMRData_liag % LoadNMRData_bgr -% LoadNMRData_bgr2 +% LoadNMRData_mousecpmg % LoadNMRData_bgrmat +% LoadNMRData_helios % LoadNMRData_bamtom % % Subfunctions: @@ -38,8 +39,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -50,10 +51,14 @@ out = LoadNMRData_bamtom(in); case 'bgr' out = LoadNMRData_bgr(in); - case 'bgr2' - out = LoadNMRData_bgr2(in); + case 'MouseCPMG' + out = LoadNMRData_mousecpmg(in); case 'bgrmat' out = LoadNMRData_bgrmat(in); + case {'MouseLiftSingle','MouseLiftAll'} + out = LoadNMRData_mouselift(in); + case 'helios' + out = LoadNMRData_helios(in); case 'corelab' out = LoadNMRData_corelab(in); case 'dart' @@ -75,16 +80,18 @@ % if an imported T2 signal has no imaginary part, the noise is estimated % from an exponential fit -for i = 1:numel(out.nmrData) - if strcmp(out.nmrData{i}.flag,'T2') && isreal(out.nmrData{i}.signal) - disp('NUCLUESinv import: Estimating noise from exponential fit ...'); - param.T1IRfac = out.nmrData{i}.T1IRfac; - param.noise = 0; - param.optim = 'off'; - invstd = fitDataFree(out.nmrData{i}.time,out.nmrData{i}.signal,... - 'T2',param,5); - out.nmrData{i}.noise = invstd.rms; - disp('NUCLUESinv import: done.') +if ~strcmp(in.fileformat,'helios') + for i = 1:numel(out.nmrData) + if isreal(out.nmrData{i}.signal) + disp('NUCLUESinv import: Estimating noise from exponential fit ...'); + param.T1IRfac = out.nmrData{i}.T1IRfac; + param.noise = 0; + param.optim = 'off'; + invstd = fitDataFree(out.nmrData{i}.time,out.nmrData{i}.signal,... + out.nmrData{i}.flag,param,5); + out.nmrData{i}.noise = invstd.rms; + disp('NUCLUESinv import: done.') + end end end diff --git a/functions/import/LoadNMRData_field.m b/functions/import/LoadNMRData_field.m index e8da616..b11e8cc 100644 --- a/functions/import/LoadNMRData_field.m +++ b/functions/import/LoadNMRData_field.m @@ -30,8 +30,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_helios.m b/functions/import/LoadNMRData_helios.m new file mode 100644 index 0000000..96bb02c --- /dev/null +++ b/functions/import/LoadNMRData_helios.m @@ -0,0 +1,115 @@ +function out = LoadNMRData_helios(in) +%LoadNMRData_helios loads BGR NMR data from a typical folder structure +%produced by the Helios NMR Scanner +% +% Syntax: +% out = LoadNMRData_helios(in) +% +% Inputs: +% in - input structure +% in.path - data path +% in.T1T2 - T1 / T2 flag +% in.fileformat - 'helios' +% +% Outputs: +% out - output structure +% out.parData - parameter file data +% out.nmrData - NMR data +% +% Example: +% out = LoadNMRData_helios(in) +% +% Other m-files required: +% fixParameterString +% +% Subfunctions: +% LoadDataFile +% +% MAT-files required: +% none +% +% See also: NUCLEUSinv +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +%% start processing the files + +% read the data file +datafile = dir(fullfile(in.path,in.name)); +[data,parData] = LoadDataFile(in.path,in.name,in.T1T2); + +% get file statistics +nmrData.datfile = datafile.name; +nmrData.date = datafile.date; +nmrData.datenum = datafile.datenum; +nmrData.bytes = datafile.bytes; + +% save the NMR data +nmrData.flag = data.flag; +nmrData.T1IRfac = 1; +nmrData.time = data.time; +nmrData.signal = data.signal; +nmrData.raw = data.raw; +if strcmp(in.T1T2,'T2') + nmrData.phase = data.phase; +end + +% save data to output struct +out.parData = parData; +out.nmrData = nmrData; + +end + +%% load NMR data file +function [data,pardata] = LoadDataFile(datapath,fname,flag) + +A = importdata(fullfile(datapath,fname),'\t'); +t_echo = A(1,2); +n_echo = A(1,3); + +time = t_echo:t_echo:t_echo*n_echo; +time = time(:); +re = -A(2,1:length(time)); +im = -A(3,1:length(time)); +re = re(:); +im = im(:); + +data.flag = flag; +data.time = time; +data.signal = complex(re,im); +[data.signal,data.phase] = rotateT2phase(data.signal); + +data.raw.time = data.time; +data.raw.signal = data.signal; + +pardata.t_echo = t_echo; +pardata.n_echo = n_echo; + +end +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2021 Stephan Costabel +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. diff --git a/functions/import/LoadNMRData_ibac.m b/functions/import/LoadNMRData_ibac.m index e94bb08..2e6bfb8 100644 --- a/functions/import/LoadNMRData_ibac.m +++ b/functions/import/LoadNMRData_ibac.m @@ -32,8 +32,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_liag.m b/functions/import/LoadNMRData_liag.m index 9465fe5..bfb981f 100644 --- a/functions/import/LoadNMRData_liag.m +++ b/functions/import/LoadNMRData_liag.m @@ -28,8 +28,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/LoadNMRData_mouse.m b/functions/import/LoadNMRData_mouse.m index 0ea04e4..22ff06c 100644 --- a/functions/import/LoadNMRData_mouse.m +++ b/functions/import/LoadNMRData_mouse.m @@ -28,8 +28,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -134,7 +134,7 @@ %% License: % MIT License % -% Copyright (c) 2018 Thomas Hiller +% Copyright (c) 2021 Stephan Costabel % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the "Software"), to deal diff --git a/functions/import/LoadNMRData_mouselift.m b/functions/import/LoadNMRData_mouselift.m new file mode 100644 index 0000000..b7e12fd --- /dev/null +++ b/functions/import/LoadNMRData_mouselift.m @@ -0,0 +1,151 @@ +function out = LoadNMRData_mouselift(in) +%LoadNMRData_mouselift loads NMR Mouse data from an original folder structure as +%generated by the NMR MOUSE when using it wogether with the lift +% +% Syntax: +% out = LoadNMRData_mouselift(in) +% +% Inputs: +% in - input structure +% in.path - data path +% in.T1T2 - T1 / T2 flag +% in.fileformat - 'mouse' +% +% Outputs: +% out - output structure +% out.parData - parameter file data +% out.nmrData - NMR data +% +% Example: +% out = LoadNMRData_mouselift(in) +% +% Other m-files required: +% fixParameterString +% +% Subfunctions: +% LoadDataFile +% LoadParameterFile +% +% MAT-files required: +% none +% +% See also: NUCLEUSinv +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +%% start processing the files +% load Parameter file +[parData] = LoadParameterFile(in.path,'acq.par'); + +% find all data files +files = dir(fullfile(in.path,'*.dat')); + +% remove not needed dat-files +switch in.T1T2 + case 'T1' + files = files(~ismember({files.name},... + {'data2D.dat','T1Axis.dat'})); + case 'T2' + files = files(~ismember({files.name},... + {'data2D.dat','T2Axis.dat'})); +end + +nmrData = cell(1,size(files,1)); +for i = 1:size(files,1) + % read the data file + data = LoadDataFile(in.path,files(i).name,in.T1T2); + + % get file statistics + nmrData{i}.datfile = files(i).name; + nmrData{i}.date = files(i).date; + nmrData{i}.datenum = files(i).datenum; + nmrData{i}.bytes = files(i).bytes; + + % save the NMR data + nmrData{i}.flag = data.flag; + nmrData{i}.T1IRfac = 1; + nmrData{i}.time = data.time; + nmrData{i}.signal = data.signal; + nmrData{i}.raw = data.raw; + if strcmp(in.T1T2,'T2') + nmrData{i}.phase = data.phase; + end + clear data +end + +% save data to output struct +out.parData = parData; +out.nmrData = nmrData; + +end + +%% load NMR data file +function [data] = LoadDataFile(datapath,fname,flag) + +d = load(fullfile(datapath,fname)); + +if size(d,2) == 3 + data.flag = flag; + data.time = d(:,1); + switch flag + case 'T1' + data.signal = d(:,2); + case 'T2' + data.signal = complex(d(:,2),d(:,3)); + [data.signal,data.phase] = rotateT2phase(data.signal); + end +else + data.flag = '0'; + data.time = 0; + data.signal = 0; + data.phase = 0; +end + +data.raw.time = data.time; +data.raw.signal = data.signal; + +end + +%% load parameter file +function [data] = LoadParameterFile(datapath,fname) + +fid = fopen(fullfile(datapath,fname)); +d = textscan(fid,'%s','Delimiter','\n'); +fclose(fid); + +for i = 1:size(d{1},1) + str = char(d{1}(i)); + str = fixParameterString(str); + eval(['data.',str,';']); +end +data.all = d; + +end + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2021 Stephan Costabel +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. diff --git a/functions/import/LoadNMRData_rwth.m b/functions/import/LoadNMRData_rwth.m index 2450979..31f182f 100644 --- a/functions/import/LoadNMRData_rwth.m +++ b/functions/import/LoadNMRData_rwth.m @@ -30,8 +30,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/fixParameterString.m b/functions/import/fixParameterString.m index d463800..05b4a07 100644 --- a/functions/import/fixParameterString.m +++ b/functions/import/fixParameterString.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/import/rotateT2phase.m b/functions/import/rotateT2phase.m index 49b3a04..b88a0e5 100644 --- a/functions/import/rotateT2phase.m +++ b/functions/import/rotateT2phase.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/ConductView.m b/functions/interface/ConductView.m index 1767c02..372e6b5 100644 --- a/functions/interface/ConductView.m +++ b/functions/interface/ConductView.m @@ -27,8 +27,8 @@ function ConductView(src,~) % none % % See also: NUCLEUSmod, NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/PhaseView.m b/functions/interface/PhaseView.m index 371d116..e12c1c3 100644 --- a/functions/interface/PhaseView.m +++ b/functions/interface/PhaseView.m @@ -28,8 +28,8 @@ function PhaseView(src,~) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/beautifyAxes.m b/functions/interface/beautifyAxes.m index b3a3b73..815f0b0 100644 --- a/functions/interface/beautifyAxes.m +++ b/functions/interface/beautifyAxes.m @@ -23,8 +23,8 @@ function beautifyAxes(figh) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/calculateGeometry.m b/functions/interface/calculateGeometry.m index a9e72be..e854dd9 100644 --- a/functions/interface/calculateGeometry.m +++ b/functions/interface/calculateGeometry.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/calculateGuiOnMonitorPosition.m b/functions/interface/calculateGuiOnMonitorPosition.m index 3fc49d9..d8a6cd1 100644 --- a/functions/interface/calculateGuiOnMonitorPosition.m +++ b/functions/interface/calculateGuiOnMonitorPosition.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -33,6 +33,7 @@ %% get the monitor layout scr = get(0,'MonitorPosition'); if size(scr,1) > 1 + % find main screen ind = find(scr(:,1)==1 & scr(:,2)==1); sw = scr(ind,3); % width sh = scr(ind,4); % height @@ -41,31 +42,16 @@ sh = scr(4); % height end -%% positioning of the GUI -if numel(scr) > 4 - % dual screen mode - % GUI on second screen - gh = 730; % reference height - gw = ceil(gh*aspect_ratio); % reference width (1152) - if any(scr(:,1)<0) - pos = [-sw+(sw-gw)/2 (sh-gh)/3 gw gh]; - else - pos = [sw+(sw-gw)/2 (sh-gh)/3 gw gh]; - end - % if any screen is smaller than 800 - if any(scr(:,4)<800) - pos = [(sw-gw)/2 (sh-gh)/3 gw gh]; - end +%% GUI positioning +if any(sh<800) + gh = 600; % reference height for small screens else - % single screen mode - if any(scr(:,4)<800) - gh = 600; % reference height for small screens - else - gh = 730; % reference height - end - gw = ceil(gh*aspect_ratio); % reference width (960) - pos = [(sw-gw)/2 (sh-gh)/2 gw gh]; + gh = 730; % reference height end +% reference width +gw = ceil(gh*aspect_ratio); % 960 or 1152 +% GUI position +pos = round([(sw-gw)/2 (sh-gh)/3 gw gh]); return diff --git a/functions/interface/calculateNMR.m b/functions/interface/calculateNMR.m index b139c58..b93af8c 100644 --- a/functions/interface/calculateNMR.m +++ b/functions/interface/calculateNMR.m @@ -28,8 +28,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -45,6 +45,7 @@ % generate a time vector from the echo time 'TE' and number of echoes 'echosN' nmr.t = getNMRTimeVector(data.nmr.TE,'µs','N',data.nmr.echosN); nmr.Tb = data.nmr.Tbulk; + nmr.Td = data.nmr.Tdiff; nmr.rho = data.nmr.rho/1e6; % µm/s to m/s % wait-bar option diff --git a/functions/interface/calibratePorosity.m b/functions/interface/calibratePorosity.m index 8bd66f6..4f4dfdb 100644 --- a/functions/interface/calibratePorosity.m +++ b/functions/interface/calibratePorosity.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/caluclatePressureSaturation.m b/functions/interface/caluclatePressureSaturation.m index 69f7ec0..cd05dce 100644 --- a/functions/interface/caluclatePressureSaturation.m +++ b/functions/interface/caluclatePressureSaturation.m @@ -29,8 +29,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -87,8 +87,11 @@ %% reset NMR plots clearSingleAxis(gui.axes_handles.nmr); displayStatusText(gui,'Calculating saturation ... done'); -% enable the RUN button again +% enable the pressure RUN button again set(gui.push_handles.press_RUN,'String','RUN','Enable','on'); +% reset the NMR RUN button +set(gui.push_handles.nmr_RUN,'String','RUN','Enable','on',... + 'BackgroundColor','g','Callback',@onPushRun); % enable hydraulic conductivity GUI menu only for PSD data if data.geometry.ispsd set(gui.menu.view_conduct,'Enable','on'); diff --git a/functions/interface/changeColorTheme.m b/functions/interface/changeColorTheme.m index 1661a55..01dd0e8 100644 --- a/functions/interface/changeColorTheme.m +++ b/functions/interface/changeColorTheme.m @@ -31,8 +31,8 @@ function changeColorTheme(fig_tag,th) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/checkIfInversionExists.m b/functions/interface/checkIfInversionExists.m index 9565f3e..043dccc 100644 --- a/functions/interface/checkIfInversionExists.m +++ b/functions/interface/checkIfInversionExists.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/cleanupGUIData.m b/functions/interface/cleanupGUIData.m index dc6ae58..6d17cf7 100644 --- a/functions/interface/cleanupGUIData.m +++ b/functions/interface/cleanupGUIData.m @@ -28,8 +28,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/clearAllAxes.m b/functions/interface/clearAllAxes.m index 09fa837..d4d3158 100644 --- a/functions/interface/clearAllAxes.m +++ b/functions/interface/clearAllAxes.m @@ -23,8 +23,8 @@ function clearAllAxes(figh) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/clearInversion.m b/functions/interface/clearInversion.m index a1a242c..68ff173 100644 --- a/functions/interface/clearInversion.m +++ b/functions/interface/clearInversion.m @@ -28,8 +28,8 @@ function clearInversion(id) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/clearSingleAxis.m b/functions/interface/clearSingleAxis.m index c9c7d0b..e49489e 100644 --- a/functions/interface/clearSingleAxis.m +++ b/functions/interface/clearSingleAxis.m @@ -23,8 +23,8 @@ function clearSingleAxis(axh) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/displayStatusText.m b/functions/interface/displayStatusText.m index 82fb912..60ac746 100644 --- a/functions/interface/displayStatusText.m +++ b/functions/interface/displayStatusText.m @@ -1,5 +1,6 @@ function displayStatusText(gui,string) -%displayStatusText clears all axes of a given figure +%displayStatusText shows status information either in the GUI or on the +%commandline % % Syntax: % displayStatusText(gui,string) @@ -24,15 +25,19 @@ function displayStatusText(gui,string) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- %% display status / info text -set(gui.textStatus,'String',string); -pause(0.001); +if isstruct(gui) + set(gui.textStatus,'String',string); + pause(0.001); +else + disp(string); +end end diff --git a/functions/interface/enableGUIelements.m b/functions/interface/enableGUIelements.m index 42244c1..f588368 100644 --- a/functions/interface/enableGUIelements.m +++ b/functions/interface/enableGUIelements.m @@ -24,8 +24,8 @@ function enableGUIelements(importtype) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -54,7 +54,7 @@ function enableGUIelements(importtype) % process panel data.process.end = 0; switch data.import.fileformat - case {'field','dart'} + case {'dart','field','helios'} data.process.gatetype = 'raw'; otherwise data.process.gatetype = 'log'; diff --git a/functions/interface/exportData.m b/functions/interface/exportData.m index 41e6ba6..3f26a79 100644 --- a/functions/interface/exportData.m +++ b/functions/interface/exportData.m @@ -29,8 +29,8 @@ function exportData(fig_tag,format) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -149,7 +149,7 @@ function exportData(fig_tag,format) idata.NUCLEUSinv_GUI = rmfield(idata.NUCLEUSinv_GUI,'results'); % save to file - save(fullfile(spath,sfile),'idata'); + save(fullfile(spath,sfile),'idata','-v7.3'); % display info text displayStatusText(gui,... @@ -183,7 +183,7 @@ function exportData(fig_tag,format) idata.NUCLEUSinv_GUI = rmfield(idata.NUCLEUSinv_GUI,'results'); % save to file - save(fullfile(spath,sfile),'idata'); + save(fullfile(spath,sfile),'idata','-v7.3'); % display info text displayStatusText(gui,... @@ -330,7 +330,7 @@ function exportData(fig_tag,format) if ~isequal(FileName,0) || ~isequal(PathName,0) clear data data = out; %#ok - save(fullfile(PathName,FileName),'data'); + save(fullfile(PathName,FileName),'data','-v7.3'); displayStatusText(gui,'Saving to MAT-file ... done.'); else displayStatusText(gui,'Saving to MAT-file ... canceled.'); @@ -424,7 +424,7 @@ function exportINV_EXCEL(gui,INVdata,id,sfile,spath) header4 = {['T2 [',unit,']'],'amplitude [a.u.]'}; end - case {'LU','NNLS'} + case {'LU','NNLS','MUMO'} tmp4 = [INVdata{id}.results.invstd.T1T2me(:)... INVdata{id}.results.invstd.T1T2f(:)]; header4 = {['relaxation times [',unit,']'],'amplitudes [a.u.]'}; diff --git a/functions/interface/exportGraphics.m b/functions/interface/exportGraphics.m index 69c19c6..f9a654e 100644 --- a/functions/interface/exportGraphics.m +++ b/functions/interface/exportGraphics.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -303,9 +303,19 @@ switch fig_tag case 'INV' + % which NMR signal + id = get(gui.listbox_handles.signal,'Value'); + % get the new file name + sfilename = data.import.NMR.filesShort{id}; + ind1 = strfind(sfilename,'.'); + if isempty(ind1) + sfilename = [sfilename,'_INV']; + else + sfilename = [sfilename(1:ind1-1),'_INV']; + end [FileName,PathName,~] = uiputfile({putext,put1},... ['NUCLEUSinv: Save ',statstr,' Graphics'],... - fullfile(pwd,'NUCLEUSinv_inversion')); + fullfile(data.import.path,sfilename)); case 'MOD' [FileName,PathName,~] = uiputfile({putext,put1},... ['NUCLEUSmod: Save ',statstr,' Graphics'],... diff --git a/functions/interface/exportINV.m b/functions/interface/exportINV.m index 7568505..d17c113 100644 --- a/functions/interface/exportINV.m +++ b/functions/interface/exportINV.m @@ -30,8 +30,8 @@ function exportINV(format,varargin) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -63,7 +63,7 @@ function exportINV(format,varargin) savedata.filesShort = data.import.NMR.filesShort; % save to default file at local data path - save(fullfile(data.import.path,'NUCLEUSinv_raw.mat'),'savedata'); + save(fullfile(data.import.path,'NUCLEUSinv_raw.mat'),'savedata','-v7.3'); clear savedata; % display info text @@ -90,7 +90,7 @@ function exportINV(format,varargin) if dosilent - save(fullfile(sfile),'savedata'); + save(fullfile(sfile),'savedata','-v7.3'); clear savedata; else % session file name @@ -109,7 +109,7 @@ function exportINV(format,varargin) % if user didn't cancel save session if sum([sfile spath]) > 0 - save(fullfile(spath,sfile),'savedata'); + save(fullfile(spath,sfile),'savedata','-v7.3'); clear savedata; % display info text diff --git a/functions/interface/findParentOfType.m b/functions/interface/findParentOfType.m index e56b249..368fea3 100644 --- a/functions/interface/findParentOfType.m +++ b/functions/interface/findParentOfType.m @@ -28,8 +28,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/fixAxes.m b/functions/interface/fixAxes.m index d195712..b2ed2ef 100644 --- a/functions/interface/fixAxes.m +++ b/functions/interface/fixAxes.m @@ -27,8 +27,8 @@ function fixAxes(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/getColorIndex.m b/functions/interface/getColorIndex.m index a7f9a99..866c48f 100644 --- a/functions/interface/getColorIndex.m +++ b/functions/interface/getColorIndex.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/getColorTheme.m b/functions/interface/getColorTheme.m index c9630a1..cdba4d7 100644 --- a/functions/interface/getColorTheme.m +++ b/functions/interface/getColorTheme.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/getVersionNoFromString.m b/functions/interface/getVersionNoFromString.m index c8ed450..17933a3 100644 --- a/functions/interface/getVersionNoFromString.m +++ b/functions/interface/getVersionNoFromString.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/importASCIIdata.m b/functions/interface/importASCIIdata.m index dd36042..1a9e05b 100644 --- a/functions/interface/importASCIIdata.m +++ b/functions/interface/importASCIIdata.m @@ -26,8 +26,8 @@ function importASCIIdata(src) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -129,11 +129,19 @@ function importASCIIdata(src) data.import.NMR.data{c}.phase = tmp_phase; else data.import.NMR.data{c}.signal = tmp_data(:,2); + data.import.NMR.data{c}.phase = 0; + + param.T1IRfac = 1; + param.noise = 0; + param.optim = 'off'; + invstd = fitDataFree(data.import.NMR.data{c}.time,data.import.NMR.data{c}.signal,... + 'T2',param,5); + data.import.NMR.data{c}.noise = invstd.rms; end data.import.NMR.data{c}.T1IRfac = 1; data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; data.import.NMR.data{c}.raw.signal = data.import.NMR.data{c}.signal; - + % dummy parameter data data.import.NMR.para{c} = 0; end diff --git a/functions/interface/importCPSdata.m b/functions/interface/importCPSdata.m index 26b07a6..99e474f 100644 --- a/functions/interface/importCPSdata.m +++ b/functions/interface/importCPSdata.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/importEXCELdata.m b/functions/interface/importEXCELdata.m index f56bc62..b9d09ec 100644 --- a/functions/interface/importEXCELdata.m +++ b/functions/interface/importEXCELdata.m @@ -27,8 +27,8 @@ function importEXCELdata(src) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/importINV2INV.m b/functions/interface/importINV2INV.m index 7b041c0..fc1710a 100644 --- a/functions/interface/importINV2INV.m +++ b/functions/interface/importINV2INV.m @@ -27,8 +27,8 @@ function importINV2INV(src) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -55,7 +55,10 @@ function importINV2INV(src) end % only continue if user didn't cancel -if sum(Sessionpath) > 0 +if sum(Sessionpath) > 0 + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ...'); + % check if it is a valid session file tmp = load(fullfile(Sessionpath,Sessionfile),'savedata'); if isfield(tmp,'savedata') && isfield(tmp.savedata,'data') && ... @@ -65,65 +68,43 @@ function importINV2INV(src) % check import uimenu set(src,'Checked','on'); - % adjust menu entry for expert mode - switch savedata.data.info.ExpertMode - case 'on' - set(gui.menu.extra_expert,'Checked','off'); - case 'off' - set(gui.menu.extra_expert,'Checked','on'); - end - onMenuExpert(gui.menu.extra_expert); - - % adjust menu entry for joint inversion - switch savedata.data.info.JointInv - case 'on' - set(gui.menu.extra_joint,'Checked','off'); - case 'off' - set(gui.menu.extra_joint,'Checked','on'); - end - onMenuJointInversion(gui.menu.extra_joint); - enableGUIelements('NMR'); - - % adjust menu entry for comand line inversion info - switch savedata.data.info.InvInfo - case 'on' - set(gui.menu.view_invinfo,'Checked','off'); - case 'off' - set(gui.menu.view_invinfo,'Checked','on'); - end - onMenuView(gui.menu.view_invinfo); - - % adjust menu entry for tool tips - switch savedata.data.info.ToolTips - case 'on' - set(gui.menu.view_tooltips,'Checked','off'); - case 'off' - set(gui.menu.view_tooltips,'Checked','on'); - end - onMenuView(gui.menu.view_tooltips); - - % update GUI data from session mat-file - data = savedata.data; - INVdata = savedata.INVdata; - % backward compatibility with older versions + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... backward compatibility.'); % current GUI version version = getVersionNoFromString(gui.myui.version); % import GUI version version_in = getVersionNoFromString(savedata.myui.version); if version_in < version if version_in < 19 % changes introduced with v.0.1.9 + % rename 'ILA' to 'LU' in savedata if strcmp(savedata.data.invstd.invtype,'ILA') - data.invstd.invtype = 'LU'; + savedata.data.invstd.invtype = 'LU'; + end + for i = 1:numel(savedata.INVdata) + if isstruct(savedata.INVdata{i}) && ... + strcmp(savedata.INVdata{i}.invstd.invtype,'ILA') + savedata.INVdata{i}.invstd.invtype = 'LU'; + end end + end + if version_in < 112 % changes introduced with v.0.1.12 + % add 'Tdiff' field to savedata + savedata.data.invstd.Tdiff = data.invstd.Tdiff; for i = 1:numel(savedata.INVdata) - if strcmp(savedata.INVdata{i}.invstd.invtype,'ILA') - INVdata{i}.invstd.invtype = 'LU'; + if isstruct(savedata.INVdata{i}) + savedata.INVdata{i}.invstd.Tdiff = data.invstd.Tdiff; end end end - end + end + % update GUI data from session mat-file + data = savedata.data; + INVdata = savedata.INVdata; + + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... update GUI data.'); % check if the import path exists on this machine isdir_import = dir(data.import.path); % if not replace it with the path the session-file was loaded from @@ -159,6 +140,8 @@ function importINV2INV(src) set(gui.listbox_handles.signal,'String',data.import.NMR.filesShort); set(gui.listbox_handles.signal,'Value',[],'Max',2,'Min',0); + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... update GUI interface.'); % update GUI interface NUCLEUSinv_updateInterface; @@ -174,32 +157,63 @@ function importINV2INV(src) set(gui.listbox_handles.signal,'String',strL); end end - + + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... update GUI menus.'); + % adjust menu entry for expert mode + switch savedata.data.info.ExpertMode + case 'on' + set(gui.menu.extra_expert,'Checked','off'); + case 'off' + set(gui.menu.extra_expert,'Checked','on'); + end + onMenuExpert(gui.menu.extra_expert); + + % adjust menu entry for joint inversion + switch savedata.data.info.JointInv + case 'on' + set(gui.menu.extra_joint,'Checked','off'); + case 'off' + set(gui.menu.extra_joint,'Checked','on'); + end + onMenuJointInversion(gui.menu.extra_joint); + enableGUIelements('NMR'); + + % adjust menu entry for comand line inversion info + switch savedata.data.info.InvInfo + case 'on' + set(gui.menu.view_invinfo,'Checked','off'); + case 'off' + set(gui.menu.view_invinfo,'Checked','on'); + end + onMenuView(gui.menu.view_invinfo); + + % adjust menu entry for tool tips + switch savedata.data.info.ToolTips + case 'on' + set(gui.menu.view_tooltips,'Checked','off'); + case 'off' + set(gui.menu.view_tooltips,'Checked','on'); + end + onMenuView(gui.menu.view_tooltips); + + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... show last file.'); % set focus on last file used in previous session set(gui.listbox_handles.signal,'Value',savedata.id); + % and simulate click to update all relevant GUI elements + onListboxData(gui.listbox_handles.signal); - % show corresponding file data - updatePlotsSignal; - if isstruct(INVdata{savedata.id}) - if isfield(data,'results') - if isfield(data.results,'invstd') - updatePlotsDistribution; - updateInfo(gui.plots.SignalPanel); - end - if isfield(data.results,'invjoint') - updatePlotsJointInversion; - end - if isfield(data.results,'lcurve') - updatePlotsLcurve; - end - end - end + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... done.'); else + % display info text + displayStatusText(gui,'Importing GUI session from mat-file ... cancelled.'); + helpdlg({'importINV2INV:';... 'This seems to be not a valid NUCLEUSinv session file'},... 'No session data found'); end - end %------------- END OF CODE -------------- diff --git a/functions/interface/importMOD2INV.m b/functions/interface/importMOD2INV.m index 8106862..32adf72 100644 --- a/functions/interface/importMOD2INV.m +++ b/functions/interface/importMOD2INV.m @@ -26,8 +26,8 @@ function importMOD2INV(src) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -173,8 +173,10 @@ function importMOD2INV(src) switch T1T2 case 'T1' data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EdT1(dL(i),:)'; + data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(dL(i),2); case 'T2' data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EdT2(dL(i),:)'; + data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(dL(i),4); end data.import.NMR.data{c}.phase = 0; data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; @@ -182,6 +184,11 @@ function importMOD2INV(src) data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; + if isfield(data.import.NMRMOD.nmr,'Td') + data.import.NMR.para{c}.Tdiff = data.import.NMRMOD.nmr.Td; + else + data.import.NMR.para{c}.Tdiff = 1e6; + end data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; @@ -213,8 +220,10 @@ function importMOD2INV(src) switch T1T2 case 'T1' data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT1(iL(i),:)'; + data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(iL(i),1); case 'T2' data.import.NMR.data{c}.signal = data.import.NMRMOD.nmr.EiT2(iL(i),:)'; + data.import.NMR.data{c}.noise = data.import.NMRMOD.nmr.noise(iL(i),3); end data.import.NMR.data{c}.phase = 0; data.import.NMR.data{c}.raw.time = data.import.NMR.data{c}.time; @@ -222,6 +231,11 @@ function importMOD2INV(src) data.import.NMR.para{c}.geom = data.import.NMRMOD.geom.type; data.import.NMR.para{c}.Tbulk = data.import.NMRMOD.nmr.Tb; + if isfield(data.import.NMRMOD.nmr,'Td') + data.import.NMR.para{c}.Tdiff = data.import.NMRMOD.nmr.Td; + else + data.import.NMR.para{c}.Tdiff = 1e6; + end data.import.NMR.para{c}.rho = data.import.NMRMOD.nmr.rho; data.import.NMR.para{c}.porosity = data.import.NMRMOD.nmr.porosity; diff --git a/functions/interface/importMOD2MOD.m b/functions/interface/importMOD2MOD.m index 122c4ee..46f1de3 100644 --- a/functions/interface/importMOD2MOD.m +++ b/functions/interface/importMOD2MOD.m @@ -26,8 +26,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -51,6 +51,9 @@ data.geometry = guidata.NMRMOD_GUI.geometry; data.pressure = guidata.NMRMOD_GUI.pressure; data.nmr = guidata.NMRMOD_GUI.nmr; + if ~isfield(guidata.NMRMOD_GUI.nmr,'Td') + data.nmr.Td = 1e6; + end data.results.constants = guidata.constants; data.results.GEOM = guidata.GEOM; data.results.NMR = guidata.NMR; diff --git a/functions/interface/importNMRdata.m b/functions/interface/importNMRdata.m index 956c766..3b2bb0f 100644 --- a/functions/interface/importNMRdata.m +++ b/functions/interface/importNMRdata.m @@ -30,6 +30,7 @@ function importNMRdata(src) % importDataLIAG % importDataLIAGproject % importDataMouse +% importDataHelios % loadGUIParameters % loadGUIrawdata % @@ -37,8 +38,8 @@ function importNMRdata(src) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -73,10 +74,16 @@ function importNMRdata(src) data.import.fileformat = 'liag'; elseif strcmp(label,'BGR std') data.import.fileformat = 'bgr'; - elseif strcmp(label,'BGR org') - data.import.fileformat = 'bgr2'; + elseif strcmp(label,'MouseCPMG') + data.import.fileformat = 'MouseCPMG'; elseif strcmp(label,'BGR mat') data.import.fileformat = 'bgrmat'; + elseif strcmp(label,'MouseLiftSingle') + data.import.fileformat = 'MouseLiftSingle'; + elseif strcmp(label,'MouseLiftAll') + data.import.fileformat = 'MouseLiftAll'; + elseif strcmp(label,'Helios') + data.import.fileformat = 'helios'; elseif strcmp(label,'BAM TOM') data.import.fileformat = 'bamtom'; elseif strcmp(label,'PM5') @@ -102,7 +109,7 @@ function importNMRdata(src) % check for mat-file with GUI rawdata and import data isfile = dir(fullfile(NMRpath,'NUCLEUSinv_raw.mat')); - % if there is nor raw-file import from folder/file + % if there is no raw-file import from folder/file if isempty(isfile) % import data data.import.path = NMRpath; @@ -129,11 +136,18 @@ function importNMRdata(src) tmp_h(5) = gui.myui.heights(1,3); set(gui.panels.main,'Heights',tmp_h); set(gui.panels.petro.main,'Minimized',false); - case 'BGR org' - [data,gui] = importDataBGR(data,gui); case 'BGR mat' data.import.file = NMRfile; [data,gui] = importDataBGRmat(data,gui); + case 'MouseCPMG' + [data,gui] = importDataMouseCPMG(data,gui); + case 'MouseLiftSingle' + [data,gui] = importDataBGRliftSingle(data,gui); + case 'MouseLiftAll' + [data,gui] = importDataBGRliftAll(data,gui); + case 'Helios' + data.import.file = NMRfile; + [data,gui] = importDataHelios(data,gui); case {'PM5','PM25'} data.import.file = NMRfile; [data,gui] = importDataIBAC(data,gui); @@ -232,7 +246,7 @@ function importNMRdata(src) % for almost all import cases we load a folder ... but not for all switch label case {'GGE ascii','GGE field','CoreLab ascii','MOUSE','LIAG single',... - 'BGR std','BGR org','BAM TOM','PM25'} + 'BGR std','MouseCPMG','MouseLiftSingle','MouseLiftAll','Helios','BAM TOM','PM25'} % if there is already a data folder present we start from here if isfield(import,'path') NMRpath = uigetdir(import.path,'Choose Data Path'); @@ -302,14 +316,10 @@ function importNMRdata(src) end %% -function [data,gui] = importDataBGR(data,gui) +function [data,gui] = importDataMouseCPMG(data,gui) -% first check the subpaths -% there should be 'cpmgfastauto' and 't1test' -t1path = dir(fullfile(data.import.path,'t1test')); -t1path = t1path(~ismember({t1path.name},{'.','..'})); -t2path = dir(fullfile(data.import.path,'cpmgfastauto')); -t2path = t2path(~ismember({t2path.name},{'.','..'})); +csv_t2path = dir(fullfile(data.import.path,'CPMG')); +csv_t2path = csv_t2path(~ismember({csv_t2path.name},{'.','..'})); fnames = struct; % shownames is just a dummy to hold all data file names that @@ -317,48 +327,21 @@ function importNMRdata(src) shownames = cell(1,1); c = 0; -if ~isempty(t1path) - for i = 1:size(t1path,1) - in.T1T2 = 'T1'; - in.path = fullfile(data.import.path,'t1test',t1path(i).name); - in.fileformat = data.import.fileformat; - out = LoadNMRData_driver(in); - - for j = 1:size(out.nmrData,2) - % the individual file names - c = c + 1; - fnames(c).parfile = 'acq.par'; - fnames(c).datafile = out.nmrData{j}.datfile; - - shownames{c} = ['T1_',t1path(i).name,'_',fnames(c).datafile]; - - % the NMR data - % here we fix the time scale from [ms] to [s] - if max(out.nmrData{j}.time) > 100 - out.nmrData{j}.time = out.nmrData{j}.time/1000; - out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; - end - data.import.NMR.data{c} = out.nmrData{j}; - data.import.NMR.para{c} = out.parData; - end - end -end - -if ~isempty(t2path) - for i = 1:size(t2path,1) +if ~isempty(csv_t2path) + for i = 1:size(csv_t2path,1) in.T1T2 = 'T2'; - in.path = fullfile(data.import.path,'cpmgfastauto',t2path(i).name); + in.path = fullfile(data.import.path,'CPMG',csv_t2path(i).name); in.fileformat = data.import.fileformat; out = LoadNMRData_driver(in); for j = 1:size(out.nmrData,2) % the individual file names c = c + 1; - fnames(c).parfile = 'acq.par'; + fnames(c).parfile = 'acqu.par'; fnames(c).datafile = out.nmrData{j}.datfile; fnames(c).T2specfile = ''; - shownames{c} = ['T2_',t2path(i).name,'_',fnames(c).datafile]; + shownames{c} = ['T2_',csv_t2path(i).name,'_',fnames(c).datafile]; % the NMR data % here we fix the time scale from [ms] to [s] @@ -372,7 +355,7 @@ function importNMRdata(src) end end -if isempty(t1path) && isempty(t2path) +if isempty(csv_t2path) helpdlg('No data folders in the given directory.','onMenuImport: No data.'); else % update the global data structure @@ -435,6 +418,174 @@ function importNMRdata(src) end +%% +function [data,gui] = importDataBGRliftSingle(data,gui) + +% first check whether T1 or T2 was measured... +% by analyzing the name of data folder +indiz = find(data.import.path == filesep); +checkT1T2 = data.import.path(indiz(end-1)+1:indiz(end)-1); +if strcmp(checkT1T2,'t1test') + in.T1T2 = 'T1'; +elseif strcmp(checkT1T2,'cpmgfastautotest') + in.T1T2 = 'T2'; +elseif strcmp(checkT1T2,'cpmgfastauto') + in.T1T2 = 'T2'; +else + disp('Please chose an original Prospa folder for Mouse Lift data!'); +end + +% % there should be folders with integer values in their names +% t1t2path = data.import.path; + +fnames = struct; +% shownames is just a dummy to hold all data file names that +% will be shown in the listbox +shownames = cell(1,1); + +c = 0; +if ~isempty(data.import.path) +% for i = 1:size(t1t2path,1) + in.path = data.import.path; + in.fileformat = data.import.fileformat; + out = LoadNMRData_driver(in); + + for j = 1:size(out.nmrData,2) + % the individual file names + c = c + 1; + fnames(c).parfile = 'acq.par'; + fnames(c).datafile = out.nmrData{j}.datfile; + + shownames{c} = [in.T1T2,'_',fnames(c).datafile]; + + % the NMR data + % here we fix the time scale from [ms] to [s] + if max(out.nmrData{j}.time) > 100 + out.nmrData{j}.time = out.nmrData{j}.time/1000; + out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; + end + data.import.NMR.data{c} = out.nmrData{j}; + data.import.NMR.para{c} = out.parData; + end +else + helpdlg('No data folders in the given directory.','onMenuImport: No data.'); +end + + + % update the global data structure + data.import.NMR.files = fnames; + data.import.NMR.filesShort = shownames; + +end +%% +function [data,gui] = importDataBGRliftAll(data,gui) + +% first check whether T1 or T2 was measured +indiz = find(data.import.path == filesep); +checkT1T2 = data.import.path(indiz(end)+1:end); +if strcmp(checkT1T2,'t1test') + in.T1T2 = 'T1'; +elseif strcmp(checkT1T2,'cpmgfastautotest') + in.T1T2 = 'T2'; +elseif strcmp(checkT1T2,'cpmgfastauto') + in.T1T2 = 'T2'; +else + helpdlg('No original data folder','onMenuImport: No data.'); +end + +% there should be folders with integer values in their names +t1t2path = dir(data.import.path); +t1t2path = t1t2path(~ismember({t1t2path.name},{'.','..'})); + +fnames = struct; +% shownames is just a dummy to hold all data file names that +% will be shown in the listbox +shownames = cell(1,1); + +c = 0; +if ~isempty(t1t2path) + for i = 1:size(t1t2path,1) + in.path = fullfile(data.import.path,t1t2path(i).name); + in.fileformat = data.import.fileformat; + out = LoadNMRData_driver(in); + + for j = 1:size(out.nmrData,2) + % the individual file names + c = c + 1; + fnames(c).parfile = 'acq.par'; + fnames(c).datafile = out.nmrData{j}.datfile; + + shownames{c} = [in.T1T2,'_',t1t2path(i).name,'_',fnames(c).datafile]; + + % the NMR data + % here we fix the time scale from [ms] to [s] + if max(out.nmrData{j}.time) > 100 + out.nmrData{j}.time = out.nmrData{j}.time/1000; + out.nmrData{j}.raw.time = out.nmrData{j}.raw.time/1000; + end + data.import.NMR.data{c} = out.nmrData{j}; + data.import.NMR.para{c} = out.parData; + end + end +else + helpdlg('No data folders in the given directory.','onMenuImport: No data.'); +end + % update the global data structure + data.import.NMR.files = fnames; + data.import.NMR.filesShort = shownames; + +end + +%% +function [data,gui] = importDataHelios(data,gui) + +% first check the subpaths +% there should be some folders with names ... +% ... similar to the data filenames inside them +datpath = dir(data.import.path); +datpath = datpath(~ismember({datpath.name},{'.','..'})); + +fnames = struct; +% shownames is just a dummy to hold all data file names that +% will be shown in the listbox +shownames = cell(1,1); + +c = 0; +if ~isempty(datpath) + for i = 1:size(datpath,1) + % does datpath.name points to a folder? + if datpath(i).isdir + % check if datpath.name includes regular Helios data files + content = dir([data.import.path,filesep,datpath(i).name]); + content = content(~ismember({content.name},{'.','..'})); + for j = 1:size(content,1) + if strcmp(content(j).name,[datpath(i).name,'_1.hrd']) + in.T1T2 = 'T2'; + in.name = content(j).name; + in.path = fullfile(data.import.path,filesep,datpath(i).name); + in.fileformat = data.import.fileformat; + out = LoadNMRData_driver(in); + + % the individual file names + c = c + 1; + fnames(c).parfile = ''; + fnames(c).datafile = out.nmrData.datfile; + fnames(c).T2specfile = ''; + shownames{c} = ['T2_',datpath(i).name]; + + data.import.NMR.data{c} = out.nmrData; + data.import.NMR.para{c} = out.parData; + end + end + end + end +end + +data.import.NMR.files = fnames; +data.import.NMR.filesShort = shownames; + +end + %% function [data,gui] = importDataDart(data,gui) diff --git a/functions/interface/makeINIfile.m b/functions/interface/makeINIfile.m index 238361c..734de69 100644 --- a/functions/interface/makeINIfile.m +++ b/functions/interface/makeINIfile.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/minimizePanel.m b/functions/interface/minimizePanel.m index 03fade9..d594f96 100644 --- a/functions/interface/minimizePanel.m +++ b/functions/interface/minimizePanel.m @@ -24,8 +24,8 @@ function minimizePanel(src,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/moveEntryInList.m b/functions/interface/moveEntryInList.m index 8a64bd9..eb8a14b 100644 --- a/functions/interface/moveEntryInList.m +++ b/functions/interface/moveEntryInList.m @@ -23,8 +23,8 @@ function moveEntryInList(inc) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/onFigureSizeChange.m b/functions/interface/onFigureSizeChange.m index 06a8382..ad68f6e 100644 --- a/functions/interface/onFigureSizeChange.m +++ b/functions/interface/onFigureSizeChange.m @@ -27,8 +27,8 @@ function onFigureSizeChange(fig,~) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -40,34 +40,32 @@ function onFigureSizeChange(fig,~) switch fig_tag case 'INV' % proceed if there is data - if ~isempty(gui) - if isfield(gui,'panels') - heights = get(gui.panels.main,'Heights'); - set(gui.left,'Heights',-1,'MinimumHeights',sum(heights(2:end))+250); - % check if CPS settings panel is activated - if heights(end) > 100 - % check if there is a vertical scrollbar - hpos = get(gui.top,'InnerPosition'); - if get(gui.left,'MinimumHeights') > hpos(4) - % if yes shift the table slightly to the right to avoid - % horizontal resizing - set(gui.panels.invjoint.TabCPS,'Widths',[180 -1]); - else - % if not use the default layout - set(gui.panels.invjoint.TabCPS,'Widths',[200 -1]); - end + if ~isempty(gui) && isfield(gui,'panels') + heights = get(gui.panels.main,'Heights'); + set(gui.left,'Heights',-1,'MinimumHeights',sum(heights(2:end))+250); + % check if CPS settings panel is activated + if heights(end) > 100 + % check if there is a vertical scrollbar + hpos = get(gui.top,'InnerPosition'); + if get(gui.left,'MinimumHeights') > hpos(4) + % if yes shift the table slightly to the right to avoid + % horizontal resizing + set(gui.panels.invjoint.TabCPS,'Widths',[180 -1]); + else + % if not use the default layout + set(gui.panels.invjoint.TabCPS,'Widths',[200 -1]); end - % check if process panel is activated - if heights(2) > 100 - % check if there is a vertical scrollbar - hpos = get(gui.top,'InnerPosition'); - if get(gui.left,'MinimumHeights') > hpos(4) - % if yes shift the hbox2 slightly to the right - set(gui.panels.process.HBox2,'Widths',[180 -1 -1 -1.5 50]); - else - % if not use the default layout - set(gui.panels.process.HBox2,'Widths',[200 -1 -1 -1.5 50]); - end + end + % check if process panel is activated + if heights(2) > 100 + % check if there is a vertical scrollbar + hpos = get(gui.top,'InnerPosition'); + if get(gui.left,'MinimumHeights') > hpos(4) + % if yes shift the hbox2 slightly to the right + set(gui.panels.process.HBox2,'Widths',[180 -1 -1 -1.5 50]); + else + % if not use the default layout + set(gui.panels.process.HBox2,'Widths',[200 -1 -1 -1.5 50]); end end end @@ -78,7 +76,7 @@ function onFigureSizeChange(fig,~) case 'MOD' % proceed if there is data - if ~isempty(gui) + if ~isempty(gui) && isfield(gui,'panels') heights = get(gui.panels.main,'Heights'); if heights(2) == -1 set(gui.left,'Heights',-1,'MinimumHeights',sum(heights(1:end))+250); diff --git a/functions/interface/processNMRData.m b/functions/interface/processNMRData.m index fafd41c..0d9583e 100644 --- a/functions/interface/processNMRData.m +++ b/functions/interface/processNMRData.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -68,8 +68,9 @@ if isreal(nmrraw.s) s = nmrraw.s; % check if noise was calculated / estimated during import + % and normalize if necessary if isfield(nmrraw,'noise') - noise = nmrraw.noise; + noise = nmrraw.noise./nmrproc.normfac; else noise = 0; end diff --git a/functions/interface/processNMRDataControl.m b/functions/interface/processNMRDataControl.m index bd58640..cd18a8a 100644 --- a/functions/interface/processNMRDataControl.m +++ b/functions/interface/processNMRDataControl.m @@ -25,8 +25,8 @@ function processNMRDataControl(fig,id) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -65,24 +65,6 @@ function processNMRDataControl(fig,id) data.results.nmrproc = nmrproc; data.process.normfac = nmrproc.normfac; -% some special treatment of NUCLEUSmod data -if isfield(data.import,'NMRMOD') - noise = data.import.NMRMOD.nmr.noise; - data.results.nmrproc.noise = noise; - if noise ~=0 && ~strcmp(nmrproc.gatetype,'raw') - e = noise ./ sqrt(nmrproc.N); - W = diag(e) * eye(size(e,1)); - data.results.nmrproc.e = e; - data.results.nmrproc.W = W; - else - e = noise*ones(size(nmrproc.s)); - data.results.nmrproc.e = e; - if isfield(data.results.nmrproc,'W') - data.results.nmrproc = rmfield(data.results.nmrproc,'W'); - end - end -end - % update GUI data setappdata(fig,'data',data); diff --git a/functions/interface/readINIfile.m b/functions/interface/readINIfile.m index 946b67f..5898461 100644 --- a/functions/interface/readINIfile.m +++ b/functions/interface/readINIfile.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/removeCalculationFields.m b/functions/interface/removeCalculationFields.m index 7cbeb34..8f5eb1e 100644 --- a/functions/interface/removeCalculationFields.m +++ b/functions/interface/removeCalculationFields.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/removeInversionFields.m b/functions/interface/removeInversionFields.m index e173b3f..2e7e3f9 100644 --- a/functions/interface/removeInversionFields.m +++ b/functions/interface/removeInversionFields.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/removeSignalFromList.m b/functions/interface/removeSignalFromList.m index b10ed8b..b0e169e 100644 --- a/functions/interface/removeSignalFromList.m +++ b/functions/interface/removeSignalFromList.m @@ -24,8 +24,8 @@ function removeSignalFromList(id) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/resortDataList.m b/functions/interface/resortDataList.m index df1be55..bdf6e47 100644 --- a/functions/interface/resortDataList.m +++ b/functions/interface/resortDataList.m @@ -23,8 +23,8 @@ function resortDataList(label) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/runInversionBatch.m b/functions/interface/runInversionBatch.m index 96bff81..34853b5 100644 --- a/functions/interface/runInversionBatch.m +++ b/functions/interface/runInversionBatch.m @@ -32,8 +32,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -127,10 +127,24 @@ invstd = fitDataFree(data.results.nmrproc.t,... data.results.nmrproc.s,flag,param,data.invstd.freeDT); + case 'LU' + param.T1T2 = data.results.nmrproc.T1T2; + param.T1IRfac = data.results.nmrproc.T1IRfac; + param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; + param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; + param.Lorder = data.invstd.Lorder; + param.lambda = data.invstd.lambda; + param.noise = data.results.nmrproc.noise; + + invstd = fitDataLUdecomp(data.results.nmrproc.t,... + data.results.nmrproc.s,param); + case 'NNLS' param.T1T2 = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; param.regMethod = data.invstd.regtype; param.Lorder = data.invstd.Lorder; @@ -144,17 +158,45 @@ invstd = fitDataLSQ(data.results.nmrproc.t,... data.results.nmrproc.s,param); - case 'LU' + case 'MUMO' % N free distribution inversion param.T1T2 = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; - param.Lorder = data.invstd.Lorder; - param.lambda = data.invstd.lambda; + param.regMethod = data.invstd.regtype; param.noise = data.results.nmrproc.noise; + param.solver = data.info.solver; + param.optim = data.info.has_optim; + if isfield(data.results.nmrproc,'W') + param.W = data.results.nmrproc.W; + end - invstd = fitDataLUdecomp(data.results.nmrproc.t,... - data.results.nmrproc.s,param); + % status bar information + switch data.info.solver + case 'lsqlin' + infostring = 'Inversion using ''Optimization Toolbox'' ... '; + case 'lsqnonneg' + infostring = 'Inversion using ''fminsearchbnd'' ... '; + end + displayStatusText(gui,infostring); + invstd = fitDataMultiModal(data.results.nmrproc.t,... + data.results.nmrproc.s,param,data.invstd.freeDT); + + % estimate uncertainty + if data.invstd.useUncert + % original fit parameter + iparam = param; + % uncertainty parameter + uparam.time = data.results.nmrproc.t; + uparam.signal = data.results.nmrproc.s; + uparam.uncertMethod = data.invstd.uncertMethod; + uparam.uncertThresh = data.invstd.uncertThresh; + uparam.uncertChi2 = data.invstd.uncertChi2; + uparam.uncertN = data.invstd.uncertN; + uparam.uncertMax = data.invstd.uncertMax; + invstd = estimateUncertainty(data.invstd.invtype,invstd,iparam,uparam); + end end % save inversion results diff --git a/functions/interface/runInversionJoint.m b/functions/interface/runInversionJoint.m index f8367b0..40659d1 100644 --- a/functions/interface/runInversionJoint.m +++ b/functions/interface/runInversionJoint.m @@ -44,8 +44,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -276,6 +276,7 @@ iparam.W = W; end iparam.Tb = data.invstd.Tbulk; + iparam.Td = data.invstd.Tdiff; iparam.T1T2 = T1T2flag; iparam.T1IRfac = T1IRfac; iparam.L = L; @@ -370,8 +371,8 @@ case 'manual' % disable the RUN button to indicate a running inversion set(gui.push_handles.invjoint_run,'String','RUNNING ...',... - 'Enable','inactive'); - + 'Enable','inactive'); + % inversion parameter iparam.t = t; iparam.g = g; @@ -379,6 +380,7 @@ iparam.W = W; end iparam.Tb = data.invstd.Tbulk; + iparam.Td = data.invstd.Tdiff; iparam.T1T2 = T1T2flag; iparam.T1IRfac = T1IRfac; iparam.L = L; @@ -405,6 +407,7 @@ 'Algorithm','levenberg-marquardt',... 'MaxIter',1000); [X,~,~,exitflag] = lsqnonlin(@(X)fcn_JointInvfree(X,iparam),x0,lb,ub,options); + % status bar information displayStatusText(gui,[infostring,'done']); % get the final fit @@ -496,6 +499,7 @@ end iparam.indt = indt; iparam.Tb = data.invstd.Tbulk; + iparam.Td = data.invstd.Tdiff; iparam.T1T2 = T1T2flag; iparam.T1IRfac = T1IRfac; iparam.SatImbDrain = SatImbDrain; @@ -604,6 +608,7 @@ end iparam.indt = indt; iparam.Tb = data.invstd.Tbulk; + iparam.Td = data.invstd.Tdiff; iparam.T1T2 = T1T2flag; iparam.T1IRfac = T1IRfac; iparam.SatImbDrain = SatImbDrain; diff --git a/functions/interface/runInversionStd.m b/functions/interface/runInversionStd.m index 7477a8c..784955f 100644 --- a/functions/interface/runInversionStd.m +++ b/functions/interface/runInversionStd.m @@ -42,8 +42,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -101,6 +101,7 @@ param.T1T2 = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; param.regMethod = 'manual'; param.Lorder = data.invstd.Lorder; @@ -211,7 +212,7 @@ invstd = fitDataFree(data.results.nmrproc.t,... data.results.nmrproc.s,flag,param,1); - case 'free' % bi-exponential inversion + case 'free' % N free single-exponential inversion flag = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.noise = data.results.nmrproc.noise; @@ -235,6 +236,7 @@ param.T1T2 = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; param.Lorder = data.invstd.Lorder; param.lambda = data.invstd.lambda; @@ -253,6 +255,7 @@ param.T1T2 = data.results.nmrproc.T1T2; param.T1IRfac = data.results.nmrproc.T1IRfac; param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; param.regMethod = data.invstd.regtype; param.Lorder = data.invstd.Lorder; @@ -273,17 +276,57 @@ displayStatusText(gui,infostring); invstd = fitDataLSQ(data.results.nmrproc.t,... data.results.nmrproc.s,param); + + case 'MUMO' % N free distribution inversion + param.T1T2 = data.results.nmrproc.T1T2; + param.T1IRfac = data.results.nmrproc.T1IRfac; + param.Tb = data.invstd.Tbulk; + param.Td = data.invstd.Tdiff; + param.Tint = [log10(data.invstd.time) data.invstd.Ntime]; + param.noise = data.results.nmrproc.noise; + param.optim = data.info.has_optim; + if isfield(data.results.nmrproc,'W') + param.W = data.results.nmrproc.W; + end + + % status bar information + switch data.info.solver + case 'lsqlin' + infostring = 'Inversion using ''Optimization Toolbox'' ... '; + case 'lsqnonneg' + infostring = 'Inversion using ''fminsearchbnd'' ... '; + end + displayStatusText(gui,infostring); + invstd = fitDataMultiModal(data.results.nmrproc.t,... + data.results.nmrproc.s,param,data.invstd.freeDT); + + % estimate uncertainty + if data.invstd.useUncert + % original fit parameter + iparam = param; + % uncertainty parameter + uparam.time = data.results.nmrproc.t; + uparam.signal = data.results.nmrproc.s; + uparam.uncertMethod = data.invstd.uncertMethod; + uparam.uncertThresh = data.invstd.uncertThresh; + uparam.uncertChi2 = data.invstd.uncertChi2; + uparam.uncertN = data.invstd.uncertN; + uparam.uncertMax = data.invstd.uncertMax; + invstd = estimateUncertainty(data.invstd.invtype,invstd,iparam,uparam); + end end % normalize to 1 if data.process.norm == 1 switch data.invstd.invtype - case {'LU','NNLS'} - % normalization with sum(f*dt) -> area~=1 - % dt = log10(invstd.T1T2me(2))-log10(invstd.T1T2me(1)); - % invstd.T1T2f = invstd.T1T2f/sum(invstd.T1T2f*dt); + case {'LU','NNLS','MUMO'} % normalization with sum() -> sum(f)=1 + % this is the standard way of doing it invstd.T1T2f = invstd.T1T2f./sum(invstd.T1T2f); - % normalization with trapz() -> area=1 but sum(f)>1 + % alternatives depending on the objective: + % 1.) normalization with sum(f*dt) -> area~=1 + % dt = log10(invstd.T1T2me(2))-log10(invstd.T1T2me(1)); + % invstd.T1T2f = invstd.T1T2f/sum(invstd.T1T2f*dt); + % 2.) normalization with trapz() -> area=1 but sum(f)>1 % invstd.T1T2f = invstd.T1T2f./trapz(invstd.T1T2me,invstd.T1T2f); otherwise % nothing to do diff --git a/functions/interface/showExtraGraphics.m b/functions/interface/showExtraGraphics.m index ffaa21e..3a64831 100644 --- a/functions/interface/showExtraGraphics.m +++ b/functions/interface/showExtraGraphics.m @@ -24,8 +24,8 @@ function showExtraGraphics(method) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/showFitStatistics.m b/functions/interface/showFitStatistics.m index 91151f5..3024c0a 100644 --- a/functions/interface/showFitStatistics.m +++ b/functions/interface/showFitStatistics.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/showParameterInfo.m b/functions/interface/showParameterInfo.m index b5efe50..5b37cfe 100644 --- a/functions/interface/showParameterInfo.m +++ b/functions/interface/showParameterInfo.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/switchToolTips.m b/functions/interface/switchToolTips.m index a157b9c..25f4ef9 100644 --- a/functions/interface/switchToolTips.m +++ b/functions/interface/switchToolTips.m @@ -24,8 +24,8 @@ function switchToolTips(gui,onoff) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/uncheckImportMenus.m b/functions/interface/uncheckImportMenus.m index 1c72693..cd541f0 100644 --- a/functions/interface/uncheckImportMenus.m +++ b/functions/interface/uncheckImportMenus.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -45,8 +45,11 @@ set(gui.menu.file_import_lab_liag_single,'Checked','off'); set(gui.menu.file_import_lab_liag_project,'Checked','off'); set(gui.menu.file_import_lab_bgr_std,'Checked','off'); -set(gui.menu.file_import_lab_bgr_org,'Checked','off'); set(gui.menu.file_import_lab_bgr_mat,'Checked','off'); +set(gui.menu.file_import_lab_bgr_mouse_cpmg,'Checked','off'); +set(gui.menu.file_import_lab_bgr_mouse_liftsingle,'Checked','off'); +set(gui.menu.file_import_lab_bgr_mouse_liftall,'Checked','off'); +set(gui.menu.file_import_lab_bgr_helios,'Checked','off'); set(gui.menu.file_import_lab_bam_tom,'Checked','off'); set(gui.menu.file_import_lab_ascii_T1,'Checked','off'); set(gui.menu.file_import_lab_ascii_T2,'Checked','off'); diff --git a/functions/interface/updateCPSTable.m b/functions/interface/updateCPSTable.m index 97c493e..2395526 100644 --- a/functions/interface/updateCPSTable.m +++ b/functions/interface/updateCPSTable.m @@ -23,8 +23,8 @@ function updateCPSTable(method) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updateCPSTableSize.m b/functions/interface/updateCPSTableSize.m index 6fc0045..8e0329f 100644 --- a/functions/interface/updateCPSTableSize.m +++ b/functions/interface/updateCPSTableSize.m @@ -23,8 +23,8 @@ function updateCPSTableSize(inc) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updateInfo.m b/functions/interface/updateInfo.m index 63dfc29..344bc19 100644 --- a/functions/interface/updateInfo.m +++ b/functions/interface/updateInfo.m @@ -23,8 +23,8 @@ function updateInfo(src,~) %#ok % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -65,11 +65,11 @@ function updateInfo(src,~) %#ok case 'T2' switch data.process.gatetype case 'raw' - info{end+1,1} = ['Echos  = ',... + info{end+1,1} = ['Echos = ',... sprintf('%d',length(nmrproc.t)),... ' (',data.process.gatetype,')','']; otherwise - info{end+1,1} = ['Gates  = ',... + info{end+1,1} = ['Gates = ',... sprintf('%d',length(nmrproc.t)),... ' (',data.process.gatetype,')','']; end @@ -78,12 +78,12 @@ function updateInfo(src,~) %#ok switch data.process.norm case 0 if max(nmrproc.s) < 1e-3 - info{end+1,1} = ['A_max  = ',... - sprintf('%5.4e',max(nmrproc.s)),... + info{end+1,1} = ['A_max = ',... + sprintf('%5.3e',max(nmrproc.s)),... '']; else - info{end+1,1} = ['A_max  = ',... - sprintf('%5.4f',max(nmrproc.s)),... + info{end+1,1} = ['A_max = ',... + sprintf('%5.3f',max(nmrproc.s)),... '']; end case 1 @@ -93,12 +93,12 @@ function updateInfo(src,~) %#ok '']; else info{end+1,1} = ['Normfac = ',... - sprintf('%5.4f',max(data.process.normfac)),... + sprintf('%5.3f',max(data.process.normfac)),... '']; end end - info{end+1,1} = ['noise  = ',sprintf('%4.3e',nmrproc.noise),'']; + info{end+1,1} = ['noise = ',sprintf('%4.2e',nmrproc.noise),'']; info{end+1,1} = ' '; % possible inversion results/statistics @@ -112,12 +112,12 @@ function updateInfo(src,~) %#ok switch nmrproc.T1T2 case 'T1' info{end+1,1} = ['E&infin = ',... - sprintf('%4.3e',sum(invstd.E0)),... - ' ∓ (',sprintf('%4.3e',ciE0),')','']; + sprintf('%4.2e',sum(invstd.E0)),... + ' ∓ (',sprintf('%4.2e',ciE0),')','']; case 'T2' info{end+1,1} = ['E0 = ',... - sprintf('%4.3e',sum(invstd.E0)),... - ' ∓ (',sprintf('%4.3e',ciE0),')','']; + sprintf('%4.2e',sum(invstd.E0)),... + ' ∓ (',sprintf('%4.2e',ciE0),')','']; end info{end+1,1} = ' '; if isfield(invstd,'chi2') @@ -126,9 +126,10 @@ function updateInfo(src,~) %#ok info{end+1,1} = ['',str,'']; end end - str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; + str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; info{end+1,1} = ['',str,'']; - if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +% if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 + if nmrproc.noise ~= 0 info{end+1,1} = ['S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'']; end @@ -138,12 +139,12 @@ function updateInfo(src,~) %#ok switch nmrproc.T1T2 case 'T1' info{end+1,1} = ['E&infin = ',... - sprintf('%4.3e',sum(E0)),... - ' ∓ (',sprintf('%4.3e',ciE0s),')','']; + sprintf('%4.2e',sum(E0)),... + ' ∓ (',sprintf('%4.2e',ciE0s),')','']; case 'T2' info{end+1,1} = ['E0 = ',... - sprintf('%4.3e',sum(E0)),... - ' ∓ (',sprintf('%4.3e',ciE0s),')','']; + sprintf('%4.2e',sum(E0)),... + ' ∓ (',sprintf('%4.2e',ciE0s),')','']; end info{end+1,1} = ' '; if isfield(invstd,'chi2') @@ -152,10 +153,11 @@ function updateInfo(src,~) %#ok info{end+1,1} = ['',str,'']; end end - str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; + str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; info{end+1,1} = ['',str,'']; - if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 +% if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 + if nmrproc.noise ~= 0 info{end+1,1} = ['S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'']; end @@ -163,10 +165,10 @@ function updateInfo(src,~) %#ok switch nmrproc.T1T2 case 'T1' info{end+1,1} = ['E&infin = ',... - sprintf('%4.3e',sum(invstd.E0)),'']; + sprintf('%4.2e',sum(invstd.E0)),'']; case 'T2' info{end+1,1} = ['E0 = ',... - sprintf('%4.3e',sum(invstd.E0)),'']; + sprintf('%4.2e',sum(invstd.E0)),'']; end info{end+1,1} = ' '; if isfield(invstd,'chi2') @@ -175,15 +177,43 @@ function updateInfo(src,~) %#ok info{end+1,1} = ['',str,'']; end end - str = ['RMS = ',sprintf('%4.3e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; + str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; info{end+1,1} = ['',str,'']; - if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 + % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 + if nmrproc.noise ~= 0 info{end+1,1} = ['S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'']; end info{end+1,1} = ['&lambda = ',sprintf('%6.5f',invstd.lambda_out),'']; info{end+1,1} = ' '; + case {'MUMO'} + switch nmrproc.T1T2 + case 'T1' + info{end+1,1} = ['E&infin = ',... + sprintf('%4.2e',sum(invstd.E0)),... + ' ∓ (',sprintf('%4.2e',invstd.ciE0),')','']; + case 'T2' + info{end+1,1} = ['E0 = ',... + sprintf('%4.2e',sum(invstd.E0)),... + ' ∓ (',sprintf('%4.2e',invstd.ciE0),')','']; + end + info{end+1,1} = ' '; + + if isfield(invstd,'chi2') + if ~isnan(invstd.chi2) + str = ['&Chi2 = ',sprintf('%4.2f',invstd.chi2)]; + info{end+1,1} = ['',str,'']; + end + end + str = ['RMS = ',sprintf('%4.2e',invstd.rms),' (',sprintf('%4.2f',invstd.rms*100./sum(invstd.E0)),'%)']; + info{end+1,1} = ['',str,'']; + + % if strcmp(nmrproc.T1T2,'T2') && nmrproc.noise ~= 0 + if nmrproc.noise ~= 0 + info{end+1,1} = ['S/N = ',sprintf('%4d',floor(sum(invstd.E0)/nmrproc.noise)),'']; + end + info{end+1,1} = ' '; end end end @@ -232,9 +262,11 @@ function updateInfo(src,~) %#ok sprintf('%5.2f',rad2deg(a)),... '°']; end - info{end+1,1} = ['phase fit = ',... + if isfield(data.results.nmrraw,'phase') + info{end+1,1} = ['phase fit = ',... sprintf('%5.2f',rad2deg(data.results.nmrraw.phase)),... '°']; + end info{end+1,1} = ' '; end @@ -317,8 +349,8 @@ function updateInfo(src,~) %#ok ' ∓ (',sprintf('%5.4f',ciT(i)),')','']; %#ok<*AGROW> info{end+1,1} = ['E(',num2str(i),') = ',sprintf('%5.4f',E0(i)),... ' ∓ (',sprintf('%5.4f',ciE0(i)),')','']; + info{end+1,1} = ' '; end - info{end+1,1} = ' '; case {'LU','NNLS'} % info is a cell array @@ -352,6 +384,57 @@ function updateInfo(src,~) %#ok ') = ',sprintf('%5.2f',por*BVI*100),' [vol. %]']; info{end+1,1} = ['BVM = ',sprintf('%5.2f',por*BVM*100),' [vol. %]']; + case {'MUMO'} + + % info is a cell array + str = [invtype,' ',num2str(data.invstd.freeDT)]; + info{end+1,1} = str; + info{end+1,1} = ' '; + + % TLGM + info{end+1,1} = ['TLGM = ',sprintf('%5.4f',invstd.Tlgm),'']; + info{end+1,1} = ' '; + + % clay-bound water CBW < tcut ms + % irreducible water / capillary water BVI ccut - tcut ms + % movable water BVM > tcut ms + switch data.process.timescale + case 's' + ccut = data.param.CBWcutoff/1000; + tcut = data.param.BVIcutoff/1000; + case 'ms' + ccut = data.param.CBWcutoff; + tcut = data.param.BVIcutoff; + end + por = data.invstd.porosity; + CBW = abs(sum(invstd.T1T2f(invstd.T1T2me<=ccut))/sum(invstd.T1T2f)); + BVI = abs(sum(invstd.T1T2f(invstd.T1T2me>ccut & invstd.T1T2me<=tcut))/sum(invstd.T1T2f)); + BVM = abs(sum(invstd.T1T2f(invstd.T1T2me>tcut))/sum(invstd.T1T2f)); + info{end+1,1} = ['CBW(',sprintf('%2d',data.param.CBWcutoff),... + ') = ',sprintf('%5.2f',por*CBW*100),' [vol. %]']; + + info{end+1,1} = ['BVI(',sprintf('%2d',data.param.BVIcutoff),... + ') = ',sprintf('%5.2f',por*BVI*100),' [vol. %]']; + info{end+1,1} = ['BVM = ',sprintf('%5.2f',por*BVM*100),' [vol. %]']; + info{end+1,1} = ' '; + + % values for T, sigma and amplitude + T = invstd.T; ciT = invstd.ci(1:3:end); + S = invstd.S; ciS = invstd.ci(2:3:end); + E = invstd.E; ciE = invstd.ci(3:3:end); + % transform ciT because it is in log scale + tmpT = log(T)-ciT'; + ciT = T - exp(tmpT); + + for i = 1:length(T) + info{end+1,1} = ['T(',num2str(i),') = ',sprintf('%5.4f',T(i)),... + ' ∓ (',sprintf('%5.4f',ciT(i)),')','']; %#ok<*AGROW> + info{end+1,1} = ['S(',num2str(i),') = ',sprintf('%5.4f',S(i)),... + ' ∓ (',sprintf('%5.4f',ciS(i)),')','']; + info{end+1,1} = ['E(',num2str(i),') = ',sprintf('%5.4f',E(i)),... + ' ∓ (',sprintf('%5.4f',ciE(i)),')','']; + info{end+1,1} = ' '; + end end end end diff --git a/functions/interface/updateNMRsignals.m b/functions/interface/updateNMRsignals.m index e44dccb..c663ecd 100644 --- a/functions/interface/updateNMRsignals.m +++ b/functions/interface/updateNMRsignals.m @@ -16,6 +16,7 @@ % % Other m-files required: % addNoiseToSignal +% updatePlotsNMR % % Subfunctions: % none @@ -24,8 +25,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -34,21 +35,46 @@ fig = findobj('Tag','MOD'); data = getappdata(fig,'data'); +%% first check what is the noise type + +switch data.nmr.noisetype + case 'level' + noise = data.nmr.noise; + case 'SNR' + SNR = data.nmr.noise; + noise = 1./SNR; +end + %% only proceed if the noise is larger than 0 -if data.nmr.noise > 0 - % scale noise by porosity - noise = data.nmr.noise/data.nmr.porosity; - % add noise to NMR signals - [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noise); - [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noise); - [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noise); - [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noise); +if noise > 0 + switch data.nmr.noisetype + case 'level' + % scale noise by porosity + noise = noise/data.nmr.porosity; + % add noise to NMR signals + [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noise); + [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noise); + [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noise); + [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noise); + noiseM = noise*ones(size(data.results.NMR.EiT1,2),4); + case 'SNR' + SNR = data.nmr.noise; + noiseM(:,1) = data.results.NMR.EiT1(:,end)./SNR; + [data.results.NMR.EiT1,~] = addNoiseToSignal(data.results.NMR.raw.EiT1,0,noiseM(:,1)); + noiseM(:,2) = data.results.NMR.EdT1(:,end)./SNR; + [data.results.NMR.EdT1,~] = addNoiseToSignal(data.results.NMR.raw.EdT1,0,noiseM(:,2)); + noiseM(:,3) = data.results.NMR.EiT2(:,1)./SNR; + [data.results.NMR.EiT2,~] = addNoiseToSignal(data.results.NMR.raw.EiT2,0,noiseM(:,3)); + noiseM(:,4) = data.results.NMR.EdT2(:,1)./SNR; + [data.results.NMR.EdT2,~] = addNoiseToSignal(data.results.NMR.raw.EdT2,0,noiseM(:,4)); + end else % reset the NMR signals with the raw data (without noise) data.results.NMR.EiT1 = data.results.NMR.raw.EiT1; data.results.NMR.EdT1 = data.results.NMR.raw.EdT1; data.results.NMR.EiT2 = data.results.NMR.raw.EiT2; data.results.NMR.EdT2 = data.results.NMR.raw.EdT2; + noiseM = zeros(size(data.results.NMR.EiT1,2),4); end % scale NMR signals by porosity @@ -57,12 +83,14 @@ data.results.NMR.EiT2 = data.nmr.porosity.*data.results.NMR.EiT2; data.results.NMR.EdT2 = data.nmr.porosity.*data.results.NMR.EdT2; -% save the noise value -data.results.NMR.noise = data.nmr.noise; +% save the noise matrix values +data.results.NMR.noise = noiseM; % save the porosity value data.results.NMR.porosity = data.nmr.porosity; % update the GUI data setappdata(fig,'data',data); +% update the NMR plot window +updatePlotsNMR; end diff --git a/functions/interface/updatePlotsCPS.m b/functions/interface/updatePlotsCPS.m index e855dca..80625b1 100644 --- a/functions/interface/updatePlotsCPS.m +++ b/functions/interface/updatePlotsCPS.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsDistribution.m b/functions/interface/updatePlotsDistribution.m index 0f0f802..18f1c1c 100644 --- a/functions/interface/updatePlotsDistribution.m +++ b/functions/interface/updatePlotsDistribution.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -78,7 +78,8 @@ case 'free' mymark = {'o-','+-','s-','d-','x-'}; - mycols = [22 140 75;22 87 140;140 22 87;140 75 22;64 64 64]./255; + mycols = [0.8941 0.1020 0.1098;0.3020 0.6863 0.2902; + 0.2157 0.4941 0.7216;0.5961 0.3059 0.6392;0.6510 0.3373 0.1569]; switch nmrproc.T1T2 case 'T1' T = invstd.T1; @@ -102,7 +103,7 @@ % grid grid(ax,'on'); - case {'LU','NNLS'} + case {'LU'} % scale distribution by porosity F = invstd.T1T2f; if sum(F)>0 @@ -145,7 +146,211 @@ % y-limits set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); % y-label - set(get(ax,'YLabel'),'String',ylab2); + set(get(ax,'YLabel'),'String',ylab2); + end + + % x-limits + ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); + set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); + % x-label + set(get(ax,'XLabel'),'String',xlstring); + % grid + grid(ax,'on'); + + case {'NNLS'} + % scale distribution by porosity + F = invstd.T1T2f; + if sum(F)>0 + % apply same scaling to the uncertainty patch + if isfield(data.results.invstd,'uncert') + f_min = data.results.invstd.uncert.interp_f_min; + f_max = data.results.invstd.uncert.interp_f_max; + f_min = (data.invstd.porosity*100).*f_min./sum(F); + f_max = (data.invstd.porosity*100).*f_max./sum(F); + end + F = (data.invstd.porosity*100).*F./sum(F); + + ylims = [0 max(F)*1.05]; + else + ylims = [-1 1]; + end + if data.invstd.porosity == 1 + ylab1 = 'amplitudes [-]'; + ylab2 = 'cumulative amplitudes [-]'; + else + ylab1 = 'water content [vol. %]'; + ylab2 = 'cumulative water content [vol. %]'; + end + % F = data.invstd.porosity.*F./trapz(T,F); + + switch data.info.RTDflag + case 'freq' + if isfield(data.results.invstd,'uncert') + % plot uncertainty + for i = 1:size(invstd.uncert.interp_f,1) + plot(invstd.T1T2me,(data.invstd.porosity*100).*invstd.uncert.interp_f(i,:)./sum(invstd.T1T2f),... + '-','Color',[0.5 0.5 0.5],'LineWidth',1,'Parent',ax); + end +% verts = [invstd.T1T2me f_min'; flipud(invstd.T1T2me) flipud(f_max')]; +% faces = 1:1:size(verts,1); +% patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... +% 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + % adjust y-limits + ylims(2) = max([ylims(2) max(f_max)*1.05]); + end + + plot(invstd.T1T2me,F,'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + % find approx. TLGM amplitude + amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); + stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... + 'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',ylims); + % y-label + set(get(ax,'YLabel'),'String',ylab1); + + case 'cum' + if isfield(data.results.invstd,'uncert') + verts = [invstd.T1T2me cumsum(F)-f_min'; flipud(invstd.T1T2me) flipud(cumsum(F)+f_max')]; + faces = 1:1:size(verts,1); + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + end + plot(invstd.T1T2me,cumsum(F),'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + % find approx. TLGM amplitude + amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); + stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... + 'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); + % y-label + set(get(ax,'YLabel'),'String',ylab2); + end + + % x-limits + ticks = round(log10(min(invstd.T1T2me)) :1: log10(max(invstd.T1T2me))); + set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); + % x-label + set(get(ax,'XLabel'),'String',xlstring); + % grid + grid(ax,'on'); + + case {'MUMO'} + % single distributions for different T, sigma and amplitude + dist = zeros(length(invstd.x)/3,numel(invstd.T1T2me)); + for i = 1:length(invstd.x)/3 + mu = invstd.T(i); + sigma = invstd.S(i); + amp = invstd.E(i); + + tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(invstd.T1T2me) - log(mu))/ sqrt(2)/sigma).^2); + + % scale to amplitude + dist(i,:) = (tmp/sum(tmp)) * amp; + end + + % scale distribution by porosity + F = invstd.T1T2f; + if sum(F)>0 + for i = 1:length(invstd.x)/3 + dist(i,:) = (data.invstd.porosity*100).*dist(i,:)./sum(F); + end + % apply same scaling to the uncertainty patch + if isfield(data.results.invstd,'uncert') + f_min = data.results.invstd.uncert.interp_f_min; + f_max = data.results.invstd.uncert.interp_f_max; + f_min = (data.invstd.porosity*100).*f_min./sum(F); + f_max = (data.invstd.porosity*100).*f_max./sum(F); + end + F = (data.invstd.porosity*100).*F./sum(F); + + ylims = [0 max(F)*1.05]; + else + ylims = [-1 1]; + end + if data.invstd.porosity == 1 + ylab1 = 'amplitudes [-]'; + ylab2 = 'cumulative amplitudes [-]'; + else + ylab1 = 'water content [vol. %]'; + ylab2 = 'cumulative water content [vol. %]'; + end + % F = data.invstd.porosity.*F./trapz(T,F); + + mycols = [0.2157 0.4941 0.7216;0.3020 0.6863 0.2902; + 0.5961 0.3059 0.6392;0.6510 0.3373 0.1569;0.8941 0.1020 0.1098]; + + switch data.info.RTDflag + case 'freq' + if isfield(data.results.invstd,'uncert') + % plot uncertainty + % lines + % for i = 1:size(invstd.TDIST,1) + % plot(invstd.T1T2me,(data.invstd.porosity*100).*invstd.TDIST(i,:)./sum(invstd.T1T2f),... + % '-','Color',[0.5 0.5 0.5],'LineWidth',1,'Parent',ax); + % end + % patch +% TDIST = invstd.TDIST; +% for i = 1:size(invstd.TDIST,1) +% TDIST(i,:) = (data.invstd.porosity*100).*invstd.TDIST(i,:)./sum(invstd.T1T2f); +% end +% TDISTmin = min(TDIST); +% TDISTmax = max(TDIST); +% verts = [nmrproc.t(nmrproc.t>0) s_min; flipud(nmrproc.t(nmrproc.t>0)) flipud(s_max)]; +% faces = 1:1:size(verts,1); +% + verts = [invstd.T1T2me f_min'; flipud(invstd.T1T2me) flipud(f_max')]; + faces = 1:1:size(verts,1); + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + % adjust y-limits + ylims(2) = max([ylims(2) max(f_max)*1.05]); + end + + % plot total RTD + plot(invstd.T1T2me,F,'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + % plot individual RTDs + for i = 1:length(invstd.x)/3 + plot(invstd.T1T2me,dist(i,:),'--','Color',mycols(i,:),... + 'LineWidth',2,'Parent',ax); + end + % find approx. TLGM amplitude + amp = findApproxTlgmAmplitude(invstd.T1T2me,F,invstd.Tlgm); + stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... + 'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',ylims); + % y-label + set(get(ax,'YLabel'),'String',ylab1); + + case 'cum' + if isfield(data.results.invstd,'uncert') + verts = [invstd.T1T2me cumsum(F)-f_min'; flipud(invstd.T1T2me) flipud(cumsum(F)+f_max')]; + faces = 1:1:size(verts,1); + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + end + plot(invstd.T1T2me,cumsum(F),'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + for i = 1:length(invstd.x)/3 + plot(invstd.T1T2me,cumsum(dist(i,:)),'--','Color',mycols(i,:),... + 'LineWidth',2,'Parent',ax); + end + % find approx. TLGM amplitude + amp = findApproxTlgmAmplitude(invstd.T1T2me,cumsum(F),invstd.Tlgm); + stem(invstd.Tlgm,amp,'x-','Color',col.axisL,... + 'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); + % y-label + set(get(ax,'YLabel'),'String',ylab2); end % x-limits @@ -198,7 +403,8 @@ case 'free' mymark = {'o-','+-','s-','d-','x-'}; - mycols = [22 140 75;22 87 140;140 22 87;140 75 22;64 64 64]./255; + mycols = [0.8941 0.1020 0.1098;0.3020 0.6863 0.2902; + 0.2157 0.4941 0.7216;0.5961 0.3059 0.6392;0.6510 0.3373 0.1569]; switch nmrproc.T1T2 case 'T1' T = invstd.T1; @@ -233,7 +439,7 @@ 'LineWidth',2,'Parent',ax); % find approx. RLGM amplitude amp = findApproxTlgmAmplitude(requiv,F,Rlgm); - stem(Rlgm,amp,'x-','Color',[0.3 0.3 0.3],'LineWidth',2,'Tag','TLGM','Parent',ax); + stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); % y-limits set(ax,'YScale','lin','YLim',ylims); @@ -245,7 +451,67 @@ 'LineWidth',2,'Parent',ax); % find approx. RLGM amplitude amp = findApproxTlgmAmplitude(requiv,cumsum(F),Rlgm); - stem(Rlgm,amp,'x-','Color',[0.3 0.3 0.3],'LineWidth',2,'Tag','TLGM','Parent',ax); + stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); + % y-label + set(get(ax,'YLabel'),'String',ylab2); + end + + % x-limits + ticks = floor(log10(min(requiv))) :1: ceil(log10(max(requiv))); + % set(ax,'XScale','log','XLim',[10^(ticks(1)) 10^(ticks(end))],'XTick',10.^ticks); + set(ax,'XScale','log','XLim',[min(requiv) max(requiv)],'XTick',10.^ticks); + % x-label + set(get(ax,'XLabel'),'String',xlstring); + % grid + grid(ax,'on'); + + case {'MUMO'} + % very basic RTD to PSD conversion + requiv = invstd.T1T2me.*rho.*a; + Rlgm = invstd.Tlgm.*rho.*a; + + switch data.info.PSDflag + case 'freq' + if isfield(data.results.invstd,'uncert') + verts = [requiv f_min'; flipud(requiv) flipud(f_max')]; + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + % adjust y-limits + ylims(2) = max([ylims(2) max(f_max)*1.05]); + end + plot(requiv,F,'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + for i = 1:length(invstd.x)/3 + plot(requiv,dist(i,:),'--','Color',mycols(i,:),... + 'LineWidth',2,'Parent',ax); + end + % find approx. RLGM amplitude + amp = findApproxTlgmAmplitude(requiv,F,Rlgm); + stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); + + % y-limits + set(ax,'YScale','lin','YLim',ylims); + % y-label + set(get(ax,'YLabel'),'String',ylab1); + + case 'cum' + if isfield(data.results.invstd,'uncert') + verts = [requiv cumsum(F)-f_min'; flipud(requiv) flipud(cumsum(F)+f_max')]; + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + end + plot(requiv,cumsum(F),'-','Color',col.FIT,... + 'LineWidth',2,'Parent',ax); + for i = 1:length(invstd.x)/3 + plot(requiv,cumsum(dist(i,:)),'--','Color',mycols(i,:),... + 'LineWidth',2,'Parent',ax); + end + % find approx. RLGM amplitude + amp = findApproxTlgmAmplitude(requiv,cumsum(F),Rlgm); + stem(Rlgm,amp,'x-','Color',col.axisL,'LineWidth',2,'Tag','TLGM','Parent',ax); % y-limits set(ax,'YScale','lin','YLim',[0 sum(F)*1.05]); diff --git a/functions/interface/updatePlotsDistributionInfo.m b/functions/interface/updatePlotsDistributionInfo.m index 199f4e8..2e843b2 100644 --- a/functions/interface/updatePlotsDistributionInfo.m +++ b/functions/interface/updatePlotsDistributionInfo.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsGeometryType.m b/functions/interface/updatePlotsGeometryType.m index 3782325..a950982 100644 --- a/functions/interface/updatePlotsGeometryType.m +++ b/functions/interface/updatePlotsGeometryType.m @@ -23,8 +23,8 @@ function updatePlotsGeometryType(ax) % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsJointInversion.m b/functions/interface/updatePlotsJointInversion.m index d1ef415..dca69df 100644 --- a/functions/interface/updatePlotsJointInversion.m +++ b/functions/interface/updatePlotsJointInversion.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsLcurve.m b/functions/interface/updatePlotsLcurve.m index 7a7d18c..eb3847c 100644 --- a/functions/interface/updatePlotsLcurve.m +++ b/functions/interface/updatePlotsLcurve.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsNMR.m b/functions/interface/updatePlotsNMR.m index 89ebbe9..a06ca0c 100644 --- a/functions/interface/updatePlotsNMR.m +++ b/functions/interface/updatePlotsNMR.m @@ -23,8 +23,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsPSD.m b/functions/interface/updatePlotsPSD.m index acb4eec..3c71417 100644 --- a/functions/interface/updatePlotsPSD.m +++ b/functions/interface/updatePlotsPSD.m @@ -25,8 +25,8 @@ % none % % See also: NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updatePlotsSignal.m b/functions/interface/updatePlotsSignal.m index 34f30e1..aa08f3d 100644 --- a/functions/interface/updatePlotsSignal.m +++ b/functions/interface/updatePlotsSignal.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -121,8 +121,10 @@ line(xlims,[0 0],'LineStyle','--','LineWidth',1,'Color','k','Parent',axI); imag_mean = mean(imag(nmrraw.s)); imag_std = std(imag(nmrraw.s)); - yticks = linspace(min(imag(nmrraw.s)),max(imag(nmrraw.s)),3); - set(axI,'XTickLabel','','YTick',yticks,'YTickLabelMode','auto'); +% yticks = linspace(min(imag(nmrraw.s)),max(imag(nmrraw.s)),3); + yticks = [imag_mean-imag_std*2 0 imag_mean+imag_std*2]; + ylim = [imag_mean-imag_std*3 imag_mean+imag_std*3]; + set(axI,'XTickLabel','','YLim',ylim,'YTick',yticks,'YTickLabelMode','auto'); switch loglinx case 'x-axis -> lin' % log axes set(axI,'XScale','log','XLim',xlims); @@ -144,16 +146,47 @@ hold(axE,'on'); % data - plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); - if isfield(data.results,'invstd') - plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); - if nmrproc.noise > 0 - plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... - 'LineWidth',1,'Parent',axE); - else - plot(nmrproc.t,invstd.residual,'Color',col.IM,... - 'LineWidth',1,'Parent',axE); - end + switch data.invstd.invtype + case {'MUMO','NNLS'} + if isfield(data.results,'invstd') && isfield(data.results.invstd,'uncert') + % uncertainty patch created from min max of uncertainty + % data + s_min = data.results.invstd.uncert.interp_s_min; + s_max = data.results.invstd.uncert.interp_s_max; + t = data.results.invstd.uncert.interp_t; + verts = [t(t>0) s_min(t>0); flipud(t(t>0)) flipud(s_max(t>0))]; + faces = 1:1:size(verts,1); + patch('Faces',faces,'Vertices',verts,'FaceColor',[0.64 0.64 0.64],... + 'FaceAlpha',0.75,'EdgeColor','none','Parent',ax); + end + + if isfield(data.results,'invstd') + plot(nmrproc.t,nmrproc.s,'-','Color',col.RE,'LineWidth',1,'Parent',ax); + plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); + if nmrproc.noise > 0 + plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... + 'LineWidth',1,'Parent',axE); + else + plot(nmrproc.t,invstd.residual,'Color',col.IM,... + 'LineWidth',1,'Parent',axE); + end + else + plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); + end + otherwise + if isfield(data.results,'invstd') + plot(nmrproc.t,nmrproc.s,'-','Color',col.RE,'LineWidth',1,'Parent',ax); + plot(invstd.fit_t,invstd.fit_s,'Color',col.FIT,'LineWidth',2,'Parent',ax); + if nmrproc.noise > 0 + plot(nmrproc.t,invstd.residual./nmrproc.e,'Color',col.IM,... + 'LineWidth',1,'Parent',axE); + else + plot(nmrproc.t,invstd.residual,'Color',col.IM,... + 'LineWidth',1,'Parent',axE); + end + else + plot(nmrproc.t,nmrproc.s,'o','Color',col.RE,'LineWidth',1,'Parent',ax); + end end % limits & ticks @@ -199,10 +232,10 @@ switch logliny case 'y-axis -> lin' % log axes ticks = floor(log10(ymin))-1 :1: ceil(log10(ymax)); - set(ax,'YScale','log','YLim',[10^(ticks(1)) ymax*1.05],... + set(ax,'YScale','log','YLim',[10^(ticks(1)) ymax*1.1],... 'YTick',10.^ticks); case 'y-axis -> log' % lin axes - set(ax,'YScale','lin','YLim',[ymin ymax*1.05],... + set(ax,'YScale','lin','YLim',[ymin ymax*1.1],... 'YTickMode','auto'); end end @@ -221,7 +254,15 @@ % legend if isfield(data.results,'invstd') - lgdstr = {'signal','fit'}; + if isfield(data.results,'invstd') && isfield(data.results.invstd,'uncert') + lgdstr = {'uncert','signal','fit'}; + else + lgdstr = {'signal','fit'}; + end +% switch data.invstd.invtype +% case {'MUMO','NNLS'} +% otherwise +% end switch nmrproc.T1T2 case 'T1' lgh = legend(ax,lgdstr,'Location','NorthWest',... diff --git a/functions/interface/updateStatusInformation.m b/functions/interface/updateStatusInformation.m index 6e206fc..d9cf38b 100644 --- a/functions/interface/updateStatusInformation.m +++ b/functions/interface/updateStatusInformation.m @@ -23,8 +23,8 @@ function updateStatusInformation(fig) % none % % See also: NUCLEUSinv, NUCLEUSmod -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/interface/updateToolTips.m b/functions/interface/updateToolTips.m index 733a2de..783ddd6 100644 --- a/functions/interface/updateToolTips.m +++ b/functions/interface/updateToolTips.m @@ -24,8 +24,8 @@ % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -44,8 +44,9 @@ 'Available options:
    ',... 'Mono exp. Mono-exponential fitting.
    ',... 'Several free exp. (2-5) Multi-exponential fitting with up to 5 free relaxation times.
    ',... - 'Multi exp. (LSQ) Multi-exponential fitting with Optimization Toolbox.
    ',... - 'Multi exp. (LU decomp.) Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.

    ',... + 'Multi exp. (LSQ) Multi-exponential fitting using the Optimization Toolbox.
    ',... + 'Multi exp. (LU decomp.) Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.
    ',... + 'Multi modal Multi modal fitting using the Optimization Toolbox.

    ',... 'Depending on the chosen method there are additional options available.

    ',... 'Default value:
    ',... 'Multi exp. (LSQ)
    ']; @@ -64,7 +65,9 @@ 'L-curve Perform the L-curve test to find optimal regularization parameter lambda.
    ',... 'Multi exp. (LU decomp.):
    ',... 'Manual Manual regularization.
    ',... - 'Automatic Automatic regularization.

    ',... + 'Automatic Automatic regularization.
    ',... + 'Multi modal:
    ',... + '1-4 choose how many modes to use.

    ',... 'Default value:
    ',... 'Manual
    ']; case 'lsqnonneg' @@ -73,7 +76,8 @@ 'Mono exp. Mono-exponential fitting.
    ',... 'Several free exp. (2-5) Multi-exponential fitting with up to 5 free relaxation times.
    ',... 'Multi exp. (LSQ) Multi-exponential fitting with Non Negative Least Squares (LSQNONNEG).
    ',... - 'Multi exp. (LU decomp.) Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.

    ',... + 'Multi exp. (LU decomp.) Multi-exponential fitting using a LU decomposition and Matlab''s "\"-operator.
    ',... + 'Multi modal Multi modal fitting using fminsearchbnd.

    ',... 'Depending on the chosen method there are additional options available.

    ',... 'Default value:
    ',... 'Multi exp. (LSQ)
    ']; @@ -92,7 +96,9 @@ 'L-curve Perform the L-curve test to find optimal regularization parameter lambda.
    ',... 'Multi exp. (LU decomp.):
    ',... 'Manual Manual regularization.
    ',... - 'Automatic Automatic regularization.

    ',... + 'Automatic Automatic regularization.
    ',... + 'Multi modal:
    ',... + '1-4 choose how many modes to use.

    ',... 'Default value:
    ',... 'Manual
    ']; end diff --git a/functions/interface/useSignalAsCalibration.m b/functions/interface/useSignalAsCalibration.m index d44e4a2..c536472 100644 --- a/functions/interface/useSignalAsCalibration.m +++ b/functions/interface/useSignalAsCalibration.m @@ -23,8 +23,8 @@ function useSignalAsCalibration(id) % none % % See also: NUCLEUSinv -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/applyGatesToSignal.m b/functions/inversion/applyGatesToSignal.m index fc637ad..b5b4389 100644 --- a/functions/inversion/applyGatesToSignal.m +++ b/functions/inversion/applyGatesToSignal.m @@ -32,8 +32,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/applyRegularization.m b/functions/inversion/applyRegularization.m index 8a52989..eedea83 100644 --- a/functions/inversion/applyRegularization.m +++ b/functions/inversion/applyRegularization.m @@ -37,8 +37,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/createKernelMatrix.m b/functions/inversion/createKernelMatrix.m index 648b586..a77addc 100644 --- a/functions/inversion/createKernelMatrix.m +++ b/functions/inversion/createKernelMatrix.m @@ -1,4 +1,4 @@ -function K = createKernelMatrix(t,T,Tbulk,Tflag,T1IRfac) +function K = createKernelMatrix(t,T,Tbulk,Tdiff,Tflag,T1IRfac) %createKernelMatrix creates a Kernel matrix from signal time vector "t" %and relaxation time vector "T" % @@ -9,6 +9,7 @@ % t - signal time vector % T - relaxation times vector % Tbulk - bulk relaxation time +% Tdiff - diffusion relaxation time % Tflag - 'T1' or 'T2' % T1IRfac - 1 or 2 (Sat. or Inv. Recovery) % @@ -16,7 +17,7 @@ % K - Kernel matrix size(length(t),length(T)) % % Example: -% K = createKernelMatrix(t,T,2,'T1') +% K = createKernelMatrix(t,T,2,3,'T1',1) % % Other m-files required: % none @@ -28,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -42,9 +43,9 @@ %% calculate K switch Tflag case 'T1' - K = 1-T1IRfac.*(exp(-tr./Tr).*exp(-tr./Tbulk)); + K = 1-T1IRfac.*(exp(-tr./Tr).*exp(-tr./Tbulk).*exp(-tr./Tdiff)); case 'T2' - K = exp(-tr./Tr).*exp(-tr./Tbulk); + K = exp(-tr./Tr).*exp(-tr./Tbulk).*exp(-tr./Tdiff); end return diff --git a/functions/inversion/estimateJacobian.m b/functions/inversion/estimateJacobian.m new file mode 100644 index 0000000..b418456 --- /dev/null +++ b/functions/inversion/estimateJacobian.m @@ -0,0 +1,79 @@ +function J = estimateJacobian(f,x) +%estimateJacobian numerically estimates (in a very simple manner) a +%Jacobian at point x for a given function f +% +% Syntax: +% estimateJacobian +% +% Inputs: +% f - function handle +% x - point on function f +% +% Outputs: +% J - Jacobian +% +% Example: +% estimateJacobian(@(x)fcn_fitMultiModal(x,iparam),x); +% +% Other m-files required: +% none +% +% Subfunctions: +% none +% +% MAT-files required: +% none +% +% See also: NUCLEUSinv +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +% define increment +delta = 1e-7*sqrt(norm(x)); +% evaluate function at point x +y = feval(f,x); +% get dimensions of J +n = length(y); +m = length(x); +% initialize J +J = zeros(n,m); +% loop over paramters +for i = 1:m + dx = zeros(1,m); + dx(i) = delta/2; + % evaluate single parameters at x+dx and x-dx + % and divide the differenece by the increment + col = (feval(f,x+dx)-feval(f,x-dx))/delta; + % save result + J(:,i) = col; +end + +return + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2022 Thomas Hiller +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. \ No newline at end of file diff --git a/functions/inversion/estimateUncertainty.m b/functions/inversion/estimateUncertainty.m new file mode 100644 index 0000000..f9206d3 --- /dev/null +++ b/functions/inversion/estimateUncertainty.m @@ -0,0 +1,516 @@ +function [invstd,uncert] = estimateUncertainty(invtype,invstd,iparam,parameter) +%estimateUncertainty calculates pseudo uncertainty estimates for multi +%modal and LSQ inversion results +% +% Syntax: +% estimateUncertainty(invtype,invstd,iparam,parameter) +% +% Inputs: +% invtype - string indicating the inversion method of the optimal +% fit ('NNLS' or 'MUMO') +% invstd - struct holding inversion results of the optimal fit +% iparam - struct holding original inversion settings +% parameter - struct that holds settings: +% uncertMethod : which calculation method to use for +% 'MUMO' the options are 'thresh' and 'ci' for +% 'NNLS' the options are 'RTD_var', 'Lambda', +% 'RMS_bound' and 'RMS_free' +% uncertThresh : threshold for uncertainty search range +% uncertChi2 : stop criteria for the chi2 deviation +% uncertN : number of models to calculate +% uncertMax : total number of unsuccessful attempts +% after which the calculation is stopped +% +% Outputs: +% invstd - same as input struct +% uncert - uncertainty data +% +% Example: +% [invstd] = estimateUncertainty('MUMO',invstd,iparam,uparam) +% +% Other m-files required: +% createKernelMatrix +% displayStatusText +% fitDataLSQ +% getFitErrors +% +% Subfunctions: +% none +% +% MAT-files required: +% none +% +% See also: +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +%% get GUI handle and data +fig = findobj('Tag','INV'); +if ~isempty(fig) + gui = getappdata(fig,'gui'); +else + % this routine will sill call 'displayStatusText' but then the output + % is displayed at the command line + gui = 0; +end + +% get the main parameters +uncertMethod = parameter.uncertMethod; +uncertChi2 = parameter.uncertChi2; +uncertThresh = parameter.uncertThresh; +uncertN = parameter.uncertN; +uncertMax = parameter.uncertMax; + +% original data that was fitted +time = parameter.time; +signal = parameter.signal; + +% kernel matrices for pure (single) E0 estimation and a second one +% that extends the original time vector to "0" -> needed for nicer plots of +% uncertainty towards shorter times +switch iparam.T1T2 + case 'T1' + K0 = createKernelMatrix(10*time(end),invstd.T1T2me',... + iparam.Tb,iparam.Td,'T1',iparam.T1IRfac); + + time0 = [time' 2*time(end) 5*time(end) 10*time(end)]; + K0f = createKernelMatrix(time0,invstd.T1T2me',... + iparam.Tb,iparam.Td,'T1',iparam.T1IRfac); + case 'T2' + K0 = createKernelMatrix(0,invstd.T1T2me',iparam.Tb,... + iparam.Td,'T2',iparam.T1IRfac); + + time0 = [0 1e-6 time(1)/10 time(1)/5 time(1)/3 time(1)/2 time']; + K0f = createKernelMatrix(time0,invstd.T1T2me',iparam.Tb,... + iparam.Td,'T2',iparam.T1IRfac); +end + +% switch depending on inversion method +switch invtype + case 'MUMO' + % data needed from the optimal fit + T = invstd.T; + S = invstd.S; + E = invstd.E; + x = invstd.x; + lb = invstd.lb; + ub = invstd.ub; + ci = invstd.ci; + + % kernel for the original fit needed for comparison of the uncertainty models + K = createKernelMatrix(time,invstd.T1T2me',iparam.Tb,iparam.Td,... + iparam.T1T2,iparam.T1IRfac); + + % counter + count = 0; + countm = 0; + + % initialize variables + TDIST = zeros(uncertN,numel(invstd.T1T2me)); + SINTERP = zeros(numel(time0),uncertN); + E0INTERP = zeros(uncertN,1); + % calculate uncertainty models + while count < uncertN + switch uncertMethod + case 'thresh' + % randomly vary all parameters +- thresh + a = 1-uncertThresh; + b = 1+uncertThresh; + rr = (b-a).*rand(1,length(ci)) + a; + Ti = log(T).*rr(1:3:end); % do it on log-scale + Si = S.*rr(2:3:end); + Ei = E.*rr(3:3:end); + + xi = zeros(size(x)); + xi(1:3:end) = Ti; + xi(2:3:end) = Si; + xi(3:3:end) = Ei; + case 'ci' + % randomly vary parameters within confidence interval + rr = 2.*rand(1,length(ci))-1; + xi = zeros(size(x)); + xi(1:3:end) = log(T); + xi(2:3:end) = S; + xi(3:3:end) = E; + xi = xi+ci'.*rr; + end + + % adjust for bounds if necessary + xi(xiub) = lb(xi>ub); + + % temporary values + Ti = exp(xi(1:3:end)); % transform back to lin-scale + Si = xi(2:3:end); + Ei = xi(3:3:end); + + % create a temporary distribution with the new parameters + TdistI = 0; + for i = 1:numel(T) + tmp = 1./( Si(i)*sqrt(2*pi)).*exp(-((log(invstd.T1T2me') - log(Ti(i)))/ sqrt(2)/Si(i)).^2); + % scale to amplitude + if sum(tmp)>0 + tmp = (tmp/sum(tmp)) * Ei(i); + end + % add the tmp per mu to Tdist + TdistI = TdistI + tmp; + end + % calculate temporary signal(s) + s_interp = K*TdistI'; + s0_interp = K0f*TdistI'; + + % get residuals and error measures + if isfield(iparam,'W') + % when signal gating was used the error estimates need to be adjusted + outI = getFitErrors(signal,s_interp,iparam.noise,iparam.W); + else + outI = getFitErrors(signal,s_interp,iparam.noise); + end + + % check if the temporary chi2 is within the desired limit + if abs(1-outI.chi2/invstd.chi2) <= uncertChi2 + % if YES then keep it + count = count + 1; + % save RTD + TDIST(count,:) = TdistI; + % save signal + SINTERP(:,count) = s0_interp; + % save E0 + E0INTERP(count,1) = K0*TdistI'; + % status bar info + infostring = ['Calculating uncertainty models: ',... + num2str(count),' / ',num2str(uncertN)]; + displayStatusText(gui,infostring); + % reset max counter + countm = 0; + else + % as long as we did not find a model keep counting + if count < 1 + countm = countm + 1; + infostring = ['Trying to find uncertainty model: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + end + end + % after to many unsuccessful attempts STOP + if countm > uncertMax + infostring = ['No uncertainty model found: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + break; + end + end + + % output data + % simple E0 confidence interval + invstd.ciE0 = 2*std(E0INTERP); + + % uncertainty calculation results + uncert.interp_t = time0(:); + uncert.interp_E0 = E0INTERP; + uncert.interp_f = TDIST; + uncert.interp_s = SINTERP; + + % uncertainty patch for fitted signal + uncert.interp_s_min = min(SINTERP,[],2); + uncert.interp_s_max = max(SINTERP,[],2); + + % uncertainty patch for fitted RTD + uncert.interp_f_min = min(TDIST); + uncert.interp_f_max = max(TDIST); + + invstd.uncert = uncert; + + case 'NNLS' + + % 'RTD_var' | 'Lambda' | 'RMS_bound' | 'RMS_free' + % uncertMethod = 'RMS_bound'; + + switch uncertMethod + case 'RTD_var' + % find uncertainty models by varying the optimal RTD bins + % individually + + % uncertN = 5; + % uncertThresh = 0.1; + % uncertChi2 = 0.05; + f_final = invstd.T1T2f; + + % initialize variables + % NOTE: we save 'uncertN' models for each RTD bin + TDIST = zeros(uncertN*numel(invstd.T1T2me),numel(invstd.T1T2me)); + SINTERP = zeros(numel(time0),uncertN*numel(invstd.T1T2me)); + E0INTERP = zeros(uncertN*numel(invstd.T1T2me),1); + % loop over all RTD bins + for i1 = 1:numel(f_final) + count = 0; + while count < uncertN + % start RTD is always the optimal one + x0 = f_final; + % global bounds + lb = zeros(size(x0)); + ub = max(f_final).*3.*ones(size(x0)); + % now draw a random value for the current RTD bin + a = 1-uncertThresh; + b = 1+uncertThresh; + rr = (b-a).*rand(1,1) + a; + % adjust the initial model and bounds accordingly + x0(i1) = f_final(i1)*rr; + lb(i1) = x0(i1); + ub(i1) = x0(i1); + + % save bounds for the LSQ inversion + iparam.bounds.lb = lb; + iparam.bounds.ub = ub; + iparam.bounds.f0 = x0; + + % calculate solution + invtmp = fitDataLSQ(time,signal,iparam); + s0_interp = K0f*invtmp.T1T2f; + + % check if the temporary chi2 and model norm are + % within the desired limits + if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 && ... + abs(1-invtmp.xn/invstd.xn) <= uncertChi2/2 + % if YES then keep it + count = count + 1; + % save RTD + TDIST(i1*uncertN-(uncertN-count),:) = invtmp.T1T2f; + % save signal + SINTERP(:,i1*uncertN-(uncertN-count)) = s0_interp; + % save E0 + E0INTERP(i1*uncertN-(uncertN-count),1) = invtmp.E0; + % status bar info + infostring = ['Calculating uncertainty models: ',... + num2str(count),' / ',num2str(uncertN),... + ' for RTD bin: ',num2str(i1),' / ',num2str(numel(f_final))]; + displayStatusText(gui,infostring); + end + end + end + + case 'Lambda' + % find uncertainty models by varying the optimal + % regularization parameter lambda + + % set regularization to 'manual' just in case + iparam.regMethod = 'manual'; + + % lambda search range + a = log10(invstd.lambda_out/100); + b = log10(invstd.lambda_out*10); + + % counter + count = 0; + countm = 0; + + % initialize variables + TDIST = zeros(uncertN,numel(invstd.T1T2me)); + SINTERP = zeros(numel(time0),uncertN); + E0INTERP = zeros(uncertN,1); + % calculate uncertainty models + while count < uncertN + % draw a random lambda value + rr = (b-a).*rand(1,1) + a; + iparam.lambda = 10^(rr); + % calculate solution + invtmp = fitDataLSQ(time,signal,iparam); + s0_interp = K0f*invtmp.T1T2f; + + % check if temporary chi2 and model norm are within the + % desired limits + if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 &&... + abs(1-invtmp.xn/invstd.xn) <= uncertChi2*10 + % if YES then keep it + count = count + 1; + % save RTD + TDIST(count,:) = invtmp.T1T2f; + % save signal + SINTERP(:,count) = s0_interp; + % save E0 + E0INTERP(count,1) = invtmp.E0; + % status bar info + infostring = ['Calculating uncertainty models: ',... + num2str(count),' / ',num2str(uncertN)]; + displayStatusText(gui,infostring); + % reset max counter + countm = 0; + else +% % update the lambda search range +% if invtmp.xn > invstd.xn % lambda was too small +% % new lower lambda search bound +% a = rr; +% end +% if invtmp.chi2 > invstd.chi2 % lambda was too big +% % new upper lambda search bound +% b = rr; +% end + % as long as we did not find a model keep counting + if count < 1 + countm = countm + 1; + infostring = ['Trying to find uncertainty model: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + end + end + % after to many unsuccessful attempts STOP + if countm > uncertMax + infostring = ['No uncertainty model found: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + break; + end + end + + case 'RMS_bound' + % find uncertainty models by adding random noise (based on + % fit RMS) on the optimal fit to create new "raw data" and + % invert them with the original optimal inversion settings + % select models based on chi2 and model norm bounds + + % set regularization to 'manual' just in case + iparam.regMethod = 'manual'; + + % counter + count = 0; + countm = 0; + + % initialize variables + TDIST = zeros(uncertN,numel(invstd.T1T2me)); + SINTERP = zeros(numel(time0),uncertN); + E0INTERP = zeros(uncertN,1); + % calculate uncertainty models + while count < uncertN + % the original fit + sig1 = invstd.fit_s; + % create some random noise based on original fit RMS + [signalN,~] = addNoiseToSignal(sig1,0,invstd.rms); + % calculate solution + invtmp = fitDataLSQ(time,signalN,iparam); + s0_interp = K0f*invtmp.T1T2f; + + % check if temporary chi2 and model norm are within the + % desired limits + if abs(1-invtmp.chi2/invstd.chi2) <= uncertChi2 &&... + abs(1-invtmp.xn/invstd.xn) <= uncertChi2*10 + % if YES then keep it + count = count + 1; + % save RTD + TDIST(count,:) = invtmp.T1T2f; + % save signal + SINTERP(:,count) = s0_interp; + % save E0 + E0INTERP(count,1) = invtmp.E0; + % status bar info + infostring = ['Calculating uncertainty models: ',... + num2str(count),' / ',num2str(uncertN)]; + displayStatusText(gui,infostring); + % reset max counter + countm = 0; + else + % as long as we did not find a model keep counting + if count < 1 + countm = countm + 1; + infostring = ['Trying to find uncertainty model: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + end + end + % after to many unsuccessful attempts STOP + if countm > uncertMax + infostring = ['No uncertainty model found: ',... + num2str(countm),' / ',num2str(uncertMax)]; + displayStatusText(gui,infostring); + break; + end + end + + case 'RMS_free' + % find uncertainty models by adding random noise (based on + % fit RMS) on the optimal fit to create new "raw data" and + % invert them with the original optimal inversion settings + + % set regularization to 'manual' just in case + iparam.regMethod = 'manual'; + + % initialize variables + TDIST = zeros(uncertN,numel(invstd.T1T2me)); + SINTERP = zeros(numel(time0),uncertN); + E0INTERP = zeros(uncertN,1); + % calculate uncertainty models + for count = 1:uncertN + % the original fit + sig1 = invstd.fit_s; + % create some random noise based on original fit RMS + [signalN,~] = addNoiseToSignal(sig1,0,invstd.rms); + + % calculate solution + invtmp = fitDataLSQ(time,signalN,iparam); + s0_interp = K0f*invtmp.T1T2f; + % save RTD + TDIST(count,:) = invtmp.T1T2f; + % save signal + SINTERP(:,count) = s0_interp; + % save E0 + E0INTERP(count,1) = invtmp.E0; + % status bar info + infostring = ['Calculating uncertainty models: ',... + num2str(count),' / ',num2str(uncertN)]; + displayStatusText(gui,infostring); + end + end + + % output data + % simple E0 confidence interval + invstd.ciE0 = 2*std(E0INTERP); + + % uncertainty calculation results + uncert.interp_t = time0(:); + uncert.interp_E0 = E0INTERP; + uncert.interp_f = TDIST; + uncert.interp_s = SINTERP; + + % uncertainty patch for fitted signal + uncert.interp_s_min = min(SINTERP,[],2); + uncert.interp_s_max = max(SINTERP,[],2); + + % uncertainty patch for fitted RTD + uncert.interp_f_min = min(TDIST); + uncert.interp_f_max = max(TDIST); + + invstd.uncert = uncert; + + otherwise + % nothing to do + uncert = []; +end + +return + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2022 Thomas Hiller +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. \ No newline at end of file diff --git a/functions/inversion/fcn_JointInvfixed.m b/functions/inversion/fcn_JointInvfixed.m index 1cc508f..a819ca4 100644 --- a/functions/inversion/fcn_JointInvfixed.m +++ b/functions/inversion/fcn_JointInvfixed.m @@ -13,6 +13,7 @@ % g : signal vector (all NMR signals) % indt : number of echoes/points per NMR signal % Tb : bulk relaxation time +% Td : diffusion relaxation time % T1T2 : flag between 'T1' or 'T2' inversion % T1IRfac : either '1' or '2' depending on T1 method % SatImbDrain : string indicating if a NMR signal is from @@ -47,8 +48,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -58,6 +59,7 @@ g = iparam.g; indt = iparam.indt; Tb = iparam.Tb; +Td = iparam.Td; T1T2 = iparam.T1T2; T1IRfac = iparam.T1IRfac; SatImbDrain = iparam.SatImbDrain; @@ -93,11 +95,11 @@ switch T1T2 case 'T1' for i=1:length(SV) - Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end case 'T2' for i=1:length(SV) - Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end end @@ -130,21 +132,23 @@ switch T1T2 case 'T1' for i=1:length(SV) - Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) - Kc = Kc + ( squeeze(Amp(i,:,:)) .* ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); + Kc = Kc + ( squeeze(Amp(i,:,:)) .*... + ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); end case 'T2' for i=1:length(SV) - Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) - Kc = Kc + ( squeeze(Amp(i,:,:)) .* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); + Kc = Kc + ( squeeze(Amp(i,:,:)) .*... + exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); end end diff --git a/functions/inversion/fcn_JointInvfree.m b/functions/inversion/fcn_JointInvfree.m index 9c31d0a..bb5215c 100644 --- a/functions/inversion/fcn_JointInvfree.m +++ b/functions/inversion/fcn_JointInvfree.m @@ -11,6 +11,7 @@ % t : augmented time vector % g : augmented signal vector % Tb : bulk relaxation time +% Td : diffusion relaxation time % T1T2 : 'T1' / 'T2' flag % T1IRfac : either '1' or '2' depending on T1 method % L : smoothness constraint @@ -38,8 +39,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -48,6 +49,7 @@ t = iparam.t; g = iparam.g; Tb = iparam.Tb; +Td = iparam.Td; T1T2 = iparam.T1T2; T1IRfac = iparam.T1IRfac; L = iparam.L; @@ -73,11 +75,11 @@ switch T1T2 case 'T1' for i=1:length(SV) - Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end case 'T2' for i=1:length(SV) - Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end end K = Kf; @@ -97,23 +99,23 @@ switch T1T2 case 'T1' for i=1:length(SV) - Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) Kc = Kc + ( squeeze(Amp(i,:,:)) .*... - ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); + ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); end case 'T2' for i=1:length(SV) - Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) Kc = Kc + ( squeeze(Amp(i,:,:)) .*... - exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); + exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); end end % full saturation matrix @@ -161,19 +163,20 @@ case 'cyl' DD = zeros(length(t),length(SV)); for i=1:length(SV) - DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end case {'ang','poly'} % Kernel matrix for full saturation DD = zeros(length(t),length(SV)); for i=1:length(SV) - DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + DD(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation D = zeros(length(t),length(SV)); for i=1:size(SVC,1) - D = D + ( squeeze(Amp(i,:,:)).* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); + D = D + ( squeeze(Amp(i,:,:)).*... + exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); end DD(IPS~=1) = D(IPS~=1); end diff --git a/functions/inversion/fcn_JointInvshape.m b/functions/inversion/fcn_JointInvshape.m index c67baef..9739cfb 100644 --- a/functions/inversion/fcn_JointInvshape.m +++ b/functions/inversion/fcn_JointInvshape.m @@ -15,6 +15,7 @@ % g : signal vector (all NMR signals) % indt : number of echoes/points per NMR signal % Tb : bulk relaxation time +% Td : diffusion relaxation time % T1T2 : flag between 'T1' or 'T2' inversion % T1IRfac : either '1' or '2' depending on T1 method % SatImbDrain : string indicating if a NMR signal is from @@ -49,8 +50,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -60,6 +61,7 @@ g = iparam.g; indt = iparam.indt; Tb = iparam.Tb; +Td = iparam.Td; T1T2 = iparam.T1T2; T1IRfac = iparam.T1IRfac; SatImbDrain = iparam.SatImbDrain; @@ -112,21 +114,23 @@ switch T1T2 case 'T1' for i=1:length(SV) - Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = 1-T1IRfac.*exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) - Kc = Kc + ( squeeze(Amp(i,:,:)) .* ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) )); + Kc = Kc + ( squeeze(Amp(i,:,:)) .*... + ( 1-T1IRfac.*exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) )); end case 'T2' for i=1:length(SV) - Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb)); + Kf(:,i) = exp(-t.*(rhos*SV(i) + 1/Tb + 1/Td)); end % Kernel matrix for partial saturation Kc = zeros(length(t),length(SV)); for i=1:size(SVC,1) - Kc = Kc + ( squeeze(Amp(i,:,:)) .* exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb)) ); + Kc = Kc + ( squeeze(Amp(i,:,:)) .*... + exp(-TT.*(rhos*squeeze(SVC(i,:,:)) + 1/Tb + 1/Td)) ); end end diff --git a/functions/inversion/fcn_fitFreeT1.m b/functions/inversion/fcn_fitFreeT1.m index ad849c9..a3d20df 100644 --- a/functions/inversion/fcn_fitFreeT1.m +++ b/functions/inversion/fcn_fitFreeT1.m @@ -28,8 +28,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/fcn_fitFreeT1_fmin.m b/functions/inversion/fcn_fitFreeT1_fmin.m index 8dc2903..30f66ba 100644 --- a/functions/inversion/fcn_fitFreeT1_fmin.m +++ b/functions/inversion/fcn_fitFreeT1_fmin.m @@ -29,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/fcn_fitFreeT2.m b/functions/inversion/fcn_fitFreeT2.m index d53a1db..ed91709 100644 --- a/functions/inversion/fcn_fitFreeT2.m +++ b/functions/inversion/fcn_fitFreeT2.m @@ -27,8 +27,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/fcn_fitFreeT2_fmin.m b/functions/inversion/fcn_fitFreeT2_fmin.m index 24d3cec..823d1f8 100644 --- a/functions/inversion/fcn_fitFreeT2_fmin.m +++ b/functions/inversion/fcn_fitFreeT2_fmin.m @@ -29,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/fcn_fitFreeT2w.m b/functions/inversion/fcn_fitFreeT2w.m index 4c0b325..f264333 100644 --- a/functions/inversion/fcn_fitFreeT2w.m +++ b/functions/inversion/fcn_fitFreeT2w.m @@ -31,8 +31,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/fcn_fitMultiModal.m b/functions/inversion/fcn_fitMultiModal.m new file mode 100644 index 0000000..154f150 --- /dev/null +++ b/functions/inversion/fcn_fitMultiModal.m @@ -0,0 +1,116 @@ +function F = fcn_fitMultiModal(x,iparam) +%fcn_fitMultiModal is the objective function for N free distribution +%fitting that is minimized with 'lsqnonlin' +% +% Syntax: +% fcn_fitMultiModal(x,iparam) +% +% Inputs: +% x - parameter vector +% x(3*i-2) = mu (relaxation time) +% x(3*i-1) = sigma (width of distribution) +% x(3*i) = amp (relative amplitude) +% iparam - struct that holds additional settings: +% t : time vector +% s : signal vector +% T : relaxation times +% e : noise vector / error weights (optional) +% +% Outputs: +% F - residual +% +% Example: +% F = fcn_fitMultiModal(x,params) +% +% Other m-files required: +% createKernelMatrix +% +% Subfunctions: +% none +% +% MAT-files required: +% none +% +% See also: +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +% get all neccessary parameters +flag = iparam.flag; +T1IRfac = iparam.T1IRfac; +nModes = iparam.nModes; +t = iparam.t; +s = iparam.s; +T = iparam.T; +Tb = iparam.Tb; +Td = iparam.Td; + +% get the global (combined) RTD distribution +Tdist = 0; +for i = 1:nModes + mu = exp(x(3*i-2)); + sigma = x(3*i-1); + amp = x(3*i); + + % get the temporary RTD with current mu and sigma + tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(T) - log(mu))/ sqrt(2)/sigma).^2); + + % scale the temporary RDT to current amplitude + tmp = (tmp/sum(tmp)) * amp; + + % add the current temporary RTD to the global Tdist + Tdist = Tdist + tmp; +end + +% get the kernel function to calculate the signal out of the global RTD +K = createKernelMatrix(t,T,Tb,Td,flag,T1IRfac); +si = K*Tdist'; + + +% get error weights if available +if isfield(iparam,'e') + e = iparam.e; +else + e = ones(size(s)); +end + +% change output depending on solver +switch iparam.optim + case 'on' % lsqnonlin + % scale the residual + F = e.*(si - s); + case 'off' % fminsearchbnd + F = e.*(si - s); + SSE = sum(F.^2); + F = SSE; +end + +return + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2022 Thomas Hiller +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. \ No newline at end of file diff --git a/functions/inversion/fitDataFree.m b/functions/inversion/fitDataFree.m index 257e310..34e8161 100644 --- a/functions/inversion/fitDataFree.m +++ b/functions/inversion/fitDataFree.m @@ -54,8 +54,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -85,8 +85,9 @@ % start values for E and T x0 = zeros(1,2*nExp); for i = 1:nExp - x0(2*i-1) = max(signal)/nExp; - x0(2*i) = i*max(t)/4; + x0(2*i-1) = i*max(signal)/nExp; + x0(2*i) = i*max(t)/nExp; +% x0(2*i) = exp(i*max(log(t))/nExp); end switch parameter.optim @@ -112,10 +113,10 @@ iparam.t = t; iparam.s = s; - [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitFreeT2w(x,iparam),... - x0,zeros(size(x0)),[],options); - % [x,~,~,~,output,~,jacobian] = lsqcurvefit(@fcn_fitFreeT2,... - % x0,t,s,zeros(size(x0)),[],options); +% [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitFreeT2w(x,iparam),... +% x0,zeros(size(x0)),[],options); + [x,~,~,~,output,~,jacobian] = lsqcurvefit(@fcn_fitFreeT2,... + x0,t,s,zeros(size(x0)),[],options); end case 'off' % solver options diff --git a/functions/inversion/fitDataLSQ.m b/functions/inversion/fitDataLSQ.m index 373a62c..5f8f5c9 100644 --- a/functions/inversion/fitDataLSQ.m +++ b/functions/inversion/fitDataLSQ.m @@ -14,6 +14,7 @@ % T1T2 : flag between 'T1' or 'T2' inversion % T1IRfac : either '1' or '2' depending on T1 method % Tb : bulk relaxation time +% Td : diffusion relaxation time % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] % regMethod: 'manual', 'gcv_tikh', 'gcv_trunc', % 'gcv_damp', 'discrep' @@ -23,6 +24,8 @@ % principle % W : error weighting matrix (optional) % solver : LSQ solver ('lsqlin' or 'lsqnonneg') +% bounds : predefined lower and upper bounds and start +% model (optional and only for 'lsqlin') % % Outputs: % fitdata - struct that holds the inversion results: @@ -30,7 +33,7 @@ % fit_s : signal vector for plotting % T1T2me : relaxation time values % T1T2f : relaxation time spectrum -% Tlgm : T logmean +% Tlgm : T log-mean % E0 : initial amplitude at t=0 (T2) or t=inf (T1) % resnorm : residual norm % residual : vector of residuals @@ -64,8 +67,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -86,6 +89,7 @@ flag = parameter.T1T2; % T1/T2 switch T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor Tb = parameter.Tb; % bulk relaxation time +Td = parameter.Td; % diffusion relaxation time tstart = parameter.Tint(1); % log10 value tend = parameter.Tint(2); % log10 value N = parameter.Tint(3); % N per decade @@ -98,13 +102,23 @@ T1T2me = logspace(tstart,tend,(tend-tstart)*N); % create the Kernel matrix for inversion -K = createKernelMatrix(t,T1T2me,Tb,flag,T1IRfac); +K = createKernelMatrix(t,T1T2me,Tb,Td,flag,T1IRfac); if strcmp(parameter.solver,'lsqlin') - % initial T2 amplitudes - f0 = zeros(size(T1T2me)); - f0_lb = f0; - f0_ub = 1.5*max(g)*ones(size(T1T2me)); + if isfield(parameter,'bounds') + f0 = parameter.bounds.f0; + f0_lb = parameter.bounds.lb; + f0_ub = parameter.bounds.ub; + else + % initial T2 amplitudes + f0 = zeros(size(T1T2me)); + f0_lb = f0; + f0_ub = 1.5*max(g)*ones(size(T1T2me)); + % T2 measurements: cut everything < first considered echo to zero + f0_ub(T1T2me < time(1)/5) = 0; + % T1 measurements: cut everything > 5*time of last point in SR-curve + % f0_ub(T1T2me > time(end)) = 0; + end end % derivative matrix @@ -138,14 +152,19 @@ options.Display = parameter.info; options.OptimalityTolerance = 1e-18; options.StepTolerance = 1e-18; - [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... - f0_lb,f0_ub,[],options); + if isfield(parameter,'bounds') + [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... + f0_lb,f0_ub,f0,options); + else + [f,~,~,~,~,~] = lsqlin(KK,gg,[],[],[],[],... + f0_lb,f0_ub,[],options); + end case 'lsqnonneg' options = optimset('Display',parameter.info,'TolX',1e-12); [f,~,~,~,~,~] = lsqnonneg(KK,gg,options); end -% rescale f so that the sum(f)= unscaled E0 +% rescale f so that the sum(f) = unscaled E0 f = (f.*maxS); % the 'inverted' signal @@ -177,13 +196,11 @@ % get "initial" value E0 if strcmp(flag,'T1') - t2 = 10*time(end); - K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); + K0 = createKernelMatrix(10*time(end),T1T2me,Tb,Td,flag,T1IRfac); elseif strcmp(flag,'T2') - t2 = 0; - K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); + K0 = createKernelMatrix(0,T1T2me,Tb,Td,flag,T1IRfac); end -E0 = K2*f; +E0 = K0*f; % output struct fitdata.fit_t = time(:); @@ -192,6 +209,7 @@ fitdata.T1T2f = f(:); fitdata.Tlgm = getTLogMean(T1T2me,f); fitdata.E0 = E0; +fitdata.ciE0 = NaN; fitdata.resnorm = out.resnorm; fitdata.residual = out.residual; fitdata.chi2 = out.chi2; diff --git a/functions/inversion/fitDataLUdecomp.m b/functions/inversion/fitDataLUdecomp.m index aeb93e8..579111d 100644 --- a/functions/inversion/fitDataLUdecomp.m +++ b/functions/inversion/fitDataLUdecomp.m @@ -12,6 +12,7 @@ % T1T2 : flag between 'T1' or 'T2' inversion % T1IRfac : either '1' or '2' depending on T1 method % Tb : bulk relaxation time +% Td : diffusion relaxation time % Tint : relaxation times [log10(tmin) log10(tmax) Ndec] % Lorder : smoothness constraint (derivative matrix) % lambda : regularization parameter (if -1 automatic @@ -54,8 +55,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % NOTE: I harvested this routine partly from the Internet but forgot where % I found the routines ... so there is no warranty at all @@ -77,6 +78,7 @@ flag = parameter.T1T2; % T1/T2 switch T1IRfac = parameter.T1IRfac; % T1 Sat/Inv Recovery factor Tb = parameter.Tb; % bulk relaxation time +Td = parameter.Td; % diffusion relaxation time tstart = parameter.Tint(1); % log10 value tend = parameter.Tint(2); % log10 value N = parameter.Tint(3); % N per decade @@ -88,7 +90,7 @@ T1T2me = logspace(tstart,tend,(tend-tstart)*N); % create the Kernel matrix -K = createKernelMatrix(t,T1T2me,Tb,flag,T1IRfac); +K = createKernelMatrix(t,T1T2me,Tb,Td,flag,T1IRfac); % calculate reg matrix H m = length(T1T2me); @@ -165,10 +167,10 @@ % get "initial" value E0 if strcmp(flag,'T1') t2 = 10*time(end); - K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); + K2 = createKernelMatrix(t2,T1T2me,Tb,Td,flag,T1IRfac); elseif strcmp(flag,'T2') t2 = 0; - K2 = createKernelMatrix(t2,T1T2me,Tb,flag,T1IRfac); + K2 = createKernelMatrix(t2,T1T2me,Tb,Td,flag,T1IRfac); end E0 = K2*f; diff --git a/functions/inversion/fitDataMultiModal.m b/functions/inversion/fitDataMultiModal.m new file mode 100644 index 0000000..9e776e0 --- /dev/null +++ b/functions/inversion/fitDataMultiModal.m @@ -0,0 +1,299 @@ +function [fitdata] = fitDataMultiModal(time,signal,parameter,nModes) +%fitDataMultiModal is a control routine that uses either 'lsqnonlin' or +%'fminsearchbnd' to fit NMR data with 'nModes' multi modal relaxation time +%distributions (T1 or T2) +% +% Syntax: +% fitDataMultiModal(time,signal,flag,parameter,nExp) +% +% InputsR: +% time - time vector +% signal - NMR signal vector (no complex data allowed!) +% parameter - struct that holds additional settings: +% T1T2 : flag between 'T1' or 'T2' inversion +% T1IRfac : either '1' or '2' depending on T1 method +% Tb : bulk relaxation time +% Td : diffusion relaxation time +% Tint : relaxation times [log10(tmin) log10(tmax) Ndec] +% noise : noise level needed for 'discrep' discrepancy +% principle +% optim : 'on' or 'off' (Optimization Toolbox) +% W : error weighting matrix (optional) +% nModes - No. of free distributions +% +% Outputs: +% fitdata - struct that holds the inversion results: +% fit_t : time vector for plotting +% fit_s : signal vector for plotting +% T1T2me : relaxation time values +% T1T2f : relaxation time spectrum +% Tlgm : T log-mean +% E0 : initial amplitude at t=0 (T2) or t=max (T1) +% ciE : E0 confidence interval (NaN as placeholder) +% resnorm : residual norm +% residual: vector of residuals +% errnorm : error norm +% lambda_out : dummy 0 +% rms : RMS error +% chi2 : chi square error +% ci : confidence interval +% T : relaxation times per mode +% S : width per mode +% E : amplitude per mode; +% x : all parameters combined +% lb : lower bounds +% ub : upper bounds +% output : output struct (output from 'lsqnonlin' or +% 'fminsearchbnd') +% +% Example: +% [fitdata] = fitDataMultiModal(t,s,parameter,2) +% +% Other m-files required: +% createKernelMatrix +% estimateJacobian +% fcn_fitMultiModal +% fitDataFree +% fminsearchbnd +% getFitErrors +% getFitFreeJacobian +% getConfInterval +% lsqnonlin (Optimization Toolbox) +% +% Subfunctions: +% none +% +% MAT-files required: +% none +% +% See also: +% Author: see AUTHORS.md +% email: see AUTHORS.md +% License: MIT License (at end) + +%------------- BEGIN CODE -------------- + +% make column vector +t = time(:); +s = signal(:); + +% error weights after gating +if isfield(parameter,'W') + e = diag(parameter.W); + iparam.e = sqrt(e); +end + +% get the input parameters +% T1/T2 switch +flag = parameter.T1T2; +% T1 Sat/Inv Recovery factor +T1IRfac = parameter.T1IRfac; +% bulk relaxation time +Tb = parameter.Tb; +% diffusion relaxation time +Td = parameter.Td; +% smallest value in RTD (log10 value) +tstart = parameter.Tint(1); +% largest value in RTD (log10 value) +tend = parameter.Tint(2); +% N per decade in RTD +N = parameter.Tint(3); + +% get boundary values for mu, sigma and amp by first applying a free +% exponential fit +param0.T1IRfac = T1IRfac; +param0.noise = parameter.noise; +param0.optim = parameter.optim; +if isfield(parameter,'W') + param0.W = parameter.W; +end +% free exponential fit to get some reasonable start values +invstd0 = fitDataFree(t,s,flag,param0,nModes); + +% start values for E and T +x0 = zeros(1,3*nModes); +lb = zeros(1,3*nModes); +ub = zeros(1,3*nModes); +for i = 1:nModes + % initial values for T, sigma and E + x0(3*i-2) = log(invstd0.x(2*i)); + x0(3*i-1) = 1; + x0(3*i) = invstd0.x(2*i-1); + + % lower bounds for T, sigma and E + lb(3*i-2) = log(1e-6);%log(invstd0.x(2*i)*0.8);%log(invstd0.x(2*i) - 10*invstd0.ci(2*i)); + lb(3*i-1) = 0.01; + lb(3*i) = invstd0.x(2*i-1)*0.8;%invstd0.x(2*i-1) - 10*invstd0.ci(2*i-1); + + % upper bounds for T, sigma and E + ub(3*i-2) = log(10);%log(invstd0.x(2*i) + 50*invstd0.ci(2*i)); + ub(3*i-1) = 3.5; + ub(3*i) = max(invstd0.E0)*1.1;%invstd0.x(2*i-1) + 50*invstd0.ci(2*i-1); +end + +% switch off output if no option is given via 'parameter' +if ~isfield(parameter,'info') + parameter.info = 'off'; +end + +% create the relaxation time vector +T1T2me = logspace(tstart,tend,(tend-tstart)*N); + +% just needed for debugging the Optimization Toolbox availability +% parameter.optim = 'off'; + +switch parameter.optim + case 'on' + % solver options + options = optimoptions('lsqnonlin'); + options.Algorithm = 'levenberg-marquardt'; + options.Display = parameter.info; + options.OptimalityTolerance = 1e-12; + options.StepTolerance = 1e-12; + + iparam.optim = parameter.optim; + iparam.flag = flag; + iparam.T1IRfac = T1IRfac; + iparam.nModes = nModes; + iparam.t = t; + iparam.s = s; + iparam.T = T1T2me; + iparam.Tb = Tb; + iparam.Td = Td; + [x,~,~,~,output,~,jacobian] = lsqnonlin(@(x)fcn_fitMultiModal(x,iparam),... + x0,lb,ub,options); + + case 'off' + % solver options + options = optimset('Display',parameter.info,'MaxFunEvals',10^6,... + 'MaxIter',5000,'TolFun',1e-12,'TolX',1e-12); + + iparam.optim = parameter.optim; + iparam.flag = flag; + iparam.T1IRfac = T1IRfac; + iparam.nModes = nModes; + iparam.t = t; + iparam.s = s; + iparam.T = T1T2me; + iparam.Tb = Tb; + iparam.Td = Td; + [x,~,~,output] = fminsearchbnd(@(x) fcn_fitMultiModal(x,iparam),... + x0,lb,ub,options); + + % get Jacobian + % therefore we need to switch the 'optim' on to get the correct + % output of 'fcn_fitMultiModal' + iparam.optim = 'on'; + jacobian = estimateJacobian(@(x)fcn_fitMultiModal(x,iparam),x); +end + +% assemble the final RTD +Tdist = 0; +for i = 1:length(x)/3 + mu = exp(x(3*i-2)); % T + sigma = x(3*i-1); % S + amp = x(3*i); % E + + tmp = 1./( sigma*sqrt(2*pi)).*exp(-((log(T1T2me) - log(mu))/ sqrt(2)/sigma).^2); + + % scale to amplitude + tmp = (tmp/sum(tmp)) * amp; + % add the tmp per mu to Tdist + Tdist = Tdist + tmp; +end +f = Tdist; +% the fitted signal +K = createKernelMatrix(t,T1T2me,Tb,Td,flag,1); +si = K*f'; + +% get the fit +fit_t = t; +fit_s = si; + +% get residuals and error measures +if isfield(parameter,'W') + % when signal gating was used the error estimates need to be adjusted + out = getFitErrors(signal,fit_s,parameter.noise,parameter.W); +else + out = getFitErrors(signal,fit_s,parameter.noise); +end + +% confidence interval +ci = getConfInterval(out.resnorm,jacobian,0.05); + +% sort the relaxation times in ascending order and adjust the confidence +% interval accordingly +T = exp(x(1:3:end)); +S = x(2:3:end); +E = x(3:3:end); +[T,idx] = sort(T); +S = S(idx); +E = E(idx); +ciT = ci(1:3:end); +ciS = ci(2:3:end); +ciE = ci(3:3:end); +ciT = ciT(idx); +ciS = ciS(idx); +ciE = ciE(idx); +ci(1:3:end) = ciT; +ci(2:3:end) = ciS; +ci(3:3:end) = ciE; + +% get "initial" value E0 +switch flag + case 'T1' + K0 = createKernelMatrix(10*time(end),T1T2me,Tb,Td,flag,T1IRfac); + case 'T2' + K0 = createKernelMatrix(0,T1T2me,Tb,Td,flag,T1IRfac); +end +E0 = K0*f'; + +% output struct +fitdata.fit_t = fit_t; +fitdata.fit_s = fit_s; +fitdata.T1T2me = T1T2me(:); +fitdata.T1T2f = f(:); +fitdata.Tlgm = getTLogMean(T1T2me,f); +fitdata.E0 = E0; +fitdata.ciE0 = NaN; +fitdata.resnorm = out.resnorm; +fitdata.residual = out.residual; +fitdata.errornorm = out.errnorm1; +fitdata.lambda_out = 0; +fitdata.rms = out.rms; +fitdata.chi2 = out.chi2; +fitdata.ci = ci; +fitdata.T = T; +fitdata.S = S; +fitdata.E = E; +fitdata.x = x; +fitdata.lb = lb; +fitdata.ub = ub; +fitdata.output = output; + +return + +%------------- END OF CODE -------------- + +%% License: +% MIT License +% +% Copyright (c) 2022 Thomas Hiller +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be included in all +% copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +% SOFTWARE. \ No newline at end of file diff --git a/functions/inversion/getChi2.m b/functions/inversion/getChi2.m index ebad62c..b7b672d 100644 --- a/functions/inversion/getChi2.m +++ b/functions/inversion/getChi2.m @@ -25,8 +25,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getConfInterval.m b/functions/inversion/getConfInterval.m index f55869a..6a6e8ba 100644 --- a/functions/inversion/getConfInterval.m +++ b/functions/inversion/getConfInterval.m @@ -31,8 +31,8 @@ % % See also: "Parameter Estimation and Inverse Problems", 2nd Ed. % by Aster et. al p.32 ff -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getFitErrors.m b/functions/inversion/getFitErrors.m index 354202f..1852f40 100644 --- a/functions/inversion/getFitErrors.m +++ b/functions/inversion/getFitErrors.m @@ -34,8 +34,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getFitFreeJacobian.m b/functions/inversion/getFitFreeJacobian.m index 0c0237d..88bf645 100644 --- a/functions/inversion/getFitFreeJacobian.m +++ b/functions/inversion/getFitFreeJacobian.m @@ -28,8 +28,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getLambdaFromLCurve.m b/functions/inversion/getLambdaFromLCurve.m index e6a1901..6692d30 100644 --- a/functions/inversion/getLambdaFromLCurve.m +++ b/functions/inversion/getLambdaFromLCurve.m @@ -26,8 +26,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getLambdaFromRMS.m b/functions/inversion/getLambdaFromRMS.m index c101b7a..d5f1770 100644 --- a/functions/inversion/getLambdaFromRMS.m +++ b/functions/inversion/getLambdaFromRMS.m @@ -27,8 +27,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getStudentInvCDF.m b/functions/inversion/getStudentInvCDF.m index 26787e0..44b086d 100644 --- a/functions/inversion/getStudentInvCDF.m +++ b/functions/inversion/getStudentInvCDF.m @@ -28,8 +28,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/inversion/getTLogMean.m b/functions/inversion/getTLogMean.m index da658dc..dc772c5 100644 --- a/functions/inversion/getTLogMean.m +++ b/functions/inversion/getTLogMean.m @@ -26,8 +26,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/addNoiseToSignal.m b/functions/modeling/addNoiseToSignal.m index 6c7c265..9e69f4b 100644 --- a/functions/modeling/addNoiseToSignal.m +++ b/functions/modeling/addNoiseToSignal.m @@ -27,8 +27,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/createPSD.m b/functions/modeling/createPSD.m index 4dde474..795f340 100644 --- a/functions/modeling/createPSD.m +++ b/functions/modeling/createPSD.m @@ -29,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getAngularityFactor.m b/functions/modeling/getAngularityFactor.m index 110494c..a98f48f 100644 --- a/functions/modeling/getAngularityFactor.m +++ b/functions/modeling/getAngularityFactor.m @@ -24,8 +24,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getAreaFactor.m b/functions/modeling/getAreaFactor.m index 0260b62..cf50a4b 100644 --- a/functions/modeling/getAreaFactor.m +++ b/functions/modeling/getAreaFactor.m @@ -25,8 +25,8 @@ % % See also: % Tuller & Or, 2001, WRR, Vol. 37(5), 1257-1276 -% Author: Stepahn Costabel -% email: stephan.costabel[at]bgr.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getConduct.m b/functions/modeling/getConduct.m index d3e1de2..02b49a7 100644 --- a/functions/modeling/getConduct.m +++ b/functions/modeling/getConduct.m @@ -32,8 +32,8 @@ % See also: % Tuller & Or, 2001, WRR, Vol. 37(5), 1257-1276 % Patzek & Silin, 2001, JColIntSci, Vol. 236(2), 295-304 -% Author: Stepahn Costabel -% email: stephan.costabel[at]bgr.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getConstants.m b/functions/modeling/getConstants.m index 46315f5..71f37a2 100644 --- a/functions/modeling/getConstants.m +++ b/functions/modeling/getConstants.m @@ -24,8 +24,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getCornerNMRparameter.m b/functions/modeling/getCornerNMRparameter.m index 57c1477..3e4fb89 100644 --- a/functions/modeling/getCornerNMRparameter.m +++ b/functions/modeling/getCornerNMRparameter.m @@ -32,8 +32,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getCornerSaturation.m b/functions/modeling/getCornerSaturation.m index a6aea23..07cc5ad 100644 --- a/functions/modeling/getCornerSaturation.m +++ b/functions/modeling/getCornerSaturation.m @@ -27,8 +27,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getCriticalPressure.m b/functions/modeling/getCriticalPressure.m index 1a292dc..77b687d 100644 --- a/functions/modeling/getCriticalPressure.m +++ b/functions/modeling/getCriticalPressure.m @@ -29,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getGeometryParameter.m b/functions/modeling/getGeometryParameter.m index b577c02..0fba4a6 100644 --- a/functions/modeling/getGeometryParameter.m +++ b/functions/modeling/getGeometryParameter.m @@ -38,8 +38,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getNMRSignal.m b/functions/modeling/getNMRSignal.m index 15a0146..ece93be 100644 --- a/functions/modeling/getNMRSignal.m +++ b/functions/modeling/getNMRSignal.m @@ -9,6 +9,7 @@ % nmr - structure containing fields: % t : time vector [s] % Tb : bulk relaxation time [s] +% Td : diffusion relaxation time [s] % rho : surface relaxivity [m/s] % type - either 'cyl', 'ang' and 'poly' % SatData - structure (output from 'getSaturationfromPressure') @@ -40,8 +41,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- @@ -56,6 +57,7 @@ % get general parameters t = nmr.t; Tb = nmr.Tb; +Td = nmr.Td; rho = nmr.rho; %% some informative wait-bar ;-) @@ -88,13 +90,13 @@ % for all time steps for j = 1:length(t) EiT1(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... - (1-exp(-t(j) .* (1./Tb+rho.*SVi(p,:)) ))); + (1-exp(-t(j) .* (1./Td+1./Tb+rho.*SVi(p,:)) ))); EiT2(p,j) = sum(SatData.Si(p,:) .* psdData.psd .* ... - exp(-t(j) .* (1./Tb+rho.*SVi(p,:)) ) ); + exp(-t(j) .* (1./Td+1./Tb+rho.*SVi(p,:)) ) ); EdT1(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... - (1-exp(-t(j) .* (1./Tb+rho.*SVd(p,:)) ))); + (1-exp(-t(j) .* (1./Td+1./Tb+rho.*SVd(p,:)) ))); EdT2(p,j) = sum(SatData.Sd(p,:) .* psdData.psd .* ... - exp(-t(j) .* (1./Tb+rho.*SVd(p,:)) ) ); + exp(-t(j) .* (1./Td+1./Tb+rho.*SVd(p,:)) ) ); end if wbopts.show waitbar(p / steps,hwb,['processing ... ',num2str(p),' / ',... @@ -128,31 +130,31 @@ for j = 1:numel(psdData.r) % --- imbibition --- if SatData.isfullsati(p,j) == 1 % if fully saturated -> Ampl = 1 - sigiT1(j,:) = (1-exp(-t .* (1./Tb + rho.*SVi(j,1)) )); - sigiT2(j,:) = exp(-t .* (1./Tb + rho.*SVi(j,1)) ); + sigiT1(j,:) = (1-exp(-t .* (1./Td + 1./Tb + rho.*SVi(j,1)) )); + sigiT2(j,:) = exp(-t .* (1./Td + 1./Tb + rho.*SVi(j,1)) ); else % partially saturated pore -> account for corners for jj = 1:Ncorners Ampl = Aai(j,jj) / SatData.A0(j); sigiT1(j,:) = sigiT1(j,:) + (Ampl * (1-exp(-t .* ... - (1./Tb + rho.*SVi(j,jj)))) ); + (1./Td + 1./Tb + rho.*SVi(j,jj)))) ); sigiT2(j,:) = sigiT2(j,:) + (Ampl * exp(-t .* ... - (1./Tb + rho.*SVi(j,jj))) ); + (1./Td + 1./Tb + rho.*SVi(j,jj))) ); end end % --- drainage --- if SatData.isfullsatd(p,j) == 1 % if fully saturated -> Ampl = 1 - sigdT1(j,:) = (1-exp(-t .* (1./Tb + rho.*SVd(j,1)) )); - sigdT2(j,:) = exp(-t .* (1./Tb + rho.*SVd(j,1)) ); + sigdT1(j,:) = (1-exp(-t .* (1./Td + 1./Tb + rho.*SVd(j,1)) )); + sigdT2(j,:) = exp(-t .* (1./Td + 1./Tb + rho.*SVd(j,1)) ); else % partially saturated pore -> account for corners for jj = 1:Ncorners Ampl = Aad(j,jj) / SatData.A0(j); sigdT1(j,:) = sigdT1(j,:) + (Ampl * (1-exp(-t .* ... - (1./Tb + rho.*SVd(j,jj)))) ); + (1./Td + 1./Tb + rho.*SVd(j,jj)))) ); sigdT2(j,:) = sigdT2(j,:) + (Ampl * exp(-t .* ... - (1./Tb + rho.*SVd(j,jj))) ); + (1./Td + 1./Tb + rho.*SVd(j,jj))) ); end end diff --git a/functions/modeling/getNMRTimeVector.m b/functions/modeling/getNMRTimeVector.m index 04b7ec9..450fbbe 100644 --- a/functions/modeling/getNMRTimeVector.m +++ b/functions/modeling/getNMRTimeVector.m @@ -35,8 +35,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getPartialSaturationMatrix.m b/functions/modeling/getPartialSaturationMatrix.m index 702eac9..e2aafa2 100644 --- a/functions/modeling/getPartialSaturationMatrix.m +++ b/functions/modeling/getPartialSaturationMatrix.m @@ -28,8 +28,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getPointCoordinates.m b/functions/modeling/getPointCoordinates.m index f3cddd8..a19b60d 100644 --- a/functions/modeling/getPointCoordinates.m +++ b/functions/modeling/getPointCoordinates.m @@ -25,8 +25,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getPressureRangeFromPSD.m b/functions/modeling/getPressureRangeFromPSD.m index 375faa4..420b434 100644 --- a/functions/modeling/getPressureRangeFromPSD.m +++ b/functions/modeling/getPressureRangeFromPSD.m @@ -27,8 +27,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getRaRaEps.m b/functions/modeling/getRaRaEps.m index 8068cb4..e67b490 100644 --- a/functions/modeling/getRaRaEps.m +++ b/functions/modeling/getRaRaEps.m @@ -27,8 +27,8 @@ % % See also: % Ransohoff & Radke, 1988, JColIntSci, Vol. 121(2), 392-401 -% Author: Stepahn Costabel -% email: stephan.costabel[at]bgr.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getSaturationFromPressure.m b/functions/modeling/getSaturationFromPressure.m index 88f0323..d382d0f 100644 --- a/functions/modeling/getSaturationFromPressure.m +++ b/functions/modeling/getSaturationFromPressure.m @@ -38,8 +38,8 @@ % none % % See also: -% Author: Thomas Hiller, Stephan Costabel -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getSaturationFromPressureBatch.m b/functions/modeling/getSaturationFromPressureBatch.m index 8b54955..3176cd8 100644 --- a/functions/modeling/getSaturationFromPressureBatch.m +++ b/functions/modeling/getSaturationFromPressureBatch.m @@ -46,8 +46,8 @@ % none % % See also: -% Author: Thomas Hiller, Stephan Costabel -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getSaturationLevelData.m b/functions/modeling/getSaturationLevelData.m index b065f0a..f84b538 100644 --- a/functions/modeling/getSaturationLevelData.m +++ b/functions/modeling/getSaturationLevelData.m @@ -29,8 +29,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/functions/modeling/getShapeFactor.m b/functions/modeling/getShapeFactor.m index a49856c..2953b1e 100644 --- a/functions/modeling/getShapeFactor.m +++ b/functions/modeling/getShapeFactor.m @@ -24,8 +24,8 @@ % none % % See also: -% Author: Thomas Hiller -% email: thomas.hiller[at]leibniz-liag.de +% Author: see AUTHORS.md +% email: see AUTHORS.md % License: MIT License (at end) %------------- BEGIN CODE -------------- diff --git a/nucleusinv_gui.png b/nucleusinv_gui.png index 58d0b20629856f35fec1ab3b1638932424cac074..f773e1987692e94547085dbd38e63f0b67bc4353 100644 GIT binary patch literal 124974 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 LMW$Tpb-@1vq)y9G literal 131322 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 INSTKGA2dSX*8l(j diff --git a/nucleusmod_gui.png b/nucleusmod_gui.png index cf771cae921977e1f79f92df528356a9c1618ffd..024b3024f6feff1f66d410197b32427d2e29b4eb 100644 GIT binary patch delta 90201 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 delta 84196 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